summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2019-04-04 19:47:41 +0200
committerLaszlo Agocs <laszlo.agocs@qt.io>2019-04-09 18:53:22 +0000
commit0fde097e31e908b18db68f05e2122c5280d36304 (patch)
treefef45d90f164983dd49130a2d14e86055f84f1da
parent8c10e6cb759f8d4acdcf54a25b70530d4c936bf6 (diff)
Import the Qt Shader Tools module
...from https://git.qt.io/laagocs/qtshadertools/ Change-Id: I3aa655da81978e13016f8150634e278448015709 Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
-rw-r--r--.qmake.conf6
-rw-r--r--qtshadertools.pro3
-rw-r--r--src/3rdparty/SPIRV-Cross/GLSL.std.450.h131
-rw-r--r--src/3rdparty/SPIRV-Cross/LICENSE202
-rw-r--r--src/3rdparty/SPIRV-Cross/Makefile41
-rw-r--r--src/3rdparty/SPIRV-Cross/qt_attribution.json16
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv.h1213
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv.hpp1216
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_cfg.cpp226
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_cfg.hpp149
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_common.hpp1518
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_cpp.cpp547
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_cpp.hpp87
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_cross.cpp4121
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_cross.hpp969
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_cross_c.cpp1864
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_cross_c.h703
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_cross_parsed_ir.cpp648
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_cross_parsed_ir.hpp186
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_cross_util.cpp70
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_cross_util.hpp29
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_glsl.cpp11451
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_glsl.hpp655
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_hlsl.cpp4705
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_hlsl.hpp227
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_msl.cpp7715
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_msl.hpp600
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_parser.cpp1121
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_parser.hpp95
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_reflect.cpp594
-rw-r--r--src/3rdparty/SPIRV-Cross/spirv_reflect.hpp84
-rw-r--r--src/3rdparty/glslang/OGLCompilersDLL/InitializeDll.cpp165
-rw-r--r--src/3rdparty/glslang/OGLCompilersDLL/InitializeDll.h49
-rw-r--r--src/3rdparty/glslang/README.md332
-rw-r--r--src/3rdparty/glslang/SPIRV/GLSL.ext.AMD.h108
-rw-r--r--src/3rdparty/glslang/SPIRV/GLSL.ext.EXT.h38
-rw-r--r--src/3rdparty/glslang/SPIRV/GLSL.ext.KHR.h45
-rw-r--r--src/3rdparty/glslang/SPIRV/GLSL.ext.NV.h78
-rw-r--r--src/3rdparty/glslang/SPIRV/GLSL.std.450.h131
-rw-r--r--src/3rdparty/glslang/SPIRV/GlslangToSpv.cpp7980
-rw-r--r--src/3rdparty/glslang/SPIRV/GlslangToSpv.h61
-rw-r--r--src/3rdparty/glslang/SPIRV/InReadableOrder.cpp113
-rw-r--r--src/3rdparty/glslang/SPIRV/Logger.cpp68
-rw-r--r--src/3rdparty/glslang/SPIRV/Logger.h74
-rw-r--r--src/3rdparty/glslang/SPIRV/SPVRemapper.cpp1487
-rw-r--r--src/3rdparty/glslang/SPIRV/SPVRemapper.h304
-rw-r--r--src/3rdparty/glslang/SPIRV/SpvBuilder.cpp3048
-rw-r--r--src/3rdparty/glslang/SPIRV/SpvBuilder.h749
-rw-r--r--src/3rdparty/glslang/SPIRV/SpvPostProcess.cpp389
-rw-r--r--src/3rdparty/glslang/SPIRV/SpvTools.cpp201
-rw-r--r--src/3rdparty/glslang/SPIRV/SpvTools.h80
-rw-r--r--src/3rdparty/glslang/SPIRV/bitutils.h81
-rw-r--r--src/3rdparty/glslang/SPIRV/disassemble.cpp759
-rw-r--r--src/3rdparty/glslang/SPIRV/disassemble.h53
-rw-r--r--src/3rdparty/glslang/SPIRV/doc.cpp2757
-rw-r--r--src/3rdparty/glslang/SPIRV/doc.h258
-rw-r--r--src/3rdparty/glslang/SPIRV/hex_float.h1078
-rw-r--r--src/3rdparty/glslang/SPIRV/spirv.hpp1343
-rw-r--r--src/3rdparty/glslang/SPIRV/spvIR.h441
-rw-r--r--src/3rdparty/glslang/glslang/GenericCodeGen/CodeGen.cpp76
-rw-r--r--src/3rdparty/glslang/glslang/GenericCodeGen/Link.cpp91
-rw-r--r--src/3rdparty/glslang/glslang/Include/BaseTypes.h545
-rw-r--r--src/3rdparty/glslang/glslang/Include/Common.h291
-rw-r--r--src/3rdparty/glslang/glslang/Include/ConstantUnion.h938
-rw-r--r--src/3rdparty/glslang/glslang/Include/InfoSink.h144
-rw-r--r--src/3rdparty/glslang/glslang/Include/InitializeGlobals.h44
-rw-r--r--src/3rdparty/glslang/glslang/Include/PoolAlloc.h317
-rw-r--r--src/3rdparty/glslang/glslang/Include/ResourceLimits.h149
-rw-r--r--src/3rdparty/glslang/glslang/Include/ShHandle.h176
-rw-r--r--src/3rdparty/glslang/glslang/Include/Types.h2276
-rw-r--r--src/3rdparty/glslang/glslang/Include/arrays.h341
-rw-r--r--src/3rdparty/glslang/glslang/Include/intermediate.h1730
-rw-r--r--src/3rdparty/glslang/glslang/Include/revision.h3
-rw-r--r--src/3rdparty/glslang/glslang/Include/revision.template13
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/Constant.cpp1405
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/InfoSink.cpp113
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/Initialize.cpp9634
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/Initialize.h110
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/IntermTraverse.cpp302
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/Intermediate.cpp3967
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/LiveTraverser.h138
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/ParseContextBase.cpp628
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/ParseHelper.cpp7997
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/ParseHelper.h510
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/PoolAlloc.cpp315
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/RemoveTree.cpp118
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/RemoveTree.h41
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/Scan.cpp1793
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/Scan.h276
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/ScanContext.h93
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/ShaderLang.cpp2041
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/SymbolTable.cpp396
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/SymbolTable.h871
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/Versions.cpp1126
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/Versions.h299
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/attribute.cpp257
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/attribute.h102
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/gl_types.h214
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/glslang.y3796
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/glslang_tab.cpp10468
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/glslang_tab.cpp.h509
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/intermOut.cpp1518
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/iomapper.cpp818
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/iomapper.h63
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/limits.cpp198
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/linkValidate.cpp1686
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/localintermediate.h896
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/parseConst.cpp204
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/parseVersions.h159
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/pch.cpp35
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/pch.h49
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp1320
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp181
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp119
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpContext.h702
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp1246
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp219
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpTokens.h179
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/propagateNoContraction.cpp866
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/propagateNoContraction.h55
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/reflection.cpp1256
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/reflection.h203
-rw-r--r--src/3rdparty/glslang/glslang/OSDependent/Unix/ossource.cpp207
-rw-r--r--src/3rdparty/glslang/glslang/OSDependent/Windows/main.cpp74
-rw-r--r--src/3rdparty/glslang/glslang/OSDependent/Windows/ossource.cpp147
-rw-r--r--src/3rdparty/glslang/glslang/OSDependent/osinclude.h63
-rw-r--r--src/3rdparty/glslang/glslang/Public/ShaderLang.h846
-rw-r--r--src/3rdparty/glslang/glslang/updateGrammar3
-rw-r--r--src/3rdparty/glslang/hlsl/hlslAttributes.cpp106
-rw-r--r--src/3rdparty/glslang/hlsl/hlslAttributes.h59
-rw-r--r--src/3rdparty/glslang/hlsl/hlslGrammar.cpp4170
-rw-r--r--src/3rdparty/glslang/hlsl/hlslGrammar.h142
-rw-r--r--src/3rdparty/glslang/hlsl/hlslOpMap.cpp173
-rw-r--r--src/3rdparty/glslang/hlsl/hlslOpMap.h69
-rw-r--r--src/3rdparty/glslang/hlsl/hlslParseHelper.cpp9984
-rw-r--r--src/3rdparty/glslang/hlsl/hlslParseHelper.h507
-rw-r--r--src/3rdparty/glslang/hlsl/hlslParseables.cpp1324
-rw-r--r--src/3rdparty/glslang/hlsl/hlslParseables.h64
-rw-r--r--src/3rdparty/glslang/hlsl/hlslScanContext.cpp903
-rw-r--r--src/3rdparty/glslang/hlsl/hlslScanContext.h109
-rw-r--r--src/3rdparty/glslang/hlsl/hlslTokenStream.cpp150
-rw-r--r--src/3rdparty/glslang/hlsl/hlslTokenStream.h96
-rw-r--r--src/3rdparty/glslang/hlsl/hlslTokens.h374
-rw-r--r--src/3rdparty/glslang/qt_attribution.json16
-rw-r--r--src/SPIRV-Cross/SPIRV-Cross.pro27
-rw-r--r--src/glslang/glslang-glslang.pro42
-rw-r--r--src/glslang/glslang-hlsl.pro19
-rw-r--r--src/glslang/glslang-oglcompiler.pro13
-rw-r--r--src/glslang/glslang-osdependent.pro17
-rw-r--r--src/glslang/glslang-spirv.pro21
-rw-r--r--src/glslang/glslang.pro8
-rw-r--r--src/glslang/glslang_common.pri6
-rw-r--r--src/shadertools/doc/doc.pri3
-rw-r--r--src/shadertools/doc/qtshadertools.qdocconf49
-rw-r--r--src/shadertools/doc/snippets/color.frag16
-rw-r--r--src/shadertools/doc/snippets/color.vert20
-rw-r--r--src/shadertools/doc/src/qtshadertools-copyright.qdoc37
-rw-r--r--src/shadertools/doc/src/qtshadertools-cpp.qdoc45
-rw-r--r--src/shadertools/doc/src/qtshadertools-index.qdoc63
-rw-r--r--src/shadertools/qshaderbaker.cpp421
-rw-r--r--src/shadertools/qshaderbaker.h78
-rw-r--r--src/shadertools/qshaderbatchablerewriter.cpp223
-rw-r--r--src/shadertools/qshaderbatchablerewriter_p.h64
-rw-r--r--src/shadertools/qspirvcompiler.cpp390
-rw-r--r--src/shadertools/qspirvcompiler_p.h89
-rw-r--r--src/shadertools/qspirvshader.cpp468
-rw-r--r--src/shadertools/qspirvshader_p.h101
-rw-r--r--src/shadertools/qtshadertoolsglobal.h62
-rw-r--r--src/shadertools/qtshadertoolsglobal_p.h48
-rw-r--r--src/shadertools/shadertools.pro41
-rw-r--r--src/src.pro8
-rw-r--r--sync.profile14
-rw-r--r--tests/auto/auto.pro3
-rw-r--r--tests/auto/cmake/CMakeLists.txt12
-rw-r--r--tests/auto/cmake/cmake.pro5
-rw-r--r--tests/auto/qshaderbaker/data/color.frag14
-rw-r--r--tests/auto/qshaderbaker/data/color.vert18
-rw-r--r--tests/auto/qshaderbaker/data/error.vert18
-rw-r--r--tests/auto/qshaderbaker/data/hlsl_cbuf_error.frag47
-rw-r--r--tests/auto/qshaderbaker/qshaderbaker.pro8
-rw-r--r--tests/auto/qshaderbaker/qshaderbaker.qrc5
-rw-r--r--tests/auto/qshaderbaker/tst_qshaderbaker.cpp377
-rw-r--r--tests/playground/array.frag49
-rw-r--r--tests/playground/cbuf.frag21
-rw-r--r--tests/playground/color.frag10
-rw-r--r--tests/playground/color.vert18
-rw-r--r--tests/playground/color_pc.frag12
-rw-r--r--tests/playground/color_pc.vert14
-rw-r--r--tests/playground/color_phong.frag39
-rw-r--r--tests/playground/color_phong.vert32
-rw-r--r--tests/playground/fragcolor.inc1
-rw-r--r--tests/playground/hlsl_cbuf_error.frag47
-rw-r--r--tests/playground/includetest.frag10
-rw-r--r--tests/playground/texture.frag12
-rw-r--r--tests/playground/texture.vert18
-rw-r--r--tests/tests.pro3
-rw-r--r--tools/qsb/qsb.cpp490
-rw-r--r--tools/qsb/qsb.pro5
-rw-r--r--tools/tools.pro2
199 files changed, 153356 insertions, 0 deletions
diff --git a/.qmake.conf b/.qmake.conf
new file mode 100644
index 0000000..c6f1702
--- /dev/null
+++ b/.qmake.conf
@@ -0,0 +1,6 @@
+load(qt_build_config)
+
+CONFIG += warning_clean
+DEFINES += QT_NO_FOREACH
+
+MODULE_VERSION = 5.14.0
diff --git a/qtshadertools.pro b/qtshadertools.pro
new file mode 100644
index 0000000..7d21914
--- /dev/null
+++ b/qtshadertools.pro
@@ -0,0 +1,3 @@
+load(qt_parts)
+
+requires(qtHaveModule(gui))
diff --git a/src/3rdparty/SPIRV-Cross/GLSL.std.450.h b/src/3rdparty/SPIRV-Cross/GLSL.std.450.h
new file mode 100644
index 0000000..54cc00e
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/GLSL.std.450.h
@@ -0,0 +1,131 @@
+/*
+** Copyright (c) 2014-2016 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and/or associated documentation files (the "Materials"),
+** to deal in the Materials without restriction, including without limitation
+** the rights to use, copy, modify, merge, publish, distribute, sublicense,
+** and/or sell copies of the Materials, and to permit persons to whom the
+** Materials are furnished to do so, subject to the following conditions:
+**
+** The above copyright notice and this permission notice shall be included in
+** all copies or substantial portions of the Materials.
+**
+** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
+** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
+** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
+** IN THE MATERIALS.
+*/
+
+#ifndef GLSLstd450_H
+#define GLSLstd450_H
+
+static const int GLSLstd450Version = 100;
+static const int GLSLstd450Revision = 3;
+
+enum GLSLstd450 {
+ GLSLstd450Bad = 0, // Don't use
+
+ GLSLstd450Round = 1,
+ GLSLstd450RoundEven = 2,
+ GLSLstd450Trunc = 3,
+ GLSLstd450FAbs = 4,
+ GLSLstd450SAbs = 5,
+ GLSLstd450FSign = 6,
+ GLSLstd450SSign = 7,
+ GLSLstd450Floor = 8,
+ GLSLstd450Ceil = 9,
+ GLSLstd450Fract = 10,
+
+ GLSLstd450Radians = 11,
+ GLSLstd450Degrees = 12,
+ GLSLstd450Sin = 13,
+ GLSLstd450Cos = 14,
+ GLSLstd450Tan = 15,
+ GLSLstd450Asin = 16,
+ GLSLstd450Acos = 17,
+ GLSLstd450Atan = 18,
+ GLSLstd450Sinh = 19,
+ GLSLstd450Cosh = 20,
+ GLSLstd450Tanh = 21,
+ GLSLstd450Asinh = 22,
+ GLSLstd450Acosh = 23,
+ GLSLstd450Atanh = 24,
+ GLSLstd450Atan2 = 25,
+
+ GLSLstd450Pow = 26,
+ GLSLstd450Exp = 27,
+ GLSLstd450Log = 28,
+ GLSLstd450Exp2 = 29,
+ GLSLstd450Log2 = 30,
+ GLSLstd450Sqrt = 31,
+ GLSLstd450InverseSqrt = 32,
+
+ GLSLstd450Determinant = 33,
+ GLSLstd450MatrixInverse = 34,
+
+ GLSLstd450Modf = 35, // second operand needs an OpVariable to write to
+ GLSLstd450ModfStruct = 36, // no OpVariable operand
+ GLSLstd450FMin = 37,
+ GLSLstd450UMin = 38,
+ GLSLstd450SMin = 39,
+ GLSLstd450FMax = 40,
+ GLSLstd450UMax = 41,
+ GLSLstd450SMax = 42,
+ GLSLstd450FClamp = 43,
+ GLSLstd450UClamp = 44,
+ GLSLstd450SClamp = 45,
+ GLSLstd450FMix = 46,
+ GLSLstd450IMix = 47, // Reserved
+ GLSLstd450Step = 48,
+ GLSLstd450SmoothStep = 49,
+
+ GLSLstd450Fma = 50,
+ GLSLstd450Frexp = 51, // second operand needs an OpVariable to write to
+ GLSLstd450FrexpStruct = 52, // no OpVariable operand
+ GLSLstd450Ldexp = 53,
+
+ GLSLstd450PackSnorm4x8 = 54,
+ GLSLstd450PackUnorm4x8 = 55,
+ GLSLstd450PackSnorm2x16 = 56,
+ GLSLstd450PackUnorm2x16 = 57,
+ GLSLstd450PackHalf2x16 = 58,
+ GLSLstd450PackDouble2x32 = 59,
+ GLSLstd450UnpackSnorm2x16 = 60,
+ GLSLstd450UnpackUnorm2x16 = 61,
+ GLSLstd450UnpackHalf2x16 = 62,
+ GLSLstd450UnpackSnorm4x8 = 63,
+ GLSLstd450UnpackUnorm4x8 = 64,
+ GLSLstd450UnpackDouble2x32 = 65,
+
+ GLSLstd450Length = 66,
+ GLSLstd450Distance = 67,
+ GLSLstd450Cross = 68,
+ GLSLstd450Normalize = 69,
+ GLSLstd450FaceForward = 70,
+ GLSLstd450Reflect = 71,
+ GLSLstd450Refract = 72,
+
+ GLSLstd450FindILsb = 73,
+ GLSLstd450FindSMsb = 74,
+ GLSLstd450FindUMsb = 75,
+
+ GLSLstd450InterpolateAtCentroid = 76,
+ GLSLstd450InterpolateAtSample = 77,
+ GLSLstd450InterpolateAtOffset = 78,
+
+ GLSLstd450NMin = 79,
+ GLSLstd450NMax = 80,
+ GLSLstd450NClamp = 81,
+
+ GLSLstd450Count
+};
+
+#endif // #ifndef GLSLstd450_H
diff --git a/src/3rdparty/SPIRV-Cross/LICENSE b/src/3rdparty/SPIRV-Cross/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/src/3rdparty/SPIRV-Cross/Makefile b/src/3rdparty/SPIRV-Cross/Makefile
new file mode 100644
index 0000000..0564b65
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/Makefile
@@ -0,0 +1,41 @@
+TARGET := spirv-cross
+
+SOURCES := $(wildcard spirv_*.cpp)
+CLI_SOURCES := main.cpp
+
+OBJECTS := $(SOURCES:.cpp=.o)
+CLI_OBJECTS := $(CLI_SOURCES:.cpp=.o)
+
+STATIC_LIB := lib$(TARGET).a
+
+DEPS := $(OBJECTS:.o=.d) $(CLI_OBJECTS:.o=.d)
+
+CXXFLAGS += -std=c++11 -Wall -Wextra -Wshadow -D__STDC_LIMIT_MACROS
+
+ifeq ($(DEBUG), 1)
+ CXXFLAGS += -O0 -g
+else
+ CXXFLAGS += -O2 -DNDEBUG
+endif
+
+ifeq ($(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS), 1)
+ CXXFLAGS += -DSPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS -fno-exceptions
+endif
+
+all: $(TARGET)
+
+-include $(DEPS)
+
+$(TARGET): $(CLI_OBJECTS) $(STATIC_LIB)
+ $(CXX) -o $@ $(CLI_OBJECTS) $(STATIC_LIB) $(LDFLAGS)
+
+$(STATIC_LIB): $(OBJECTS)
+ $(AR) rcs $@ $(OBJECTS)
+
+%.o: %.cpp
+ $(CXX) -c -o $@ $< $(CXXFLAGS) -MMD
+
+clean:
+ rm -f $(TARGET) $(OBJECTS) $(CLI_OBJECTS) $(STATIC_LIB) $(DEPS)
+
+.PHONY: clean
diff --git a/src/3rdparty/SPIRV-Cross/qt_attribution.json b/src/3rdparty/SPIRV-Cross/qt_attribution.json
new file mode 100644
index 0000000..46ba2b9
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/qt_attribution.json
@@ -0,0 +1,16 @@
+[
+ {
+ "Id": "SpirvCross",
+ "Name": "SPIRV-Cross",
+ "QDocModule": "qtshadertools",
+ "Description": "A practical tool and library for performing reflection on SPIR-V and disassembling SPIR-V back to high level languages.",
+ "QtUsage": "Shader code generation.",
+
+ "Homepage": "https://github.com/KhronosGroup/SPIRV-Cross",
+ "Version": "3fa09f5677c7a62c71a1c25fd09c1d1c4842d922",
+ "License": "Apache License 2.0",
+ "LicenseId": "Apache-2.0",
+ "LicenseFile": "LICENSE",
+ "Copyright": "Copyright 2016-2018 ARM Limited"
+ }
+]
diff --git a/src/3rdparty/SPIRV-Cross/spirv.h b/src/3rdparty/SPIRV-Cross/spirv.h
new file mode 100644
index 0000000..8da27dd
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv.h
@@ -0,0 +1,1213 @@
+/*
+** Copyright (c) 2014-2019 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and/or associated documentation files (the "Materials"),
+** to deal in the Materials without restriction, including without limitation
+** the rights to use, copy, modify, merge, publish, distribute, sublicense,
+** and/or sell copies of the Materials, and to permit persons to whom the
+** Materials are furnished to do so, subject to the following conditions:
+**
+** The above copyright notice and this permission notice shall be included in
+** all copies or substantial portions of the Materials.
+**
+** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
+** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
+** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
+** IN THE MATERIALS.
+*/
+
+/*
+** This header is automatically generated by the same tool that creates
+** the Binary Section of the SPIR-V specification.
+*/
+
+/*
+** Enumeration tokens for SPIR-V, in various styles:
+** C, C++, C++11, JSON, Lua, Python, C#, D
+**
+** - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL
+** - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL
+** - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL
+** - Lua will use tables, e.g.: spv.SourceLanguage.GLSL
+** - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']
+** - C# will use enum classes in the Specification class located in the "Spv" namespace,
+** e.g.: Spv.Specification.SourceLanguage.GLSL
+** - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL
+**
+** Some tokens act like mask values, which can be OR'd together,
+** while others are mutually exclusive. The mask-like ones have
+** "Mask" in their name, and a parallel enum that has the shift
+** amount (1 << x) for each corresponding enumerant.
+*/
+
+#ifndef spirv_H
+#define spirv_H
+
+typedef unsigned int SpvId;
+
+#define SPV_VERSION 0x10300
+#define SPV_REVISION 6
+
+static const unsigned int SpvMagicNumber = 0x07230203;
+static const unsigned int SpvVersion = 0x00010300;
+static const unsigned int SpvRevision = 6;
+static const unsigned int SpvOpCodeMask = 0xffff;
+static const unsigned int SpvWordCountShift = 16;
+
+typedef enum SpvSourceLanguage_ {
+ SpvSourceLanguageUnknown = 0,
+ SpvSourceLanguageESSL = 1,
+ SpvSourceLanguageGLSL = 2,
+ SpvSourceLanguageOpenCL_C = 3,
+ SpvSourceLanguageOpenCL_CPP = 4,
+ SpvSourceLanguageHLSL = 5,
+ SpvSourceLanguageMax = 0x7fffffff,
+} SpvSourceLanguage;
+
+typedef enum SpvExecutionModel_ {
+ SpvExecutionModelVertex = 0,
+ SpvExecutionModelTessellationControl = 1,
+ SpvExecutionModelTessellationEvaluation = 2,
+ SpvExecutionModelGeometry = 3,
+ SpvExecutionModelFragment = 4,
+ SpvExecutionModelGLCompute = 5,
+ SpvExecutionModelKernel = 6,
+ SpvExecutionModelTaskNV = 5267,
+ SpvExecutionModelMeshNV = 5268,
+ SpvExecutionModelRayGenerationNV = 5313,
+ SpvExecutionModelIntersectionNV = 5314,
+ SpvExecutionModelAnyHitNV = 5315,
+ SpvExecutionModelClosestHitNV = 5316,
+ SpvExecutionModelMissNV = 5317,
+ SpvExecutionModelCallableNV = 5318,
+ SpvExecutionModelMax = 0x7fffffff,
+} SpvExecutionModel;
+
+typedef enum SpvAddressingModel_ {
+ SpvAddressingModelLogical = 0,
+ SpvAddressingModelPhysical32 = 1,
+ SpvAddressingModelPhysical64 = 2,
+ SpvAddressingModelPhysicalStorageBuffer64EXT = 5348,
+ SpvAddressingModelMax = 0x7fffffff,
+} SpvAddressingModel;
+
+typedef enum SpvMemoryModel_ {
+ SpvMemoryModelSimple = 0,
+ SpvMemoryModelGLSL450 = 1,
+ SpvMemoryModelOpenCL = 2,
+ SpvMemoryModelVulkanKHR = 3,
+ SpvMemoryModelMax = 0x7fffffff,
+} SpvMemoryModel;
+
+typedef enum SpvExecutionMode_ {
+ SpvExecutionModeInvocations = 0,
+ SpvExecutionModeSpacingEqual = 1,
+ SpvExecutionModeSpacingFractionalEven = 2,
+ SpvExecutionModeSpacingFractionalOdd = 3,
+ SpvExecutionModeVertexOrderCw = 4,
+ SpvExecutionModeVertexOrderCcw = 5,
+ SpvExecutionModePixelCenterInteger = 6,
+ SpvExecutionModeOriginUpperLeft = 7,
+ SpvExecutionModeOriginLowerLeft = 8,
+ SpvExecutionModeEarlyFragmentTests = 9,
+ SpvExecutionModePointMode = 10,
+ SpvExecutionModeXfb = 11,
+ SpvExecutionModeDepthReplacing = 12,
+ SpvExecutionModeDepthGreater = 14,
+ SpvExecutionModeDepthLess = 15,
+ SpvExecutionModeDepthUnchanged = 16,
+ SpvExecutionModeLocalSize = 17,
+ SpvExecutionModeLocalSizeHint = 18,
+ SpvExecutionModeInputPoints = 19,
+ SpvExecutionModeInputLines = 20,
+ SpvExecutionModeInputLinesAdjacency = 21,
+ SpvExecutionModeTriangles = 22,
+ SpvExecutionModeInputTrianglesAdjacency = 23,
+ SpvExecutionModeQuads = 24,
+ SpvExecutionModeIsolines = 25,
+ SpvExecutionModeOutputVertices = 26,
+ SpvExecutionModeOutputPoints = 27,
+ SpvExecutionModeOutputLineStrip = 28,
+ SpvExecutionModeOutputTriangleStrip = 29,
+ SpvExecutionModeVecTypeHint = 30,
+ SpvExecutionModeContractionOff = 31,
+ SpvExecutionModeInitializer = 33,
+ SpvExecutionModeFinalizer = 34,
+ SpvExecutionModeSubgroupSize = 35,
+ SpvExecutionModeSubgroupsPerWorkgroup = 36,
+ SpvExecutionModeSubgroupsPerWorkgroupId = 37,
+ SpvExecutionModeLocalSizeId = 38,
+ SpvExecutionModeLocalSizeHintId = 39,
+ SpvExecutionModePostDepthCoverage = 4446,
+ SpvExecutionModeDenormPreserve = 4459,
+ SpvExecutionModeDenormFlushToZero = 4460,
+ SpvExecutionModeSignedZeroInfNanPreserve = 4461,
+ SpvExecutionModeRoundingModeRTE = 4462,
+ SpvExecutionModeRoundingModeRTZ = 4463,
+ SpvExecutionModeStencilRefReplacingEXT = 5027,
+ SpvExecutionModeOutputLinesNV = 5269,
+ SpvExecutionModeOutputPrimitivesNV = 5270,
+ SpvExecutionModeDerivativeGroupQuadsNV = 5289,
+ SpvExecutionModeDerivativeGroupLinearNV = 5290,
+ SpvExecutionModeOutputTrianglesNV = 5298,
+ SpvExecutionModeMax = 0x7fffffff,
+} SpvExecutionMode;
+
+typedef enum SpvStorageClass_ {
+ SpvStorageClassUniformConstant = 0,
+ SpvStorageClassInput = 1,
+ SpvStorageClassUniform = 2,
+ SpvStorageClassOutput = 3,
+ SpvStorageClassWorkgroup = 4,
+ SpvStorageClassCrossWorkgroup = 5,
+ SpvStorageClassPrivate = 6,
+ SpvStorageClassFunction = 7,
+ SpvStorageClassGeneric = 8,
+ SpvStorageClassPushConstant = 9,
+ SpvStorageClassAtomicCounter = 10,
+ SpvStorageClassImage = 11,
+ SpvStorageClassStorageBuffer = 12,
+ SpvStorageClassCallableDataNV = 5328,
+ SpvStorageClassIncomingCallableDataNV = 5329,
+ SpvStorageClassRayPayloadNV = 5338,
+ SpvStorageClassHitAttributeNV = 5339,
+ SpvStorageClassIncomingRayPayloadNV = 5342,
+ SpvStorageClassShaderRecordBufferNV = 5343,
+ SpvStorageClassPhysicalStorageBufferEXT = 5349,
+ SpvStorageClassMax = 0x7fffffff,
+} SpvStorageClass;
+
+typedef enum SpvDim_ {
+ SpvDim1D = 0,
+ SpvDim2D = 1,
+ SpvDim3D = 2,
+ SpvDimCube = 3,
+ SpvDimRect = 4,
+ SpvDimBuffer = 5,
+ SpvDimSubpassData = 6,
+ SpvDimMax = 0x7fffffff,
+} SpvDim;
+
+typedef enum SpvSamplerAddressingMode_ {
+ SpvSamplerAddressingModeNone = 0,
+ SpvSamplerAddressingModeClampToEdge = 1,
+ SpvSamplerAddressingModeClamp = 2,
+ SpvSamplerAddressingModeRepeat = 3,
+ SpvSamplerAddressingModeRepeatMirrored = 4,
+ SpvSamplerAddressingModeMax = 0x7fffffff,
+} SpvSamplerAddressingMode;
+
+typedef enum SpvSamplerFilterMode_ {
+ SpvSamplerFilterModeNearest = 0,
+ SpvSamplerFilterModeLinear = 1,
+ SpvSamplerFilterModeMax = 0x7fffffff,
+} SpvSamplerFilterMode;
+
+typedef enum SpvImageFormat_ {
+ SpvImageFormatUnknown = 0,
+ SpvImageFormatRgba32f = 1,
+ SpvImageFormatRgba16f = 2,
+ SpvImageFormatR32f = 3,
+ SpvImageFormatRgba8 = 4,
+ SpvImageFormatRgba8Snorm = 5,
+ SpvImageFormatRg32f = 6,
+ SpvImageFormatRg16f = 7,
+ SpvImageFormatR11fG11fB10f = 8,
+ SpvImageFormatR16f = 9,
+ SpvImageFormatRgba16 = 10,
+ SpvImageFormatRgb10A2 = 11,
+ SpvImageFormatRg16 = 12,
+ SpvImageFormatRg8 = 13,
+ SpvImageFormatR16 = 14,
+ SpvImageFormatR8 = 15,
+ SpvImageFormatRgba16Snorm = 16,
+ SpvImageFormatRg16Snorm = 17,
+ SpvImageFormatRg8Snorm = 18,
+ SpvImageFormatR16Snorm = 19,
+ SpvImageFormatR8Snorm = 20,
+ SpvImageFormatRgba32i = 21,
+ SpvImageFormatRgba16i = 22,
+ SpvImageFormatRgba8i = 23,
+ SpvImageFormatR32i = 24,
+ SpvImageFormatRg32i = 25,
+ SpvImageFormatRg16i = 26,
+ SpvImageFormatRg8i = 27,
+ SpvImageFormatR16i = 28,
+ SpvImageFormatR8i = 29,
+ SpvImageFormatRgba32ui = 30,
+ SpvImageFormatRgba16ui = 31,
+ SpvImageFormatRgba8ui = 32,
+ SpvImageFormatR32ui = 33,
+ SpvImageFormatRgb10a2ui = 34,
+ SpvImageFormatRg32ui = 35,
+ SpvImageFormatRg16ui = 36,
+ SpvImageFormatRg8ui = 37,
+ SpvImageFormatR16ui = 38,
+ SpvImageFormatR8ui = 39,
+ SpvImageFormatMax = 0x7fffffff,
+} SpvImageFormat;
+
+typedef enum SpvImageChannelOrder_ {
+ SpvImageChannelOrderR = 0,
+ SpvImageChannelOrderA = 1,
+ SpvImageChannelOrderRG = 2,
+ SpvImageChannelOrderRA = 3,
+ SpvImageChannelOrderRGB = 4,
+ SpvImageChannelOrderRGBA = 5,
+ SpvImageChannelOrderBGRA = 6,
+ SpvImageChannelOrderARGB = 7,
+ SpvImageChannelOrderIntensity = 8,
+ SpvImageChannelOrderLuminance = 9,
+ SpvImageChannelOrderRx = 10,
+ SpvImageChannelOrderRGx = 11,
+ SpvImageChannelOrderRGBx = 12,
+ SpvImageChannelOrderDepth = 13,
+ SpvImageChannelOrderDepthStencil = 14,
+ SpvImageChannelOrdersRGB = 15,
+ SpvImageChannelOrdersRGBx = 16,
+ SpvImageChannelOrdersRGBA = 17,
+ SpvImageChannelOrdersBGRA = 18,
+ SpvImageChannelOrderABGR = 19,
+ SpvImageChannelOrderMax = 0x7fffffff,
+} SpvImageChannelOrder;
+
+typedef enum SpvImageChannelDataType_ {
+ SpvImageChannelDataTypeSnormInt8 = 0,
+ SpvImageChannelDataTypeSnormInt16 = 1,
+ SpvImageChannelDataTypeUnormInt8 = 2,
+ SpvImageChannelDataTypeUnormInt16 = 3,
+ SpvImageChannelDataTypeUnormShort565 = 4,
+ SpvImageChannelDataTypeUnormShort555 = 5,
+ SpvImageChannelDataTypeUnormInt101010 = 6,
+ SpvImageChannelDataTypeSignedInt8 = 7,
+ SpvImageChannelDataTypeSignedInt16 = 8,
+ SpvImageChannelDataTypeSignedInt32 = 9,
+ SpvImageChannelDataTypeUnsignedInt8 = 10,
+ SpvImageChannelDataTypeUnsignedInt16 = 11,
+ SpvImageChannelDataTypeUnsignedInt32 = 12,
+ SpvImageChannelDataTypeHalfFloat = 13,
+ SpvImageChannelDataTypeFloat = 14,
+ SpvImageChannelDataTypeUnormInt24 = 15,
+ SpvImageChannelDataTypeUnormInt101010_2 = 16,
+ SpvImageChannelDataTypeMax = 0x7fffffff,
+} SpvImageChannelDataType;
+
+typedef enum SpvImageOperandsShift_ {
+ SpvImageOperandsBiasShift = 0,
+ SpvImageOperandsLodShift = 1,
+ SpvImageOperandsGradShift = 2,
+ SpvImageOperandsConstOffsetShift = 3,
+ SpvImageOperandsOffsetShift = 4,
+ SpvImageOperandsConstOffsetsShift = 5,
+ SpvImageOperandsSampleShift = 6,
+ SpvImageOperandsMinLodShift = 7,
+ SpvImageOperandsMakeTexelAvailableKHRShift = 8,
+ SpvImageOperandsMakeTexelVisibleKHRShift = 9,
+ SpvImageOperandsNonPrivateTexelKHRShift = 10,
+ SpvImageOperandsVolatileTexelKHRShift = 11,
+ SpvImageOperandsMax = 0x7fffffff,
+} SpvImageOperandsShift;
+
+typedef enum SpvImageOperandsMask_ {
+ SpvImageOperandsMaskNone = 0,
+ SpvImageOperandsBiasMask = 0x00000001,
+ SpvImageOperandsLodMask = 0x00000002,
+ SpvImageOperandsGradMask = 0x00000004,
+ SpvImageOperandsConstOffsetMask = 0x00000008,
+ SpvImageOperandsOffsetMask = 0x00000010,
+ SpvImageOperandsConstOffsetsMask = 0x00000020,
+ SpvImageOperandsSampleMask = 0x00000040,
+ SpvImageOperandsMinLodMask = 0x00000080,
+ SpvImageOperandsMakeTexelAvailableKHRMask = 0x00000100,
+ SpvImageOperandsMakeTexelVisibleKHRMask = 0x00000200,
+ SpvImageOperandsNonPrivateTexelKHRMask = 0x00000400,
+ SpvImageOperandsVolatileTexelKHRMask = 0x00000800,
+} SpvImageOperandsMask;
+
+typedef enum SpvFPFastMathModeShift_ {
+ SpvFPFastMathModeNotNaNShift = 0,
+ SpvFPFastMathModeNotInfShift = 1,
+ SpvFPFastMathModeNSZShift = 2,
+ SpvFPFastMathModeAllowRecipShift = 3,
+ SpvFPFastMathModeFastShift = 4,
+ SpvFPFastMathModeMax = 0x7fffffff,
+} SpvFPFastMathModeShift;
+
+typedef enum SpvFPFastMathModeMask_ {
+ SpvFPFastMathModeMaskNone = 0,
+ SpvFPFastMathModeNotNaNMask = 0x00000001,
+ SpvFPFastMathModeNotInfMask = 0x00000002,
+ SpvFPFastMathModeNSZMask = 0x00000004,
+ SpvFPFastMathModeAllowRecipMask = 0x00000008,
+ SpvFPFastMathModeFastMask = 0x00000010,
+} SpvFPFastMathModeMask;
+
+typedef enum SpvFPRoundingMode_ {
+ SpvFPRoundingModeRTE = 0,
+ SpvFPRoundingModeRTZ = 1,
+ SpvFPRoundingModeRTP = 2,
+ SpvFPRoundingModeRTN = 3,
+ SpvFPRoundingModeMax = 0x7fffffff,
+} SpvFPRoundingMode;
+
+typedef enum SpvLinkageType_ {
+ SpvLinkageTypeExport = 0,
+ SpvLinkageTypeImport = 1,
+ SpvLinkageTypeMax = 0x7fffffff,
+} SpvLinkageType;
+
+typedef enum SpvAccessQualifier_ {
+ SpvAccessQualifierReadOnly = 0,
+ SpvAccessQualifierWriteOnly = 1,
+ SpvAccessQualifierReadWrite = 2,
+ SpvAccessQualifierMax = 0x7fffffff,
+} SpvAccessQualifier;
+
+typedef enum SpvFunctionParameterAttribute_ {
+ SpvFunctionParameterAttributeZext = 0,
+ SpvFunctionParameterAttributeSext = 1,
+ SpvFunctionParameterAttributeByVal = 2,
+ SpvFunctionParameterAttributeSret = 3,
+ SpvFunctionParameterAttributeNoAlias = 4,
+ SpvFunctionParameterAttributeNoCapture = 5,
+ SpvFunctionParameterAttributeNoWrite = 6,
+ SpvFunctionParameterAttributeNoReadWrite = 7,
+ SpvFunctionParameterAttributeMax = 0x7fffffff,
+} SpvFunctionParameterAttribute;
+
+typedef enum SpvDecoration_ {
+ SpvDecorationRelaxedPrecision = 0,
+ SpvDecorationSpecId = 1,
+ SpvDecorationBlock = 2,
+ SpvDecorationBufferBlock = 3,
+ SpvDecorationRowMajor = 4,
+ SpvDecorationColMajor = 5,
+ SpvDecorationArrayStride = 6,
+ SpvDecorationMatrixStride = 7,
+ SpvDecorationGLSLShared = 8,
+ SpvDecorationGLSLPacked = 9,
+ SpvDecorationCPacked = 10,
+ SpvDecorationBuiltIn = 11,
+ SpvDecorationNoPerspective = 13,
+ SpvDecorationFlat = 14,
+ SpvDecorationPatch = 15,
+ SpvDecorationCentroid = 16,
+ SpvDecorationSample = 17,
+ SpvDecorationInvariant = 18,
+ SpvDecorationRestrict = 19,
+ SpvDecorationAliased = 20,
+ SpvDecorationVolatile = 21,
+ SpvDecorationConstant = 22,
+ SpvDecorationCoherent = 23,
+ SpvDecorationNonWritable = 24,
+ SpvDecorationNonReadable = 25,
+ SpvDecorationUniform = 26,
+ SpvDecorationSaturatedConversion = 28,
+ SpvDecorationStream = 29,
+ SpvDecorationLocation = 30,
+ SpvDecorationComponent = 31,
+ SpvDecorationIndex = 32,
+ SpvDecorationBinding = 33,
+ SpvDecorationDescriptorSet = 34,
+ SpvDecorationOffset = 35,
+ SpvDecorationXfbBuffer = 36,
+ SpvDecorationXfbStride = 37,
+ SpvDecorationFuncParamAttr = 38,
+ SpvDecorationFPRoundingMode = 39,
+ SpvDecorationFPFastMathMode = 40,
+ SpvDecorationLinkageAttributes = 41,
+ SpvDecorationNoContraction = 42,
+ SpvDecorationInputAttachmentIndex = 43,
+ SpvDecorationAlignment = 44,
+ SpvDecorationMaxByteOffset = 45,
+ SpvDecorationAlignmentId = 46,
+ SpvDecorationMaxByteOffsetId = 47,
+ SpvDecorationNoSignedWrap = 4469,
+ SpvDecorationNoUnsignedWrap = 4470,
+ SpvDecorationExplicitInterpAMD = 4999,
+ SpvDecorationOverrideCoverageNV = 5248,
+ SpvDecorationPassthroughNV = 5250,
+ SpvDecorationViewportRelativeNV = 5252,
+ SpvDecorationSecondaryViewportRelativeNV = 5256,
+ SpvDecorationPerPrimitiveNV = 5271,
+ SpvDecorationPerViewNV = 5272,
+ SpvDecorationPerTaskNV = 5273,
+ SpvDecorationPerVertexNV = 5285,
+ SpvDecorationNonUniformEXT = 5300,
+ SpvDecorationRestrictPointerEXT = 5355,
+ SpvDecorationAliasedPointerEXT = 5356,
+ SpvDecorationHlslCounterBufferGOOGLE = 5634,
+ SpvDecorationHlslSemanticGOOGLE = 5635,
+ SpvDecorationMax = 0x7fffffff,
+} SpvDecoration;
+
+typedef enum SpvBuiltIn_ {
+ SpvBuiltInPosition = 0,
+ SpvBuiltInPointSize = 1,
+ SpvBuiltInClipDistance = 3,
+ SpvBuiltInCullDistance = 4,
+ SpvBuiltInVertexId = 5,
+ SpvBuiltInInstanceId = 6,
+ SpvBuiltInPrimitiveId = 7,
+ SpvBuiltInInvocationId = 8,
+ SpvBuiltInLayer = 9,
+ SpvBuiltInViewportIndex = 10,
+ SpvBuiltInTessLevelOuter = 11,
+ SpvBuiltInTessLevelInner = 12,
+ SpvBuiltInTessCoord = 13,
+ SpvBuiltInPatchVertices = 14,
+ SpvBuiltInFragCoord = 15,
+ SpvBuiltInPointCoord = 16,
+ SpvBuiltInFrontFacing = 17,
+ SpvBuiltInSampleId = 18,
+ SpvBuiltInSamplePosition = 19,
+ SpvBuiltInSampleMask = 20,
+ SpvBuiltInFragDepth = 22,
+ SpvBuiltInHelperInvocation = 23,
+ SpvBuiltInNumWorkgroups = 24,
+ SpvBuiltInWorkgroupSize = 25,
+ SpvBuiltInWorkgroupId = 26,
+ SpvBuiltInLocalInvocationId = 27,
+ SpvBuiltInGlobalInvocationId = 28,
+ SpvBuiltInLocalInvocationIndex = 29,
+ SpvBuiltInWorkDim = 30,
+ SpvBuiltInGlobalSize = 31,
+ SpvBuiltInEnqueuedWorkgroupSize = 32,
+ SpvBuiltInGlobalOffset = 33,
+ SpvBuiltInGlobalLinearId = 34,
+ SpvBuiltInSubgroupSize = 36,
+ SpvBuiltInSubgroupMaxSize = 37,
+ SpvBuiltInNumSubgroups = 38,
+ SpvBuiltInNumEnqueuedSubgroups = 39,
+ SpvBuiltInSubgroupId = 40,
+ SpvBuiltInSubgroupLocalInvocationId = 41,
+ SpvBuiltInVertexIndex = 42,
+ SpvBuiltInInstanceIndex = 43,
+ SpvBuiltInSubgroupEqMask = 4416,
+ SpvBuiltInSubgroupEqMaskKHR = 4416,
+ SpvBuiltInSubgroupGeMask = 4417,
+ SpvBuiltInSubgroupGeMaskKHR = 4417,
+ SpvBuiltInSubgroupGtMask = 4418,
+ SpvBuiltInSubgroupGtMaskKHR = 4418,
+ SpvBuiltInSubgroupLeMask = 4419,
+ SpvBuiltInSubgroupLeMaskKHR = 4419,
+ SpvBuiltInSubgroupLtMask = 4420,
+ SpvBuiltInSubgroupLtMaskKHR = 4420,
+ SpvBuiltInBaseVertex = 4424,
+ SpvBuiltInBaseInstance = 4425,
+ SpvBuiltInDrawIndex = 4426,
+ SpvBuiltInDeviceIndex = 4438,
+ SpvBuiltInViewIndex = 4440,
+ SpvBuiltInBaryCoordNoPerspAMD = 4992,
+ SpvBuiltInBaryCoordNoPerspCentroidAMD = 4993,
+ SpvBuiltInBaryCoordNoPerspSampleAMD = 4994,
+ SpvBuiltInBaryCoordSmoothAMD = 4995,
+ SpvBuiltInBaryCoordSmoothCentroidAMD = 4996,
+ SpvBuiltInBaryCoordSmoothSampleAMD = 4997,
+ SpvBuiltInBaryCoordPullModelAMD = 4998,
+ SpvBuiltInFragStencilRefEXT = 5014,
+ SpvBuiltInViewportMaskNV = 5253,
+ SpvBuiltInSecondaryPositionNV = 5257,
+ SpvBuiltInSecondaryViewportMaskNV = 5258,
+ SpvBuiltInPositionPerViewNV = 5261,
+ SpvBuiltInViewportMaskPerViewNV = 5262,
+ SpvBuiltInFullyCoveredEXT = 5264,
+ SpvBuiltInTaskCountNV = 5274,
+ SpvBuiltInPrimitiveCountNV = 5275,
+ SpvBuiltInPrimitiveIndicesNV = 5276,
+ SpvBuiltInClipDistancePerViewNV = 5277,
+ SpvBuiltInCullDistancePerViewNV = 5278,
+ SpvBuiltInLayerPerViewNV = 5279,
+ SpvBuiltInMeshViewCountNV = 5280,
+ SpvBuiltInMeshViewIndicesNV = 5281,
+ SpvBuiltInBaryCoordNV = 5286,
+ SpvBuiltInBaryCoordNoPerspNV = 5287,
+ SpvBuiltInFragSizeEXT = 5292,
+ SpvBuiltInFragmentSizeNV = 5292,
+ SpvBuiltInFragInvocationCountEXT = 5293,
+ SpvBuiltInInvocationsPerPixelNV = 5293,
+ SpvBuiltInLaunchIdNV = 5319,
+ SpvBuiltInLaunchSizeNV = 5320,
+ SpvBuiltInWorldRayOriginNV = 5321,
+ SpvBuiltInWorldRayDirectionNV = 5322,
+ SpvBuiltInObjectRayOriginNV = 5323,
+ SpvBuiltInObjectRayDirectionNV = 5324,
+ SpvBuiltInRayTminNV = 5325,
+ SpvBuiltInRayTmaxNV = 5326,
+ SpvBuiltInInstanceCustomIndexNV = 5327,
+ SpvBuiltInObjectToWorldNV = 5330,
+ SpvBuiltInWorldToObjectNV = 5331,
+ SpvBuiltInHitTNV = 5332,
+ SpvBuiltInHitKindNV = 5333,
+ SpvBuiltInIncomingRayFlagsNV = 5351,
+ SpvBuiltInMax = 0x7fffffff,
+} SpvBuiltIn;
+
+typedef enum SpvSelectionControlShift_ {
+ SpvSelectionControlFlattenShift = 0,
+ SpvSelectionControlDontFlattenShift = 1,
+ SpvSelectionControlMax = 0x7fffffff,
+} SpvSelectionControlShift;
+
+typedef enum SpvSelectionControlMask_ {
+ SpvSelectionControlMaskNone = 0,
+ SpvSelectionControlFlattenMask = 0x00000001,
+ SpvSelectionControlDontFlattenMask = 0x00000002,
+} SpvSelectionControlMask;
+
+typedef enum SpvLoopControlShift_ {
+ SpvLoopControlUnrollShift = 0,
+ SpvLoopControlDontUnrollShift = 1,
+ SpvLoopControlDependencyInfiniteShift = 2,
+ SpvLoopControlDependencyLengthShift = 3,
+ SpvLoopControlMax = 0x7fffffff,
+} SpvLoopControlShift;
+
+typedef enum SpvLoopControlMask_ {
+ SpvLoopControlMaskNone = 0,
+ SpvLoopControlUnrollMask = 0x00000001,
+ SpvLoopControlDontUnrollMask = 0x00000002,
+ SpvLoopControlDependencyInfiniteMask = 0x00000004,
+ SpvLoopControlDependencyLengthMask = 0x00000008,
+} SpvLoopControlMask;
+
+typedef enum SpvFunctionControlShift_ {
+ SpvFunctionControlInlineShift = 0,
+ SpvFunctionControlDontInlineShift = 1,
+ SpvFunctionControlPureShift = 2,
+ SpvFunctionControlConstShift = 3,
+ SpvFunctionControlMax = 0x7fffffff,
+} SpvFunctionControlShift;
+
+typedef enum SpvFunctionControlMask_ {
+ SpvFunctionControlMaskNone = 0,
+ SpvFunctionControlInlineMask = 0x00000001,
+ SpvFunctionControlDontInlineMask = 0x00000002,
+ SpvFunctionControlPureMask = 0x00000004,
+ SpvFunctionControlConstMask = 0x00000008,
+} SpvFunctionControlMask;
+
+typedef enum SpvMemorySemanticsShift_ {
+ SpvMemorySemanticsAcquireShift = 1,
+ SpvMemorySemanticsReleaseShift = 2,
+ SpvMemorySemanticsAcquireReleaseShift = 3,
+ SpvMemorySemanticsSequentiallyConsistentShift = 4,
+ SpvMemorySemanticsUniformMemoryShift = 6,
+ SpvMemorySemanticsSubgroupMemoryShift = 7,
+ SpvMemorySemanticsWorkgroupMemoryShift = 8,
+ SpvMemorySemanticsCrossWorkgroupMemoryShift = 9,
+ SpvMemorySemanticsAtomicCounterMemoryShift = 10,
+ SpvMemorySemanticsImageMemoryShift = 11,
+ SpvMemorySemanticsOutputMemoryKHRShift = 12,
+ SpvMemorySemanticsMakeAvailableKHRShift = 13,
+ SpvMemorySemanticsMakeVisibleKHRShift = 14,
+ SpvMemorySemanticsMax = 0x7fffffff,
+} SpvMemorySemanticsShift;
+
+typedef enum SpvMemorySemanticsMask_ {
+ SpvMemorySemanticsMaskNone = 0,
+ SpvMemorySemanticsAcquireMask = 0x00000002,
+ SpvMemorySemanticsReleaseMask = 0x00000004,
+ SpvMemorySemanticsAcquireReleaseMask = 0x00000008,
+ SpvMemorySemanticsSequentiallyConsistentMask = 0x00000010,
+ SpvMemorySemanticsUniformMemoryMask = 0x00000040,
+ SpvMemorySemanticsSubgroupMemoryMask = 0x00000080,
+ SpvMemorySemanticsWorkgroupMemoryMask = 0x00000100,
+ SpvMemorySemanticsCrossWorkgroupMemoryMask = 0x00000200,
+ SpvMemorySemanticsAtomicCounterMemoryMask = 0x00000400,
+ SpvMemorySemanticsImageMemoryMask = 0x00000800,
+ SpvMemorySemanticsOutputMemoryKHRMask = 0x00001000,
+ SpvMemorySemanticsMakeAvailableKHRMask = 0x00002000,
+ SpvMemorySemanticsMakeVisibleKHRMask = 0x00004000,
+} SpvMemorySemanticsMask;
+
+typedef enum SpvMemoryAccessShift_ {
+ SpvMemoryAccessVolatileShift = 0,
+ SpvMemoryAccessAlignedShift = 1,
+ SpvMemoryAccessNontemporalShift = 2,
+ SpvMemoryAccessMakePointerAvailableKHRShift = 3,
+ SpvMemoryAccessMakePointerVisibleKHRShift = 4,
+ SpvMemoryAccessNonPrivatePointerKHRShift = 5,
+ SpvMemoryAccessMax = 0x7fffffff,
+} SpvMemoryAccessShift;
+
+typedef enum SpvMemoryAccessMask_ {
+ SpvMemoryAccessMaskNone = 0,
+ SpvMemoryAccessVolatileMask = 0x00000001,
+ SpvMemoryAccessAlignedMask = 0x00000002,
+ SpvMemoryAccessNontemporalMask = 0x00000004,
+ SpvMemoryAccessMakePointerAvailableKHRMask = 0x00000008,
+ SpvMemoryAccessMakePointerVisibleKHRMask = 0x00000010,
+ SpvMemoryAccessNonPrivatePointerKHRMask = 0x00000020,
+} SpvMemoryAccessMask;
+
+typedef enum SpvScope_ {
+ SpvScopeCrossDevice = 0,
+ SpvScopeDevice = 1,
+ SpvScopeWorkgroup = 2,
+ SpvScopeSubgroup = 3,
+ SpvScopeInvocation = 4,
+ SpvScopeQueueFamilyKHR = 5,
+ SpvScopeMax = 0x7fffffff,
+} SpvScope;
+
+typedef enum SpvGroupOperation_ {
+ SpvGroupOperationReduce = 0,
+ SpvGroupOperationInclusiveScan = 1,
+ SpvGroupOperationExclusiveScan = 2,
+ SpvGroupOperationClusteredReduce = 3,
+ SpvGroupOperationPartitionedReduceNV = 6,
+ SpvGroupOperationPartitionedInclusiveScanNV = 7,
+ SpvGroupOperationPartitionedExclusiveScanNV = 8,
+ SpvGroupOperationMax = 0x7fffffff,
+} SpvGroupOperation;
+
+typedef enum SpvKernelEnqueueFlags_ {
+ SpvKernelEnqueueFlagsNoWait = 0,
+ SpvKernelEnqueueFlagsWaitKernel = 1,
+ SpvKernelEnqueueFlagsWaitWorkGroup = 2,
+ SpvKernelEnqueueFlagsMax = 0x7fffffff,
+} SpvKernelEnqueueFlags;
+
+typedef enum SpvKernelProfilingInfoShift_ {
+ SpvKernelProfilingInfoCmdExecTimeShift = 0,
+ SpvKernelProfilingInfoMax = 0x7fffffff,
+} SpvKernelProfilingInfoShift;
+
+typedef enum SpvKernelProfilingInfoMask_ {
+ SpvKernelProfilingInfoMaskNone = 0,
+ SpvKernelProfilingInfoCmdExecTimeMask = 0x00000001,
+} SpvKernelProfilingInfoMask;
+
+typedef enum SpvCapability_ {
+ SpvCapabilityMatrix = 0,
+ SpvCapabilityShader = 1,
+ SpvCapabilityGeometry = 2,
+ SpvCapabilityTessellation = 3,
+ SpvCapabilityAddresses = 4,
+ SpvCapabilityLinkage = 5,
+ SpvCapabilityKernel = 6,
+ SpvCapabilityVector16 = 7,
+ SpvCapabilityFloat16Buffer = 8,
+ SpvCapabilityFloat16 = 9,
+ SpvCapabilityFloat64 = 10,
+ SpvCapabilityInt64 = 11,
+ SpvCapabilityInt64Atomics = 12,
+ SpvCapabilityImageBasic = 13,
+ SpvCapabilityImageReadWrite = 14,
+ SpvCapabilityImageMipmap = 15,
+ SpvCapabilityPipes = 17,
+ SpvCapabilityGroups = 18,
+ SpvCapabilityDeviceEnqueue = 19,
+ SpvCapabilityLiteralSampler = 20,
+ SpvCapabilityAtomicStorage = 21,
+ SpvCapabilityInt16 = 22,
+ SpvCapabilityTessellationPointSize = 23,
+ SpvCapabilityGeometryPointSize = 24,
+ SpvCapabilityImageGatherExtended = 25,
+ SpvCapabilityStorageImageMultisample = 27,
+ SpvCapabilityUniformBufferArrayDynamicIndexing = 28,
+ SpvCapabilitySampledImageArrayDynamicIndexing = 29,
+ SpvCapabilityStorageBufferArrayDynamicIndexing = 30,
+ SpvCapabilityStorageImageArrayDynamicIndexing = 31,
+ SpvCapabilityClipDistance = 32,
+ SpvCapabilityCullDistance = 33,
+ SpvCapabilityImageCubeArray = 34,
+ SpvCapabilitySampleRateShading = 35,
+ SpvCapabilityImageRect = 36,
+ SpvCapabilitySampledRect = 37,
+ SpvCapabilityGenericPointer = 38,
+ SpvCapabilityInt8 = 39,
+ SpvCapabilityInputAttachment = 40,
+ SpvCapabilitySparseResidency = 41,
+ SpvCapabilityMinLod = 42,
+ SpvCapabilitySampled1D = 43,
+ SpvCapabilityImage1D = 44,
+ SpvCapabilitySampledCubeArray = 45,
+ SpvCapabilitySampledBuffer = 46,
+ SpvCapabilityImageBuffer = 47,
+ SpvCapabilityImageMSArray = 48,
+ SpvCapabilityStorageImageExtendedFormats = 49,
+ SpvCapabilityImageQuery = 50,
+ SpvCapabilityDerivativeControl = 51,
+ SpvCapabilityInterpolationFunction = 52,
+ SpvCapabilityTransformFeedback = 53,
+ SpvCapabilityGeometryStreams = 54,
+ SpvCapabilityStorageImageReadWithoutFormat = 55,
+ SpvCapabilityStorageImageWriteWithoutFormat = 56,
+ SpvCapabilityMultiViewport = 57,
+ SpvCapabilitySubgroupDispatch = 58,
+ SpvCapabilityNamedBarrier = 59,
+ SpvCapabilityPipeStorage = 60,
+ SpvCapabilityGroupNonUniform = 61,
+ SpvCapabilityGroupNonUniformVote = 62,
+ SpvCapabilityGroupNonUniformArithmetic = 63,
+ SpvCapabilityGroupNonUniformBallot = 64,
+ SpvCapabilityGroupNonUniformShuffle = 65,
+ SpvCapabilityGroupNonUniformShuffleRelative = 66,
+ SpvCapabilityGroupNonUniformClustered = 67,
+ SpvCapabilityGroupNonUniformQuad = 68,
+ SpvCapabilitySubgroupBallotKHR = 4423,
+ SpvCapabilityDrawParameters = 4427,
+ SpvCapabilitySubgroupVoteKHR = 4431,
+ SpvCapabilityStorageBuffer16BitAccess = 4433,
+ SpvCapabilityStorageUniformBufferBlock16 = 4433,
+ SpvCapabilityStorageUniform16 = 4434,
+ SpvCapabilityUniformAndStorageBuffer16BitAccess = 4434,
+ SpvCapabilityStoragePushConstant16 = 4435,
+ SpvCapabilityStorageInputOutput16 = 4436,
+ SpvCapabilityDeviceGroup = 4437,
+ SpvCapabilityMultiView = 4439,
+ SpvCapabilityVariablePointersStorageBuffer = 4441,
+ SpvCapabilityVariablePointers = 4442,
+ SpvCapabilityAtomicStorageOps = 4445,
+ SpvCapabilitySampleMaskPostDepthCoverage = 4447,
+ SpvCapabilityStorageBuffer8BitAccess = 4448,
+ SpvCapabilityUniformAndStorageBuffer8BitAccess = 4449,
+ SpvCapabilityStoragePushConstant8 = 4450,
+ SpvCapabilityDenormPreserve = 4464,
+ SpvCapabilityDenormFlushToZero = 4465,
+ SpvCapabilitySignedZeroInfNanPreserve = 4466,
+ SpvCapabilityRoundingModeRTE = 4467,
+ SpvCapabilityRoundingModeRTZ = 4468,
+ SpvCapabilityFloat16ImageAMD = 5008,
+ SpvCapabilityImageGatherBiasLodAMD = 5009,
+ SpvCapabilityFragmentMaskAMD = 5010,
+ SpvCapabilityStencilExportEXT = 5013,
+ SpvCapabilityImageReadWriteLodAMD = 5015,
+ SpvCapabilitySampleMaskOverrideCoverageNV = 5249,
+ SpvCapabilityGeometryShaderPassthroughNV = 5251,
+ SpvCapabilityShaderViewportIndexLayerEXT = 5254,
+ SpvCapabilityShaderViewportIndexLayerNV = 5254,
+ SpvCapabilityShaderViewportMaskNV = 5255,
+ SpvCapabilityShaderStereoViewNV = 5259,
+ SpvCapabilityPerViewAttributesNV = 5260,
+ SpvCapabilityFragmentFullyCoveredEXT = 5265,
+ SpvCapabilityMeshShadingNV = 5266,
+ SpvCapabilityImageFootprintNV = 5282,
+ SpvCapabilityFragmentBarycentricNV = 5284,
+ SpvCapabilityComputeDerivativeGroupQuadsNV = 5288,
+ SpvCapabilityFragmentDensityEXT = 5291,
+ SpvCapabilityShadingRateNV = 5291,
+ SpvCapabilityGroupNonUniformPartitionedNV = 5297,
+ SpvCapabilityShaderNonUniformEXT = 5301,
+ SpvCapabilityRuntimeDescriptorArrayEXT = 5302,
+ SpvCapabilityInputAttachmentArrayDynamicIndexingEXT = 5303,
+ SpvCapabilityUniformTexelBufferArrayDynamicIndexingEXT = 5304,
+ SpvCapabilityStorageTexelBufferArrayDynamicIndexingEXT = 5305,
+ SpvCapabilityUniformBufferArrayNonUniformIndexingEXT = 5306,
+ SpvCapabilitySampledImageArrayNonUniformIndexingEXT = 5307,
+ SpvCapabilityStorageBufferArrayNonUniformIndexingEXT = 5308,
+ SpvCapabilityStorageImageArrayNonUniformIndexingEXT = 5309,
+ SpvCapabilityInputAttachmentArrayNonUniformIndexingEXT = 5310,
+ SpvCapabilityUniformTexelBufferArrayNonUniformIndexingEXT = 5311,
+ SpvCapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312,
+ SpvCapabilityRayTracingNV = 5340,
+ SpvCapabilityVulkanMemoryModelKHR = 5345,
+ SpvCapabilityVulkanMemoryModelDeviceScopeKHR = 5346,
+ SpvCapabilityPhysicalStorageBufferAddressesEXT = 5347,
+ SpvCapabilityComputeDerivativeGroupLinearNV = 5350,
+ SpvCapabilityCooperativeMatrixNV = 5357,
+ SpvCapabilitySubgroupShuffleINTEL = 5568,
+ SpvCapabilitySubgroupBufferBlockIOINTEL = 5569,
+ SpvCapabilitySubgroupImageBlockIOINTEL = 5570,
+ SpvCapabilitySubgroupImageMediaBlockIOINTEL = 5579,
+ SpvCapabilityMax = 0x7fffffff,
+} SpvCapability;
+
+typedef enum SpvOp_ {
+ SpvOpNop = 0,
+ SpvOpUndef = 1,
+ SpvOpSourceContinued = 2,
+ SpvOpSource = 3,
+ SpvOpSourceExtension = 4,
+ SpvOpName = 5,
+ SpvOpMemberName = 6,
+ SpvOpString = 7,
+ SpvOpLine = 8,
+ SpvOpExtension = 10,
+ SpvOpExtInstImport = 11,
+ SpvOpExtInst = 12,
+ SpvOpMemoryModel = 14,
+ SpvOpEntryPoint = 15,
+ SpvOpExecutionMode = 16,
+ SpvOpCapability = 17,
+ SpvOpTypeVoid = 19,
+ SpvOpTypeBool = 20,
+ SpvOpTypeInt = 21,
+ SpvOpTypeFloat = 22,
+ SpvOpTypeVector = 23,
+ SpvOpTypeMatrix = 24,
+ SpvOpTypeImage = 25,
+ SpvOpTypeSampler = 26,
+ SpvOpTypeSampledImage = 27,
+ SpvOpTypeArray = 28,
+ SpvOpTypeRuntimeArray = 29,
+ SpvOpTypeStruct = 30,
+ SpvOpTypeOpaque = 31,
+ SpvOpTypePointer = 32,
+ SpvOpTypeFunction = 33,
+ SpvOpTypeEvent = 34,
+ SpvOpTypeDeviceEvent = 35,
+ SpvOpTypeReserveId = 36,
+ SpvOpTypeQueue = 37,
+ SpvOpTypePipe = 38,
+ SpvOpTypeForwardPointer = 39,
+ SpvOpConstantTrue = 41,
+ SpvOpConstantFalse = 42,
+ SpvOpConstant = 43,
+ SpvOpConstantComposite = 44,
+ SpvOpConstantSampler = 45,
+ SpvOpConstantNull = 46,
+ SpvOpSpecConstantTrue = 48,
+ SpvOpSpecConstantFalse = 49,
+ SpvOpSpecConstant = 50,
+ SpvOpSpecConstantComposite = 51,
+ SpvOpSpecConstantOp = 52,
+ SpvOpFunction = 54,
+ SpvOpFunctionParameter = 55,
+ SpvOpFunctionEnd = 56,
+ SpvOpFunctionCall = 57,
+ SpvOpVariable = 59,
+ SpvOpImageTexelPointer = 60,
+ SpvOpLoad = 61,
+ SpvOpStore = 62,
+ SpvOpCopyMemory = 63,
+ SpvOpCopyMemorySized = 64,
+ SpvOpAccessChain = 65,
+ SpvOpInBoundsAccessChain = 66,
+ SpvOpPtrAccessChain = 67,
+ SpvOpArrayLength = 68,
+ SpvOpGenericPtrMemSemantics = 69,
+ SpvOpInBoundsPtrAccessChain = 70,
+ SpvOpDecorate = 71,
+ SpvOpMemberDecorate = 72,
+ SpvOpDecorationGroup = 73,
+ SpvOpGroupDecorate = 74,
+ SpvOpGroupMemberDecorate = 75,
+ SpvOpVectorExtractDynamic = 77,
+ SpvOpVectorInsertDynamic = 78,
+ SpvOpVectorShuffle = 79,
+ SpvOpCompositeConstruct = 80,
+ SpvOpCompositeExtract = 81,
+ SpvOpCompositeInsert = 82,
+ SpvOpCopyObject = 83,
+ SpvOpTranspose = 84,
+ SpvOpSampledImage = 86,
+ SpvOpImageSampleImplicitLod = 87,
+ SpvOpImageSampleExplicitLod = 88,
+ SpvOpImageSampleDrefImplicitLod = 89,
+ SpvOpImageSampleDrefExplicitLod = 90,
+ SpvOpImageSampleProjImplicitLod = 91,
+ SpvOpImageSampleProjExplicitLod = 92,
+ SpvOpImageSampleProjDrefImplicitLod = 93,
+ SpvOpImageSampleProjDrefExplicitLod = 94,
+ SpvOpImageFetch = 95,
+ SpvOpImageGather = 96,
+ SpvOpImageDrefGather = 97,
+ SpvOpImageRead = 98,
+ SpvOpImageWrite = 99,
+ SpvOpImage = 100,
+ SpvOpImageQueryFormat = 101,
+ SpvOpImageQueryOrder = 102,
+ SpvOpImageQuerySizeLod = 103,
+ SpvOpImageQuerySize = 104,
+ SpvOpImageQueryLod = 105,
+ SpvOpImageQueryLevels = 106,
+ SpvOpImageQuerySamples = 107,
+ SpvOpConvertFToU = 109,
+ SpvOpConvertFToS = 110,
+ SpvOpConvertSToF = 111,
+ SpvOpConvertUToF = 112,
+ SpvOpUConvert = 113,
+ SpvOpSConvert = 114,
+ SpvOpFConvert = 115,
+ SpvOpQuantizeToF16 = 116,
+ SpvOpConvertPtrToU = 117,
+ SpvOpSatConvertSToU = 118,
+ SpvOpSatConvertUToS = 119,
+ SpvOpConvertUToPtr = 120,
+ SpvOpPtrCastToGeneric = 121,
+ SpvOpGenericCastToPtr = 122,
+ SpvOpGenericCastToPtrExplicit = 123,
+ SpvOpBitcast = 124,
+ SpvOpSNegate = 126,
+ SpvOpFNegate = 127,
+ SpvOpIAdd = 128,
+ SpvOpFAdd = 129,
+ SpvOpISub = 130,
+ SpvOpFSub = 131,
+ SpvOpIMul = 132,
+ SpvOpFMul = 133,
+ SpvOpUDiv = 134,
+ SpvOpSDiv = 135,
+ SpvOpFDiv = 136,
+ SpvOpUMod = 137,
+ SpvOpSRem = 138,
+ SpvOpSMod = 139,
+ SpvOpFRem = 140,
+ SpvOpFMod = 141,
+ SpvOpVectorTimesScalar = 142,
+ SpvOpMatrixTimesScalar = 143,
+ SpvOpVectorTimesMatrix = 144,
+ SpvOpMatrixTimesVector = 145,
+ SpvOpMatrixTimesMatrix = 146,
+ SpvOpOuterProduct = 147,
+ SpvOpDot = 148,
+ SpvOpIAddCarry = 149,
+ SpvOpISubBorrow = 150,
+ SpvOpUMulExtended = 151,
+ SpvOpSMulExtended = 152,
+ SpvOpAny = 154,
+ SpvOpAll = 155,
+ SpvOpIsNan = 156,
+ SpvOpIsInf = 157,
+ SpvOpIsFinite = 158,
+ SpvOpIsNormal = 159,
+ SpvOpSignBitSet = 160,
+ SpvOpLessOrGreater = 161,
+ SpvOpOrdered = 162,
+ SpvOpUnordered = 163,
+ SpvOpLogicalEqual = 164,
+ SpvOpLogicalNotEqual = 165,
+ SpvOpLogicalOr = 166,
+ SpvOpLogicalAnd = 167,
+ SpvOpLogicalNot = 168,
+ SpvOpSelect = 169,
+ SpvOpIEqual = 170,
+ SpvOpINotEqual = 171,
+ SpvOpUGreaterThan = 172,
+ SpvOpSGreaterThan = 173,
+ SpvOpUGreaterThanEqual = 174,
+ SpvOpSGreaterThanEqual = 175,
+ SpvOpULessThan = 176,
+ SpvOpSLessThan = 177,
+ SpvOpULessThanEqual = 178,
+ SpvOpSLessThanEqual = 179,
+ SpvOpFOrdEqual = 180,
+ SpvOpFUnordEqual = 181,
+ SpvOpFOrdNotEqual = 182,
+ SpvOpFUnordNotEqual = 183,
+ SpvOpFOrdLessThan = 184,
+ SpvOpFUnordLessThan = 185,
+ SpvOpFOrdGreaterThan = 186,
+ SpvOpFUnordGreaterThan = 187,
+ SpvOpFOrdLessThanEqual = 188,
+ SpvOpFUnordLessThanEqual = 189,
+ SpvOpFOrdGreaterThanEqual = 190,
+ SpvOpFUnordGreaterThanEqual = 191,
+ SpvOpShiftRightLogical = 194,
+ SpvOpShiftRightArithmetic = 195,
+ SpvOpShiftLeftLogical = 196,
+ SpvOpBitwiseOr = 197,
+ SpvOpBitwiseXor = 198,
+ SpvOpBitwiseAnd = 199,
+ SpvOpNot = 200,
+ SpvOpBitFieldInsert = 201,
+ SpvOpBitFieldSExtract = 202,
+ SpvOpBitFieldUExtract = 203,
+ SpvOpBitReverse = 204,
+ SpvOpBitCount = 205,
+ SpvOpDPdx = 207,
+ SpvOpDPdy = 208,
+ SpvOpFwidth = 209,
+ SpvOpDPdxFine = 210,
+ SpvOpDPdyFine = 211,
+ SpvOpFwidthFine = 212,
+ SpvOpDPdxCoarse = 213,
+ SpvOpDPdyCoarse = 214,
+ SpvOpFwidthCoarse = 215,
+ SpvOpEmitVertex = 218,
+ SpvOpEndPrimitive = 219,
+ SpvOpEmitStreamVertex = 220,
+ SpvOpEndStreamPrimitive = 221,
+ SpvOpControlBarrier = 224,
+ SpvOpMemoryBarrier = 225,
+ SpvOpAtomicLoad = 227,
+ SpvOpAtomicStore = 228,
+ SpvOpAtomicExchange = 229,
+ SpvOpAtomicCompareExchange = 230,
+ SpvOpAtomicCompareExchangeWeak = 231,
+ SpvOpAtomicIIncrement = 232,
+ SpvOpAtomicIDecrement = 233,
+ SpvOpAtomicIAdd = 234,
+ SpvOpAtomicISub = 235,
+ SpvOpAtomicSMin = 236,
+ SpvOpAtomicUMin = 237,
+ SpvOpAtomicSMax = 238,
+ SpvOpAtomicUMax = 239,
+ SpvOpAtomicAnd = 240,
+ SpvOpAtomicOr = 241,
+ SpvOpAtomicXor = 242,
+ SpvOpPhi = 245,
+ SpvOpLoopMerge = 246,
+ SpvOpSelectionMerge = 247,
+ SpvOpLabel = 248,
+ SpvOpBranch = 249,
+ SpvOpBranchConditional = 250,
+ SpvOpSwitch = 251,
+ SpvOpKill = 252,
+ SpvOpReturn = 253,
+ SpvOpReturnValue = 254,
+ SpvOpUnreachable = 255,
+ SpvOpLifetimeStart = 256,
+ SpvOpLifetimeStop = 257,
+ SpvOpGroupAsyncCopy = 259,
+ SpvOpGroupWaitEvents = 260,
+ SpvOpGroupAll = 261,
+ SpvOpGroupAny = 262,
+ SpvOpGroupBroadcast = 263,
+ SpvOpGroupIAdd = 264,
+ SpvOpGroupFAdd = 265,
+ SpvOpGroupFMin = 266,
+ SpvOpGroupUMin = 267,
+ SpvOpGroupSMin = 268,
+ SpvOpGroupFMax = 269,
+ SpvOpGroupUMax = 270,
+ SpvOpGroupSMax = 271,
+ SpvOpReadPipe = 274,
+ SpvOpWritePipe = 275,
+ SpvOpReservedReadPipe = 276,
+ SpvOpReservedWritePipe = 277,
+ SpvOpReserveReadPipePackets = 278,
+ SpvOpReserveWritePipePackets = 279,
+ SpvOpCommitReadPipe = 280,
+ SpvOpCommitWritePipe = 281,
+ SpvOpIsValidReserveId = 282,
+ SpvOpGetNumPipePackets = 283,
+ SpvOpGetMaxPipePackets = 284,
+ SpvOpGroupReserveReadPipePackets = 285,
+ SpvOpGroupReserveWritePipePackets = 286,
+ SpvOpGroupCommitReadPipe = 287,
+ SpvOpGroupCommitWritePipe = 288,
+ SpvOpEnqueueMarker = 291,
+ SpvOpEnqueueKernel = 292,
+ SpvOpGetKernelNDrangeSubGroupCount = 293,
+ SpvOpGetKernelNDrangeMaxSubGroupSize = 294,
+ SpvOpGetKernelWorkGroupSize = 295,
+ SpvOpGetKernelPreferredWorkGroupSizeMultiple = 296,
+ SpvOpRetainEvent = 297,
+ SpvOpReleaseEvent = 298,
+ SpvOpCreateUserEvent = 299,
+ SpvOpIsValidEvent = 300,
+ SpvOpSetUserEventStatus = 301,
+ SpvOpCaptureEventProfilingInfo = 302,
+ SpvOpGetDefaultQueue = 303,
+ SpvOpBuildNDRange = 304,
+ SpvOpImageSparseSampleImplicitLod = 305,
+ SpvOpImageSparseSampleExplicitLod = 306,
+ SpvOpImageSparseSampleDrefImplicitLod = 307,
+ SpvOpImageSparseSampleDrefExplicitLod = 308,
+ SpvOpImageSparseSampleProjImplicitLod = 309,
+ SpvOpImageSparseSampleProjExplicitLod = 310,
+ SpvOpImageSparseSampleProjDrefImplicitLod = 311,
+ SpvOpImageSparseSampleProjDrefExplicitLod = 312,
+ SpvOpImageSparseFetch = 313,
+ SpvOpImageSparseGather = 314,
+ SpvOpImageSparseDrefGather = 315,
+ SpvOpImageSparseTexelsResident = 316,
+ SpvOpNoLine = 317,
+ SpvOpAtomicFlagTestAndSet = 318,
+ SpvOpAtomicFlagClear = 319,
+ SpvOpImageSparseRead = 320,
+ SpvOpSizeOf = 321,
+ SpvOpTypePipeStorage = 322,
+ SpvOpConstantPipeStorage = 323,
+ SpvOpCreatePipeFromPipeStorage = 324,
+ SpvOpGetKernelLocalSizeForSubgroupCount = 325,
+ SpvOpGetKernelMaxNumSubgroups = 326,
+ SpvOpTypeNamedBarrier = 327,
+ SpvOpNamedBarrierInitialize = 328,
+ SpvOpMemoryNamedBarrier = 329,
+ SpvOpModuleProcessed = 330,
+ SpvOpExecutionModeId = 331,
+ SpvOpDecorateId = 332,
+ SpvOpGroupNonUniformElect = 333,
+ SpvOpGroupNonUniformAll = 334,
+ SpvOpGroupNonUniformAny = 335,
+ SpvOpGroupNonUniformAllEqual = 336,
+ SpvOpGroupNonUniformBroadcast = 337,
+ SpvOpGroupNonUniformBroadcastFirst = 338,
+ SpvOpGroupNonUniformBallot = 339,
+ SpvOpGroupNonUniformInverseBallot = 340,
+ SpvOpGroupNonUniformBallotBitExtract = 341,
+ SpvOpGroupNonUniformBallotBitCount = 342,
+ SpvOpGroupNonUniformBallotFindLSB = 343,
+ SpvOpGroupNonUniformBallotFindMSB = 344,
+ SpvOpGroupNonUniformShuffle = 345,
+ SpvOpGroupNonUniformShuffleXor = 346,
+ SpvOpGroupNonUniformShuffleUp = 347,
+ SpvOpGroupNonUniformShuffleDown = 348,
+ SpvOpGroupNonUniformIAdd = 349,
+ SpvOpGroupNonUniformFAdd = 350,
+ SpvOpGroupNonUniformIMul = 351,
+ SpvOpGroupNonUniformFMul = 352,
+ SpvOpGroupNonUniformSMin = 353,
+ SpvOpGroupNonUniformUMin = 354,
+ SpvOpGroupNonUniformFMin = 355,
+ SpvOpGroupNonUniformSMax = 356,
+ SpvOpGroupNonUniformUMax = 357,
+ SpvOpGroupNonUniformFMax = 358,
+ SpvOpGroupNonUniformBitwiseAnd = 359,
+ SpvOpGroupNonUniformBitwiseOr = 360,
+ SpvOpGroupNonUniformBitwiseXor = 361,
+ SpvOpGroupNonUniformLogicalAnd = 362,
+ SpvOpGroupNonUniformLogicalOr = 363,
+ SpvOpGroupNonUniformLogicalXor = 364,
+ SpvOpGroupNonUniformQuadBroadcast = 365,
+ SpvOpGroupNonUniformQuadSwap = 366,
+ SpvOpSubgroupBallotKHR = 4421,
+ SpvOpSubgroupFirstInvocationKHR = 4422,
+ SpvOpSubgroupAllKHR = 4428,
+ SpvOpSubgroupAnyKHR = 4429,
+ SpvOpSubgroupAllEqualKHR = 4430,
+ SpvOpSubgroupReadInvocationKHR = 4432,
+ SpvOpGroupIAddNonUniformAMD = 5000,
+ SpvOpGroupFAddNonUniformAMD = 5001,
+ SpvOpGroupFMinNonUniformAMD = 5002,
+ SpvOpGroupUMinNonUniformAMD = 5003,
+ SpvOpGroupSMinNonUniformAMD = 5004,
+ SpvOpGroupFMaxNonUniformAMD = 5005,
+ SpvOpGroupUMaxNonUniformAMD = 5006,
+ SpvOpGroupSMaxNonUniformAMD = 5007,
+ SpvOpFragmentMaskFetchAMD = 5011,
+ SpvOpFragmentFetchAMD = 5012,
+ SpvOpImageSampleFootprintNV = 5283,
+ SpvOpGroupNonUniformPartitionNV = 5296,
+ SpvOpWritePackedPrimitiveIndices4x8NV = 5299,
+ SpvOpReportIntersectionNV = 5334,
+ SpvOpIgnoreIntersectionNV = 5335,
+ SpvOpTerminateRayNV = 5336,
+ SpvOpTraceNV = 5337,
+ SpvOpTypeAccelerationStructureNV = 5341,
+ SpvOpExecuteCallableNV = 5344,
+ SpvOpTypeCooperativeMatrixNV = 5358,
+ SpvOpCooperativeMatrixLoadNV = 5359,
+ SpvOpCooperativeMatrixStoreNV = 5360,
+ SpvOpCooperativeMatrixMulAddNV = 5361,
+ SpvOpCooperativeMatrixLengthNV = 5362,
+ SpvOpSubgroupShuffleINTEL = 5571,
+ SpvOpSubgroupShuffleDownINTEL = 5572,
+ SpvOpSubgroupShuffleUpINTEL = 5573,
+ SpvOpSubgroupShuffleXorINTEL = 5574,
+ SpvOpSubgroupBlockReadINTEL = 5575,
+ SpvOpSubgroupBlockWriteINTEL = 5576,
+ SpvOpSubgroupImageBlockReadINTEL = 5577,
+ SpvOpSubgroupImageBlockWriteINTEL = 5578,
+ SpvOpSubgroupImageMediaBlockReadINTEL = 5580,
+ SpvOpSubgroupImageMediaBlockWriteINTEL = 5581,
+ SpvOpDecorateStringGOOGLE = 5632,
+ SpvOpMemberDecorateStringGOOGLE = 5633,
+ SpvOpMax = 0x7fffffff,
+} SpvOp;
+
+#endif
+
diff --git a/src/3rdparty/SPIRV-Cross/spirv.hpp b/src/3rdparty/SPIRV-Cross/spirv.hpp
new file mode 100644
index 0000000..adc13de
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv.hpp
@@ -0,0 +1,1216 @@
+// Copyright (c) 2014-2019 The Khronos Group Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and/or associated documentation files (the "Materials"),
+// to deal in the Materials without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Materials, and to permit persons to whom the
+// Materials are furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
+// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
+// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
+// IN THE MATERIALS.
+
+// This header is automatically generated by the same tool that creates
+// the Binary Section of the SPIR-V specification.
+
+// Enumeration tokens for SPIR-V, in various styles:
+// C, C++, C++11, JSON, Lua, Python, C#, D
+//
+// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL
+// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL
+// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL
+// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL
+// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']
+// - C# will use enum classes in the Specification class located in the "Spv" namespace,
+// e.g.: Spv.Specification.SourceLanguage.GLSL
+// - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL
+//
+// Some tokens act like mask values, which can be OR'd together,
+// while others are mutually exclusive. The mask-like ones have
+// "Mask" in their name, and a parallel enum that has the shift
+// amount (1 << x) for each corresponding enumerant.
+
+#ifndef spirv_HPP
+#define spirv_HPP
+
+namespace spv {
+
+typedef unsigned int Id;
+
+#define SPV_VERSION 0x10300
+#define SPV_REVISION 6
+
+static const unsigned int MagicNumber = 0x07230203;
+static const unsigned int Version = 0x00010300;
+static const unsigned int Revision = 6;
+static const unsigned int OpCodeMask = 0xffff;
+static const unsigned int WordCountShift = 16;
+
+enum SourceLanguage {
+ SourceLanguageUnknown = 0,
+ SourceLanguageESSL = 1,
+ SourceLanguageGLSL = 2,
+ SourceLanguageOpenCL_C = 3,
+ SourceLanguageOpenCL_CPP = 4,
+ SourceLanguageHLSL = 5,
+ SourceLanguageMax = 0x7fffffff,
+};
+
+enum ExecutionModel {
+ ExecutionModelVertex = 0,
+ ExecutionModelTessellationControl = 1,
+ ExecutionModelTessellationEvaluation = 2,
+ ExecutionModelGeometry = 3,
+ ExecutionModelFragment = 4,
+ ExecutionModelGLCompute = 5,
+ ExecutionModelKernel = 6,
+ ExecutionModelTaskNV = 5267,
+ ExecutionModelMeshNV = 5268,
+ ExecutionModelRayGenerationNV = 5313,
+ ExecutionModelIntersectionNV = 5314,
+ ExecutionModelAnyHitNV = 5315,
+ ExecutionModelClosestHitNV = 5316,
+ ExecutionModelMissNV = 5317,
+ ExecutionModelCallableNV = 5318,
+ ExecutionModelMax = 0x7fffffff,
+};
+
+enum AddressingModel {
+ AddressingModelLogical = 0,
+ AddressingModelPhysical32 = 1,
+ AddressingModelPhysical64 = 2,
+ AddressingModelPhysicalStorageBuffer64EXT = 5348,
+ AddressingModelMax = 0x7fffffff,
+};
+
+enum MemoryModel {
+ MemoryModelSimple = 0,
+ MemoryModelGLSL450 = 1,
+ MemoryModelOpenCL = 2,
+ MemoryModelVulkanKHR = 3,
+ MemoryModelMax = 0x7fffffff,
+};
+
+enum ExecutionMode {
+ ExecutionModeInvocations = 0,
+ ExecutionModeSpacingEqual = 1,
+ ExecutionModeSpacingFractionalEven = 2,
+ ExecutionModeSpacingFractionalOdd = 3,
+ ExecutionModeVertexOrderCw = 4,
+ ExecutionModeVertexOrderCcw = 5,
+ ExecutionModePixelCenterInteger = 6,
+ ExecutionModeOriginUpperLeft = 7,
+ ExecutionModeOriginLowerLeft = 8,
+ ExecutionModeEarlyFragmentTests = 9,
+ ExecutionModePointMode = 10,
+ ExecutionModeXfb = 11,
+ ExecutionModeDepthReplacing = 12,
+ ExecutionModeDepthGreater = 14,
+ ExecutionModeDepthLess = 15,
+ ExecutionModeDepthUnchanged = 16,
+ ExecutionModeLocalSize = 17,
+ ExecutionModeLocalSizeHint = 18,
+ ExecutionModeInputPoints = 19,
+ ExecutionModeInputLines = 20,
+ ExecutionModeInputLinesAdjacency = 21,
+ ExecutionModeTriangles = 22,
+ ExecutionModeInputTrianglesAdjacency = 23,
+ ExecutionModeQuads = 24,
+ ExecutionModeIsolines = 25,
+ ExecutionModeOutputVertices = 26,
+ ExecutionModeOutputPoints = 27,
+ ExecutionModeOutputLineStrip = 28,
+ ExecutionModeOutputTriangleStrip = 29,
+ ExecutionModeVecTypeHint = 30,
+ ExecutionModeContractionOff = 31,
+ ExecutionModeInitializer = 33,
+ ExecutionModeFinalizer = 34,
+ ExecutionModeSubgroupSize = 35,
+ ExecutionModeSubgroupsPerWorkgroup = 36,
+ ExecutionModeSubgroupsPerWorkgroupId = 37,
+ ExecutionModeLocalSizeId = 38,
+ ExecutionModeLocalSizeHintId = 39,
+ ExecutionModePostDepthCoverage = 4446,
+ ExecutionModeDenormPreserve = 4459,
+ ExecutionModeDenormFlushToZero = 4460,
+ ExecutionModeSignedZeroInfNanPreserve = 4461,
+ ExecutionModeRoundingModeRTE = 4462,
+ ExecutionModeRoundingModeRTZ = 4463,
+ ExecutionModeStencilRefReplacingEXT = 5027,
+ ExecutionModeOutputLinesNV = 5269,
+ ExecutionModeOutputPrimitivesNV = 5270,
+ ExecutionModeDerivativeGroupQuadsNV = 5289,
+ ExecutionModeDerivativeGroupLinearNV = 5290,
+ ExecutionModeOutputTrianglesNV = 5298,
+ ExecutionModeMax = 0x7fffffff,
+};
+
+enum StorageClass {
+ StorageClassUniformConstant = 0,
+ StorageClassInput = 1,
+ StorageClassUniform = 2,
+ StorageClassOutput = 3,
+ StorageClassWorkgroup = 4,
+ StorageClassCrossWorkgroup = 5,
+ StorageClassPrivate = 6,
+ StorageClassFunction = 7,
+ StorageClassGeneric = 8,
+ StorageClassPushConstant = 9,
+ StorageClassAtomicCounter = 10,
+ StorageClassImage = 11,
+ StorageClassStorageBuffer = 12,
+ StorageClassCallableDataNV = 5328,
+ StorageClassIncomingCallableDataNV = 5329,
+ StorageClassRayPayloadNV = 5338,
+ StorageClassHitAttributeNV = 5339,
+ StorageClassIncomingRayPayloadNV = 5342,
+ StorageClassShaderRecordBufferNV = 5343,
+ StorageClassPhysicalStorageBufferEXT = 5349,
+ StorageClassMax = 0x7fffffff,
+};
+
+enum Dim {
+ Dim1D = 0,
+ Dim2D = 1,
+ Dim3D = 2,
+ DimCube = 3,
+ DimRect = 4,
+ DimBuffer = 5,
+ DimSubpassData = 6,
+ DimMax = 0x7fffffff,
+};
+
+enum SamplerAddressingMode {
+ SamplerAddressingModeNone = 0,
+ SamplerAddressingModeClampToEdge = 1,
+ SamplerAddressingModeClamp = 2,
+ SamplerAddressingModeRepeat = 3,
+ SamplerAddressingModeRepeatMirrored = 4,
+ SamplerAddressingModeMax = 0x7fffffff,
+};
+
+enum SamplerFilterMode {
+ SamplerFilterModeNearest = 0,
+ SamplerFilterModeLinear = 1,
+ SamplerFilterModeMax = 0x7fffffff,
+};
+
+enum ImageFormat {
+ ImageFormatUnknown = 0,
+ ImageFormatRgba32f = 1,
+ ImageFormatRgba16f = 2,
+ ImageFormatR32f = 3,
+ ImageFormatRgba8 = 4,
+ ImageFormatRgba8Snorm = 5,
+ ImageFormatRg32f = 6,
+ ImageFormatRg16f = 7,
+ ImageFormatR11fG11fB10f = 8,
+ ImageFormatR16f = 9,
+ ImageFormatRgba16 = 10,
+ ImageFormatRgb10A2 = 11,
+ ImageFormatRg16 = 12,
+ ImageFormatRg8 = 13,
+ ImageFormatR16 = 14,
+ ImageFormatR8 = 15,
+ ImageFormatRgba16Snorm = 16,
+ ImageFormatRg16Snorm = 17,
+ ImageFormatRg8Snorm = 18,
+ ImageFormatR16Snorm = 19,
+ ImageFormatR8Snorm = 20,
+ ImageFormatRgba32i = 21,
+ ImageFormatRgba16i = 22,
+ ImageFormatRgba8i = 23,
+ ImageFormatR32i = 24,
+ ImageFormatRg32i = 25,
+ ImageFormatRg16i = 26,
+ ImageFormatRg8i = 27,
+ ImageFormatR16i = 28,
+ ImageFormatR8i = 29,
+ ImageFormatRgba32ui = 30,
+ ImageFormatRgba16ui = 31,
+ ImageFormatRgba8ui = 32,
+ ImageFormatR32ui = 33,
+ ImageFormatRgb10a2ui = 34,
+ ImageFormatRg32ui = 35,
+ ImageFormatRg16ui = 36,
+ ImageFormatRg8ui = 37,
+ ImageFormatR16ui = 38,
+ ImageFormatR8ui = 39,
+ ImageFormatMax = 0x7fffffff,
+};
+
+enum ImageChannelOrder {
+ ImageChannelOrderR = 0,
+ ImageChannelOrderA = 1,
+ ImageChannelOrderRG = 2,
+ ImageChannelOrderRA = 3,
+ ImageChannelOrderRGB = 4,
+ ImageChannelOrderRGBA = 5,
+ ImageChannelOrderBGRA = 6,
+ ImageChannelOrderARGB = 7,
+ ImageChannelOrderIntensity = 8,
+ ImageChannelOrderLuminance = 9,
+ ImageChannelOrderRx = 10,
+ ImageChannelOrderRGx = 11,
+ ImageChannelOrderRGBx = 12,
+ ImageChannelOrderDepth = 13,
+ ImageChannelOrderDepthStencil = 14,
+ ImageChannelOrdersRGB = 15,
+ ImageChannelOrdersRGBx = 16,
+ ImageChannelOrdersRGBA = 17,
+ ImageChannelOrdersBGRA = 18,
+ ImageChannelOrderABGR = 19,
+ ImageChannelOrderMax = 0x7fffffff,
+};
+
+enum ImageChannelDataType {
+ ImageChannelDataTypeSnormInt8 = 0,
+ ImageChannelDataTypeSnormInt16 = 1,
+ ImageChannelDataTypeUnormInt8 = 2,
+ ImageChannelDataTypeUnormInt16 = 3,
+ ImageChannelDataTypeUnormShort565 = 4,
+ ImageChannelDataTypeUnormShort555 = 5,
+ ImageChannelDataTypeUnormInt101010 = 6,
+ ImageChannelDataTypeSignedInt8 = 7,
+ ImageChannelDataTypeSignedInt16 = 8,
+ ImageChannelDataTypeSignedInt32 = 9,
+ ImageChannelDataTypeUnsignedInt8 = 10,
+ ImageChannelDataTypeUnsignedInt16 = 11,
+ ImageChannelDataTypeUnsignedInt32 = 12,
+ ImageChannelDataTypeHalfFloat = 13,
+ ImageChannelDataTypeFloat = 14,
+ ImageChannelDataTypeUnormInt24 = 15,
+ ImageChannelDataTypeUnormInt101010_2 = 16,
+ ImageChannelDataTypeMax = 0x7fffffff,
+};
+
+enum ImageOperandsShift {
+ ImageOperandsBiasShift = 0,
+ ImageOperandsLodShift = 1,
+ ImageOperandsGradShift = 2,
+ ImageOperandsConstOffsetShift = 3,
+ ImageOperandsOffsetShift = 4,
+ ImageOperandsConstOffsetsShift = 5,
+ ImageOperandsSampleShift = 6,
+ ImageOperandsMinLodShift = 7,
+ ImageOperandsMakeTexelAvailableKHRShift = 8,
+ ImageOperandsMakeTexelVisibleKHRShift = 9,
+ ImageOperandsNonPrivateTexelKHRShift = 10,
+ ImageOperandsVolatileTexelKHRShift = 11,
+ ImageOperandsMax = 0x7fffffff,
+};
+
+enum ImageOperandsMask {
+ ImageOperandsMaskNone = 0,
+ ImageOperandsBiasMask = 0x00000001,
+ ImageOperandsLodMask = 0x00000002,
+ ImageOperandsGradMask = 0x00000004,
+ ImageOperandsConstOffsetMask = 0x00000008,
+ ImageOperandsOffsetMask = 0x00000010,
+ ImageOperandsConstOffsetsMask = 0x00000020,
+ ImageOperandsSampleMask = 0x00000040,
+ ImageOperandsMinLodMask = 0x00000080,
+ ImageOperandsMakeTexelAvailableKHRMask = 0x00000100,
+ ImageOperandsMakeTexelVisibleKHRMask = 0x00000200,
+ ImageOperandsNonPrivateTexelKHRMask = 0x00000400,
+ ImageOperandsVolatileTexelKHRMask = 0x00000800,
+};
+
+enum FPFastMathModeShift {
+ FPFastMathModeNotNaNShift = 0,
+ FPFastMathModeNotInfShift = 1,
+ FPFastMathModeNSZShift = 2,
+ FPFastMathModeAllowRecipShift = 3,
+ FPFastMathModeFastShift = 4,
+ FPFastMathModeMax = 0x7fffffff,
+};
+
+enum FPFastMathModeMask {
+ FPFastMathModeMaskNone = 0,
+ FPFastMathModeNotNaNMask = 0x00000001,
+ FPFastMathModeNotInfMask = 0x00000002,
+ FPFastMathModeNSZMask = 0x00000004,
+ FPFastMathModeAllowRecipMask = 0x00000008,
+ FPFastMathModeFastMask = 0x00000010,
+};
+
+enum FPRoundingMode {
+ FPRoundingModeRTE = 0,
+ FPRoundingModeRTZ = 1,
+ FPRoundingModeRTP = 2,
+ FPRoundingModeRTN = 3,
+ FPRoundingModeMax = 0x7fffffff,
+};
+
+enum LinkageType {
+ LinkageTypeExport = 0,
+ LinkageTypeImport = 1,
+ LinkageTypeMax = 0x7fffffff,
+};
+
+enum AccessQualifier {
+ AccessQualifierReadOnly = 0,
+ AccessQualifierWriteOnly = 1,
+ AccessQualifierReadWrite = 2,
+ AccessQualifierMax = 0x7fffffff,
+};
+
+enum FunctionParameterAttribute {
+ FunctionParameterAttributeZext = 0,
+ FunctionParameterAttributeSext = 1,
+ FunctionParameterAttributeByVal = 2,
+ FunctionParameterAttributeSret = 3,
+ FunctionParameterAttributeNoAlias = 4,
+ FunctionParameterAttributeNoCapture = 5,
+ FunctionParameterAttributeNoWrite = 6,
+ FunctionParameterAttributeNoReadWrite = 7,
+ FunctionParameterAttributeMax = 0x7fffffff,
+};
+
+enum Decoration {
+ DecorationRelaxedPrecision = 0,
+ DecorationSpecId = 1,
+ DecorationBlock = 2,
+ DecorationBufferBlock = 3,
+ DecorationRowMajor = 4,
+ DecorationColMajor = 5,
+ DecorationArrayStride = 6,
+ DecorationMatrixStride = 7,
+ DecorationGLSLShared = 8,
+ DecorationGLSLPacked = 9,
+ DecorationCPacked = 10,
+ DecorationBuiltIn = 11,
+ DecorationNoPerspective = 13,
+ DecorationFlat = 14,
+ DecorationPatch = 15,
+ DecorationCentroid = 16,
+ DecorationSample = 17,
+ DecorationInvariant = 18,
+ DecorationRestrict = 19,
+ DecorationAliased = 20,
+ DecorationVolatile = 21,
+ DecorationConstant = 22,
+ DecorationCoherent = 23,
+ DecorationNonWritable = 24,
+ DecorationNonReadable = 25,
+ DecorationUniform = 26,
+ DecorationSaturatedConversion = 28,
+ DecorationStream = 29,
+ DecorationLocation = 30,
+ DecorationComponent = 31,
+ DecorationIndex = 32,
+ DecorationBinding = 33,
+ DecorationDescriptorSet = 34,
+ DecorationOffset = 35,
+ DecorationXfbBuffer = 36,
+ DecorationXfbStride = 37,
+ DecorationFuncParamAttr = 38,
+ DecorationFPRoundingMode = 39,
+ DecorationFPFastMathMode = 40,
+ DecorationLinkageAttributes = 41,
+ DecorationNoContraction = 42,
+ DecorationInputAttachmentIndex = 43,
+ DecorationAlignment = 44,
+ DecorationMaxByteOffset = 45,
+ DecorationAlignmentId = 46,
+ DecorationMaxByteOffsetId = 47,
+ DecorationNoSignedWrap = 4469,
+ DecorationNoUnsignedWrap = 4470,
+ DecorationExplicitInterpAMD = 4999,
+ DecorationOverrideCoverageNV = 5248,
+ DecorationPassthroughNV = 5250,
+ DecorationViewportRelativeNV = 5252,
+ DecorationSecondaryViewportRelativeNV = 5256,
+ DecorationPerPrimitiveNV = 5271,
+ DecorationPerViewNV = 5272,
+ DecorationPerTaskNV = 5273,
+ DecorationPerVertexNV = 5285,
+ DecorationNonUniformEXT = 5300,
+ DecorationRestrictPointerEXT = 5355,
+ DecorationAliasedPointerEXT = 5356,
+ DecorationHlslCounterBufferGOOGLE = 5634,
+ DecorationHlslSemanticGOOGLE = 5635,
+ DecorationMax = 0x7fffffff,
+};
+
+enum BuiltIn {
+ BuiltInPosition = 0,
+ BuiltInPointSize = 1,
+ BuiltInClipDistance = 3,
+ BuiltInCullDistance = 4,
+ BuiltInVertexId = 5,
+ BuiltInInstanceId = 6,
+ BuiltInPrimitiveId = 7,
+ BuiltInInvocationId = 8,
+ BuiltInLayer = 9,
+ BuiltInViewportIndex = 10,
+ BuiltInTessLevelOuter = 11,
+ BuiltInTessLevelInner = 12,
+ BuiltInTessCoord = 13,
+ BuiltInPatchVertices = 14,
+ BuiltInFragCoord = 15,
+ BuiltInPointCoord = 16,
+ BuiltInFrontFacing = 17,
+ BuiltInSampleId = 18,
+ BuiltInSamplePosition = 19,
+ BuiltInSampleMask = 20,
+ BuiltInFragDepth = 22,
+ BuiltInHelperInvocation = 23,
+ BuiltInNumWorkgroups = 24,
+ BuiltInWorkgroupSize = 25,
+ BuiltInWorkgroupId = 26,
+ BuiltInLocalInvocationId = 27,
+ BuiltInGlobalInvocationId = 28,
+ BuiltInLocalInvocationIndex = 29,
+ BuiltInWorkDim = 30,
+ BuiltInGlobalSize = 31,
+ BuiltInEnqueuedWorkgroupSize = 32,
+ BuiltInGlobalOffset = 33,
+ BuiltInGlobalLinearId = 34,
+ BuiltInSubgroupSize = 36,
+ BuiltInSubgroupMaxSize = 37,
+ BuiltInNumSubgroups = 38,
+ BuiltInNumEnqueuedSubgroups = 39,
+ BuiltInSubgroupId = 40,
+ BuiltInSubgroupLocalInvocationId = 41,
+ BuiltInVertexIndex = 42,
+ BuiltInInstanceIndex = 43,
+ BuiltInSubgroupEqMask = 4416,
+ BuiltInSubgroupEqMaskKHR = 4416,
+ BuiltInSubgroupGeMask = 4417,
+ BuiltInSubgroupGeMaskKHR = 4417,
+ BuiltInSubgroupGtMask = 4418,
+ BuiltInSubgroupGtMaskKHR = 4418,
+ BuiltInSubgroupLeMask = 4419,
+ BuiltInSubgroupLeMaskKHR = 4419,
+ BuiltInSubgroupLtMask = 4420,
+ BuiltInSubgroupLtMaskKHR = 4420,
+ BuiltInBaseVertex = 4424,
+ BuiltInBaseInstance = 4425,
+ BuiltInDrawIndex = 4426,
+ BuiltInDeviceIndex = 4438,
+ BuiltInViewIndex = 4440,
+ BuiltInBaryCoordNoPerspAMD = 4992,
+ BuiltInBaryCoordNoPerspCentroidAMD = 4993,
+ BuiltInBaryCoordNoPerspSampleAMD = 4994,
+ BuiltInBaryCoordSmoothAMD = 4995,
+ BuiltInBaryCoordSmoothCentroidAMD = 4996,
+ BuiltInBaryCoordSmoothSampleAMD = 4997,
+ BuiltInBaryCoordPullModelAMD = 4998,
+ BuiltInFragStencilRefEXT = 5014,
+ BuiltInViewportMaskNV = 5253,
+ BuiltInSecondaryPositionNV = 5257,
+ BuiltInSecondaryViewportMaskNV = 5258,
+ BuiltInPositionPerViewNV = 5261,
+ BuiltInViewportMaskPerViewNV = 5262,
+ BuiltInFullyCoveredEXT = 5264,
+ BuiltInTaskCountNV = 5274,
+ BuiltInPrimitiveCountNV = 5275,
+ BuiltInPrimitiveIndicesNV = 5276,
+ BuiltInClipDistancePerViewNV = 5277,
+ BuiltInCullDistancePerViewNV = 5278,
+ BuiltInLayerPerViewNV = 5279,
+ BuiltInMeshViewCountNV = 5280,
+ BuiltInMeshViewIndicesNV = 5281,
+ BuiltInBaryCoordNV = 5286,
+ BuiltInBaryCoordNoPerspNV = 5287,
+ BuiltInFragSizeEXT = 5292,
+ BuiltInFragmentSizeNV = 5292,
+ BuiltInFragInvocationCountEXT = 5293,
+ BuiltInInvocationsPerPixelNV = 5293,
+ BuiltInLaunchIdNV = 5319,
+ BuiltInLaunchSizeNV = 5320,
+ BuiltInWorldRayOriginNV = 5321,
+ BuiltInWorldRayDirectionNV = 5322,
+ BuiltInObjectRayOriginNV = 5323,
+ BuiltInObjectRayDirectionNV = 5324,
+ BuiltInRayTminNV = 5325,
+ BuiltInRayTmaxNV = 5326,
+ BuiltInInstanceCustomIndexNV = 5327,
+ BuiltInObjectToWorldNV = 5330,
+ BuiltInWorldToObjectNV = 5331,
+ BuiltInHitTNV = 5332,
+ BuiltInHitKindNV = 5333,
+ BuiltInIncomingRayFlagsNV = 5351,
+ BuiltInMax = 0x7fffffff,
+};
+
+enum SelectionControlShift {
+ SelectionControlFlattenShift = 0,
+ SelectionControlDontFlattenShift = 1,
+ SelectionControlMax = 0x7fffffff,
+};
+
+enum SelectionControlMask {
+ SelectionControlMaskNone = 0,
+ SelectionControlFlattenMask = 0x00000001,
+ SelectionControlDontFlattenMask = 0x00000002,
+};
+
+enum LoopControlShift {
+ LoopControlUnrollShift = 0,
+ LoopControlDontUnrollShift = 1,
+ LoopControlDependencyInfiniteShift = 2,
+ LoopControlDependencyLengthShift = 3,
+ LoopControlMax = 0x7fffffff,
+};
+
+enum LoopControlMask {
+ LoopControlMaskNone = 0,
+ LoopControlUnrollMask = 0x00000001,
+ LoopControlDontUnrollMask = 0x00000002,
+ LoopControlDependencyInfiniteMask = 0x00000004,
+ LoopControlDependencyLengthMask = 0x00000008,
+};
+
+enum FunctionControlShift {
+ FunctionControlInlineShift = 0,
+ FunctionControlDontInlineShift = 1,
+ FunctionControlPureShift = 2,
+ FunctionControlConstShift = 3,
+ FunctionControlMax = 0x7fffffff,
+};
+
+enum FunctionControlMask {
+ FunctionControlMaskNone = 0,
+ FunctionControlInlineMask = 0x00000001,
+ FunctionControlDontInlineMask = 0x00000002,
+ FunctionControlPureMask = 0x00000004,
+ FunctionControlConstMask = 0x00000008,
+};
+
+enum MemorySemanticsShift {
+ MemorySemanticsAcquireShift = 1,
+ MemorySemanticsReleaseShift = 2,
+ MemorySemanticsAcquireReleaseShift = 3,
+ MemorySemanticsSequentiallyConsistentShift = 4,
+ MemorySemanticsUniformMemoryShift = 6,
+ MemorySemanticsSubgroupMemoryShift = 7,
+ MemorySemanticsWorkgroupMemoryShift = 8,
+ MemorySemanticsCrossWorkgroupMemoryShift = 9,
+ MemorySemanticsAtomicCounterMemoryShift = 10,
+ MemorySemanticsImageMemoryShift = 11,
+ MemorySemanticsOutputMemoryKHRShift = 12,
+ MemorySemanticsMakeAvailableKHRShift = 13,
+ MemorySemanticsMakeVisibleKHRShift = 14,
+ MemorySemanticsMax = 0x7fffffff,
+};
+
+enum MemorySemanticsMask {
+ MemorySemanticsMaskNone = 0,
+ MemorySemanticsAcquireMask = 0x00000002,
+ MemorySemanticsReleaseMask = 0x00000004,
+ MemorySemanticsAcquireReleaseMask = 0x00000008,
+ MemorySemanticsSequentiallyConsistentMask = 0x00000010,
+ MemorySemanticsUniformMemoryMask = 0x00000040,
+ MemorySemanticsSubgroupMemoryMask = 0x00000080,
+ MemorySemanticsWorkgroupMemoryMask = 0x00000100,
+ MemorySemanticsCrossWorkgroupMemoryMask = 0x00000200,
+ MemorySemanticsAtomicCounterMemoryMask = 0x00000400,
+ MemorySemanticsImageMemoryMask = 0x00000800,
+ MemorySemanticsOutputMemoryKHRMask = 0x00001000,
+ MemorySemanticsMakeAvailableKHRMask = 0x00002000,
+ MemorySemanticsMakeVisibleKHRMask = 0x00004000,
+};
+
+enum MemoryAccessShift {
+ MemoryAccessVolatileShift = 0,
+ MemoryAccessAlignedShift = 1,
+ MemoryAccessNontemporalShift = 2,
+ MemoryAccessMakePointerAvailableKHRShift = 3,
+ MemoryAccessMakePointerVisibleKHRShift = 4,
+ MemoryAccessNonPrivatePointerKHRShift = 5,
+ MemoryAccessMax = 0x7fffffff,
+};
+
+enum MemoryAccessMask {
+ MemoryAccessMaskNone = 0,
+ MemoryAccessVolatileMask = 0x00000001,
+ MemoryAccessAlignedMask = 0x00000002,
+ MemoryAccessNontemporalMask = 0x00000004,
+ MemoryAccessMakePointerAvailableKHRMask = 0x00000008,
+ MemoryAccessMakePointerVisibleKHRMask = 0x00000010,
+ MemoryAccessNonPrivatePointerKHRMask = 0x00000020,
+};
+
+enum Scope {
+ ScopeCrossDevice = 0,
+ ScopeDevice = 1,
+ ScopeWorkgroup = 2,
+ ScopeSubgroup = 3,
+ ScopeInvocation = 4,
+ ScopeQueueFamilyKHR = 5,
+ ScopeMax = 0x7fffffff,
+};
+
+enum GroupOperation {
+ GroupOperationReduce = 0,
+ GroupOperationInclusiveScan = 1,
+ GroupOperationExclusiveScan = 2,
+ GroupOperationClusteredReduce = 3,
+ GroupOperationPartitionedReduceNV = 6,
+ GroupOperationPartitionedInclusiveScanNV = 7,
+ GroupOperationPartitionedExclusiveScanNV = 8,
+ GroupOperationMax = 0x7fffffff,
+};
+
+enum KernelEnqueueFlags {
+ KernelEnqueueFlagsNoWait = 0,
+ KernelEnqueueFlagsWaitKernel = 1,
+ KernelEnqueueFlagsWaitWorkGroup = 2,
+ KernelEnqueueFlagsMax = 0x7fffffff,
+};
+
+enum KernelProfilingInfoShift {
+ KernelProfilingInfoCmdExecTimeShift = 0,
+ KernelProfilingInfoMax = 0x7fffffff,
+};
+
+enum KernelProfilingInfoMask {
+ KernelProfilingInfoMaskNone = 0,
+ KernelProfilingInfoCmdExecTimeMask = 0x00000001,
+};
+
+enum Capability {
+ CapabilityMatrix = 0,
+ CapabilityShader = 1,
+ CapabilityGeometry = 2,
+ CapabilityTessellation = 3,
+ CapabilityAddresses = 4,
+ CapabilityLinkage = 5,
+ CapabilityKernel = 6,
+ CapabilityVector16 = 7,
+ CapabilityFloat16Buffer = 8,
+ CapabilityFloat16 = 9,
+ CapabilityFloat64 = 10,
+ CapabilityInt64 = 11,
+ CapabilityInt64Atomics = 12,
+ CapabilityImageBasic = 13,
+ CapabilityImageReadWrite = 14,
+ CapabilityImageMipmap = 15,
+ CapabilityPipes = 17,
+ CapabilityGroups = 18,
+ CapabilityDeviceEnqueue = 19,
+ CapabilityLiteralSampler = 20,
+ CapabilityAtomicStorage = 21,
+ CapabilityInt16 = 22,
+ CapabilityTessellationPointSize = 23,
+ CapabilityGeometryPointSize = 24,
+ CapabilityImageGatherExtended = 25,
+ CapabilityStorageImageMultisample = 27,
+ CapabilityUniformBufferArrayDynamicIndexing = 28,
+ CapabilitySampledImageArrayDynamicIndexing = 29,
+ CapabilityStorageBufferArrayDynamicIndexing = 30,
+ CapabilityStorageImageArrayDynamicIndexing = 31,
+ CapabilityClipDistance = 32,
+ CapabilityCullDistance = 33,
+ CapabilityImageCubeArray = 34,
+ CapabilitySampleRateShading = 35,
+ CapabilityImageRect = 36,
+ CapabilitySampledRect = 37,
+ CapabilityGenericPointer = 38,
+ CapabilityInt8 = 39,
+ CapabilityInputAttachment = 40,
+ CapabilitySparseResidency = 41,
+ CapabilityMinLod = 42,
+ CapabilitySampled1D = 43,
+ CapabilityImage1D = 44,
+ CapabilitySampledCubeArray = 45,
+ CapabilitySampledBuffer = 46,
+ CapabilityImageBuffer = 47,
+ CapabilityImageMSArray = 48,
+ CapabilityStorageImageExtendedFormats = 49,
+ CapabilityImageQuery = 50,
+ CapabilityDerivativeControl = 51,
+ CapabilityInterpolationFunction = 52,
+ CapabilityTransformFeedback = 53,
+ CapabilityGeometryStreams = 54,
+ CapabilityStorageImageReadWithoutFormat = 55,
+ CapabilityStorageImageWriteWithoutFormat = 56,
+ CapabilityMultiViewport = 57,
+ CapabilitySubgroupDispatch = 58,
+ CapabilityNamedBarrier = 59,
+ CapabilityPipeStorage = 60,
+ CapabilityGroupNonUniform = 61,
+ CapabilityGroupNonUniformVote = 62,
+ CapabilityGroupNonUniformArithmetic = 63,
+ CapabilityGroupNonUniformBallot = 64,
+ CapabilityGroupNonUniformShuffle = 65,
+ CapabilityGroupNonUniformShuffleRelative = 66,
+ CapabilityGroupNonUniformClustered = 67,
+ CapabilityGroupNonUniformQuad = 68,
+ CapabilitySubgroupBallotKHR = 4423,
+ CapabilityDrawParameters = 4427,
+ CapabilitySubgroupVoteKHR = 4431,
+ CapabilityStorageBuffer16BitAccess = 4433,
+ CapabilityStorageUniformBufferBlock16 = 4433,
+ CapabilityStorageUniform16 = 4434,
+ CapabilityUniformAndStorageBuffer16BitAccess = 4434,
+ CapabilityStoragePushConstant16 = 4435,
+ CapabilityStorageInputOutput16 = 4436,
+ CapabilityDeviceGroup = 4437,
+ CapabilityMultiView = 4439,
+ CapabilityVariablePointersStorageBuffer = 4441,
+ CapabilityVariablePointers = 4442,
+ CapabilityAtomicStorageOps = 4445,
+ CapabilitySampleMaskPostDepthCoverage = 4447,
+ CapabilityStorageBuffer8BitAccess = 4448,
+ CapabilityUniformAndStorageBuffer8BitAccess = 4449,
+ CapabilityStoragePushConstant8 = 4450,
+ CapabilityDenormPreserve = 4464,
+ CapabilityDenormFlushToZero = 4465,
+ CapabilitySignedZeroInfNanPreserve = 4466,
+ CapabilityRoundingModeRTE = 4467,
+ CapabilityRoundingModeRTZ = 4468,
+ CapabilityFloat16ImageAMD = 5008,
+ CapabilityImageGatherBiasLodAMD = 5009,
+ CapabilityFragmentMaskAMD = 5010,
+ CapabilityStencilExportEXT = 5013,
+ CapabilityImageReadWriteLodAMD = 5015,
+ CapabilitySampleMaskOverrideCoverageNV = 5249,
+ CapabilityGeometryShaderPassthroughNV = 5251,
+ CapabilityShaderViewportIndexLayerEXT = 5254,
+ CapabilityShaderViewportIndexLayerNV = 5254,
+ CapabilityShaderViewportMaskNV = 5255,
+ CapabilityShaderStereoViewNV = 5259,
+ CapabilityPerViewAttributesNV = 5260,
+ CapabilityFragmentFullyCoveredEXT = 5265,
+ CapabilityMeshShadingNV = 5266,
+ CapabilityImageFootprintNV = 5282,
+ CapabilityFragmentBarycentricNV = 5284,
+ CapabilityComputeDerivativeGroupQuadsNV = 5288,
+ CapabilityFragmentDensityEXT = 5291,
+ CapabilityShadingRateNV = 5291,
+ CapabilityGroupNonUniformPartitionedNV = 5297,
+ CapabilityShaderNonUniformEXT = 5301,
+ CapabilityRuntimeDescriptorArrayEXT = 5302,
+ CapabilityInputAttachmentArrayDynamicIndexingEXT = 5303,
+ CapabilityUniformTexelBufferArrayDynamicIndexingEXT = 5304,
+ CapabilityStorageTexelBufferArrayDynamicIndexingEXT = 5305,
+ CapabilityUniformBufferArrayNonUniformIndexingEXT = 5306,
+ CapabilitySampledImageArrayNonUniformIndexingEXT = 5307,
+ CapabilityStorageBufferArrayNonUniformIndexingEXT = 5308,
+ CapabilityStorageImageArrayNonUniformIndexingEXT = 5309,
+ CapabilityInputAttachmentArrayNonUniformIndexingEXT = 5310,
+ CapabilityUniformTexelBufferArrayNonUniformIndexingEXT = 5311,
+ CapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312,
+ CapabilityRayTracingNV = 5340,
+ CapabilityVulkanMemoryModelKHR = 5345,
+ CapabilityVulkanMemoryModelDeviceScopeKHR = 5346,
+ CapabilityPhysicalStorageBufferAddressesEXT = 5347,
+ CapabilityComputeDerivativeGroupLinearNV = 5350,
+ CapabilitySubgroupShuffleINTEL = 5568,
+ CapabilitySubgroupBufferBlockIOINTEL = 5569,
+ CapabilitySubgroupImageBlockIOINTEL = 5570,
+ CapabilitySubgroupImageMediaBlockIOINTEL = 5579,
+ CapabilityMax = 0x7fffffff,
+};
+
+enum Op {
+ OpNop = 0,
+ OpUndef = 1,
+ OpSourceContinued = 2,
+ OpSource = 3,
+ OpSourceExtension = 4,
+ OpName = 5,
+ OpMemberName = 6,
+ OpString = 7,
+ OpLine = 8,
+ OpExtension = 10,
+ OpExtInstImport = 11,
+ OpExtInst = 12,
+ OpMemoryModel = 14,
+ OpEntryPoint = 15,
+ OpExecutionMode = 16,
+ OpCapability = 17,
+ OpTypeVoid = 19,
+ OpTypeBool = 20,
+ OpTypeInt = 21,
+ OpTypeFloat = 22,
+ OpTypeVector = 23,
+ OpTypeMatrix = 24,
+ OpTypeImage = 25,
+ OpTypeSampler = 26,
+ OpTypeSampledImage = 27,
+ OpTypeArray = 28,
+ OpTypeRuntimeArray = 29,
+ OpTypeStruct = 30,
+ OpTypeOpaque = 31,
+ OpTypePointer = 32,
+ OpTypeFunction = 33,
+ OpTypeEvent = 34,
+ OpTypeDeviceEvent = 35,
+ OpTypeReserveId = 36,
+ OpTypeQueue = 37,
+ OpTypePipe = 38,
+ OpTypeForwardPointer = 39,
+ OpConstantTrue = 41,
+ OpConstantFalse = 42,
+ OpConstant = 43,
+ OpConstantComposite = 44,
+ OpConstantSampler = 45,
+ OpConstantNull = 46,
+ OpSpecConstantTrue = 48,
+ OpSpecConstantFalse = 49,
+ OpSpecConstant = 50,
+ OpSpecConstantComposite = 51,
+ OpSpecConstantOp = 52,
+ OpFunction = 54,
+ OpFunctionParameter = 55,
+ OpFunctionEnd = 56,
+ OpFunctionCall = 57,
+ OpVariable = 59,
+ OpImageTexelPointer = 60,
+ OpLoad = 61,
+ OpStore = 62,
+ OpCopyMemory = 63,
+ OpCopyMemorySized = 64,
+ OpAccessChain = 65,
+ OpInBoundsAccessChain = 66,
+ OpPtrAccessChain = 67,
+ OpArrayLength = 68,
+ OpGenericPtrMemSemantics = 69,
+ OpInBoundsPtrAccessChain = 70,
+ OpDecorate = 71,
+ OpMemberDecorate = 72,
+ OpDecorationGroup = 73,
+ OpGroupDecorate = 74,
+ OpGroupMemberDecorate = 75,
+ OpVectorExtractDynamic = 77,
+ OpVectorInsertDynamic = 78,
+ OpVectorShuffle = 79,
+ OpCompositeConstruct = 80,
+ OpCompositeExtract = 81,
+ OpCompositeInsert = 82,
+ OpCopyObject = 83,
+ OpTranspose = 84,
+ OpSampledImage = 86,
+ OpImageSampleImplicitLod = 87,
+ OpImageSampleExplicitLod = 88,
+ OpImageSampleDrefImplicitLod = 89,
+ OpImageSampleDrefExplicitLod = 90,
+ OpImageSampleProjImplicitLod = 91,
+ OpImageSampleProjExplicitLod = 92,
+ OpImageSampleProjDrefImplicitLod = 93,
+ OpImageSampleProjDrefExplicitLod = 94,
+ OpImageFetch = 95,
+ OpImageGather = 96,
+ OpImageDrefGather = 97,
+ OpImageRead = 98,
+ OpImageWrite = 99,
+ OpImage = 100,
+ OpImageQueryFormat = 101,
+ OpImageQueryOrder = 102,
+ OpImageQuerySizeLod = 103,
+ OpImageQuerySize = 104,
+ OpImageQueryLod = 105,
+ OpImageQueryLevels = 106,
+ OpImageQuerySamples = 107,
+ OpConvertFToU = 109,
+ OpConvertFToS = 110,
+ OpConvertSToF = 111,
+ OpConvertUToF = 112,
+ OpUConvert = 113,
+ OpSConvert = 114,
+ OpFConvert = 115,
+ OpQuantizeToF16 = 116,
+ OpConvertPtrToU = 117,
+ OpSatConvertSToU = 118,
+ OpSatConvertUToS = 119,
+ OpConvertUToPtr = 120,
+ OpPtrCastToGeneric = 121,
+ OpGenericCastToPtr = 122,
+ OpGenericCastToPtrExplicit = 123,
+ OpBitcast = 124,
+ OpSNegate = 126,
+ OpFNegate = 127,
+ OpIAdd = 128,
+ OpFAdd = 129,
+ OpISub = 130,
+ OpFSub = 131,
+ OpIMul = 132,
+ OpFMul = 133,
+ OpUDiv = 134,
+ OpSDiv = 135,
+ OpFDiv = 136,
+ OpUMod = 137,
+ OpSRem = 138,
+ OpSMod = 139,
+ OpFRem = 140,
+ OpFMod = 141,
+ OpVectorTimesScalar = 142,
+ OpMatrixTimesScalar = 143,
+ OpVectorTimesMatrix = 144,
+ OpMatrixTimesVector = 145,
+ OpMatrixTimesMatrix = 146,
+ OpOuterProduct = 147,
+ OpDot = 148,
+ OpIAddCarry = 149,
+ OpISubBorrow = 150,
+ OpUMulExtended = 151,
+ OpSMulExtended = 152,
+ OpAny = 154,
+ OpAll = 155,
+ OpIsNan = 156,
+ OpIsInf = 157,
+ OpIsFinite = 158,
+ OpIsNormal = 159,
+ OpSignBitSet = 160,
+ OpLessOrGreater = 161,
+ OpOrdered = 162,
+ OpUnordered = 163,
+ OpLogicalEqual = 164,
+ OpLogicalNotEqual = 165,
+ OpLogicalOr = 166,
+ OpLogicalAnd = 167,
+ OpLogicalNot = 168,
+ OpSelect = 169,
+ OpIEqual = 170,
+ OpINotEqual = 171,
+ OpUGreaterThan = 172,
+ OpSGreaterThan = 173,
+ OpUGreaterThanEqual = 174,
+ OpSGreaterThanEqual = 175,
+ OpULessThan = 176,
+ OpSLessThan = 177,
+ OpULessThanEqual = 178,
+ OpSLessThanEqual = 179,
+ OpFOrdEqual = 180,
+ OpFUnordEqual = 181,
+ OpFOrdNotEqual = 182,
+ OpFUnordNotEqual = 183,
+ OpFOrdLessThan = 184,
+ OpFUnordLessThan = 185,
+ OpFOrdGreaterThan = 186,
+ OpFUnordGreaterThan = 187,
+ OpFOrdLessThanEqual = 188,
+ OpFUnordLessThanEqual = 189,
+ OpFOrdGreaterThanEqual = 190,
+ OpFUnordGreaterThanEqual = 191,
+ OpShiftRightLogical = 194,
+ OpShiftRightArithmetic = 195,
+ OpShiftLeftLogical = 196,
+ OpBitwiseOr = 197,
+ OpBitwiseXor = 198,
+ OpBitwiseAnd = 199,
+ OpNot = 200,
+ OpBitFieldInsert = 201,
+ OpBitFieldSExtract = 202,
+ OpBitFieldUExtract = 203,
+ OpBitReverse = 204,
+ OpBitCount = 205,
+ OpDPdx = 207,
+ OpDPdy = 208,
+ OpFwidth = 209,
+ OpDPdxFine = 210,
+ OpDPdyFine = 211,
+ OpFwidthFine = 212,
+ OpDPdxCoarse = 213,
+ OpDPdyCoarse = 214,
+ OpFwidthCoarse = 215,
+ OpEmitVertex = 218,
+ OpEndPrimitive = 219,
+ OpEmitStreamVertex = 220,
+ OpEndStreamPrimitive = 221,
+ OpControlBarrier = 224,
+ OpMemoryBarrier = 225,
+ OpAtomicLoad = 227,
+ OpAtomicStore = 228,
+ OpAtomicExchange = 229,
+ OpAtomicCompareExchange = 230,
+ OpAtomicCompareExchangeWeak = 231,
+ OpAtomicIIncrement = 232,
+ OpAtomicIDecrement = 233,
+ OpAtomicIAdd = 234,
+ OpAtomicISub = 235,
+ OpAtomicSMin = 236,
+ OpAtomicUMin = 237,
+ OpAtomicSMax = 238,
+ OpAtomicUMax = 239,
+ OpAtomicAnd = 240,
+ OpAtomicOr = 241,
+ OpAtomicXor = 242,
+ OpPhi = 245,
+ OpLoopMerge = 246,
+ OpSelectionMerge = 247,
+ OpLabel = 248,
+ OpBranch = 249,
+ OpBranchConditional = 250,
+ OpSwitch = 251,
+ OpKill = 252,
+ OpReturn = 253,
+ OpReturnValue = 254,
+ OpUnreachable = 255,
+ OpLifetimeStart = 256,
+ OpLifetimeStop = 257,
+ OpGroupAsyncCopy = 259,
+ OpGroupWaitEvents = 260,
+ OpGroupAll = 261,
+ OpGroupAny = 262,
+ OpGroupBroadcast = 263,
+ OpGroupIAdd = 264,
+ OpGroupFAdd = 265,
+ OpGroupFMin = 266,
+ OpGroupUMin = 267,
+ OpGroupSMin = 268,
+ OpGroupFMax = 269,
+ OpGroupUMax = 270,
+ OpGroupSMax = 271,
+ OpReadPipe = 274,
+ OpWritePipe = 275,
+ OpReservedReadPipe = 276,
+ OpReservedWritePipe = 277,
+ OpReserveReadPipePackets = 278,
+ OpReserveWritePipePackets = 279,
+ OpCommitReadPipe = 280,
+ OpCommitWritePipe = 281,
+ OpIsValidReserveId = 282,
+ OpGetNumPipePackets = 283,
+ OpGetMaxPipePackets = 284,
+ OpGroupReserveReadPipePackets = 285,
+ OpGroupReserveWritePipePackets = 286,
+ OpGroupCommitReadPipe = 287,
+ OpGroupCommitWritePipe = 288,
+ OpEnqueueMarker = 291,
+ OpEnqueueKernel = 292,
+ OpGetKernelNDrangeSubGroupCount = 293,
+ OpGetKernelNDrangeMaxSubGroupSize = 294,
+ OpGetKernelWorkGroupSize = 295,
+ OpGetKernelPreferredWorkGroupSizeMultiple = 296,
+ OpRetainEvent = 297,
+ OpReleaseEvent = 298,
+ OpCreateUserEvent = 299,
+ OpIsValidEvent = 300,
+ OpSetUserEventStatus = 301,
+ OpCaptureEventProfilingInfo = 302,
+ OpGetDefaultQueue = 303,
+ OpBuildNDRange = 304,
+ OpImageSparseSampleImplicitLod = 305,
+ OpImageSparseSampleExplicitLod = 306,
+ OpImageSparseSampleDrefImplicitLod = 307,
+ OpImageSparseSampleDrefExplicitLod = 308,
+ OpImageSparseSampleProjImplicitLod = 309,
+ OpImageSparseSampleProjExplicitLod = 310,
+ OpImageSparseSampleProjDrefImplicitLod = 311,
+ OpImageSparseSampleProjDrefExplicitLod = 312,
+ OpImageSparseFetch = 313,
+ OpImageSparseGather = 314,
+ OpImageSparseDrefGather = 315,
+ OpImageSparseTexelsResident = 316,
+ OpNoLine = 317,
+ OpAtomicFlagTestAndSet = 318,
+ OpAtomicFlagClear = 319,
+ OpImageSparseRead = 320,
+ OpSizeOf = 321,
+ OpTypePipeStorage = 322,
+ OpConstantPipeStorage = 323,
+ OpCreatePipeFromPipeStorage = 324,
+ OpGetKernelLocalSizeForSubgroupCount = 325,
+ OpGetKernelMaxNumSubgroups = 326,
+ OpTypeNamedBarrier = 327,
+ OpNamedBarrierInitialize = 328,
+ OpMemoryNamedBarrier = 329,
+ OpModuleProcessed = 330,
+ OpExecutionModeId = 331,
+ OpDecorateId = 332,
+ OpGroupNonUniformElect = 333,
+ OpGroupNonUniformAll = 334,
+ OpGroupNonUniformAny = 335,
+ OpGroupNonUniformAllEqual = 336,
+ OpGroupNonUniformBroadcast = 337,
+ OpGroupNonUniformBroadcastFirst = 338,
+ OpGroupNonUniformBallot = 339,
+ OpGroupNonUniformInverseBallot = 340,
+ OpGroupNonUniformBallotBitExtract = 341,
+ OpGroupNonUniformBallotBitCount = 342,
+ OpGroupNonUniformBallotFindLSB = 343,
+ OpGroupNonUniformBallotFindMSB = 344,
+ OpGroupNonUniformShuffle = 345,
+ OpGroupNonUniformShuffleXor = 346,
+ OpGroupNonUniformShuffleUp = 347,
+ OpGroupNonUniformShuffleDown = 348,
+ OpGroupNonUniformIAdd = 349,
+ OpGroupNonUniformFAdd = 350,
+ OpGroupNonUniformIMul = 351,
+ OpGroupNonUniformFMul = 352,
+ OpGroupNonUniformSMin = 353,
+ OpGroupNonUniformUMin = 354,
+ OpGroupNonUniformFMin = 355,
+ OpGroupNonUniformSMax = 356,
+ OpGroupNonUniformUMax = 357,
+ OpGroupNonUniformFMax = 358,
+ OpGroupNonUniformBitwiseAnd = 359,
+ OpGroupNonUniformBitwiseOr = 360,
+ OpGroupNonUniformBitwiseXor = 361,
+ OpGroupNonUniformLogicalAnd = 362,
+ OpGroupNonUniformLogicalOr = 363,
+ OpGroupNonUniformLogicalXor = 364,
+ OpGroupNonUniformQuadBroadcast = 365,
+ OpGroupNonUniformQuadSwap = 366,
+ OpSubgroupBallotKHR = 4421,
+ OpSubgroupFirstInvocationKHR = 4422,
+ OpSubgroupAllKHR = 4428,
+ OpSubgroupAnyKHR = 4429,
+ OpSubgroupAllEqualKHR = 4430,
+ OpSubgroupReadInvocationKHR = 4432,
+ OpGroupIAddNonUniformAMD = 5000,
+ OpGroupFAddNonUniformAMD = 5001,
+ OpGroupFMinNonUniformAMD = 5002,
+ OpGroupUMinNonUniformAMD = 5003,
+ OpGroupSMinNonUniformAMD = 5004,
+ OpGroupFMaxNonUniformAMD = 5005,
+ OpGroupUMaxNonUniformAMD = 5006,
+ OpGroupSMaxNonUniformAMD = 5007,
+ OpFragmentMaskFetchAMD = 5011,
+ OpFragmentFetchAMD = 5012,
+ OpImageSampleFootprintNV = 5283,
+ OpGroupNonUniformPartitionNV = 5296,
+ OpWritePackedPrimitiveIndices4x8NV = 5299,
+ OpReportIntersectionNV = 5334,
+ OpIgnoreIntersectionNV = 5335,
+ OpTerminateRayNV = 5336,
+ OpTraceNV = 5337,
+ OpTypeAccelerationStructureNV = 5341,
+ OpExecuteCallableNV = 5344,
+ OpSubgroupShuffleINTEL = 5571,
+ OpSubgroupShuffleDownINTEL = 5572,
+ OpSubgroupShuffleUpINTEL = 5573,
+ OpSubgroupShuffleXorINTEL = 5574,
+ OpSubgroupBlockReadINTEL = 5575,
+ OpSubgroupBlockWriteINTEL = 5576,
+ OpSubgroupImageBlockReadINTEL = 5577,
+ OpSubgroupImageBlockWriteINTEL = 5578,
+ OpSubgroupImageMediaBlockReadINTEL = 5580,
+ OpSubgroupImageMediaBlockWriteINTEL = 5581,
+ OpDecorateStringGOOGLE = 5632,
+ OpMemberDecorateStringGOOGLE = 5633,
+ OpMax = 0x7fffffff,
+};
+
+// Overload operator| for mask bit combining
+
+inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); }
+inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); }
+inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); }
+inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); }
+inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); }
+inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); }
+inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); }
+inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); }
+
+} // end namespace spv
+
+#endif // #ifndef spirv_HPP
+
diff --git a/src/3rdparty/SPIRV-Cross/spirv_cfg.cpp b/src/3rdparty/SPIRV-Cross/spirv_cfg.cpp
new file mode 100644
index 0000000..4ca9ef5
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_cfg.cpp
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2016-2019 Arm Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "spirv_cfg.hpp"
+#include "spirv_cross.hpp"
+#include <algorithm>
+#include <assert.h>
+
+using namespace std;
+
+namespace spirv_cross
+{
+CFG::CFG(Compiler &compiler_, const SPIRFunction &func_)
+ : compiler(compiler_)
+ , func(func_)
+{
+ build_post_order_visit_order();
+ build_immediate_dominators();
+}
+
+uint32_t CFG::find_common_dominator(uint32_t a, uint32_t b) const
+{
+ while (a != b)
+ {
+ if (get_visit_order(a) < get_visit_order(b))
+ a = get_immediate_dominator(a);
+ else
+ b = get_immediate_dominator(b);
+ }
+ return a;
+}
+
+void CFG::build_immediate_dominators()
+{
+ // Traverse the post-order in reverse and build up the immediate dominator tree.
+ immediate_dominators.clear();
+ immediate_dominators[func.entry_block] = func.entry_block;
+
+ for (auto i = post_order.size(); i; i--)
+ {
+ uint32_t block = post_order[i - 1];
+ auto &pred = preceding_edges[block];
+ if (pred.empty()) // This is for the entry block, but we've already set up the dominators.
+ continue;
+
+ for (auto &edge : pred)
+ {
+ if (immediate_dominators[block])
+ {
+ assert(immediate_dominators[edge]);
+ immediate_dominators[block] = find_common_dominator(block, edge);
+ }
+ else
+ immediate_dominators[block] = edge;
+ }
+ }
+}
+
+bool CFG::is_back_edge(uint32_t to) const
+{
+ // We have a back edge if the visit order is set with the temporary magic value 0.
+ // Crossing edges will have already been recorded with a visit order.
+ auto itr = visit_order.find(to);
+ assert(itr != end(visit_order));
+ return itr->second.get() == 0;
+}
+
+bool CFG::post_order_visit(uint32_t block_id)
+{
+ // If we have already branched to this block (back edge), stop recursion.
+ // If our branches are back-edges, we do not record them.
+ // We have to record crossing edges however.
+ if (visit_order[block_id].get() >= 0)
+ return !is_back_edge(block_id);
+
+ // Block back-edges from recursively revisiting ourselves.
+ visit_order[block_id].get() = 0;
+
+ // First visit our branch targets.
+ auto &block = compiler.get<SPIRBlock>(block_id);
+ switch (block.terminator)
+ {
+ case SPIRBlock::Direct:
+ if (post_order_visit(block.next_block))
+ add_branch(block_id, block.next_block);
+ break;
+
+ case SPIRBlock::Select:
+ if (post_order_visit(block.true_block))
+ add_branch(block_id, block.true_block);
+ if (post_order_visit(block.false_block))
+ add_branch(block_id, block.false_block);
+ break;
+
+ case SPIRBlock::MultiSelect:
+ for (auto &target : block.cases)
+ {
+ if (post_order_visit(target.block))
+ add_branch(block_id, target.block);
+ }
+ if (block.default_block && post_order_visit(block.default_block))
+ add_branch(block_id, block.default_block);
+ break;
+
+ default:
+ break;
+ }
+
+ // If this is a loop header, add an implied branch to the merge target.
+ // This is needed to avoid annoying cases with do { ... } while(false) loops often generated by inliners.
+ // To the CFG, this is linear control flow, but we risk picking the do/while scope as our dominating block.
+ // This makes sure that if we are accessing a variable outside the do/while, we choose the loop header as dominator.
+ if (block.merge == SPIRBlock::MergeLoop)
+ add_branch(block_id, block.merge_block);
+
+ // Then visit ourselves. Start counting at one, to let 0 be a magic value for testing back vs. crossing edges.
+ visit_order[block_id].get() = ++visit_count;
+ post_order.push_back(block_id);
+ return true;
+}
+
+void CFG::build_post_order_visit_order()
+{
+ uint32_t block = func.entry_block;
+ visit_count = 0;
+ visit_order.clear();
+ post_order.clear();
+ post_order_visit(block);
+}
+
+void CFG::add_branch(uint32_t from, uint32_t to)
+{
+ const auto add_unique = [](vector<uint32_t> &l, uint32_t value) {
+ auto itr = find(begin(l), end(l), value);
+ if (itr == end(l))
+ l.push_back(value);
+ };
+ add_unique(preceding_edges[to], from);
+ add_unique(succeeding_edges[from], to);
+}
+
+DominatorBuilder::DominatorBuilder(const CFG &cfg_)
+ : cfg(cfg_)
+{
+}
+
+void DominatorBuilder::add_block(uint32_t block)
+{
+ if (!cfg.get_immediate_dominator(block))
+ {
+ // Unreachable block via the CFG, we will never emit this code anyways.
+ return;
+ }
+
+ if (!dominator)
+ {
+ dominator = block;
+ return;
+ }
+
+ if (block != dominator)
+ dominator = cfg.find_common_dominator(block, dominator);
+}
+
+void DominatorBuilder::lift_continue_block_dominator()
+{
+ // It is possible for a continue block to be the dominator of a variable is only accessed inside the while block of a do-while loop.
+ // We cannot safely declare variables inside a continue block, so move any variable declared
+ // in a continue block to the entry block to simplify.
+ // It makes very little sense for a continue block to ever be a dominator, so fall back to the simplest
+ // solution.
+
+ if (!dominator)
+ return;
+
+ auto &block = cfg.get_compiler().get<SPIRBlock>(dominator);
+ auto post_order = cfg.get_visit_order(dominator);
+
+ // If we are branching to a block with a higher post-order traversal index (continue blocks), we have a problem
+ // since we cannot create sensible GLSL code for this, fallback to entry block.
+ bool back_edge_dominator = false;
+ switch (block.terminator)
+ {
+ case SPIRBlock::Direct:
+ if (cfg.get_visit_order(block.next_block) > post_order)
+ back_edge_dominator = true;
+ break;
+
+ case SPIRBlock::Select:
+ if (cfg.get_visit_order(block.true_block) > post_order)
+ back_edge_dominator = true;
+ if (cfg.get_visit_order(block.false_block) > post_order)
+ back_edge_dominator = true;
+ break;
+
+ case SPIRBlock::MultiSelect:
+ for (auto &target : block.cases)
+ {
+ if (cfg.get_visit_order(target.block) > post_order)
+ back_edge_dominator = true;
+ }
+ if (block.default_block && cfg.get_visit_order(block.default_block) > post_order)
+ back_edge_dominator = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (back_edge_dominator)
+ dominator = cfg.get_function().entry_block;
+}
+} // namespace spirv_cross
diff --git a/src/3rdparty/SPIRV-Cross/spirv_cfg.hpp b/src/3rdparty/SPIRV-Cross/spirv_cfg.hpp
new file mode 100644
index 0000000..5e89320
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_cfg.hpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2016-2019 Arm Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SPIRV_CROSS_CFG_HPP
+#define SPIRV_CROSS_CFG_HPP
+
+#include "spirv_common.hpp"
+#include <assert.h>
+
+namespace spirv_cross
+{
+class Compiler;
+class CFG
+{
+public:
+ CFG(Compiler &compiler, const SPIRFunction &function);
+
+ Compiler &get_compiler()
+ {
+ return compiler;
+ }
+
+ const Compiler &get_compiler() const
+ {
+ return compiler;
+ }
+
+ const SPIRFunction &get_function() const
+ {
+ return func;
+ }
+
+ uint32_t get_immediate_dominator(uint32_t block) const
+ {
+ auto itr = immediate_dominators.find(block);
+ if (itr != std::end(immediate_dominators))
+ return itr->second;
+ else
+ return 0;
+ }
+
+ uint32_t get_visit_order(uint32_t block) const
+ {
+ auto itr = visit_order.find(block);
+ assert(itr != std::end(visit_order));
+ int v = itr->second.get();
+ assert(v > 0);
+ return uint32_t(v);
+ }
+
+ uint32_t find_common_dominator(uint32_t a, uint32_t b) const;
+
+ const std::vector<uint32_t> &get_preceding_edges(uint32_t block) const
+ {
+ auto itr = preceding_edges.find(block);
+ if (itr != std::end(preceding_edges))
+ return itr->second;
+ else
+ return empty_vector;
+ }
+
+ const std::vector<uint32_t> &get_succeeding_edges(uint32_t block) const
+ {
+ auto itr = succeeding_edges.find(block);
+ if (itr != std::end(succeeding_edges))
+ return itr->second;
+ else
+ return empty_vector;
+ }
+
+ template <typename Op>
+ void walk_from(std::unordered_set<uint32_t> &seen_blocks, uint32_t block, const Op &op) const
+ {
+ if (seen_blocks.count(block))
+ return;
+ seen_blocks.insert(block);
+
+ op(block);
+ for (auto b : get_succeeding_edges(block))
+ walk_from(seen_blocks, b, op);
+ }
+
+private:
+ struct VisitOrder
+ {
+ int &get()
+ {
+ return v;
+ }
+
+ const int &get() const
+ {
+ return v;
+ }
+
+ int v = -1;
+ };
+
+ Compiler &compiler;
+ const SPIRFunction &func;
+ std::unordered_map<uint32_t, std::vector<uint32_t>> preceding_edges;
+ std::unordered_map<uint32_t, std::vector<uint32_t>> succeeding_edges;
+ std::unordered_map<uint32_t, uint32_t> immediate_dominators;
+ std::unordered_map<uint32_t, VisitOrder> visit_order;
+ std::vector<uint32_t> post_order;
+ std::vector<uint32_t> empty_vector;
+
+ void add_branch(uint32_t from, uint32_t to);
+ void build_post_order_visit_order();
+ void build_immediate_dominators();
+ bool post_order_visit(uint32_t block);
+ uint32_t visit_count = 0;
+
+ bool is_back_edge(uint32_t to) const;
+};
+
+class DominatorBuilder
+{
+public:
+ DominatorBuilder(const CFG &cfg);
+
+ void add_block(uint32_t block);
+ uint32_t get_dominator() const
+ {
+ return dominator;
+ }
+
+ void lift_continue_block_dominator();
+
+private:
+ const CFG &cfg;
+ uint32_t dominator = 0;
+};
+} // namespace spirv_cross
+
+#endif
diff --git a/src/3rdparty/SPIRV-Cross/spirv_common.hpp b/src/3rdparty/SPIRV-Cross/spirv_common.hpp
new file mode 100644
index 0000000..dcd27af
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_common.hpp
@@ -0,0 +1,1518 @@
+/*
+ * Copyright 2015-2019 Arm Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SPIRV_CROSS_COMMON_HPP
+#define SPIRV_CROSS_COMMON_HPP
+
+#include "spirv.hpp"
+
+#include <algorithm>
+#include <cstdio>
+#include <cstring>
+#include <functional>
+#include <memory>
+#include <sstream>
+#include <stack>
+#include <stdexcept>
+#include <stdint.h>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+namespace spirv_cross
+{
+
+#ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
+#ifndef _MSC_VER
+[[noreturn]]
+#endif
+inline void
+report_and_abort(const std::string &msg)
+{
+#ifdef NDEBUG
+ (void)msg;
+#else
+ fprintf(stderr, "There was a compiler error: %s\n", msg.c_str());
+#endif
+ fflush(stderr);
+ abort();
+}
+
+#define SPIRV_CROSS_THROW(x) report_and_abort(x)
+#else
+class CompilerError : public std::runtime_error
+{
+public:
+ explicit CompilerError(const std::string &str)
+ : std::runtime_error(str)
+ {
+ }
+};
+
+#define SPIRV_CROSS_THROW(x) throw CompilerError(x)
+#endif
+
+//#define SPIRV_CROSS_COPY_CONSTRUCTOR_SANITIZE
+
+// MSVC 2013 does not have noexcept. We need this for Variant to get move constructor to work correctly
+// instead of copy constructor.
+// MSVC 2013 ignores that move constructors cannot throw in std::vector, so just don't define it.
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define SPIRV_CROSS_NOEXCEPT
+#else
+#define SPIRV_CROSS_NOEXCEPT noexcept
+#endif
+
+#if __cplusplus >= 201402l
+#define SPIRV_CROSS_DEPRECATED(reason) [[deprecated(reason)]]
+#elif defined(__GNUC__)
+#define SPIRV_CROSS_DEPRECATED(reason) __attribute__((deprecated))
+#elif defined(_MSC_VER)
+#define SPIRV_CROSS_DEPRECATED(reason) __declspec(deprecated(reason))
+#else
+#define SPIRV_CROSS_DEPRECATED(reason)
+#endif
+
+namespace inner
+{
+template <typename T>
+void join_helper(std::ostringstream &stream, T &&t)
+{
+ stream << std::forward<T>(t);
+}
+
+template <typename T, typename... Ts>
+void join_helper(std::ostringstream &stream, T &&t, Ts &&... ts)
+{
+ stream << std::forward<T>(t);
+ join_helper(stream, std::forward<Ts>(ts)...);
+}
+} // namespace inner
+
+class Bitset
+{
+public:
+ Bitset() = default;
+ explicit inline Bitset(uint64_t lower_)
+ : lower(lower_)
+ {
+ }
+
+ inline bool get(uint32_t bit) const
+ {
+ if (bit < 64)
+ return (lower & (1ull << bit)) != 0;
+ else
+ return higher.count(bit) != 0;
+ }
+
+ inline void set(uint32_t bit)
+ {
+ if (bit < 64)
+ lower |= 1ull << bit;
+ else
+ higher.insert(bit);
+ }
+
+ inline void clear(uint32_t bit)
+ {
+ if (bit < 64)
+ lower &= ~(1ull << bit);
+ else
+ higher.erase(bit);
+ }
+
+ inline uint64_t get_lower() const
+ {
+ return lower;
+ }
+
+ inline void reset()
+ {
+ lower = 0;
+ higher.clear();
+ }
+
+ inline void merge_and(const Bitset &other)
+ {
+ lower &= other.lower;
+ std::unordered_set<uint32_t> tmp_set;
+ for (auto &v : higher)
+ if (other.higher.count(v) != 0)
+ tmp_set.insert(v);
+ higher = std::move(tmp_set);
+ }
+
+ inline void merge_or(const Bitset &other)
+ {
+ lower |= other.lower;
+ for (auto &v : other.higher)
+ higher.insert(v);
+ }
+
+ inline bool operator==(const Bitset &other) const
+ {
+ if (lower != other.lower)
+ return false;
+
+ if (higher.size() != other.higher.size())
+ return false;
+
+ for (auto &v : higher)
+ if (other.higher.count(v) == 0)
+ return false;
+
+ return true;
+ }
+
+ inline bool operator!=(const Bitset &other) const
+ {
+ return !(*this == other);
+ }
+
+ template <typename Op>
+ void for_each_bit(const Op &op) const
+ {
+ // TODO: Add ctz-based iteration.
+ for (uint32_t i = 0; i < 64; i++)
+ {
+ if (lower & (1ull << i))
+ op(i);
+ }
+
+ if (higher.empty())
+ return;
+
+ // Need to enforce an order here for reproducible results,
+ // but hitting this path should happen extremely rarely, so having this slow path is fine.
+ std::vector<uint32_t> bits;
+ bits.reserve(higher.size());
+ for (auto &v : higher)
+ bits.push_back(v);
+ std::sort(std::begin(bits), std::end(bits));
+
+ for (auto &v : bits)
+ op(v);
+ }
+
+ inline bool empty() const
+ {
+ return lower == 0 && higher.empty();
+ }
+
+private:
+ // The most common bits to set are all lower than 64,
+ // so optimize for this case. Bits spilling outside 64 go into a slower data structure.
+ // In almost all cases, higher data structure will not be used.
+ uint64_t lower = 0;
+ std::unordered_set<uint32_t> higher;
+};
+
+// Helper template to avoid lots of nasty string temporary munging.
+template <typename... Ts>
+std::string join(Ts &&... ts)
+{
+ std::ostringstream stream;
+ inner::join_helper(stream, std::forward<Ts>(ts)...);
+ return stream.str();
+}
+
+inline std::string merge(const std::vector<std::string> &list)
+{
+ std::string s;
+ for (auto &elem : list)
+ {
+ s += elem;
+ if (&elem != &list.back())
+ s += ", ";
+ }
+ return s;
+}
+
+// Make sure we don't accidentally call this with float or doubles with SFINAE.
+// Have to use the radix-aware overload.
+template <typename T, typename std::enable_if<!std::is_floating_point<T>::value, int>::type = 0>
+inline std::string convert_to_string(const T &t)
+{
+ return std::to_string(t);
+}
+
+// Allow implementations to set a convenient standard precision
+#ifndef SPIRV_CROSS_FLT_FMT
+#define SPIRV_CROSS_FLT_FMT "%.32g"
+#endif
+
+#ifdef _MSC_VER
+// sprintf warning.
+// We cannot rely on snprintf existing because, ..., MSVC.
+#pragma warning(push)
+#pragma warning(disable : 4996)
+#endif
+
+static inline void fixup_radix_point(char *str, char radix_point)
+{
+ // Setting locales is a very risky business in multi-threaded program,
+ // so just fixup locales instead. We only need to care about the radix point.
+ if (radix_point != '.')
+ {
+ while (*str != '\0')
+ {
+ if (*str == radix_point)
+ *str = '.';
+ str++;
+ }
+ }
+}
+
+inline std::string convert_to_string(float t, char locale_radix_point)
+{
+ // std::to_string for floating point values is broken.
+ // Fallback to something more sane.
+ char buf[64];
+ sprintf(buf, SPIRV_CROSS_FLT_FMT, t);
+ fixup_radix_point(buf, locale_radix_point);
+
+ // Ensure that the literal is float.
+ if (!strchr(buf, '.') && !strchr(buf, 'e'))
+ strcat(buf, ".0");
+ return buf;
+}
+
+inline std::string convert_to_string(double t, char locale_radix_point)
+{
+ // std::to_string for floating point values is broken.
+ // Fallback to something more sane.
+ char buf[64];
+ sprintf(buf, SPIRV_CROSS_FLT_FMT, t);
+ fixup_radix_point(buf, locale_radix_point);
+
+ // Ensure that the literal is float.
+ if (!strchr(buf, '.') && !strchr(buf, 'e'))
+ strcat(buf, ".0");
+ return buf;
+}
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+struct Instruction
+{
+ uint16_t op = 0;
+ uint16_t count = 0;
+ uint32_t offset = 0;
+ uint32_t length = 0;
+};
+
+// Helper for Variant interface.
+struct IVariant
+{
+ virtual ~IVariant() = default;
+ virtual std::unique_ptr<IVariant> clone() = 0;
+
+ uint32_t self = 0;
+};
+
+#define SPIRV_CROSS_DECLARE_CLONE(T) \
+ std::unique_ptr<IVariant> clone() override \
+ { \
+ return std::unique_ptr<IVariant>(new T(*this)); \
+ }
+
+enum Types
+{
+ TypeNone,
+ TypeType,
+ TypeVariable,
+ TypeConstant,
+ TypeFunction,
+ TypeFunctionPrototype,
+ TypeBlock,
+ TypeExtension,
+ TypeExpression,
+ TypeConstantOp,
+ TypeCombinedImageSampler,
+ TypeAccessChain,
+ TypeUndef,
+ TypeCount
+};
+
+struct SPIRUndef : IVariant
+{
+ enum
+ {
+ type = TypeUndef
+ };
+
+ explicit SPIRUndef(uint32_t basetype_)
+ : basetype(basetype_)
+ {
+ }
+ uint32_t basetype;
+
+ SPIRV_CROSS_DECLARE_CLONE(SPIRUndef)
+};
+
+// This type is only used by backends which need to access the combined image and sampler IDs separately after
+// the OpSampledImage opcode.
+struct SPIRCombinedImageSampler : IVariant
+{
+ enum
+ {
+ type = TypeCombinedImageSampler
+ };
+ SPIRCombinedImageSampler(uint32_t type_, uint32_t image_, uint32_t sampler_)
+ : combined_type(type_)
+ , image(image_)
+ , sampler(sampler_)
+ {
+ }
+ uint32_t combined_type;
+ uint32_t image;
+ uint32_t sampler;
+
+ SPIRV_CROSS_DECLARE_CLONE(SPIRCombinedImageSampler)
+};
+
+struct SPIRConstantOp : IVariant
+{
+ enum
+ {
+ type = TypeConstantOp
+ };
+
+ SPIRConstantOp(uint32_t result_type, spv::Op op, const uint32_t *args, uint32_t length)
+ : opcode(op)
+ , arguments(args, args + length)
+ , basetype(result_type)
+ {
+ }
+
+ spv::Op opcode;
+ std::vector<uint32_t> arguments;
+ uint32_t basetype;
+
+ SPIRV_CROSS_DECLARE_CLONE(SPIRConstantOp)
+};
+
+struct SPIRType : IVariant
+{
+ enum
+ {
+ type = TypeType
+ };
+
+ enum BaseType
+ {
+ Unknown,
+ Void,
+ Boolean,
+ SByte,
+ UByte,
+ Short,
+ UShort,
+ Int,
+ UInt,
+ Int64,
+ UInt64,
+ AtomicCounter,
+ Half,
+ Float,
+ Double,
+ Struct,
+ Image,
+ SampledImage,
+ Sampler,
+ AccelerationStructureNV,
+
+ // Keep internal types at the end.
+ ControlPointArray,
+ Char
+ };
+
+ // Scalar/vector/matrix support.
+ BaseType basetype = Unknown;
+ uint32_t width = 0;
+ uint32_t vecsize = 1;
+ uint32_t columns = 1;
+
+ // Arrays, support array of arrays by having a vector of array sizes.
+ std::vector<uint32_t> array;
+
+ // Array elements can be either specialization constants or specialization ops.
+ // This array determines how to interpret the array size.
+ // If an element is true, the element is a literal,
+ // otherwise, it's an expression, which must be resolved on demand.
+ // The actual size is not really known until runtime.
+ std::vector<bool> array_size_literal;
+
+ // Pointers
+ // Keep track of how many pointer layers we have.
+ uint32_t pointer_depth = 0;
+ bool pointer = false;
+
+ spv::StorageClass storage = spv::StorageClassGeneric;
+
+ std::vector<uint32_t> member_types;
+
+ struct ImageType
+ {
+ uint32_t type;
+ spv::Dim dim;
+ bool depth;
+ bool arrayed;
+ bool ms;
+ uint32_t sampled;
+ spv::ImageFormat format;
+ spv::AccessQualifier access;
+ } image;
+
+ // Structs can be declared multiple times if they are used as part of interface blocks.
+ // We want to detect this so that we only emit the struct definition once.
+ // Since we cannot rely on OpName to be equal, we need to figure out aliases.
+ uint32_t type_alias = 0;
+
+ // Denotes the type which this type is based on.
+ // Allows the backend to traverse how a complex type is built up during access chains.
+ uint32_t parent_type = 0;
+
+ // Used in backends to avoid emitting members with conflicting names.
+ std::unordered_set<std::string> member_name_cache;
+
+ SPIRV_CROSS_DECLARE_CLONE(SPIRType)
+};
+
+struct SPIRExtension : IVariant
+{
+ enum
+ {
+ type = TypeExtension
+ };
+
+ enum Extension
+ {
+ Unsupported,
+ GLSL,
+ SPV_AMD_shader_ballot,
+ SPV_AMD_shader_explicit_vertex_parameter,
+ SPV_AMD_shader_trinary_minmax,
+ SPV_AMD_gcn_shader
+ };
+
+ explicit SPIRExtension(Extension ext_)
+ : ext(ext_)
+ {
+ }
+
+ Extension ext;
+ SPIRV_CROSS_DECLARE_CLONE(SPIRExtension)
+};
+
+// SPIREntryPoint is not a variant since its IDs are used to decorate OpFunction,
+// so in order to avoid conflicts, we can't stick them in the ids array.
+struct SPIREntryPoint
+{
+ SPIREntryPoint(uint32_t self_, spv::ExecutionModel execution_model, const std::string &entry_name)
+ : self(self_)
+ , name(entry_name)
+ , orig_name(entry_name)
+ , model(execution_model)
+ {
+ }
+ SPIREntryPoint() = default;
+
+ uint32_t self = 0;
+ std::string name;
+ std::string orig_name;
+ std::vector<uint32_t> interface_variables;
+
+ Bitset flags;
+ struct
+ {
+ uint32_t x = 0, y = 0, z = 0;
+ uint32_t constant = 0; // Workgroup size can be expressed as a constant/spec-constant instead.
+ } workgroup_size;
+ uint32_t invocations = 0;
+ uint32_t output_vertices = 0;
+ spv::ExecutionModel model = spv::ExecutionModelMax;
+};
+
+struct SPIRExpression : IVariant
+{
+ enum
+ {
+ type = TypeExpression
+ };
+
+ // Only created by the backend target to avoid creating tons of temporaries.
+ SPIRExpression(std::string expr, uint32_t expression_type_, bool immutable_)
+ : expression(move(expr))
+ , expression_type(expression_type_)
+ , immutable(immutable_)
+ {
+ }
+
+ // If non-zero, prepend expression with to_expression(base_expression).
+ // Used in amortizing multiple calls to to_expression()
+ // where in certain cases that would quickly force a temporary when not needed.
+ uint32_t base_expression = 0;
+
+ std::string expression;
+ uint32_t expression_type = 0;
+
+ // If this expression is a forwarded load,
+ // allow us to reference the original variable.
+ uint32_t loaded_from = 0;
+
+ // If this expression will never change, we can avoid lots of temporaries
+ // in high level source.
+ // An expression being immutable can be speculative,
+ // it is assumed that this is true almost always.
+ bool immutable = false;
+
+ // Before use, this expression must be transposed.
+ // This is needed for targets which don't support row_major layouts.
+ bool need_transpose = false;
+
+ // Whether or not this is an access chain expression.
+ bool access_chain = false;
+
+ // A list of expressions which this expression depends on.
+ std::vector<uint32_t> expression_dependencies;
+
+ // By reading this expression, we implicitly read these expressions as well.
+ // Used by access chain Store and Load since we read multiple expressions in this case.
+ std::vector<uint32_t> implied_read_expressions;
+
+ SPIRV_CROSS_DECLARE_CLONE(SPIRExpression)
+};
+
+struct SPIRFunctionPrototype : IVariant
+{
+ enum
+ {
+ type = TypeFunctionPrototype
+ };
+
+ explicit SPIRFunctionPrototype(uint32_t return_type_)
+ : return_type(return_type_)
+ {
+ }
+
+ uint32_t return_type;
+ std::vector<uint32_t> parameter_types;
+
+ SPIRV_CROSS_DECLARE_CLONE(SPIRFunctionPrototype)
+};
+
+struct SPIRBlock : IVariant
+{
+ enum
+ {
+ type = TypeBlock
+ };
+
+ enum Terminator
+ {
+ Unknown,
+ Direct, // Emit next block directly without a particular condition.
+
+ Select, // Block ends with an if/else block.
+ MultiSelect, // Block ends with switch statement.
+
+ Return, // Block ends with return.
+ Unreachable, // Noop
+ Kill // Discard
+ };
+
+ enum Merge
+ {
+ MergeNone,
+ MergeLoop,
+ MergeSelection
+ };
+
+ enum Hints
+ {
+ HintNone,
+ HintUnroll,
+ HintDontUnroll,
+ HintFlatten,
+ HintDontFlatten
+ };
+
+ enum Method
+ {
+ MergeToSelectForLoop,
+ MergeToDirectForLoop,
+ MergeToSelectContinueForLoop
+ };
+
+ enum ContinueBlockType
+ {
+ ContinueNone,
+
+ // Continue block is branchless and has at least one instruction.
+ ForLoop,
+
+ // Noop continue block.
+ WhileLoop,
+
+ // Continue block is conditional.
+ DoWhileLoop,
+
+ // Highly unlikely that anything will use this,
+ // since it is really awkward/impossible to express in GLSL.
+ ComplexLoop
+ };
+
+ enum
+ {
+ NoDominator = 0xffffffffu
+ };
+
+ Terminator terminator = Unknown;
+ Merge merge = MergeNone;
+ Hints hint = HintNone;
+ uint32_t next_block = 0;
+ uint32_t merge_block = 0;
+ uint32_t continue_block = 0;
+
+ uint32_t return_value = 0; // If 0, return nothing (void).
+ uint32_t condition = 0;
+ uint32_t true_block = 0;
+ uint32_t false_block = 0;
+ uint32_t default_block = 0;
+
+ std::vector<Instruction> ops;
+
+ struct Phi
+ {
+ uint32_t local_variable; // flush local variable ...
+ uint32_t parent; // If we're in from_block and want to branch into this block ...
+ uint32_t function_variable; // to this function-global "phi" variable first.
+ };
+
+ // Before entering this block flush out local variables to magical "phi" variables.
+ std::vector<Phi> phi_variables;
+
+ // Declare these temporaries before beginning the block.
+ // Used for handling complex continue blocks which have side effects.
+ std::vector<std::pair<uint32_t, uint32_t>> declare_temporary;
+
+ // Declare these temporaries, but only conditionally if this block turns out to be
+ // a complex loop header.
+ std::vector<std::pair<uint32_t, uint32_t>> potential_declare_temporary;
+
+ struct Case
+ {
+ uint32_t value;
+ uint32_t block;
+ };
+ std::vector<Case> cases;
+
+ // If we have tried to optimize code for this block but failed,
+ // keep track of this.
+ bool disable_block_optimization = false;
+
+ // If the continue block is complex, fallback to "dumb" for loops.
+ bool complex_continue = false;
+
+ // Do we need a ladder variable to defer breaking out of a loop construct after a switch block?
+ bool need_ladder_break = false;
+
+ // The dominating block which this block might be within.
+ // Used in continue; blocks to determine if we really need to write continue.
+ uint32_t loop_dominator = 0;
+
+ // All access to these variables are dominated by this block,
+ // so before branching anywhere we need to make sure that we declare these variables.
+ std::vector<uint32_t> dominated_variables;
+
+ // These are variables which should be declared in a for loop header, if we
+ // fail to use a classic for-loop,
+ // we remove these variables, and fall back to regular variables outside the loop.
+ std::vector<uint32_t> loop_variables;
+
+ // Some expressions are control-flow dependent, i.e. any instruction which relies on derivatives or
+ // sub-group-like operations.
+ // Make sure that we only use these expressions in the original block.
+ std::vector<uint32_t> invalidate_expressions;
+
+ SPIRV_CROSS_DECLARE_CLONE(SPIRBlock)
+};
+
+struct SPIRFunction : IVariant
+{
+ enum
+ {
+ type = TypeFunction
+ };
+
+ SPIRFunction(uint32_t return_type_, uint32_t function_type_)
+ : return_type(return_type_)
+ , function_type(function_type_)
+ {
+ }
+
+ struct Parameter
+ {
+ uint32_t type;
+ uint32_t id;
+ uint32_t read_count;
+ uint32_t write_count;
+
+ // Set to true if this parameter aliases a global variable,
+ // used mostly in Metal where global variables
+ // have to be passed down to functions as regular arguments.
+ // However, for this kind of variable, we should not care about
+ // read and write counts as access to the function arguments
+ // is not local to the function in question.
+ bool alias_global_variable;
+ };
+
+ // When calling a function, and we're remapping separate image samplers,
+ // resolve these arguments into combined image samplers and pass them
+ // as additional arguments in this order.
+ // It gets more complicated as functions can pull in their own globals
+ // and combine them with parameters,
+ // so we need to distinguish if something is local parameter index
+ // or a global ID.
+ struct CombinedImageSamplerParameter
+ {
+ uint32_t id;
+ uint32_t image_id;
+ uint32_t sampler_id;
+ bool global_image;
+ bool global_sampler;
+ bool depth;
+ };
+
+ uint32_t return_type;
+ uint32_t function_type;
+ std::vector<Parameter> arguments;
+
+ // Can be used by backends to add magic arguments.
+ // Currently used by combined image/sampler implementation.
+
+ std::vector<Parameter> shadow_arguments;
+ std::vector<uint32_t> local_variables;
+ uint32_t entry_block = 0;
+ std::vector<uint32_t> blocks;
+ std::vector<CombinedImageSamplerParameter> combined_parameters;
+
+ void add_local_variable(uint32_t id)
+ {
+ local_variables.push_back(id);
+ }
+
+ void add_parameter(uint32_t parameter_type, uint32_t id, bool alias_global_variable = false)
+ {
+ // Arguments are read-only until proven otherwise.
+ arguments.push_back({ parameter_type, id, 0u, 0u, alias_global_variable });
+ }
+
+ // Hooks to be run when the function returns.
+ // Mostly used for lowering internal data structures onto flattened structures.
+ // Need to defer this, because they might rely on things which change during compilation.
+ std::vector<std::function<void()>> fixup_hooks_out;
+
+ // Hooks to be run when the function begins.
+ // Mostly used for populating internal data structures from flattened structures.
+ // Need to defer this, because they might rely on things which change during compilation.
+ std::vector<std::function<void()>> fixup_hooks_in;
+
+ // On function entry, make sure to copy a constant array into thread addr space to work around
+ // the case where we are passing a constant array by value to a function on backends which do not
+ // consider arrays value types.
+ std::vector<uint32_t> constant_arrays_needed_on_stack;
+
+ bool active = false;
+ bool flush_undeclared = true;
+ bool do_combined_parameters = true;
+
+ SPIRV_CROSS_DECLARE_CLONE(SPIRFunction)
+};
+
+struct SPIRAccessChain : IVariant
+{
+ enum
+ {
+ type = TypeAccessChain
+ };
+
+ SPIRAccessChain(uint32_t basetype_, spv::StorageClass storage_, std::string base_, std::string dynamic_index_,
+ int32_t static_index_)
+ : basetype(basetype_)
+ , storage(storage_)
+ , base(std::move(base_))
+ , dynamic_index(std::move(dynamic_index_))
+ , static_index(static_index_)
+ {
+ }
+
+ // The access chain represents an offset into a buffer.
+ // Some backends need more complicated handling of access chains to be able to use buffers, like HLSL
+ // which has no usable buffer type ala GLSL SSBOs.
+ // StructuredBuffer is too limited, so our only option is to deal with ByteAddressBuffer which works with raw addresses.
+
+ uint32_t basetype;
+ spv::StorageClass storage;
+ std::string base;
+ std::string dynamic_index;
+ int32_t static_index;
+
+ uint32_t loaded_from = 0;
+ uint32_t matrix_stride = 0;
+ bool row_major_matrix = false;
+ bool immutable = false;
+
+ // By reading this expression, we implicitly read these expressions as well.
+ // Used by access chain Store and Load since we read multiple expressions in this case.
+ std::vector<uint32_t> implied_read_expressions;
+
+ SPIRV_CROSS_DECLARE_CLONE(SPIRAccessChain)
+};
+
+struct SPIRVariable : IVariant
+{
+ enum
+ {
+ type = TypeVariable
+ };
+
+ SPIRVariable() = default;
+ SPIRVariable(uint32_t basetype_, spv::StorageClass storage_, uint32_t initializer_ = 0, uint32_t basevariable_ = 0)
+ : basetype(basetype_)
+ , storage(storage_)
+ , initializer(initializer_)
+ , basevariable(basevariable_)
+ {
+ }
+
+ uint32_t basetype = 0;
+ spv::StorageClass storage = spv::StorageClassGeneric;
+ uint32_t decoration = 0;
+ uint32_t initializer = 0;
+ uint32_t basevariable = 0;
+
+ std::vector<uint32_t> dereference_chain;
+ bool compat_builtin = false;
+
+ // If a variable is shadowed, we only statically assign to it
+ // and never actually emit a statement for it.
+ // When we read the variable as an expression, just forward
+ // shadowed_id as the expression.
+ bool statically_assigned = false;
+ uint32_t static_expression = 0;
+
+ // Temporaries which can remain forwarded as long as this variable is not modified.
+ std::vector<uint32_t> dependees;
+ bool forwardable = true;
+
+ bool deferred_declaration = false;
+ bool phi_variable = false;
+
+ // Used to deal with Phi variable flushes. See flush_phi().
+ bool allocate_temporary_copy = false;
+
+ bool remapped_variable = false;
+ uint32_t remapped_components = 0;
+
+ // The block which dominates all access to this variable.
+ uint32_t dominator = 0;
+ // If true, this variable is a loop variable, when accessing the variable
+ // outside a loop,
+ // we should statically forward it.
+ bool loop_variable = false;
+ // Set to true while we're inside the for loop.
+ bool loop_variable_enable = false;
+
+ SPIRFunction::Parameter *parameter = nullptr;
+
+ SPIRV_CROSS_DECLARE_CLONE(SPIRVariable)
+};
+
+struct SPIRConstant : IVariant
+{
+ enum
+ {
+ type = TypeConstant
+ };
+
+ union Constant {
+ uint32_t u32;
+ int32_t i32;
+ float f32;
+
+ uint64_t u64;
+ int64_t i64;
+ double f64;
+ };
+
+ struct ConstantVector
+ {
+ Constant r[4];
+ // If != 0, this element is a specialization constant, and we should keep track of it as such.
+ uint32_t id[4];
+ uint32_t vecsize = 1;
+
+ // Workaround for MSVC 2013, initializing an array breaks.
+ ConstantVector()
+ {
+ memset(r, 0, sizeof(r));
+ for (unsigned i = 0; i < 4; i++)
+ id[i] = 0;
+ }
+ };
+
+ struct ConstantMatrix
+ {
+ ConstantVector c[4];
+ // If != 0, this column is a specialization constant, and we should keep track of it as such.
+ uint32_t id[4];
+ uint32_t columns = 1;
+
+ // Workaround for MSVC 2013, initializing an array breaks.
+ ConstantMatrix()
+ {
+ for (unsigned i = 0; i < 4; i++)
+ id[i] = 0;
+ }
+ };
+
+ static inline float f16_to_f32(uint16_t u16_value)
+ {
+ // Based on the GLM implementation.
+ int s = (u16_value >> 15) & 0x1;
+ int e = (u16_value >> 10) & 0x1f;
+ int m = (u16_value >> 0) & 0x3ff;
+
+ union {
+ float f32;
+ uint32_t u32;
+ } u;
+
+ if (e == 0)
+ {
+ if (m == 0)
+ {
+ u.u32 = uint32_t(s) << 31;
+ return u.f32;
+ }
+ else
+ {
+ while ((m & 0x400) == 0)
+ {
+ m <<= 1;
+ e--;
+ }
+
+ e++;
+ m &= ~0x400;
+ }
+ }
+ else if (e == 31)
+ {
+ if (m == 0)
+ {
+ u.u32 = (uint32_t(s) << 31) | 0x7f800000u;
+ return u.f32;
+ }
+ else
+ {
+ u.u32 = (uint32_t(s) << 31) | 0x7f800000u | (m << 13);
+ return u.f32;
+ }
+ }
+
+ e += 127 - 15;
+ m <<= 13;
+ u.u32 = (uint32_t(s) << 31) | (e << 23) | m;
+ return u.f32;
+ }
+
+ inline uint32_t specialization_constant_id(uint32_t col, uint32_t row) const
+ {
+ return m.c[col].id[row];
+ }
+
+ inline uint32_t specialization_constant_id(uint32_t col) const
+ {
+ return m.id[col];
+ }
+
+ inline uint32_t scalar(uint32_t col = 0, uint32_t row = 0) const
+ {
+ return m.c[col].r[row].u32;
+ }
+
+ inline int16_t scalar_i16(uint32_t col = 0, uint32_t row = 0) const
+ {
+ return int16_t(m.c[col].r[row].u32 & 0xffffu);
+ }
+
+ inline uint16_t scalar_u16(uint32_t col = 0, uint32_t row = 0) const
+ {
+ return uint16_t(m.c[col].r[row].u32 & 0xffffu);
+ }
+
+ inline int8_t scalar_i8(uint32_t col = 0, uint32_t row = 0) const
+ {
+ return int8_t(m.c[col].r[row].u32 & 0xffu);
+ }
+
+ inline uint8_t scalar_u8(uint32_t col = 0, uint32_t row = 0) const
+ {
+ return uint8_t(m.c[col].r[row].u32 & 0xffu);
+ }
+
+ inline float scalar_f16(uint32_t col = 0, uint32_t row = 0) const
+ {
+ return f16_to_f32(scalar_u16(col, row));
+ }
+
+ inline float scalar_f32(uint32_t col = 0, uint32_t row = 0) const
+ {
+ return m.c[col].r[row].f32;
+ }
+
+ inline int32_t scalar_i32(uint32_t col = 0, uint32_t row = 0) const
+ {
+ return m.c[col].r[row].i32;
+ }
+
+ inline double scalar_f64(uint32_t col = 0, uint32_t row = 0) const
+ {
+ return m.c[col].r[row].f64;
+ }
+
+ inline int64_t scalar_i64(uint32_t col = 0, uint32_t row = 0) const
+ {
+ return m.c[col].r[row].i64;
+ }
+
+ inline uint64_t scalar_u64(uint32_t col = 0, uint32_t row = 0) const
+ {
+ return m.c[col].r[row].u64;
+ }
+
+ inline const ConstantVector &vector() const
+ {
+ return m.c[0];
+ }
+
+ inline uint32_t vector_size() const
+ {
+ return m.c[0].vecsize;
+ }
+
+ inline uint32_t columns() const
+ {
+ return m.columns;
+ }
+
+ inline void make_null(const SPIRType &constant_type_)
+ {
+ m = {};
+ m.columns = constant_type_.columns;
+ for (auto &c : m.c)
+ c.vecsize = constant_type_.vecsize;
+ }
+
+ inline bool constant_is_null() const
+ {
+ if (specialization)
+ return false;
+ if (!subconstants.empty())
+ return false;
+
+ for (uint32_t col = 0; col < columns(); col++)
+ for (uint32_t row = 0; row < vector_size(); row++)
+ if (scalar_u64(col, row) != 0)
+ return false;
+
+ return true;
+ }
+
+ explicit SPIRConstant(uint32_t constant_type_)
+ : constant_type(constant_type_)
+ {
+ }
+
+ SPIRConstant() = default;
+
+ SPIRConstant(uint32_t constant_type_, const uint32_t *elements, uint32_t num_elements, bool specialized)
+ : constant_type(constant_type_)
+ , specialization(specialized)
+ {
+ subconstants.insert(end(subconstants), elements, elements + num_elements);
+ specialization = specialized;
+ }
+
+ // Construct scalar (32-bit).
+ SPIRConstant(uint32_t constant_type_, uint32_t v0, bool specialized)
+ : constant_type(constant_type_)
+ , specialization(specialized)
+ {
+ m.c[0].r[0].u32 = v0;
+ m.c[0].vecsize = 1;
+ m.columns = 1;
+ }
+
+ // Construct scalar (64-bit).
+ SPIRConstant(uint32_t constant_type_, uint64_t v0, bool specialized)
+ : constant_type(constant_type_)
+ , specialization(specialized)
+ {
+ m.c[0].r[0].u64 = v0;
+ m.c[0].vecsize = 1;
+ m.columns = 1;
+ }
+
+ // Construct vectors and matrices.
+ SPIRConstant(uint32_t constant_type_, const SPIRConstant *const *vector_elements, uint32_t num_elements,
+ bool specialized)
+ : constant_type(constant_type_)
+ , specialization(specialized)
+ {
+ bool matrix = vector_elements[0]->m.c[0].vecsize > 1;
+
+ if (matrix)
+ {
+ m.columns = num_elements;
+
+ for (uint32_t i = 0; i < num_elements; i++)
+ {
+ m.c[i] = vector_elements[i]->m.c[0];
+ if (vector_elements[i]->specialization)
+ m.id[i] = vector_elements[i]->self;
+ }
+ }
+ else
+ {
+ m.c[0].vecsize = num_elements;
+ m.columns = 1;
+
+ for (uint32_t i = 0; i < num_elements; i++)
+ {
+ m.c[0].r[i] = vector_elements[i]->m.c[0].r[0];
+ if (vector_elements[i]->specialization)
+ m.c[0].id[i] = vector_elements[i]->self;
+ }
+ }
+ }
+
+ uint32_t constant_type = 0;
+ ConstantMatrix m;
+
+ // If this constant is a specialization constant (i.e. created with OpSpecConstant*).
+ bool specialization = false;
+ // If this constant is used as an array length which creates specialization restrictions on some backends.
+ bool is_used_as_array_length = false;
+
+ // If true, this is a LUT, and should always be declared in the outer scope.
+ bool is_used_as_lut = false;
+
+ // For composites which are constant arrays, etc.
+ std::vector<uint32_t> subconstants;
+
+ // Non-Vulkan GLSL, HLSL and sometimes MSL emits defines for each specialization constant,
+ // and uses them to initialize the constant. This allows the user
+ // to still be able to specialize the value by supplying corresponding
+ // preprocessor directives before compiling the shader.
+ std::string specialization_constant_macro_name;
+
+ SPIRV_CROSS_DECLARE_CLONE(SPIRConstant)
+};
+
+class Variant
+{
+public:
+ // MSVC 2013 workaround, we shouldn't need these constructors.
+ Variant() = default;
+
+ // Marking custom move constructor as noexcept is important.
+ Variant(Variant &&other) SPIRV_CROSS_NOEXCEPT
+ {
+ *this = std::move(other);
+ }
+
+ Variant(const Variant &variant)
+ {
+ *this = variant;
+ }
+
+ // Marking custom move constructor as noexcept is important.
+ Variant &operator=(Variant &&other) SPIRV_CROSS_NOEXCEPT
+ {
+ if (this != &other)
+ {
+ holder = std::move(other.holder);
+ type = other.type;
+ allow_type_rewrite = other.allow_type_rewrite;
+ other.type = TypeNone;
+ }
+ return *this;
+ }
+
+ // This copy/clone should only be called in the Compiler constructor.
+ // If this is called inside ::compile(), we invalidate any references we took higher in the stack.
+ // This should never happen.
+ Variant &operator=(const Variant &other)
+ {
+#ifdef SPIRV_CROSS_COPY_CONSTRUCTOR_SANITIZE
+ abort();
+#endif
+ if (this != &other)
+ {
+ holder.reset();
+ if (other.holder)
+ holder = other.holder->clone();
+ type = other.type;
+ allow_type_rewrite = other.allow_type_rewrite;
+ }
+ return *this;
+ }
+
+ void set(std::unique_ptr<IVariant> val, Types new_type)
+ {
+ holder = std::move(val);
+ if (!allow_type_rewrite && type != TypeNone && type != new_type)
+ SPIRV_CROSS_THROW("Overwriting a variant with new type.");
+ type = new_type;
+ allow_type_rewrite = false;
+ }
+
+ template <typename T>
+ T &get()
+ {
+ if (!holder)
+ SPIRV_CROSS_THROW("nullptr");
+ if (static_cast<Types>(T::type) != type)
+ SPIRV_CROSS_THROW("Bad cast");
+ return *static_cast<T *>(holder.get());
+ }
+
+ template <typename T>
+ const T &get() const
+ {
+ if (!holder)
+ SPIRV_CROSS_THROW("nullptr");
+ if (static_cast<Types>(T::type) != type)
+ SPIRV_CROSS_THROW("Bad cast");
+ return *static_cast<const T *>(holder.get());
+ }
+
+ Types get_type() const
+ {
+ return type;
+ }
+
+ uint32_t get_id() const
+ {
+ return holder ? holder->self : 0;
+ }
+
+ bool empty() const
+ {
+ return !holder;
+ }
+
+ void reset()
+ {
+ holder.reset();
+ type = TypeNone;
+ }
+
+ void set_allow_type_rewrite()
+ {
+ allow_type_rewrite = true;
+ }
+
+private:
+ std::unique_ptr<IVariant> holder;
+ Types type = TypeNone;
+ bool allow_type_rewrite = false;
+};
+
+template <typename T>
+T &variant_get(Variant &var)
+{
+ return var.get<T>();
+}
+
+template <typename T>
+const T &variant_get(const Variant &var)
+{
+ return var.get<T>();
+}
+
+template <typename T, typename... P>
+T &variant_set(Variant &var, P &&... args)
+{
+ auto uptr = std::unique_ptr<T>(new T(std::forward<P>(args)...));
+ auto ptr = uptr.get();
+ var.set(std::move(uptr), static_cast<Types>(T::type));
+ return *ptr;
+}
+
+struct AccessChainMeta
+{
+ uint32_t storage_packed_type = 0;
+ bool need_transpose = false;
+ bool storage_is_packed = false;
+ bool storage_is_invariant = false;
+};
+
+struct Meta
+{
+ struct Decoration
+ {
+ std::string alias;
+ std::string qualified_alias;
+ std::string hlsl_semantic;
+ Bitset decoration_flags;
+ spv::BuiltIn builtin_type = spv::BuiltInMax;
+ uint32_t location = 0;
+ uint32_t component = 0;
+ uint32_t set = 0;
+ uint32_t binding = 0;
+ uint32_t offset = 0;
+ uint32_t array_stride = 0;
+ uint32_t matrix_stride = 0;
+ uint32_t input_attachment = 0;
+ uint32_t spec_id = 0;
+ uint32_t index = 0;
+ spv::FPRoundingMode fp_rounding_mode = spv::FPRoundingModeMax;
+ bool builtin = false;
+
+ struct
+ {
+ uint32_t packed_type = 0;
+ bool packed = false;
+ uint32_t ib_member_index = ~(0u);
+ uint32_t ib_orig_id = 0;
+ uint32_t argument_buffer_id = ~(0u);
+ } extended;
+ };
+
+ Decoration decoration;
+ std::vector<Decoration> members;
+
+ std::unordered_map<uint32_t, uint32_t> decoration_word_offset;
+
+ // For SPV_GOOGLE_hlsl_functionality1.
+ bool hlsl_is_magic_counter_buffer = false;
+ // ID for the sibling counter buffer.
+ uint32_t hlsl_magic_counter_buffer = 0;
+};
+
+// A user callback that remaps the type of any variable.
+// var_name is the declared name of the variable.
+// name_of_type is the textual name of the type which will be used in the code unless written to by the callback.
+using VariableTypeRemapCallback =
+ std::function<void(const SPIRType &type, const std::string &var_name, std::string &name_of_type)>;
+
+class Hasher
+{
+public:
+ inline void u32(uint32_t value)
+ {
+ h = (h * 0x100000001b3ull) ^ value;
+ }
+
+ inline uint64_t get() const
+ {
+ return h;
+ }
+
+private:
+ uint64_t h = 0xcbf29ce484222325ull;
+};
+
+static inline bool type_is_floating_point(const SPIRType &type)
+{
+ return type.basetype == SPIRType::Half || type.basetype == SPIRType::Float || type.basetype == SPIRType::Double;
+}
+
+static inline bool type_is_integral(const SPIRType &type)
+{
+ return type.basetype == SPIRType::SByte || type.basetype == SPIRType::UByte || type.basetype == SPIRType::Short ||
+ type.basetype == SPIRType::UShort || type.basetype == SPIRType::Int || type.basetype == SPIRType::UInt ||
+ type.basetype == SPIRType::Int64 || type.basetype == SPIRType::UInt64;
+}
+
+static inline SPIRType::BaseType to_signed_basetype(uint32_t width)
+{
+ switch (width)
+ {
+ case 8:
+ return SPIRType::SByte;
+ case 16:
+ return SPIRType::Short;
+ case 32:
+ return SPIRType::Int;
+ case 64:
+ return SPIRType::Int64;
+ default:
+ SPIRV_CROSS_THROW("Invalid bit width.");
+ }
+}
+
+static inline SPIRType::BaseType to_unsigned_basetype(uint32_t width)
+{
+ switch (width)
+ {
+ case 8:
+ return SPIRType::UByte;
+ case 16:
+ return SPIRType::UShort;
+ case 32:
+ return SPIRType::UInt;
+ case 64:
+ return SPIRType::UInt64;
+ default:
+ SPIRV_CROSS_THROW("Invalid bit width.");
+ }
+}
+
+// Returns true if an arithmetic operation does not change behavior depending on signedness.
+static inline bool opcode_is_sign_invariant(spv::Op opcode)
+{
+ switch (opcode)
+ {
+ case spv::OpIEqual:
+ case spv::OpINotEqual:
+ case spv::OpISub:
+ case spv::OpIAdd:
+ case spv::OpIMul:
+ case spv::OpShiftLeftLogical:
+ case spv::OpBitwiseOr:
+ case spv::OpBitwiseXor:
+ case spv::OpBitwiseAnd:
+ return true;
+
+ default:
+ return false;
+ }
+}
+} // namespace spirv_cross
+
+#endif
diff --git a/src/3rdparty/SPIRV-Cross/spirv_cpp.cpp b/src/3rdparty/SPIRV-Cross/spirv_cpp.cpp
new file mode 100644
index 0000000..1b791ee
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_cpp.cpp
@@ -0,0 +1,547 @@
+/*
+ * Copyright 2015-2019 Arm Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "spirv_cpp.hpp"
+
+using namespace spv;
+using namespace spirv_cross;
+using namespace std;
+
+void CompilerCPP::emit_buffer_block(const SPIRVariable &var)
+{
+ add_resource_name(var.self);
+
+ auto &type = get<SPIRType>(var.basetype);
+ auto instance_name = to_name(var.self);
+
+ uint32_t descriptor_set = ir.meta[var.self].decoration.set;
+ uint32_t binding = ir.meta[var.self].decoration.binding;
+
+ emit_block_struct(type);
+ auto buffer_name = to_name(type.self);
+
+ statement("internal::Resource<", buffer_name, type_to_array_glsl(type), "> ", instance_name, "__;");
+ statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()");
+ resource_registrations.push_back(
+ join("s.register_resource(", instance_name, "__", ", ", descriptor_set, ", ", binding, ");"));
+ statement("");
+}
+
+void CompilerCPP::emit_interface_block(const SPIRVariable &var)
+{
+ add_resource_name(var.self);
+
+ auto &type = get<SPIRType>(var.basetype);
+
+ const char *qual = var.storage == StorageClassInput ? "StageInput" : "StageOutput";
+ const char *lowerqual = var.storage == StorageClassInput ? "stage_input" : "stage_output";
+ auto instance_name = to_name(var.self);
+ uint32_t location = ir.meta[var.self].decoration.location;
+
+ string buffer_name;
+ auto flags = ir.meta[type.self].decoration.decoration_flags;
+ if (flags.get(DecorationBlock))
+ {
+ emit_block_struct(type);
+ buffer_name = to_name(type.self);
+ }
+ else
+ buffer_name = type_to_glsl(type);
+
+ statement("internal::", qual, "<", buffer_name, type_to_array_glsl(type), "> ", instance_name, "__;");
+ statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()");
+ resource_registrations.push_back(join("s.register_", lowerqual, "(", instance_name, "__", ", ", location, ");"));
+ statement("");
+}
+
+void CompilerCPP::emit_shared(const SPIRVariable &var)
+{
+ add_resource_name(var.self);
+
+ auto instance_name = to_name(var.self);
+ statement(CompilerGLSL::variable_decl(var), ";");
+ statement_no_indent("#define ", instance_name, " __res->", instance_name);
+}
+
+void CompilerCPP::emit_uniform(const SPIRVariable &var)
+{
+ add_resource_name(var.self);
+
+ auto &type = get<SPIRType>(var.basetype);
+ auto instance_name = to_name(var.self);
+
+ uint32_t descriptor_set = ir.meta[var.self].decoration.set;
+ uint32_t binding = ir.meta[var.self].decoration.binding;
+ uint32_t location = ir.meta[var.self].decoration.location;
+
+ string type_name = type_to_glsl(type);
+ remap_variable_type_name(type, instance_name, type_name);
+
+ if (type.basetype == SPIRType::Image || type.basetype == SPIRType::SampledImage ||
+ type.basetype == SPIRType::AtomicCounter)
+ {
+ statement("internal::Resource<", type_name, type_to_array_glsl(type), "> ", instance_name, "__;");
+ statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()");
+ resource_registrations.push_back(
+ join("s.register_resource(", instance_name, "__", ", ", descriptor_set, ", ", binding, ");"));
+ }
+ else
+ {
+ statement("internal::UniformConstant<", type_name, type_to_array_glsl(type), "> ", instance_name, "__;");
+ statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()");
+ resource_registrations.push_back(
+ join("s.register_uniform_constant(", instance_name, "__", ", ", location, ");"));
+ }
+
+ statement("");
+}
+
+void CompilerCPP::emit_push_constant_block(const SPIRVariable &var)
+{
+ add_resource_name(var.self);
+
+ auto &type = get<SPIRType>(var.basetype);
+ auto &flags = ir.meta[var.self].decoration.decoration_flags;
+ if (flags.get(DecorationBinding) || flags.get(DecorationDescriptorSet))
+ SPIRV_CROSS_THROW("Push constant blocks cannot be compiled to GLSL with Binding or Set syntax. "
+ "Remap to location with reflection API first or disable these decorations.");
+
+ emit_block_struct(type);
+ auto buffer_name = to_name(type.self);
+ auto instance_name = to_name(var.self);
+
+ statement("internal::PushConstant<", buffer_name, type_to_array_glsl(type), "> ", instance_name, ";");
+ statement_no_indent("#define ", instance_name, " __res->", instance_name, ".get()");
+ resource_registrations.push_back(join("s.register_push_constant(", instance_name, "__", ");"));
+ statement("");
+}
+
+void CompilerCPP::emit_block_struct(SPIRType &type)
+{
+ // C++ can't do interface blocks, so we fake it by emitting a separate struct.
+ // However, these structs are not allowed to alias anything, so remove it before
+ // emitting the struct.
+ //
+ // The type we have here needs to be resolved to the non-pointer type so we can remove aliases.
+ auto &self = get<SPIRType>(type.self);
+ self.type_alias = 0;
+ emit_struct(self);
+}
+
+void CompilerCPP::emit_resources()
+{
+ for (auto &id : ir.ids)
+ {
+ if (id.get_type() == TypeConstant)
+ {
+ auto &c = id.get<SPIRConstant>();
+
+ bool needs_declaration = c.specialization || c.is_used_as_lut;
+
+ if (needs_declaration)
+ {
+ if (!options.vulkan_semantics && c.specialization)
+ {
+ c.specialization_constant_macro_name =
+ constant_value_macro_name(get_decoration(c.self, DecorationSpecId));
+ }
+ emit_constant(c);
+ }
+ }
+ else if (id.get_type() == TypeConstantOp)
+ {
+ emit_specialization_constant_op(id.get<SPIRConstantOp>());
+ }
+ }
+
+ // Output all basic struct types which are not Block or BufferBlock as these are declared inplace
+ // when such variables are instantiated.
+ for (auto &id : ir.ids)
+ {
+ if (id.get_type() == TypeType)
+ {
+ auto &type = id.get<SPIRType>();
+ if (type.basetype == SPIRType::Struct && type.array.empty() && !type.pointer &&
+ (!ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) &&
+ !ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock)))
+ {
+ emit_struct(type);
+ }
+ }
+ }
+
+ statement("struct Resources : ", resource_type);
+ begin_scope();
+
+ // Output UBOs and SSBOs
+ for (auto &id : ir.ids)
+ {
+ if (id.get_type() == TypeVariable)
+ {
+ auto &var = id.get<SPIRVariable>();
+ auto &type = get<SPIRType>(var.basetype);
+
+ if (var.storage != StorageClassFunction && type.pointer && type.storage == StorageClassUniform &&
+ !is_hidden_variable(var) &&
+ (ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) ||
+ ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock)))
+ {
+ emit_buffer_block(var);
+ }
+ }
+ }
+
+ // Output push constant blocks
+ for (auto &id : ir.ids)
+ {
+ if (id.get_type() == TypeVariable)
+ {
+ auto &var = id.get<SPIRVariable>();
+ auto &type = get<SPIRType>(var.basetype);
+ if (!is_hidden_variable(var) && var.storage != StorageClassFunction && type.pointer &&
+ type.storage == StorageClassPushConstant)
+ {
+ emit_push_constant_block(var);
+ }
+ }
+ }
+
+ // Output in/out interfaces.
+ for (auto &id : ir.ids)
+ {
+ if (id.get_type() == TypeVariable)
+ {
+ auto &var = id.get<SPIRVariable>();
+ auto &type = get<SPIRType>(var.basetype);
+
+ if (var.storage != StorageClassFunction && !is_hidden_variable(var) && type.pointer &&
+ (var.storage == StorageClassInput || var.storage == StorageClassOutput) &&
+ interface_variable_exists_in_entry_point(var.self))
+ {
+ emit_interface_block(var);
+ }
+ }
+ }
+
+ // Output Uniform Constants (values, samplers, images, etc).
+ for (auto &id : ir.ids)
+ {
+ if (id.get_type() == TypeVariable)
+ {
+ auto &var = id.get<SPIRVariable>();
+ auto &type = get<SPIRType>(var.basetype);
+
+ if (var.storage != StorageClassFunction && !is_hidden_variable(var) && type.pointer &&
+ (type.storage == StorageClassUniformConstant || type.storage == StorageClassAtomicCounter))
+ {
+ emit_uniform(var);
+ }
+ }
+ }
+
+ // Global variables.
+ bool emitted = false;
+ for (auto global : global_variables)
+ {
+ auto &var = get<SPIRVariable>(global);
+ if (var.storage == StorageClassWorkgroup)
+ {
+ emit_shared(var);
+ emitted = true;
+ }
+ }
+
+ if (emitted)
+ statement("");
+
+ declare_undefined_values();
+
+ statement("inline void init(spirv_cross_shader& s)");
+ begin_scope();
+ statement(resource_type, "::init(s);");
+ for (auto &reg : resource_registrations)
+ statement(reg);
+ end_scope();
+ resource_registrations.clear();
+
+ end_scope_decl();
+
+ statement("");
+ statement("Resources* __res;");
+ if (get_entry_point().model == ExecutionModelGLCompute)
+ statement("ComputePrivateResources __priv_res;");
+ statement("");
+
+ // Emit regular globals which are allocated per invocation.
+ emitted = false;
+ for (auto global : global_variables)
+ {
+ auto &var = get<SPIRVariable>(global);
+ if (var.storage == StorageClassPrivate)
+ {
+ if (var.storage == StorageClassWorkgroup)
+ emit_shared(var);
+ else
+ statement(CompilerGLSL::variable_decl(var), ";");
+ emitted = true;
+ }
+ }
+
+ if (emitted)
+ statement("");
+}
+
+string CompilerCPP::compile()
+{
+ // Do not deal with ES-isms like precision, older extensions and such.
+ options.es = false;
+ options.version = 450;
+ backend.float_literal_suffix = true;
+ backend.double_literal_suffix = false;
+ backend.long_long_literal_suffix = true;
+ backend.uint32_t_literal_suffix = true;
+ backend.basic_int_type = "int32_t";
+ backend.basic_uint_type = "uint32_t";
+ backend.swizzle_is_function = true;
+ backend.shared_is_implied = true;
+ backend.flexible_member_array_supported = false;
+ backend.explicit_struct_type = true;
+ backend.use_initializer_list = true;
+
+ build_function_control_flow_graphs_and_analyze();
+ update_active_builtins();
+
+ uint32_t pass_count = 0;
+ do
+ {
+ if (pass_count >= 3)
+ SPIRV_CROSS_THROW("Over 3 compilation loops detected. Must be a bug!");
+
+ resource_registrations.clear();
+ reset();
+
+ // Move constructor for this type is broken on GCC 4.9 ...
+ buffer = unique_ptr<ostringstream>(new ostringstream());
+
+ emit_header();
+ emit_resources();
+
+ emit_function(get<SPIRFunction>(ir.default_entry_point), Bitset());
+
+ pass_count++;
+ } while (force_recompile);
+
+ // Match opening scope of emit_header().
+ end_scope_decl();
+ // namespace
+ end_scope();
+
+ // Emit C entry points
+ emit_c_linkage();
+
+ // Entry point in CPP is always main() for the time being.
+ get_entry_point().name = "main";
+
+ return buffer->str();
+}
+
+void CompilerCPP::emit_c_linkage()
+{
+ statement("");
+
+ statement("spirv_cross_shader_t *spirv_cross_construct(void)");
+ begin_scope();
+ statement("return new ", impl_type, "();");
+ end_scope();
+
+ statement("");
+ statement("void spirv_cross_destruct(spirv_cross_shader_t *shader)");
+ begin_scope();
+ statement("delete static_cast<", impl_type, "*>(shader);");
+ end_scope();
+
+ statement("");
+ statement("void spirv_cross_invoke(spirv_cross_shader_t *shader)");
+ begin_scope();
+ statement("static_cast<", impl_type, "*>(shader)->invoke();");
+ end_scope();
+
+ statement("");
+ statement("static const struct spirv_cross_interface vtable =");
+ begin_scope();
+ statement("spirv_cross_construct,");
+ statement("spirv_cross_destruct,");
+ statement("spirv_cross_invoke,");
+ end_scope_decl();
+
+ statement("");
+ statement("const struct spirv_cross_interface *",
+ interface_name.empty() ? string("spirv_cross_get_interface") : interface_name, "(void)");
+ begin_scope();
+ statement("return &vtable;");
+ end_scope();
+}
+
+void CompilerCPP::emit_function_prototype(SPIRFunction &func, const Bitset &)
+{
+ if (func.self != ir.default_entry_point)
+ add_function_overload(func);
+
+ local_variable_names = resource_names;
+ string decl;
+
+ auto &type = get<SPIRType>(func.return_type);
+ decl += "inline ";
+ decl += type_to_glsl(type);
+ decl += " ";
+
+ if (func.self == ir.default_entry_point)
+ {
+ decl += "main";
+ processing_entry_point = true;
+ }
+ else
+ decl += to_name(func.self);
+
+ decl += "(";
+ for (auto &arg : func.arguments)
+ {
+ add_local_variable_name(arg.id);
+
+ decl += argument_decl(arg);
+ if (&arg != &func.arguments.back())
+ decl += ", ";
+
+ // Hold a pointer to the parameter so we can invalidate the readonly field if needed.
+ auto *var = maybe_get<SPIRVariable>(arg.id);
+ if (var)
+ var->parameter = &arg;
+ }
+
+ decl += ")";
+ statement(decl);
+}
+
+string CompilerCPP::argument_decl(const SPIRFunction::Parameter &arg)
+{
+ auto &type = expression_type(arg.id);
+ bool constref = !type.pointer || arg.write_count == 0;
+
+ auto &var = get<SPIRVariable>(arg.id);
+
+ string base = type_to_glsl(type);
+ string variable_name = to_name(var.self);
+ remap_variable_type_name(type, variable_name, base);
+
+ for (uint32_t i = 0; i < type.array.size(); i++)
+ base = join("std::array<", base, ", ", to_array_size(type, i), ">");
+
+ return join(constref ? "const " : "", base, " &", variable_name);
+}
+
+string CompilerCPP::variable_decl(const SPIRType &type, const string &name, uint32_t /* id */)
+{
+ string base = type_to_glsl(type);
+ remap_variable_type_name(type, name, base);
+ bool runtime = false;
+
+ for (uint32_t i = 0; i < type.array.size(); i++)
+ {
+ auto &array = type.array[i];
+ if (!array && type.array_size_literal[i])
+ {
+ // Avoid using runtime arrays with std::array since this is undefined.
+ // Runtime arrays cannot be passed around as values, so this is fine.
+ runtime = true;
+ }
+ else
+ base = join("std::array<", base, ", ", to_array_size(type, i), ">");
+ }
+ base += ' ';
+ return base + name + (runtime ? "[1]" : "");
+}
+
+void CompilerCPP::emit_header()
+{
+ auto &execution = get_entry_point();
+
+ statement("// This C++ shader is autogenerated by spirv-cross.");
+ statement("#include \"spirv_cross/internal_interface.hpp\"");
+ statement("#include \"spirv_cross/external_interface.h\"");
+ // Needed to properly implement GLSL-style arrays.
+ statement("#include <array>");
+ statement("#include <stdint.h>");
+ statement("");
+ statement("using namespace spirv_cross;");
+ statement("using namespace glm;");
+ statement("");
+
+ statement("namespace Impl");
+ begin_scope();
+
+ switch (execution.model)
+ {
+ case ExecutionModelGeometry:
+ case ExecutionModelTessellationControl:
+ case ExecutionModelTessellationEvaluation:
+ case ExecutionModelGLCompute:
+ case ExecutionModelFragment:
+ case ExecutionModelVertex:
+ statement("struct Shader");
+ begin_scope();
+ break;
+
+ default:
+ SPIRV_CROSS_THROW("Unsupported execution model.");
+ }
+
+ switch (execution.model)
+ {
+ case ExecutionModelGeometry:
+ impl_type = "GeometryShader<Impl::Shader, Impl::Shader::Resources>";
+ resource_type = "GeometryResources";
+ break;
+
+ case ExecutionModelVertex:
+ impl_type = "VertexShader<Impl::Shader, Impl::Shader::Resources>";
+ resource_type = "VertexResources";
+ break;
+
+ case ExecutionModelFragment:
+ impl_type = "FragmentShader<Impl::Shader, Impl::Shader::Resources>";
+ resource_type = "FragmentResources";
+ break;
+
+ case ExecutionModelGLCompute:
+ impl_type = join("ComputeShader<Impl::Shader, Impl::Shader::Resources, ", execution.workgroup_size.x, ", ",
+ execution.workgroup_size.y, ", ", execution.workgroup_size.z, ">");
+ resource_type = "ComputeResources";
+ break;
+
+ case ExecutionModelTessellationControl:
+ impl_type = "TessControlShader<Impl::Shader, Impl::Shader::Resources>";
+ resource_type = "TessControlResources";
+ break;
+
+ case ExecutionModelTessellationEvaluation:
+ impl_type = "TessEvaluationShader<Impl::Shader, Impl::Shader::Resources>";
+ resource_type = "TessEvaluationResources";
+ break;
+
+ default:
+ SPIRV_CROSS_THROW("Unsupported execution model.");
+ }
+}
diff --git a/src/3rdparty/SPIRV-Cross/spirv_cpp.hpp b/src/3rdparty/SPIRV-Cross/spirv_cpp.hpp
new file mode 100644
index 0000000..bcdb669
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_cpp.hpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2015-2019 Arm Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SPIRV_CROSS_CPP_HPP
+#define SPIRV_CROSS_CPP_HPP
+
+#include "spirv_glsl.hpp"
+#include <utility>
+#include <vector>
+
+namespace spirv_cross
+{
+class CompilerCPP : public CompilerGLSL
+{
+public:
+ explicit CompilerCPP(std::vector<uint32_t> spirv_)
+ : CompilerGLSL(move(spirv_))
+ {
+ }
+
+ CompilerCPP(const uint32_t *ir_, size_t word_count)
+ : CompilerGLSL(ir_, word_count)
+ {
+ }
+
+ explicit CompilerCPP(const ParsedIR &ir_)
+ : CompilerGLSL(ir_)
+ {
+ }
+
+ explicit CompilerCPP(ParsedIR &&ir_)
+ : CompilerGLSL(std::move(ir_))
+ {
+ }
+
+ std::string compile() override;
+
+ // Sets a custom symbol name that can override
+ // spirv_cross_get_interface.
+ //
+ // Useful when several shader interfaces are linked
+ // statically into the same binary.
+ void set_interface_name(std::string name)
+ {
+ interface_name = std::move(name);
+ }
+
+private:
+ void emit_header() override;
+ void emit_c_linkage();
+ void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override;
+
+ void emit_resources();
+ void emit_buffer_block(const SPIRVariable &type) override;
+ void emit_push_constant_block(const SPIRVariable &var) override;
+ void emit_interface_block(const SPIRVariable &type);
+ void emit_block_chain(SPIRBlock &block);
+ void emit_uniform(const SPIRVariable &var) override;
+ void emit_shared(const SPIRVariable &var);
+ void emit_block_struct(SPIRType &type);
+ std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id) override;
+
+ std::string argument_decl(const SPIRFunction::Parameter &arg);
+
+ std::vector<std::string> resource_registrations;
+ std::string impl_type;
+ std::string resource_type;
+ uint32_t shared_counter = 0;
+
+ std::string interface_name;
+};
+} // namespace spirv_cross
+
+#endif
diff --git a/src/3rdparty/SPIRV-Cross/spirv_cross.cpp b/src/3rdparty/SPIRV-Cross/spirv_cross.cpp
new file mode 100644
index 0000000..7ca2fe1
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_cross.cpp
@@ -0,0 +1,4121 @@
+/*
+ * Copyright 2015-2019 Arm Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "spirv_cross.hpp"
+#include "GLSL.std.450.h"
+#include "spirv_cfg.hpp"
+#include "spirv_parser.hpp"
+#include <algorithm>
+#include <cstring>
+#include <utility>
+
+using namespace std;
+using namespace spv;
+using namespace spirv_cross;
+
+Compiler::Compiler(vector<uint32_t> ir_)
+{
+ Parser parser(move(ir_));
+ parser.parse();
+ set_ir(move(parser.get_parsed_ir()));
+}
+
+Compiler::Compiler(const uint32_t *ir_, size_t word_count)
+{
+ Parser parser(ir_, word_count);
+ parser.parse();
+ set_ir(move(parser.get_parsed_ir()));
+}
+
+Compiler::Compiler(const ParsedIR &ir_)
+{
+ set_ir(ir_);
+}
+
+Compiler::Compiler(ParsedIR &&ir_)
+{
+ set_ir(move(ir_));
+}
+
+void Compiler::set_ir(ParsedIR &&ir_)
+{
+ ir = move(ir_);
+ parse_fixup();
+}
+
+void Compiler::set_ir(const ParsedIR &ir_)
+{
+ ir = ir_;
+ parse_fixup();
+}
+
+string Compiler::compile()
+{
+ return "";
+}
+
+bool Compiler::variable_storage_is_aliased(const SPIRVariable &v)
+{
+ auto &type = get<SPIRType>(v.basetype);
+ bool ssbo = v.storage == StorageClassStorageBuffer ||
+ ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock);
+ bool image = type.basetype == SPIRType::Image;
+ bool counter = type.basetype == SPIRType::AtomicCounter;
+
+ bool is_restrict;
+ if (ssbo)
+ is_restrict = ir.get_buffer_block_flags(v).get(DecorationRestrict);
+ else
+ is_restrict = has_decoration(v.self, DecorationRestrict);
+
+ return !is_restrict && (ssbo || image || counter);
+}
+
+bool Compiler::block_is_pure(const SPIRBlock &block)
+{
+ for (auto &i : block.ops)
+ {
+ auto ops = stream(i);
+ auto op = static_cast<Op>(i.op);
+
+ switch (op)
+ {
+ case OpFunctionCall:
+ {
+ uint32_t func = ops[2];
+ if (!function_is_pure(get<SPIRFunction>(func)))
+ return false;
+ break;
+ }
+
+ case OpCopyMemory:
+ case OpStore:
+ {
+ auto &type = expression_type(ops[0]);
+ if (type.storage != StorageClassFunction)
+ return false;
+ break;
+ }
+
+ case OpImageWrite:
+ return false;
+
+ // Atomics are impure.
+ case OpAtomicLoad:
+ case OpAtomicStore:
+ case OpAtomicExchange:
+ case OpAtomicCompareExchange:
+ case OpAtomicCompareExchangeWeak:
+ case OpAtomicIIncrement:
+ case OpAtomicIDecrement:
+ case OpAtomicIAdd:
+ case OpAtomicISub:
+ case OpAtomicSMin:
+ case OpAtomicUMin:
+ case OpAtomicSMax:
+ case OpAtomicUMax:
+ case OpAtomicAnd:
+ case OpAtomicOr:
+ case OpAtomicXor:
+ return false;
+
+ // Geometry shader builtins modify global state.
+ case OpEndPrimitive:
+ case OpEmitStreamVertex:
+ case OpEndStreamPrimitive:
+ case OpEmitVertex:
+ return false;
+
+ // Barriers disallow any reordering, so we should treat blocks with barrier as writing.
+ case OpControlBarrier:
+ case OpMemoryBarrier:
+ return false;
+
+ // Ray tracing builtins are impure.
+ case OpReportIntersectionNV:
+ case OpIgnoreIntersectionNV:
+ case OpTerminateRayNV:
+ case OpTraceNV:
+ case OpExecuteCallableNV:
+ return false;
+
+ // OpExtInst is potentially impure depending on extension, but GLSL builtins are at least pure.
+
+ default:
+ break;
+ }
+ }
+
+ return true;
+}
+
+string Compiler::to_name(uint32_t id, bool allow_alias) const
+{
+ if (allow_alias && ir.ids[id].get_type() == TypeType)
+ {
+ // If this type is a simple alias, emit the
+ // name of the original type instead.
+ // We don't want to override the meta alias
+ // as that can be overridden by the reflection APIs after parse.
+ auto &type = get<SPIRType>(id);
+ if (type.type_alias)
+ {
+ // If the alias master has been specially packed, we will have emitted a clean variant as well,
+ // so skip the name aliasing here.
+ if (!has_extended_decoration(type.type_alias, SPIRVCrossDecorationPacked))
+ return to_name(type.type_alias);
+ }
+ }
+
+ auto &alias = ir.get_name(id);
+ if (alias.empty())
+ return join("_", id);
+ else
+ return alias;
+}
+
+bool Compiler::function_is_pure(const SPIRFunction &func)
+{
+ for (auto block : func.blocks)
+ {
+ if (!block_is_pure(get<SPIRBlock>(block)))
+ {
+ //fprintf(stderr, "Function %s is impure!\n", to_name(func.self).c_str());
+ return false;
+ }
+ }
+
+ //fprintf(stderr, "Function %s is pure!\n", to_name(func.self).c_str());
+ return true;
+}
+
+void Compiler::register_global_read_dependencies(const SPIRBlock &block, uint32_t id)
+{
+ for (auto &i : block.ops)
+ {
+ auto ops = stream(i);
+ auto op = static_cast<Op>(i.op);
+
+ switch (op)
+ {
+ case OpFunctionCall:
+ {
+ uint32_t func = ops[2];
+ register_global_read_dependencies(get<SPIRFunction>(func), id);
+ break;
+ }
+
+ case OpLoad:
+ case OpImageRead:
+ {
+ // If we're in a storage class which does not get invalidated, adding dependencies here is no big deal.
+ auto *var = maybe_get_backing_variable(ops[2]);
+ if (var && var->storage != StorageClassFunction)
+ {
+ auto &type = get<SPIRType>(var->basetype);
+
+ // InputTargets are immutable.
+ if (type.basetype != SPIRType::Image && type.image.dim != DimSubpassData)
+ var->dependees.push_back(id);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+}
+
+void Compiler::register_global_read_dependencies(const SPIRFunction &func, uint32_t id)
+{
+ for (auto block : func.blocks)
+ register_global_read_dependencies(get<SPIRBlock>(block), id);
+}
+
+SPIRVariable *Compiler::maybe_get_backing_variable(uint32_t chain)
+{
+ auto *var = maybe_get<SPIRVariable>(chain);
+ if (!var)
+ {
+ auto *cexpr = maybe_get<SPIRExpression>(chain);
+ if (cexpr)
+ var = maybe_get<SPIRVariable>(cexpr->loaded_from);
+
+ auto *access_chain = maybe_get<SPIRAccessChain>(chain);
+ if (access_chain)
+ var = maybe_get<SPIRVariable>(access_chain->loaded_from);
+ }
+
+ return var;
+}
+
+void Compiler::register_read(uint32_t expr, uint32_t chain, bool forwarded)
+{
+ auto &e = get<SPIRExpression>(expr);
+ auto *var = maybe_get_backing_variable(chain);
+
+ if (var)
+ {
+ e.loaded_from = var->self;
+
+ // If the backing variable is immutable, we do not need to depend on the variable.
+ if (forwarded && !is_immutable(var->self))
+ var->dependees.push_back(e.self);
+
+ // If we load from a parameter, make sure we create "inout" if we also write to the parameter.
+ // The default is "in" however, so we never invalidate our compilation by reading.
+ if (var && var->parameter)
+ var->parameter->read_count++;
+ }
+}
+
+void Compiler::register_write(uint32_t chain)
+{
+ auto *var = maybe_get<SPIRVariable>(chain);
+ if (!var)
+ {
+ // If we're storing through an access chain, invalidate the backing variable instead.
+ auto *expr = maybe_get<SPIRExpression>(chain);
+ if (expr && expr->loaded_from)
+ var = maybe_get<SPIRVariable>(expr->loaded_from);
+
+ auto *access_chain = maybe_get<SPIRAccessChain>(chain);
+ if (access_chain && access_chain->loaded_from)
+ var = maybe_get<SPIRVariable>(access_chain->loaded_from);
+ }
+
+ if (var)
+ {
+ // If our variable is in a storage class which can alias with other buffers,
+ // invalidate all variables which depend on aliased variables. And if this is a
+ // variable pointer, then invalidate all variables regardless.
+ if (get_variable_data_type(*var).pointer)
+ flush_all_active_variables();
+ if (variable_storage_is_aliased(*var))
+ flush_all_aliased_variables();
+ else if (var)
+ flush_dependees(*var);
+
+ // We tried to write to a parameter which is not marked with out qualifier, force a recompile.
+ if (var->parameter && var->parameter->write_count == 0)
+ {
+ var->parameter->write_count++;
+ force_recompile = true;
+ }
+ }
+ else
+ {
+ // If we stored through a variable pointer, then we don't know which
+ // variable we stored to. So *all* expressions after this point need to
+ // be invalidated.
+ // FIXME: If we can prove that the variable pointer will point to
+ // only certain variables, we can invalidate only those.
+ flush_all_active_variables();
+ }
+}
+
+void Compiler::flush_dependees(SPIRVariable &var)
+{
+ for (auto expr : var.dependees)
+ invalid_expressions.insert(expr);
+ var.dependees.clear();
+}
+
+void Compiler::flush_all_aliased_variables()
+{
+ for (auto aliased : aliased_variables)
+ flush_dependees(get<SPIRVariable>(aliased));
+}
+
+void Compiler::flush_all_atomic_capable_variables()
+{
+ for (auto global : global_variables)
+ flush_dependees(get<SPIRVariable>(global));
+ flush_all_aliased_variables();
+}
+
+void Compiler::flush_control_dependent_expressions(uint32_t block_id)
+{
+ auto &block = get<SPIRBlock>(block_id);
+ for (auto &expr : block.invalidate_expressions)
+ invalid_expressions.insert(expr);
+ block.invalidate_expressions.clear();
+}
+
+void Compiler::flush_all_active_variables()
+{
+ // Invalidate all temporaries we read from variables in this block since they were forwarded.
+ // Invalidate all temporaries we read from globals.
+ for (auto &v : current_function->local_variables)
+ flush_dependees(get<SPIRVariable>(v));
+ for (auto &arg : current_function->arguments)
+ flush_dependees(get<SPIRVariable>(arg.id));
+ for (auto global : global_variables)
+ flush_dependees(get<SPIRVariable>(global));
+
+ flush_all_aliased_variables();
+}
+
+uint32_t Compiler::expression_type_id(uint32_t id) const
+{
+ switch (ir.ids[id].get_type())
+ {
+ case TypeVariable:
+ return get<SPIRVariable>(id).basetype;
+
+ case TypeExpression:
+ return get<SPIRExpression>(id).expression_type;
+
+ case TypeConstant:
+ return get<SPIRConstant>(id).constant_type;
+
+ case TypeConstantOp:
+ return get<SPIRConstantOp>(id).basetype;
+
+ case TypeUndef:
+ return get<SPIRUndef>(id).basetype;
+
+ case TypeCombinedImageSampler:
+ return get<SPIRCombinedImageSampler>(id).combined_type;
+
+ case TypeAccessChain:
+ return get<SPIRAccessChain>(id).basetype;
+
+ default:
+ SPIRV_CROSS_THROW("Cannot resolve expression type.");
+ }
+}
+
+const SPIRType &Compiler::expression_type(uint32_t id) const
+{
+ return get<SPIRType>(expression_type_id(id));
+}
+
+bool Compiler::expression_is_lvalue(uint32_t id) const
+{
+ auto &type = expression_type(id);
+ switch (type.basetype)
+ {
+ case SPIRType::SampledImage:
+ case SPIRType::Image:
+ case SPIRType::Sampler:
+ return false;
+
+ default:
+ return true;
+ }
+}
+
+bool Compiler::is_immutable(uint32_t id) const
+{
+ if (ir.ids[id].get_type() == TypeVariable)
+ {
+ auto &var = get<SPIRVariable>(id);
+
+ // Anything we load from the UniformConstant address space is guaranteed to be immutable.
+ bool pointer_to_const = var.storage == StorageClassUniformConstant;
+ return pointer_to_const || var.phi_variable || !expression_is_lvalue(id);
+ }
+ else if (ir.ids[id].get_type() == TypeAccessChain)
+ return get<SPIRAccessChain>(id).immutable;
+ else if (ir.ids[id].get_type() == TypeExpression)
+ return get<SPIRExpression>(id).immutable;
+ else if (ir.ids[id].get_type() == TypeConstant || ir.ids[id].get_type() == TypeConstantOp ||
+ ir.ids[id].get_type() == TypeUndef)
+ return true;
+ else
+ return false;
+}
+
+static inline bool storage_class_is_interface(spv::StorageClass storage)
+{
+ switch (storage)
+ {
+ case StorageClassInput:
+ case StorageClassOutput:
+ case StorageClassUniform:
+ case StorageClassUniformConstant:
+ case StorageClassAtomicCounter:
+ case StorageClassPushConstant:
+ case StorageClassStorageBuffer:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool Compiler::is_hidden_variable(const SPIRVariable &var, bool include_builtins) const
+{
+ if ((is_builtin_variable(var) && !include_builtins) || var.remapped_variable)
+ return true;
+
+ // Combined image samplers are always considered active as they are "magic" variables.
+ if (find_if(begin(combined_image_samplers), end(combined_image_samplers), [&var](const CombinedImageSampler &samp) {
+ return samp.combined_id == var.self;
+ }) != end(combined_image_samplers))
+ {
+ return false;
+ }
+
+ bool hidden = false;
+ if (check_active_interface_variables && storage_class_is_interface(var.storage))
+ hidden = active_interface_variables.find(var.self) == end(active_interface_variables);
+ return hidden;
+}
+
+bool Compiler::is_builtin_type(const SPIRType &type) const
+{
+ auto *type_meta = ir.find_meta(type.self);
+
+ // We can have builtin structs as well. If one member of a struct is builtin, the struct must also be builtin.
+ if (type_meta)
+ for (auto &m : type_meta->members)
+ if (m.builtin)
+ return true;
+
+ return false;
+}
+
+bool Compiler::is_builtin_variable(const SPIRVariable &var) const
+{
+ auto *m = ir.find_meta(var.self);
+
+ if (var.compat_builtin || (m && m->decoration.builtin))
+ return true;
+ else
+ return is_builtin_type(get<SPIRType>(var.basetype));
+}
+
+bool Compiler::is_member_builtin(const SPIRType &type, uint32_t index, BuiltIn *builtin) const
+{
+ auto *type_meta = ir.find_meta(type.self);
+
+ if (type_meta)
+ {
+ auto &memb = type_meta->members;
+ if (index < memb.size() && memb[index].builtin)
+ {
+ if (builtin)
+ *builtin = memb[index].builtin_type;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool Compiler::is_scalar(const SPIRType &type) const
+{
+ return type.basetype != SPIRType::Struct && type.vecsize == 1 && type.columns == 1;
+}
+
+bool Compiler::is_vector(const SPIRType &type) const
+{
+ return type.vecsize > 1 && type.columns == 1;
+}
+
+bool Compiler::is_matrix(const SPIRType &type) const
+{
+ return type.vecsize > 1 && type.columns > 1;
+}
+
+bool Compiler::is_array(const SPIRType &type) const
+{
+ return !type.array.empty();
+}
+
+ShaderResources Compiler::get_shader_resources() const
+{
+ return get_shader_resources(nullptr);
+}
+
+ShaderResources Compiler::get_shader_resources(const unordered_set<uint32_t> &active_variables) const
+{
+ return get_shader_resources(&active_variables);
+}
+
+bool Compiler::InterfaceVariableAccessHandler::handle(Op opcode, const uint32_t *args, uint32_t length)
+{
+ uint32_t variable = 0;
+ switch (opcode)
+ {
+ // Need this first, otherwise, GCC complains about unhandled switch statements.
+ default:
+ break;
+
+ case OpFunctionCall:
+ {
+ // Invalid SPIR-V.
+ if (length < 3)
+ return false;
+
+ uint32_t count = length - 3;
+ args += 3;
+ for (uint32_t i = 0; i < count; i++)
+ {
+ auto *var = compiler.maybe_get<SPIRVariable>(args[i]);
+ if (var && storage_class_is_interface(var->storage))
+ variables.insert(args[i]);
+ }
+ break;
+ }
+
+ case OpSelect:
+ {
+ // Invalid SPIR-V.
+ if (length < 5)
+ return false;
+
+ uint32_t count = length - 3;
+ args += 3;
+ for (uint32_t i = 0; i < count; i++)
+ {
+ auto *var = compiler.maybe_get<SPIRVariable>(args[i]);
+ if (var && storage_class_is_interface(var->storage))
+ variables.insert(args[i]);
+ }
+ break;
+ }
+
+ case OpPhi:
+ {
+ // Invalid SPIR-V.
+ if (length < 2)
+ return false;
+
+ uint32_t count = length - 2;
+ args += 2;
+ for (uint32_t i = 0; i < count; i += 2)
+ {
+ auto *var = compiler.maybe_get<SPIRVariable>(args[i]);
+ if (var && storage_class_is_interface(var->storage))
+ variables.insert(args[i]);
+ }
+ break;
+ }
+
+ case OpAtomicStore:
+ case OpStore:
+ // Invalid SPIR-V.
+ if (length < 1)
+ return false;
+ variable = args[0];
+ break;
+
+ case OpCopyMemory:
+ {
+ if (length < 2)
+ return false;
+
+ auto *var = compiler.maybe_get<SPIRVariable>(args[0]);
+ if (var && storage_class_is_interface(var->storage))
+ variables.insert(variable);
+
+ var = compiler.maybe_get<SPIRVariable>(args[1]);
+ if (var && storage_class_is_interface(var->storage))
+ variables.insert(variable);
+ break;
+ }
+
+ case OpExtInst:
+ {
+ if (length < 5)
+ return false;
+ uint32_t extension_set = args[2];
+ if (compiler.get<SPIRExtension>(extension_set).ext == SPIRExtension::SPV_AMD_shader_explicit_vertex_parameter)
+ {
+ enum AMDShaderExplicitVertexParameter
+ {
+ InterpolateAtVertexAMD = 1
+ };
+
+ auto op = static_cast<AMDShaderExplicitVertexParameter>(args[3]);
+
+ switch (op)
+ {
+ case InterpolateAtVertexAMD:
+ {
+ auto *var = compiler.maybe_get<SPIRVariable>(args[4]);
+ if (var && storage_class_is_interface(var->storage))
+ variables.insert(args[4]);
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+ break;
+ }
+
+ case OpAccessChain:
+ case OpInBoundsAccessChain:
+ case OpPtrAccessChain:
+ case OpLoad:
+ case OpCopyObject:
+ case OpImageTexelPointer:
+ case OpAtomicLoad:
+ case OpAtomicExchange:
+ case OpAtomicCompareExchange:
+ case OpAtomicCompareExchangeWeak:
+ case OpAtomicIIncrement:
+ case OpAtomicIDecrement:
+ case OpAtomicIAdd:
+ case OpAtomicISub:
+ case OpAtomicSMin:
+ case OpAtomicUMin:
+ case OpAtomicSMax:
+ case OpAtomicUMax:
+ case OpAtomicAnd:
+ case OpAtomicOr:
+ case OpAtomicXor:
+ // Invalid SPIR-V.
+ if (length < 3)
+ return false;
+ variable = args[2];
+ break;
+ }
+
+ if (variable)
+ {
+ auto *var = compiler.maybe_get<SPIRVariable>(variable);
+ if (var && storage_class_is_interface(var->storage))
+ variables.insert(variable);
+ }
+ return true;
+}
+
+unordered_set<uint32_t> Compiler::get_active_interface_variables() const
+{
+ // Traverse the call graph and find all interface variables which are in use.
+ unordered_set<uint32_t> variables;
+ InterfaceVariableAccessHandler handler(*this, variables);
+ traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
+
+ // Make sure we preserve output variables which are only initialized, but never accessed by any code.
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, const SPIRVariable &var) {
+ if (var.storage == StorageClassOutput && var.initializer != 0)
+ variables.insert(var.self);
+ });
+
+ // If we needed to create one, we'll need it.
+ if (dummy_sampler_id)
+ variables.insert(dummy_sampler_id);
+
+ return variables;
+}
+
+void Compiler::set_enabled_interface_variables(std::unordered_set<uint32_t> active_variables)
+{
+ active_interface_variables = move(active_variables);
+ check_active_interface_variables = true;
+}
+
+ShaderResources Compiler::get_shader_resources(const unordered_set<uint32_t> *active_variables) const
+{
+ ShaderResources res;
+
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, const SPIRVariable &var) {
+ auto &type = this->get<SPIRType>(var.basetype);
+
+ // It is possible for uniform storage classes to be passed as function parameters, so detect
+ // that. To detect function parameters, check of StorageClass of variable is function scope.
+ if (var.storage == StorageClassFunction || !type.pointer || is_builtin_variable(var))
+ return;
+
+ if (active_variables && active_variables->find(var.self) == end(*active_variables))
+ return;
+
+ // Input
+ if (var.storage == StorageClassInput && interface_variable_exists_in_entry_point(var.self))
+ {
+ if (has_decoration(type.self, DecorationBlock))
+ {
+ res.stage_inputs.push_back(
+ { var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self) });
+ }
+ else
+ res.stage_inputs.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
+ }
+ // Subpass inputs
+ else if (var.storage == StorageClassUniformConstant && type.image.dim == DimSubpassData)
+ {
+ res.subpass_inputs.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
+ }
+ // Outputs
+ else if (var.storage == StorageClassOutput && interface_variable_exists_in_entry_point(var.self))
+ {
+ if (has_decoration(type.self, DecorationBlock))
+ {
+ res.stage_outputs.push_back(
+ { var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self) });
+ }
+ else
+ res.stage_outputs.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
+ }
+ // UBOs
+ else if (type.storage == StorageClassUniform && has_decoration(type.self, DecorationBlock))
+ {
+ res.uniform_buffers.push_back(
+ { var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self) });
+ }
+ // Old way to declare SSBOs.
+ else if (type.storage == StorageClassUniform && has_decoration(type.self, DecorationBufferBlock))
+ {
+ res.storage_buffers.push_back(
+ { var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self) });
+ }
+ // Modern way to declare SSBOs.
+ else if (type.storage == StorageClassStorageBuffer)
+ {
+ res.storage_buffers.push_back(
+ { var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self) });
+ }
+ // Push constant blocks
+ else if (type.storage == StorageClassPushConstant)
+ {
+ // There can only be one push constant block, but keep the vector in case this restriction is lifted
+ // in the future.
+ res.push_constant_buffers.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
+ }
+ // Images
+ else if (type.storage == StorageClassUniformConstant && type.basetype == SPIRType::Image &&
+ type.image.sampled == 2)
+ {
+ res.storage_images.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
+ }
+ // Separate images
+ else if (type.storage == StorageClassUniformConstant && type.basetype == SPIRType::Image &&
+ type.image.sampled == 1)
+ {
+ res.separate_images.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
+ }
+ // Separate samplers
+ else if (type.storage == StorageClassUniformConstant && type.basetype == SPIRType::Sampler)
+ {
+ res.separate_samplers.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
+ }
+ // Textures
+ else if (type.storage == StorageClassUniformConstant && type.basetype == SPIRType::SampledImage)
+ {
+ res.sampled_images.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
+ }
+ // Atomic counters
+ else if (type.storage == StorageClassAtomicCounter)
+ {
+ res.atomic_counters.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
+ }
+ // Acceleration structures
+ else if (type.storage == StorageClassUniformConstant && type.basetype == SPIRType::AccelerationStructureNV)
+ {
+ res.acceleration_structures.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
+ }
+ });
+
+ return res;
+}
+
+bool Compiler::type_is_block_like(const SPIRType &type) const
+{
+ if (type.basetype != SPIRType::Struct)
+ return false;
+
+ if (has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock))
+ {
+ return true;
+ }
+
+ // Block-like types may have Offset decorations.
+ for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
+ if (has_member_decoration(type.self, i, DecorationOffset))
+ return true;
+
+ return false;
+}
+
+void Compiler::fixup_type_alias()
+{
+ // Due to how some backends work, the "master" type of type_alias must be a block-like type if it exists.
+ // FIXME: Multiple alias types which are both block-like will be awkward, for now, it's best to just drop the type
+ // alias if the slave type is a block type.
+ ir.for_each_typed_id<SPIRType>([&](uint32_t self, SPIRType &type) {
+ if (type.type_alias && type_is_block_like(type))
+ {
+ // Become the master.
+ ir.for_each_typed_id<SPIRType>([&](uint32_t other_id, SPIRType &other_type) {
+ if (other_id == type.self)
+ return;
+
+ if (other_type.type_alias == type.type_alias)
+ other_type.type_alias = type.self;
+ });
+
+ this->get<SPIRType>(type.type_alias).type_alias = self;
+ type.type_alias = 0;
+ }
+ });
+
+ ir.for_each_typed_id<SPIRType>([&](uint32_t, SPIRType &type) {
+ if (type.type_alias && type_is_block_like(type))
+ {
+ // This is not allowed, drop the type_alias.
+ type.type_alias = 0;
+ }
+ });
+
+ // Reorder declaration of types so that the master of the type alias is always emitted first.
+ // We need this in case a type B depends on type A (A must come before in the vector), but A is an alias of a type Abuffer, which
+ // means declaration of A doesn't happen (yet), and order would be B, ABuffer and not ABuffer, B. Fix this up here.
+ auto &type_ids = ir.ids_for_type[TypeType];
+ for (auto alias_itr = begin(type_ids); alias_itr != end(type_ids); ++alias_itr)
+ {
+ auto &type = get<SPIRType>(*alias_itr);
+ if (type.type_alias != 0 && !has_extended_decoration(type.type_alias, SPIRVCrossDecorationPacked))
+ {
+ // We will skip declaring this type, so make sure the type_alias type comes before.
+ auto master_itr = find(begin(type_ids), end(type_ids), type.type_alias);
+ assert(master_itr != end(type_ids));
+
+ if (alias_itr < master_itr)
+ {
+ // Must also swap the type order for the constant-type joined array.
+ auto &joined_types = ir.ids_for_constant_or_type;
+ auto alt_alias_itr = find(begin(joined_types), end(joined_types), *alias_itr);
+ auto alt_master_itr = find(begin(joined_types), end(joined_types), *master_itr);
+ assert(alt_alias_itr != end(joined_types));
+ assert(alt_master_itr != end(joined_types));
+
+ swap(*alias_itr, *master_itr);
+ swap(*alt_alias_itr, *alt_master_itr);
+ }
+ }
+ }
+}
+
+void Compiler::parse_fixup()
+{
+ // Figure out specialization constants for work group sizes.
+ for (auto id_ : ir.ids_for_constant_or_variable)
+ {
+ auto &id = ir.ids[id_];
+
+ if (id.get_type() == TypeConstant)
+ {
+ auto &c = id.get<SPIRConstant>();
+ if (ir.meta[c.self].decoration.builtin && ir.meta[c.self].decoration.builtin_type == BuiltInWorkgroupSize)
+ {
+ // In current SPIR-V, there can be just one constant like this.
+ // All entry points will receive the constant value.
+ for (auto &entry : ir.entry_points)
+ {
+ entry.second.workgroup_size.constant = c.self;
+ entry.second.workgroup_size.x = c.scalar(0, 0);
+ entry.second.workgroup_size.y = c.scalar(0, 1);
+ entry.second.workgroup_size.z = c.scalar(0, 2);
+ }
+ }
+ }
+ else if (id.get_type() == TypeVariable)
+ {
+ auto &var = id.get<SPIRVariable>();
+ if (var.storage == StorageClassPrivate || var.storage == StorageClassWorkgroup ||
+ var.storage == StorageClassOutput)
+ global_variables.push_back(var.self);
+ if (variable_storage_is_aliased(var))
+ aliased_variables.push_back(var.self);
+ }
+ }
+
+ fixup_type_alias();
+}
+
+void Compiler::update_name_cache(unordered_set<string> &cache_primary, const unordered_set<string> &cache_secondary,
+ string &name)
+{
+ if (name.empty())
+ return;
+
+ const auto find_name = [&](const string &n) -> bool {
+ if (cache_primary.find(n) != end(cache_primary))
+ return true;
+
+ if (&cache_primary != &cache_secondary)
+ if (cache_secondary.find(n) != end(cache_secondary))
+ return true;
+
+ return false;
+ };
+
+ const auto insert_name = [&](const string &n) { cache_primary.insert(n); };
+
+ if (!find_name(name))
+ {
+ insert_name(name);
+ return;
+ }
+
+ uint32_t counter = 0;
+ auto tmpname = name;
+
+ bool use_linked_underscore = true;
+
+ if (tmpname == "_")
+ {
+ // We cannot just append numbers, as we will end up creating internally reserved names.
+ // Make it like _0_<counter> instead.
+ tmpname += "0";
+ }
+ else if (tmpname.back() == '_')
+ {
+ // The last_character is an underscore, so we don't need to link in underscore.
+ // This would violate double underscore rules.
+ use_linked_underscore = false;
+ }
+
+ // If there is a collision (very rare),
+ // keep tacking on extra identifier until it's unique.
+ do
+ {
+ counter++;
+ name = tmpname + (use_linked_underscore ? "_" : "") + convert_to_string(counter);
+ } while (find_name(name));
+ insert_name(name);
+}
+
+void Compiler::update_name_cache(unordered_set<string> &cache, string &name)
+{
+ update_name_cache(cache, cache, name);
+}
+
+void Compiler::set_name(uint32_t id, const std::string &name)
+{
+ ir.set_name(id, name);
+}
+
+const SPIRType &Compiler::get_type(uint32_t id) const
+{
+ return get<SPIRType>(id);
+}
+
+const SPIRType &Compiler::get_type_from_variable(uint32_t id) const
+{
+ return get<SPIRType>(get<SPIRVariable>(id).basetype);
+}
+
+uint32_t Compiler::get_pointee_type_id(uint32_t type_id) const
+{
+ auto *p_type = &get<SPIRType>(type_id);
+ if (p_type->pointer)
+ {
+ assert(p_type->parent_type);
+ type_id = p_type->parent_type;
+ }
+ return type_id;
+}
+
+const SPIRType &Compiler::get_pointee_type(const SPIRType &type) const
+{
+ auto *p_type = &type;
+ if (p_type->pointer)
+ {
+ assert(p_type->parent_type);
+ p_type = &get<SPIRType>(p_type->parent_type);
+ }
+ return *p_type;
+}
+
+const SPIRType &Compiler::get_pointee_type(uint32_t type_id) const
+{
+ return get_pointee_type(get<SPIRType>(type_id));
+}
+
+uint32_t Compiler::get_variable_data_type_id(const SPIRVariable &var) const
+{
+ if (var.phi_variable)
+ return var.basetype;
+ return get_pointee_type_id(var.basetype);
+}
+
+SPIRType &Compiler::get_variable_data_type(const SPIRVariable &var)
+{
+ return get<SPIRType>(get_variable_data_type_id(var));
+}
+
+const SPIRType &Compiler::get_variable_data_type(const SPIRVariable &var) const
+{
+ return get<SPIRType>(get_variable_data_type_id(var));
+}
+
+SPIRType &Compiler::get_variable_element_type(const SPIRVariable &var)
+{
+ SPIRType *type = &get_variable_data_type(var);
+ if (is_array(*type))
+ type = &get<SPIRType>(type->parent_type);
+ return *type;
+}
+
+const SPIRType &Compiler::get_variable_element_type(const SPIRVariable &var) const
+{
+ const SPIRType *type = &get_variable_data_type(var);
+ if (is_array(*type))
+ type = &get<SPIRType>(type->parent_type);
+ return *type;
+}
+
+bool Compiler::is_sampled_image_type(const SPIRType &type)
+{
+ return (type.basetype == SPIRType::Image || type.basetype == SPIRType::SampledImage) && type.image.sampled == 1 &&
+ type.image.dim != DimBuffer;
+}
+
+void Compiler::set_member_decoration_string(uint32_t id, uint32_t index, spv::Decoration decoration,
+ const std::string &argument)
+{
+ ir.set_member_decoration_string(id, index, decoration, argument);
+}
+
+void Compiler::set_member_decoration(uint32_t id, uint32_t index, Decoration decoration, uint32_t argument)
+{
+ ir.set_member_decoration(id, index, decoration, argument);
+}
+
+void Compiler::set_member_name(uint32_t id, uint32_t index, const std::string &name)
+{
+ ir.set_member_name(id, index, name);
+}
+
+const std::string &Compiler::get_member_name(uint32_t id, uint32_t index) const
+{
+ return ir.get_member_name(id, index);
+}
+
+void Compiler::set_qualified_name(uint32_t id, const string &name)
+{
+ ir.meta[id].decoration.qualified_alias = name;
+}
+
+void Compiler::set_member_qualified_name(uint32_t type_id, uint32_t index, const std::string &name)
+{
+ ir.meta[type_id].members.resize(max(ir.meta[type_id].members.size(), size_t(index) + 1));
+ ir.meta[type_id].members[index].qualified_alias = name;
+}
+
+const string &Compiler::get_member_qualified_name(uint32_t type_id, uint32_t index) const
+{
+ auto *m = ir.find_meta(type_id);
+ if (m && index < m->members.size())
+ return m->members[index].qualified_alias;
+ else
+ return ir.get_empty_string();
+}
+
+uint32_t Compiler::get_member_decoration(uint32_t id, uint32_t index, Decoration decoration) const
+{
+ return ir.get_member_decoration(id, index, decoration);
+}
+
+const Bitset &Compiler::get_member_decoration_bitset(uint32_t id, uint32_t index) const
+{
+ return ir.get_member_decoration_bitset(id, index);
+}
+
+bool Compiler::has_member_decoration(uint32_t id, uint32_t index, Decoration decoration) const
+{
+ return ir.has_member_decoration(id, index, decoration);
+}
+
+void Compiler::unset_member_decoration(uint32_t id, uint32_t index, Decoration decoration)
+{
+ ir.unset_member_decoration(id, index, decoration);
+}
+
+void Compiler::set_decoration_string(uint32_t id, spv::Decoration decoration, const std::string &argument)
+{
+ ir.set_decoration_string(id, decoration, argument);
+}
+
+void Compiler::set_decoration(uint32_t id, Decoration decoration, uint32_t argument)
+{
+ ir.set_decoration(id, decoration, argument);
+}
+
+void Compiler::set_extended_decoration(uint32_t id, ExtendedDecorations decoration, uint32_t value)
+{
+ auto &dec = ir.meta[id].decoration;
+ switch (decoration)
+ {
+ case SPIRVCrossDecorationPacked:
+ dec.extended.packed = true;
+ break;
+
+ case SPIRVCrossDecorationPackedType:
+ dec.extended.packed_type = value;
+ break;
+
+ case SPIRVCrossDecorationInterfaceMemberIndex:
+ dec.extended.ib_member_index = value;
+ break;
+
+ case SPIRVCrossDecorationInterfaceOrigID:
+ dec.extended.ib_orig_id = value;
+ break;
+
+ case SPIRVCrossDecorationArgumentBufferID:
+ dec.extended.argument_buffer_id = value;
+ break;
+ }
+}
+
+void Compiler::set_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration,
+ uint32_t value)
+{
+ ir.meta[type].members.resize(max(ir.meta[type].members.size(), size_t(index) + 1));
+ auto &dec = ir.meta[type].members[index];
+
+ switch (decoration)
+ {
+ case SPIRVCrossDecorationPacked:
+ dec.extended.packed = true;
+ break;
+
+ case SPIRVCrossDecorationPackedType:
+ dec.extended.packed_type = value;
+ break;
+
+ case SPIRVCrossDecorationInterfaceMemberIndex:
+ dec.extended.ib_member_index = value;
+ break;
+
+ case SPIRVCrossDecorationInterfaceOrigID:
+ dec.extended.ib_orig_id = value;
+ break;
+
+ case SPIRVCrossDecorationArgumentBufferID:
+ dec.extended.argument_buffer_id = value;
+ break;
+ }
+}
+
+uint32_t Compiler::get_extended_decoration(uint32_t id, ExtendedDecorations decoration) const
+{
+ auto *m = ir.find_meta(id);
+ if (!m)
+ return 0;
+
+ auto &dec = m->decoration;
+ switch (decoration)
+ {
+ case SPIRVCrossDecorationPacked:
+ return uint32_t(dec.extended.packed);
+
+ case SPIRVCrossDecorationPackedType:
+ return dec.extended.packed_type;
+
+ case SPIRVCrossDecorationInterfaceMemberIndex:
+ return dec.extended.ib_member_index;
+
+ case SPIRVCrossDecorationInterfaceOrigID:
+ return dec.extended.ib_orig_id;
+
+ case SPIRVCrossDecorationArgumentBufferID:
+ return dec.extended.argument_buffer_id;
+ }
+
+ return 0;
+}
+
+uint32_t Compiler::get_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const
+{
+ auto *m = ir.find_meta(type);
+ if (!m)
+ return 0;
+
+ if (index >= m->members.size())
+ return 0;
+
+ auto &dec = m->members[index];
+ switch (decoration)
+ {
+ case SPIRVCrossDecorationPacked:
+ return uint32_t(dec.extended.packed);
+
+ case SPIRVCrossDecorationPackedType:
+ return dec.extended.packed_type;
+
+ case SPIRVCrossDecorationInterfaceMemberIndex:
+ return dec.extended.ib_member_index;
+
+ case SPIRVCrossDecorationInterfaceOrigID:
+ return dec.extended.ib_orig_id;
+
+ case SPIRVCrossDecorationArgumentBufferID:
+ return dec.extended.argument_buffer_id;
+ }
+
+ return 0;
+}
+
+bool Compiler::has_extended_decoration(uint32_t id, ExtendedDecorations decoration) const
+{
+ auto *m = ir.find_meta(id);
+ if (!m)
+ return false;
+
+ auto &dec = m->decoration;
+ switch (decoration)
+ {
+ case SPIRVCrossDecorationPacked:
+ return dec.extended.packed;
+
+ case SPIRVCrossDecorationPackedType:
+ return dec.extended.packed_type != 0;
+
+ case SPIRVCrossDecorationInterfaceMemberIndex:
+ return dec.extended.ib_member_index != uint32_t(-1);
+
+ case SPIRVCrossDecorationInterfaceOrigID:
+ return dec.extended.ib_orig_id != 0;
+
+ case SPIRVCrossDecorationArgumentBufferID:
+ return dec.extended.argument_buffer_id != 0;
+ }
+
+ return false;
+}
+
+bool Compiler::has_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const
+{
+ auto *m = ir.find_meta(type);
+ if (!m)
+ return false;
+
+ if (index >= m->members.size())
+ return false;
+
+ auto &dec = m->members[index];
+ switch (decoration)
+ {
+ case SPIRVCrossDecorationPacked:
+ return dec.extended.packed;
+
+ case SPIRVCrossDecorationPackedType:
+ return dec.extended.packed_type != 0;
+
+ case SPIRVCrossDecorationInterfaceMemberIndex:
+ return dec.extended.ib_member_index != uint32_t(-1);
+
+ case SPIRVCrossDecorationInterfaceOrigID:
+ return dec.extended.ib_orig_id != 0;
+
+ case SPIRVCrossDecorationArgumentBufferID:
+ return dec.extended.argument_buffer_id != uint32_t(-1);
+ }
+
+ return false;
+}
+
+void Compiler::unset_extended_decoration(uint32_t id, ExtendedDecorations decoration)
+{
+ auto &dec = ir.meta[id].decoration;
+ switch (decoration)
+ {
+ case SPIRVCrossDecorationPacked:
+ dec.extended.packed = false;
+ break;
+
+ case SPIRVCrossDecorationPackedType:
+ dec.extended.packed_type = 0;
+ break;
+
+ case SPIRVCrossDecorationInterfaceMemberIndex:
+ dec.extended.ib_member_index = ~(0u);
+ break;
+
+ case SPIRVCrossDecorationInterfaceOrigID:
+ dec.extended.ib_orig_id = 0;
+ break;
+
+ case SPIRVCrossDecorationArgumentBufferID:
+ dec.extended.argument_buffer_id = 0;
+ break;
+ }
+}
+
+void Compiler::unset_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration)
+{
+ ir.meta[type].members.resize(max(ir.meta[type].members.size(), size_t(index) + 1));
+ auto &dec = ir.meta[type].members[index];
+
+ switch (decoration)
+ {
+ case SPIRVCrossDecorationPacked:
+ dec.extended.packed = false;
+ break;
+
+ case SPIRVCrossDecorationPackedType:
+ dec.extended.packed_type = 0;
+ break;
+
+ case SPIRVCrossDecorationInterfaceMemberIndex:
+ dec.extended.ib_member_index = ~(0u);
+ break;
+
+ case SPIRVCrossDecorationInterfaceOrigID:
+ dec.extended.ib_orig_id = 0;
+ break;
+
+ case SPIRVCrossDecorationArgumentBufferID:
+ dec.extended.argument_buffer_id = 0;
+ break;
+ }
+}
+
+StorageClass Compiler::get_storage_class(uint32_t id) const
+{
+ return get<SPIRVariable>(id).storage;
+}
+
+const std::string &Compiler::get_name(uint32_t id) const
+{
+ return ir.get_name(id);
+}
+
+const std::string Compiler::get_fallback_name(uint32_t id) const
+{
+ return join("_", id);
+}
+
+const std::string Compiler::get_block_fallback_name(uint32_t id) const
+{
+ auto &var = get<SPIRVariable>(id);
+ if (get_name(id).empty())
+ return join("_", get<SPIRType>(var.basetype).self, "_", id);
+ else
+ return get_name(id);
+}
+
+const Bitset &Compiler::get_decoration_bitset(uint32_t id) const
+{
+ return ir.get_decoration_bitset(id);
+}
+
+bool Compiler::has_decoration(uint32_t id, Decoration decoration) const
+{
+ return ir.has_decoration(id, decoration);
+}
+
+const string &Compiler::get_decoration_string(uint32_t id, Decoration decoration) const
+{
+ return ir.get_decoration_string(id, decoration);
+}
+
+const string &Compiler::get_member_decoration_string(uint32_t id, uint32_t index, Decoration decoration) const
+{
+ return ir.get_member_decoration_string(id, index, decoration);
+}
+
+uint32_t Compiler::get_decoration(uint32_t id, Decoration decoration) const
+{
+ return ir.get_decoration(id, decoration);
+}
+
+void Compiler::unset_decoration(uint32_t id, Decoration decoration)
+{
+ ir.unset_decoration(id, decoration);
+}
+
+bool Compiler::get_binary_offset_for_decoration(uint32_t id, spv::Decoration decoration, uint32_t &word_offset) const
+{
+ auto *m = ir.find_meta(id);
+ if (!m)
+ return false;
+
+ auto &word_offsets = m->decoration_word_offset;
+ auto itr = word_offsets.find(decoration);
+ if (itr == end(word_offsets))
+ return false;
+
+ word_offset = itr->second;
+ return true;
+}
+
+bool Compiler::block_is_loop_candidate(const SPIRBlock &block, SPIRBlock::Method method) const
+{
+ // Tried and failed.
+ if (block.disable_block_optimization || block.complex_continue)
+ return false;
+
+ if (method == SPIRBlock::MergeToSelectForLoop || method == SPIRBlock::MergeToSelectContinueForLoop)
+ {
+ // Try to detect common for loop pattern
+ // which the code backend can use to create cleaner code.
+ // for(;;) { if (cond) { some_body; } else { break; } }
+ // is the pattern we're looking for.
+ const auto *false_block = maybe_get<SPIRBlock>(block.false_block);
+ const auto *true_block = maybe_get<SPIRBlock>(block.true_block);
+ const auto *merge_block = maybe_get<SPIRBlock>(block.merge_block);
+
+ bool false_block_is_merge = block.false_block == block.merge_block ||
+ (false_block && merge_block && execution_is_noop(*false_block, *merge_block));
+
+ bool true_block_is_merge = block.true_block == block.merge_block ||
+ (true_block && merge_block && execution_is_noop(*true_block, *merge_block));
+
+ bool positive_candidate =
+ block.true_block != block.merge_block && block.true_block != block.self && false_block_is_merge;
+
+ bool negative_candidate =
+ block.false_block != block.merge_block && block.false_block != block.self && true_block_is_merge;
+
+ bool ret = block.terminator == SPIRBlock::Select && block.merge == SPIRBlock::MergeLoop &&
+ (positive_candidate || negative_candidate);
+
+ if (ret && positive_candidate && method == SPIRBlock::MergeToSelectContinueForLoop)
+ ret = block.true_block == block.continue_block;
+ else if (ret && negative_candidate && method == SPIRBlock::MergeToSelectContinueForLoop)
+ ret = block.false_block == block.continue_block;
+
+ // If we have OpPhi which depends on branches which came from our own block,
+ // we need to flush phi variables in else block instead of a trivial break,
+ // so we cannot assume this is a for loop candidate.
+ if (ret)
+ {
+ for (auto &phi : block.phi_variables)
+ if (phi.parent == block.self)
+ return false;
+
+ auto *merge = maybe_get<SPIRBlock>(block.merge_block);
+ if (merge)
+ for (auto &phi : merge->phi_variables)
+ if (phi.parent == block.self)
+ return false;
+ }
+ return ret;
+ }
+ else if (method == SPIRBlock::MergeToDirectForLoop)
+ {
+ // Empty loop header that just sets up merge target
+ // and branches to loop body.
+ bool ret = block.terminator == SPIRBlock::Direct && block.merge == SPIRBlock::MergeLoop && block.ops.empty();
+
+ if (!ret)
+ return false;
+
+ auto &child = get<SPIRBlock>(block.next_block);
+
+ const auto *false_block = maybe_get<SPIRBlock>(child.false_block);
+ const auto *true_block = maybe_get<SPIRBlock>(child.true_block);
+ const auto *merge_block = maybe_get<SPIRBlock>(block.merge_block);
+
+ bool false_block_is_merge = child.false_block == block.merge_block ||
+ (false_block && merge_block && execution_is_noop(*false_block, *merge_block));
+
+ bool true_block_is_merge = child.true_block == block.merge_block ||
+ (true_block && merge_block && execution_is_noop(*true_block, *merge_block));
+
+ bool positive_candidate =
+ child.true_block != block.merge_block && child.true_block != block.self && false_block_is_merge;
+
+ bool negative_candidate =
+ child.false_block != block.merge_block && child.false_block != block.self && true_block_is_merge;
+
+ ret = child.terminator == SPIRBlock::Select && child.merge == SPIRBlock::MergeNone &&
+ (positive_candidate || negative_candidate);
+
+ // If we have OpPhi which depends on branches which came from our own block,
+ // we need to flush phi variables in else block instead of a trivial break,
+ // so we cannot assume this is a for loop candidate.
+ if (ret)
+ {
+ for (auto &phi : block.phi_variables)
+ if (phi.parent == block.self || phi.parent == child.self)
+ return false;
+
+ for (auto &phi : child.phi_variables)
+ if (phi.parent == block.self)
+ return false;
+
+ auto *merge = maybe_get<SPIRBlock>(block.merge_block);
+ if (merge)
+ for (auto &phi : merge->phi_variables)
+ if (phi.parent == block.self || phi.parent == child.false_block)
+ return false;
+ }
+
+ return ret;
+ }
+ else
+ return false;
+}
+
+bool Compiler::block_is_outside_flow_control_from_block(const SPIRBlock &from, const SPIRBlock &to)
+{
+ auto *start = &from;
+
+ if (start->self == to.self)
+ return true;
+
+ // Break cycles.
+ if (is_continue(start->self))
+ return false;
+
+ // If our select block doesn't merge, we must break or continue in these blocks,
+ // so if continues occur branchless within these blocks, consider them branchless as well.
+ // This is typically used for loop control.
+ if (start->terminator == SPIRBlock::Select && start->merge == SPIRBlock::MergeNone &&
+ (block_is_outside_flow_control_from_block(get<SPIRBlock>(start->true_block), to) ||
+ block_is_outside_flow_control_from_block(get<SPIRBlock>(start->false_block), to)))
+ {
+ return true;
+ }
+ else if (start->merge_block && block_is_outside_flow_control_from_block(get<SPIRBlock>(start->merge_block), to))
+ {
+ return true;
+ }
+ else if (start->next_block && block_is_outside_flow_control_from_block(get<SPIRBlock>(start->next_block), to))
+ {
+ return true;
+ }
+ else
+ return false;
+}
+
+bool Compiler::execution_is_noop(const SPIRBlock &from, const SPIRBlock &to) const
+{
+ if (!execution_is_branchless(from, to))
+ return false;
+
+ auto *start = &from;
+ for (;;)
+ {
+ if (start->self == to.self)
+ return true;
+
+ if (!start->ops.empty())
+ return false;
+
+ auto &next = get<SPIRBlock>(start->next_block);
+ // Flushing phi variables does not count as noop.
+ for (auto &phi : next.phi_variables)
+ if (phi.parent == start->self)
+ return false;
+
+ start = &next;
+ }
+}
+
+bool Compiler::execution_is_branchless(const SPIRBlock &from, const SPIRBlock &to) const
+{
+ auto *start = &from;
+ for (;;)
+ {
+ if (start->self == to.self)
+ return true;
+
+ if (start->terminator == SPIRBlock::Direct && start->merge == SPIRBlock::MergeNone)
+ start = &get<SPIRBlock>(start->next_block);
+ else
+ return false;
+ }
+}
+
+SPIRBlock::ContinueBlockType Compiler::continue_block_type(const SPIRBlock &block) const
+{
+ // The block was deemed too complex during code emit, pick conservative fallback paths.
+ if (block.complex_continue)
+ return SPIRBlock::ComplexLoop;
+
+ // In older glslang output continue block can be equal to the loop header.
+ // In this case, execution is clearly branchless, so just assume a while loop header here.
+ if (block.merge == SPIRBlock::MergeLoop)
+ return SPIRBlock::WhileLoop;
+
+ auto &dominator = get<SPIRBlock>(block.loop_dominator);
+
+ if (execution_is_noop(block, dominator))
+ return SPIRBlock::WhileLoop;
+ else if (execution_is_branchless(block, dominator))
+ return SPIRBlock::ForLoop;
+ else
+ {
+ const auto *false_block = maybe_get<SPIRBlock>(block.false_block);
+ const auto *true_block = maybe_get<SPIRBlock>(block.true_block);
+ const auto *merge_block = maybe_get<SPIRBlock>(dominator.merge_block);
+
+ bool positive_do_while = block.true_block == dominator.self &&
+ (block.false_block == dominator.merge_block ||
+ (false_block && merge_block && execution_is_noop(*false_block, *merge_block)));
+
+ bool negative_do_while = block.false_block == dominator.self &&
+ (block.true_block == dominator.merge_block ||
+ (true_block && merge_block && execution_is_noop(*true_block, *merge_block)));
+
+ if (block.merge == SPIRBlock::MergeNone && block.terminator == SPIRBlock::Select &&
+ (positive_do_while || negative_do_while))
+ {
+ return SPIRBlock::DoWhileLoop;
+ }
+ else
+ return SPIRBlock::ComplexLoop;
+ }
+}
+
+bool Compiler::traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHandler &handler) const
+{
+ handler.set_current_block(block);
+
+ // Ideally, perhaps traverse the CFG instead of all blocks in order to eliminate dead blocks,
+ // but this shouldn't be a problem in practice unless the SPIR-V is doing insane things like recursing
+ // inside dead blocks ...
+ for (auto &i : block.ops)
+ {
+ auto ops = stream(i);
+ auto op = static_cast<Op>(i.op);
+
+ if (!handler.handle(op, ops, i.length))
+ return false;
+
+ if (op == OpFunctionCall)
+ {
+ auto &func = get<SPIRFunction>(ops[2]);
+ if (handler.follow_function_call(func))
+ {
+ if (!handler.begin_function_scope(ops, i.length))
+ return false;
+ if (!traverse_all_reachable_opcodes(get<SPIRFunction>(ops[2]), handler))
+ return false;
+ if (!handler.end_function_scope(ops, i.length))
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool Compiler::traverse_all_reachable_opcodes(const SPIRFunction &func, OpcodeHandler &handler) const
+{
+ for (auto block : func.blocks)
+ if (!traverse_all_reachable_opcodes(get<SPIRBlock>(block), handler))
+ return false;
+
+ return true;
+}
+
+uint32_t Compiler::type_struct_member_offset(const SPIRType &type, uint32_t index) const
+{
+ auto *type_meta = ir.find_meta(type.self);
+ if (type_meta)
+ {
+ // Decoration must be set in valid SPIR-V, otherwise throw.
+ auto &dec = type_meta->members[index];
+ if (dec.decoration_flags.get(DecorationOffset))
+ return dec.offset;
+ else
+ SPIRV_CROSS_THROW("Struct member does not have Offset set.");
+ }
+ else
+ SPIRV_CROSS_THROW("Struct member does not have Offset set.");
+}
+
+uint32_t Compiler::type_struct_member_array_stride(const SPIRType &type, uint32_t index) const
+{
+ auto *type_meta = ir.find_meta(type.member_types[index]);
+ if (type_meta)
+ {
+ // Decoration must be set in valid SPIR-V, otherwise throw.
+ // ArrayStride is part of the array type not OpMemberDecorate.
+ auto &dec = type_meta->decoration;
+ if (dec.decoration_flags.get(DecorationArrayStride))
+ return dec.array_stride;
+ else
+ SPIRV_CROSS_THROW("Struct member does not have ArrayStride set.");
+ }
+ else
+ SPIRV_CROSS_THROW("Struct member does not have Offset set.");
+}
+
+uint32_t Compiler::type_struct_member_matrix_stride(const SPIRType &type, uint32_t index) const
+{
+ auto *type_meta = ir.find_meta(type.self);
+ if (type_meta)
+ {
+ // Decoration must be set in valid SPIR-V, otherwise throw.
+ // MatrixStride is part of OpMemberDecorate.
+ auto &dec = type_meta->members[index];
+ if (dec.decoration_flags.get(DecorationMatrixStride))
+ return dec.matrix_stride;
+ else
+ SPIRV_CROSS_THROW("Struct member does not have MatrixStride set.");
+ }
+ else
+ SPIRV_CROSS_THROW("Struct member does not have MatrixStride set.");
+}
+
+size_t Compiler::get_declared_struct_size(const SPIRType &type) const
+{
+ if (type.member_types.empty())
+ SPIRV_CROSS_THROW("Declared struct in block cannot be empty.");
+
+ uint32_t last = uint32_t(type.member_types.size() - 1);
+ size_t offset = type_struct_member_offset(type, last);
+ size_t size = get_declared_struct_member_size(type, last);
+ return offset + size;
+}
+
+size_t Compiler::get_declared_struct_size_runtime_array(const SPIRType &type, size_t array_size) const
+{
+ if (type.member_types.empty())
+ SPIRV_CROSS_THROW("Declared struct in block cannot be empty.");
+
+ size_t size = get_declared_struct_size(type);
+ auto &last_type = get<SPIRType>(type.member_types.back());
+ if (!last_type.array.empty() && last_type.array_size_literal[0] && last_type.array[0] == 0) // Runtime array
+ size += array_size * type_struct_member_array_stride(type, uint32_t(type.member_types.size() - 1));
+
+ return size;
+}
+
+size_t Compiler::get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const
+{
+ if (struct_type.member_types.empty())
+ SPIRV_CROSS_THROW("Declared struct in block cannot be empty.");
+
+ auto &flags = get_member_decoration_bitset(struct_type.self, index);
+ auto &type = get<SPIRType>(struct_type.member_types[index]);
+
+ switch (type.basetype)
+ {
+ case SPIRType::Unknown:
+ case SPIRType::Void:
+ case SPIRType::Boolean: // Bools are purely logical, and cannot be used for externally visible types.
+ case SPIRType::AtomicCounter:
+ case SPIRType::Image:
+ case SPIRType::SampledImage:
+ case SPIRType::Sampler:
+ SPIRV_CROSS_THROW("Querying size for object with opaque size.");
+
+ default:
+ break;
+ }
+
+ if (!type.array.empty())
+ {
+ // For arrays, we can use ArrayStride to get an easy check.
+ bool array_size_literal = type.array_size_literal.back();
+ uint32_t array_size = array_size_literal ? type.array.back() : get<SPIRConstant>(type.array.back()).scalar();
+ return type_struct_member_array_stride(struct_type, index) * array_size;
+ }
+ else if (type.basetype == SPIRType::Struct)
+ {
+ return get_declared_struct_size(type);
+ }
+ else
+ {
+ unsigned vecsize = type.vecsize;
+ unsigned columns = type.columns;
+
+ // Vectors.
+ if (columns == 1)
+ {
+ size_t component_size = type.width / 8;
+ return vecsize * component_size;
+ }
+ else
+ {
+ uint32_t matrix_stride = type_struct_member_matrix_stride(struct_type, index);
+
+ // Per SPIR-V spec, matrices must be tightly packed and aligned up for vec3 accesses.
+ if (flags.get(DecorationRowMajor))
+ return matrix_stride * vecsize;
+ else if (flags.get(DecorationColMajor))
+ return matrix_stride * columns;
+ else
+ SPIRV_CROSS_THROW("Either row-major or column-major must be declared for matrices.");
+ }
+ }
+}
+
+bool Compiler::BufferAccessHandler::handle(Op opcode, const uint32_t *args, uint32_t length)
+{
+ if (opcode != OpAccessChain && opcode != OpInBoundsAccessChain && opcode != OpPtrAccessChain)
+ return true;
+
+ bool ptr_chain = (opcode == OpPtrAccessChain);
+
+ // Invalid SPIR-V.
+ if (length < (ptr_chain ? 5u : 4u))
+ return false;
+
+ if (args[2] != id)
+ return true;
+
+ // Don't bother traversing the entire access chain tree yet.
+ // If we access a struct member, assume we access the entire member.
+ uint32_t index = compiler.get<SPIRConstant>(args[ptr_chain ? 4 : 3]).scalar();
+
+ // Seen this index already.
+ if (seen.find(index) != end(seen))
+ return true;
+ seen.insert(index);
+
+ auto &type = compiler.expression_type(id);
+ uint32_t offset = compiler.type_struct_member_offset(type, index);
+
+ size_t range;
+ // If we have another member in the struct, deduce the range by looking at the next member.
+ // This is okay since structs in SPIR-V can have padding, but Offset decoration must be
+ // monotonically increasing.
+ // Of course, this doesn't take into account if the SPIR-V for some reason decided to add
+ // very large amounts of padding, but that's not really a big deal.
+ if (index + 1 < type.member_types.size())
+ {
+ range = compiler.type_struct_member_offset(type, index + 1) - offset;
+ }
+ else
+ {
+ // No padding, so just deduce it from the size of the member directly.
+ range = compiler.get_declared_struct_member_size(type, index);
+ }
+
+ ranges.push_back({ index, offset, range });
+ return true;
+}
+
+std::vector<BufferRange> Compiler::get_active_buffer_ranges(uint32_t id) const
+{
+ std::vector<BufferRange> ranges;
+ BufferAccessHandler handler(*this, ranges, id);
+ traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
+ return ranges;
+}
+
+bool Compiler::types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const
+{
+ if (a.basetype != b.basetype)
+ return false;
+ if (a.width != b.width)
+ return false;
+ if (a.vecsize != b.vecsize)
+ return false;
+ if (a.columns != b.columns)
+ return false;
+ if (a.array.size() != b.array.size())
+ return false;
+
+ size_t array_count = a.array.size();
+ if (array_count && memcmp(a.array.data(), b.array.data(), array_count * sizeof(uint32_t)) != 0)
+ return false;
+
+ if (a.basetype == SPIRType::Image || a.basetype == SPIRType::SampledImage)
+ {
+ if (memcmp(&a.image, &b.image, sizeof(SPIRType::Image)) != 0)
+ return false;
+ }
+
+ if (a.member_types.size() != b.member_types.size())
+ return false;
+
+ size_t member_types = a.member_types.size();
+ for (size_t i = 0; i < member_types; i++)
+ {
+ if (!types_are_logically_equivalent(get<SPIRType>(a.member_types[i]), get<SPIRType>(b.member_types[i])))
+ return false;
+ }
+
+ return true;
+}
+
+const Bitset &Compiler::get_execution_mode_bitset() const
+{
+ return get_entry_point().flags;
+}
+
+void Compiler::set_execution_mode(ExecutionMode mode, uint32_t arg0, uint32_t arg1, uint32_t arg2)
+{
+ auto &execution = get_entry_point();
+
+ execution.flags.set(mode);
+ switch (mode)
+ {
+ case ExecutionModeLocalSize:
+ execution.workgroup_size.x = arg0;
+ execution.workgroup_size.y = arg1;
+ execution.workgroup_size.z = arg2;
+ break;
+
+ case ExecutionModeInvocations:
+ execution.invocations = arg0;
+ break;
+
+ case ExecutionModeOutputVertices:
+ execution.output_vertices = arg0;
+ break;
+
+ default:
+ break;
+ }
+}
+
+void Compiler::unset_execution_mode(ExecutionMode mode)
+{
+ auto &execution = get_entry_point();
+ execution.flags.clear(mode);
+}
+
+uint32_t Compiler::get_work_group_size_specialization_constants(SpecializationConstant &x, SpecializationConstant &y,
+ SpecializationConstant &z) const
+{
+ auto &execution = get_entry_point();
+ x = { 0, 0 };
+ y = { 0, 0 };
+ z = { 0, 0 };
+
+ if (execution.workgroup_size.constant != 0)
+ {
+ auto &c = get<SPIRConstant>(execution.workgroup_size.constant);
+
+ if (c.m.c[0].id[0] != 0)
+ {
+ x.id = c.m.c[0].id[0];
+ x.constant_id = get_decoration(c.m.c[0].id[0], DecorationSpecId);
+ }
+
+ if (c.m.c[0].id[1] != 0)
+ {
+ y.id = c.m.c[0].id[1];
+ y.constant_id = get_decoration(c.m.c[0].id[1], DecorationSpecId);
+ }
+
+ if (c.m.c[0].id[2] != 0)
+ {
+ z.id = c.m.c[0].id[2];
+ z.constant_id = get_decoration(c.m.c[0].id[2], DecorationSpecId);
+ }
+ }
+
+ return execution.workgroup_size.constant;
+}
+
+uint32_t Compiler::get_execution_mode_argument(spv::ExecutionMode mode, uint32_t index) const
+{
+ auto &execution = get_entry_point();
+ switch (mode)
+ {
+ case ExecutionModeLocalSize:
+ switch (index)
+ {
+ case 0:
+ return execution.workgroup_size.x;
+ case 1:
+ return execution.workgroup_size.y;
+ case 2:
+ return execution.workgroup_size.z;
+ default:
+ return 0;
+ }
+
+ case ExecutionModeInvocations:
+ return execution.invocations;
+
+ case ExecutionModeOutputVertices:
+ return execution.output_vertices;
+
+ default:
+ return 0;
+ }
+}
+
+ExecutionModel Compiler::get_execution_model() const
+{
+ auto &execution = get_entry_point();
+ return execution.model;
+}
+
+bool Compiler::is_tessellation_shader(ExecutionModel model)
+{
+ return model == ExecutionModelTessellationControl || model == ExecutionModelTessellationEvaluation;
+}
+
+bool Compiler::is_tessellation_shader() const
+{
+ return is_tessellation_shader(get_execution_model());
+}
+
+void Compiler::set_remapped_variable_state(uint32_t id, bool remap_enable)
+{
+ get<SPIRVariable>(id).remapped_variable = remap_enable;
+}
+
+bool Compiler::get_remapped_variable_state(uint32_t id) const
+{
+ return get<SPIRVariable>(id).remapped_variable;
+}
+
+void Compiler::set_subpass_input_remapped_components(uint32_t id, uint32_t components)
+{
+ get<SPIRVariable>(id).remapped_components = components;
+}
+
+uint32_t Compiler::get_subpass_input_remapped_components(uint32_t id) const
+{
+ return get<SPIRVariable>(id).remapped_components;
+}
+
+void Compiler::add_implied_read_expression(SPIRExpression &e, uint32_t source)
+{
+ auto itr = find(begin(e.implied_read_expressions), end(e.implied_read_expressions), source);
+ if (itr == end(e.implied_read_expressions))
+ e.implied_read_expressions.push_back(source);
+}
+
+void Compiler::add_implied_read_expression(SPIRAccessChain &e, uint32_t source)
+{
+ auto itr = find(begin(e.implied_read_expressions), end(e.implied_read_expressions), source);
+ if (itr == end(e.implied_read_expressions))
+ e.implied_read_expressions.push_back(source);
+}
+
+void Compiler::inherit_expression_dependencies(uint32_t dst, uint32_t source_expression)
+{
+ // Don't inherit any expression dependencies if the expression in dst
+ // is not a forwarded temporary.
+ if (forwarded_temporaries.find(dst) == end(forwarded_temporaries) ||
+ forced_temporaries.find(dst) != end(forced_temporaries))
+ {
+ return;
+ }
+
+ auto &e = get<SPIRExpression>(dst);
+ auto *phi = maybe_get<SPIRVariable>(source_expression);
+ if (phi && phi->phi_variable)
+ {
+ // We have used a phi variable, which can change at the end of the block,
+ // so make sure we take a dependency on this phi variable.
+ phi->dependees.push_back(dst);
+ }
+
+ auto *s = maybe_get<SPIRExpression>(source_expression);
+ if (!s)
+ return;
+
+ auto &e_deps = e.expression_dependencies;
+ auto &s_deps = s->expression_dependencies;
+
+ // If we depend on a expression, we also depend on all sub-dependencies from source.
+ e_deps.push_back(source_expression);
+ e_deps.insert(end(e_deps), begin(s_deps), end(s_deps));
+
+ // Eliminate duplicated dependencies.
+ sort(begin(e_deps), end(e_deps));
+ e_deps.erase(unique(begin(e_deps), end(e_deps)), end(e_deps));
+}
+
+vector<EntryPoint> Compiler::get_entry_points_and_stages() const
+{
+ vector<EntryPoint> entries;
+ for (auto &entry : ir.entry_points)
+ entries.push_back({ entry.second.orig_name, entry.second.model });
+ return entries;
+}
+
+void Compiler::rename_entry_point(const std::string &old_name, const std::string &new_name, spv::ExecutionModel model)
+{
+ auto &entry = get_entry_point(old_name, model);
+ entry.orig_name = new_name;
+ entry.name = new_name;
+}
+
+void Compiler::set_entry_point(const std::string &name, spv::ExecutionModel model)
+{
+ auto &entry = get_entry_point(name, model);
+ ir.default_entry_point = entry.self;
+}
+
+SPIREntryPoint &Compiler::get_first_entry_point(const std::string &name)
+{
+ auto itr = find_if(
+ begin(ir.entry_points), end(ir.entry_points),
+ [&](const std::pair<uint32_t, SPIREntryPoint> &entry) -> bool { return entry.second.orig_name == name; });
+
+ if (itr == end(ir.entry_points))
+ SPIRV_CROSS_THROW("Entry point does not exist.");
+
+ return itr->second;
+}
+
+const SPIREntryPoint &Compiler::get_first_entry_point(const std::string &name) const
+{
+ auto itr = find_if(
+ begin(ir.entry_points), end(ir.entry_points),
+ [&](const std::pair<uint32_t, SPIREntryPoint> &entry) -> bool { return entry.second.orig_name == name; });
+
+ if (itr == end(ir.entry_points))
+ SPIRV_CROSS_THROW("Entry point does not exist.");
+
+ return itr->second;
+}
+
+SPIREntryPoint &Compiler::get_entry_point(const std::string &name, ExecutionModel model)
+{
+ auto itr = find_if(begin(ir.entry_points), end(ir.entry_points),
+ [&](const std::pair<uint32_t, SPIREntryPoint> &entry) -> bool {
+ return entry.second.orig_name == name && entry.second.model == model;
+ });
+
+ if (itr == end(ir.entry_points))
+ SPIRV_CROSS_THROW("Entry point does not exist.");
+
+ return itr->second;
+}
+
+const SPIREntryPoint &Compiler::get_entry_point(const std::string &name, ExecutionModel model) const
+{
+ auto itr = find_if(begin(ir.entry_points), end(ir.entry_points),
+ [&](const std::pair<uint32_t, SPIREntryPoint> &entry) -> bool {
+ return entry.second.orig_name == name && entry.second.model == model;
+ });
+
+ if (itr == end(ir.entry_points))
+ SPIRV_CROSS_THROW("Entry point does not exist.");
+
+ return itr->second;
+}
+
+const string &Compiler::get_cleansed_entry_point_name(const std::string &name, ExecutionModel model) const
+{
+ return get_entry_point(name, model).name;
+}
+
+const SPIREntryPoint &Compiler::get_entry_point() const
+{
+ return ir.entry_points.find(ir.default_entry_point)->second;
+}
+
+SPIREntryPoint &Compiler::get_entry_point()
+{
+ return ir.entry_points.find(ir.default_entry_point)->second;
+}
+
+bool Compiler::interface_variable_exists_in_entry_point(uint32_t id) const
+{
+ auto &var = get<SPIRVariable>(id);
+ if (var.storage != StorageClassInput && var.storage != StorageClassOutput &&
+ var.storage != StorageClassUniformConstant)
+ SPIRV_CROSS_THROW("Only Input, Output variables and Uniform constants are part of a shader linking interface.");
+
+ // This is to avoid potential problems with very old glslang versions which did
+ // not emit input/output interfaces properly.
+ // We can assume they only had a single entry point, and single entry point
+ // shaders could easily be assumed to use every interface variable anyways.
+ if (ir.entry_points.size() <= 1)
+ return true;
+
+ auto &execution = get_entry_point();
+ return find(begin(execution.interface_variables), end(execution.interface_variables), id) !=
+ end(execution.interface_variables);
+}
+
+void Compiler::CombinedImageSamplerHandler::push_remap_parameters(const SPIRFunction &func, const uint32_t *args,
+ uint32_t length)
+{
+ // If possible, pipe through a remapping table so that parameters know
+ // which variables they actually bind to in this scope.
+ unordered_map<uint32_t, uint32_t> remapping;
+ for (uint32_t i = 0; i < length; i++)
+ remapping[func.arguments[i].id] = remap_parameter(args[i]);
+ parameter_remapping.push(move(remapping));
+}
+
+void Compiler::CombinedImageSamplerHandler::pop_remap_parameters()
+{
+ parameter_remapping.pop();
+}
+
+uint32_t Compiler::CombinedImageSamplerHandler::remap_parameter(uint32_t id)
+{
+ auto *var = compiler.maybe_get_backing_variable(id);
+ if (var)
+ id = var->self;
+
+ if (parameter_remapping.empty())
+ return id;
+
+ auto &remapping = parameter_remapping.top();
+ auto itr = remapping.find(id);
+ if (itr != end(remapping))
+ return itr->second;
+ else
+ return id;
+}
+
+bool Compiler::CombinedImageSamplerHandler::begin_function_scope(const uint32_t *args, uint32_t length)
+{
+ if (length < 3)
+ return false;
+
+ auto &callee = compiler.get<SPIRFunction>(args[2]);
+ args += 3;
+ length -= 3;
+ push_remap_parameters(callee, args, length);
+ functions.push(&callee);
+ return true;
+}
+
+bool Compiler::CombinedImageSamplerHandler::end_function_scope(const uint32_t *args, uint32_t length)
+{
+ if (length < 3)
+ return false;
+
+ auto &callee = compiler.get<SPIRFunction>(args[2]);
+ args += 3;
+
+ // There are two types of cases we have to handle,
+ // a callee might call sampler2D(texture2D, sampler) directly where
+ // one or more parameters originate from parameters.
+ // Alternatively, we need to provide combined image samplers to our callees,
+ // and in this case we need to add those as well.
+
+ pop_remap_parameters();
+
+ // Our callee has now been processed at least once.
+ // No point in doing it again.
+ callee.do_combined_parameters = false;
+
+ auto &params = functions.top()->combined_parameters;
+ functions.pop();
+ if (functions.empty())
+ return true;
+
+ auto &caller = *functions.top();
+ if (caller.do_combined_parameters)
+ {
+ for (auto &param : params)
+ {
+ uint32_t image_id = param.global_image ? param.image_id : args[param.image_id];
+ uint32_t sampler_id = param.global_sampler ? param.sampler_id : args[param.sampler_id];
+
+ auto *i = compiler.maybe_get_backing_variable(image_id);
+ auto *s = compiler.maybe_get_backing_variable(sampler_id);
+ if (i)
+ image_id = i->self;
+ if (s)
+ sampler_id = s->self;
+
+ register_combined_image_sampler(caller, image_id, sampler_id, param.depth);
+ }
+ }
+
+ return true;
+}
+
+void Compiler::CombinedImageSamplerHandler::register_combined_image_sampler(SPIRFunction &caller, uint32_t image_id,
+ uint32_t sampler_id, bool depth)
+{
+ // We now have a texture ID and a sampler ID which will either be found as a global
+ // or a parameter in our own function. If both are global, they will not need a parameter,
+ // otherwise, add it to our list.
+ SPIRFunction::CombinedImageSamplerParameter param = {
+ 0u, image_id, sampler_id, true, true, depth,
+ };
+
+ auto texture_itr = find_if(begin(caller.arguments), end(caller.arguments),
+ [image_id](const SPIRFunction::Parameter &p) { return p.id == image_id; });
+ auto sampler_itr = find_if(begin(caller.arguments), end(caller.arguments),
+ [sampler_id](const SPIRFunction::Parameter &p) { return p.id == sampler_id; });
+
+ if (texture_itr != end(caller.arguments))
+ {
+ param.global_image = false;
+ param.image_id = uint32_t(texture_itr - begin(caller.arguments));
+ }
+
+ if (sampler_itr != end(caller.arguments))
+ {
+ param.global_sampler = false;
+ param.sampler_id = uint32_t(sampler_itr - begin(caller.arguments));
+ }
+
+ if (param.global_image && param.global_sampler)
+ return;
+
+ auto itr = find_if(begin(caller.combined_parameters), end(caller.combined_parameters),
+ [&param](const SPIRFunction::CombinedImageSamplerParameter &p) {
+ return param.image_id == p.image_id && param.sampler_id == p.sampler_id &&
+ param.global_image == p.global_image && param.global_sampler == p.global_sampler;
+ });
+
+ if (itr == end(caller.combined_parameters))
+ {
+ uint32_t id = compiler.ir.increase_bound_by(3);
+ auto type_id = id + 0;
+ auto ptr_type_id = id + 1;
+ auto combined_id = id + 2;
+ auto &base = compiler.expression_type(image_id);
+ auto &type = compiler.set<SPIRType>(type_id);
+ auto &ptr_type = compiler.set<SPIRType>(ptr_type_id);
+
+ type = base;
+ type.self = type_id;
+ type.basetype = SPIRType::SampledImage;
+ type.pointer = false;
+ type.storage = StorageClassGeneric;
+ type.image.depth = depth;
+
+ ptr_type = type;
+ ptr_type.pointer = true;
+ ptr_type.storage = StorageClassUniformConstant;
+ ptr_type.parent_type = type_id;
+
+ // Build new variable.
+ compiler.set<SPIRVariable>(combined_id, ptr_type_id, StorageClassFunction, 0);
+
+ // Inherit RelaxedPrecision (and potentially other useful flags if deemed relevant).
+ auto &new_flags = compiler.ir.meta[combined_id].decoration.decoration_flags;
+ auto &old_flags = compiler.ir.meta[sampler_id].decoration.decoration_flags;
+ new_flags.reset();
+ if (old_flags.get(DecorationRelaxedPrecision))
+ new_flags.set(DecorationRelaxedPrecision);
+
+ param.id = combined_id;
+
+ compiler.set_name(combined_id,
+ join("SPIRV_Cross_Combined", compiler.to_name(image_id), compiler.to_name(sampler_id)));
+
+ caller.combined_parameters.push_back(param);
+ caller.shadow_arguments.push_back({ ptr_type_id, combined_id, 0u, 0u, true });
+ }
+}
+
+bool Compiler::DummySamplerForCombinedImageHandler::handle(Op opcode, const uint32_t *args, uint32_t length)
+{
+ if (need_dummy_sampler)
+ {
+ // No need to traverse further, we know the result.
+ return false;
+ }
+
+ switch (opcode)
+ {
+ case OpLoad:
+ {
+ if (length < 3)
+ return false;
+
+ uint32_t result_type = args[0];
+
+ auto &type = compiler.get<SPIRType>(result_type);
+ bool separate_image =
+ type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer;
+
+ // If not separate image, don't bother.
+ if (!separate_image)
+ return true;
+
+ uint32_t id = args[1];
+ uint32_t ptr = args[2];
+ compiler.set<SPIRExpression>(id, "", result_type, true);
+ compiler.register_read(id, ptr, true);
+ break;
+ }
+
+ case OpImageFetch:
+ case OpImageQuerySizeLod:
+ case OpImageQuerySize:
+ case OpImageQueryLevels:
+ case OpImageQuerySamples:
+ {
+ // If we are fetching or querying LOD from a plain OpTypeImage, we must pre-combine with our dummy sampler.
+ auto *var = compiler.maybe_get_backing_variable(args[2]);
+ if (var)
+ {
+ auto &type = compiler.get<SPIRType>(var->basetype);
+ if (type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer)
+ need_dummy_sampler = true;
+ }
+
+ break;
+ }
+
+ case OpInBoundsAccessChain:
+ case OpAccessChain:
+ case OpPtrAccessChain:
+ {
+ if (length < 3)
+ return false;
+
+ uint32_t result_type = args[0];
+ auto &type = compiler.get<SPIRType>(result_type);
+ bool separate_image =
+ type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer;
+ if (!separate_image)
+ return true;
+
+ uint32_t id = args[1];
+ uint32_t ptr = args[2];
+ compiler.set<SPIRExpression>(id, "", result_type, true);
+ compiler.register_read(id, ptr, true);
+
+ // Other backends might use SPIRAccessChain for this later.
+ compiler.ir.ids[id].set_allow_type_rewrite();
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool Compiler::CombinedImageSamplerHandler::handle(Op opcode, const uint32_t *args, uint32_t length)
+{
+ // We need to figure out where samplers and images are loaded from, so do only the bare bones compilation we need.
+ bool is_fetch = false;
+
+ switch (opcode)
+ {
+ case OpLoad:
+ {
+ if (length < 3)
+ return false;
+
+ uint32_t result_type = args[0];
+
+ auto &type = compiler.get<SPIRType>(result_type);
+ bool separate_image = type.basetype == SPIRType::Image && type.image.sampled == 1;
+ bool separate_sampler = type.basetype == SPIRType::Sampler;
+
+ // If not separate image or sampler, don't bother.
+ if (!separate_image && !separate_sampler)
+ return true;
+
+ uint32_t id = args[1];
+ uint32_t ptr = args[2];
+ compiler.set<SPIRExpression>(id, "", result_type, true);
+ compiler.register_read(id, ptr, true);
+ return true;
+ }
+
+ case OpInBoundsAccessChain:
+ case OpAccessChain:
+ case OpPtrAccessChain:
+ {
+ if (length < 3)
+ return false;
+
+ // Technically, it is possible to have arrays of textures and arrays of samplers and combine them, but this becomes essentially
+ // impossible to implement, since we don't know which concrete sampler we are accessing.
+ // One potential way is to create a combinatorial explosion where N textures and M samplers are combined into N * M sampler2Ds,
+ // but this seems ridiculously complicated for a problem which is easy to work around.
+ // Checking access chains like this assumes we don't have samplers or textures inside uniform structs, but this makes no sense.
+
+ uint32_t result_type = args[0];
+
+ auto &type = compiler.get<SPIRType>(result_type);
+ bool separate_image = type.basetype == SPIRType::Image && type.image.sampled == 1;
+ bool separate_sampler = type.basetype == SPIRType::Sampler;
+ if (separate_sampler)
+ SPIRV_CROSS_THROW(
+ "Attempting to use arrays or structs of separate samplers. This is not possible to statically "
+ "remap to plain GLSL.");
+
+ if (separate_image)
+ {
+ uint32_t id = args[1];
+ uint32_t ptr = args[2];
+ compiler.set<SPIRExpression>(id, "", result_type, true);
+ compiler.register_read(id, ptr, true);
+ }
+ return true;
+ }
+
+ case OpImageFetch:
+ case OpImageQuerySizeLod:
+ case OpImageQuerySize:
+ case OpImageQueryLevels:
+ case OpImageQuerySamples:
+ {
+ // If we are fetching from a plain OpTypeImage or querying LOD, we must pre-combine with our dummy sampler.
+ auto *var = compiler.maybe_get_backing_variable(args[2]);
+ if (!var)
+ return true;
+
+ auto &type = compiler.get<SPIRType>(var->basetype);
+ if (type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer)
+ {
+ if (compiler.dummy_sampler_id == 0)
+ SPIRV_CROSS_THROW("texelFetch without sampler was found, but no dummy sampler has been created with "
+ "build_dummy_sampler_for_combined_images().");
+
+ // Do it outside.
+ is_fetch = true;
+ break;
+ }
+
+ return true;
+ }
+
+ case OpSampledImage:
+ // Do it outside.
+ break;
+
+ default:
+ return true;
+ }
+
+ // Registers sampler2D calls used in case they are parameters so
+ // that their callees know which combined image samplers to propagate down the call stack.
+ if (!functions.empty())
+ {
+ auto &callee = *functions.top();
+ if (callee.do_combined_parameters)
+ {
+ uint32_t image_id = args[2];
+
+ auto *image = compiler.maybe_get_backing_variable(image_id);
+ if (image)
+ image_id = image->self;
+
+ uint32_t sampler_id = is_fetch ? compiler.dummy_sampler_id : args[3];
+ auto *sampler = compiler.maybe_get_backing_variable(sampler_id);
+ if (sampler)
+ sampler_id = sampler->self;
+
+ auto &combined_type = compiler.get<SPIRType>(args[0]);
+ register_combined_image_sampler(callee, image_id, sampler_id, combined_type.image.depth);
+ }
+ }
+
+ // For function calls, we need to remap IDs which are function parameters into global variables.
+ // This information is statically known from the current place in the call stack.
+ // Function parameters are not necessarily pointers, so if we don't have a backing variable, remapping will know
+ // which backing variable the image/sample came from.
+ uint32_t image_id = remap_parameter(args[2]);
+ uint32_t sampler_id = is_fetch ? compiler.dummy_sampler_id : remap_parameter(args[3]);
+
+ auto itr = find_if(begin(compiler.combined_image_samplers), end(compiler.combined_image_samplers),
+ [image_id, sampler_id](const CombinedImageSampler &combined) {
+ return combined.image_id == image_id && combined.sampler_id == sampler_id;
+ });
+
+ if (itr == end(compiler.combined_image_samplers))
+ {
+ uint32_t sampled_type;
+ if (is_fetch)
+ {
+ // Have to invent the sampled image type.
+ sampled_type = compiler.ir.increase_bound_by(1);
+ auto &type = compiler.set<SPIRType>(sampled_type);
+ type = compiler.expression_type(args[2]);
+ type.self = sampled_type;
+ type.basetype = SPIRType::SampledImage;
+ type.image.depth = false;
+ }
+ else
+ {
+ sampled_type = args[0];
+ }
+
+ auto id = compiler.ir.increase_bound_by(2);
+ auto type_id = id + 0;
+ auto combined_id = id + 1;
+
+ // Make a new type, pointer to OpTypeSampledImage, so we can make a variable of this type.
+ // We will probably have this type lying around, but it doesn't hurt to make duplicates for internal purposes.
+ auto &type = compiler.set<SPIRType>(type_id);
+ auto &base = compiler.get<SPIRType>(sampled_type);
+ type = base;
+ type.pointer = true;
+ type.storage = StorageClassUniformConstant;
+ type.parent_type = type_id;
+
+ // Build new variable.
+ compiler.set<SPIRVariable>(combined_id, type_id, StorageClassUniformConstant, 0);
+
+ // Inherit RelaxedPrecision (and potentially other useful flags if deemed relevant).
+ auto &new_flags = compiler.ir.meta[combined_id].decoration.decoration_flags;
+ // Fetch inherits precision from the image, not sampler (there is no sampler).
+ auto &old_flags = compiler.ir.meta[is_fetch ? image_id : sampler_id].decoration.decoration_flags;
+ new_flags.reset();
+ if (old_flags.get(DecorationRelaxedPrecision))
+ new_flags.set(DecorationRelaxedPrecision);
+
+ // Propagate the array type for the original image as well.
+ auto *var = compiler.maybe_get_backing_variable(image_id);
+ if (var)
+ {
+ auto &parent_type = compiler.get<SPIRType>(var->basetype);
+ type.array = parent_type.array;
+ type.array_size_literal = parent_type.array_size_literal;
+ }
+
+ compiler.combined_image_samplers.push_back({ combined_id, image_id, sampler_id });
+ }
+
+ return true;
+}
+
+uint32_t Compiler::build_dummy_sampler_for_combined_images()
+{
+ DummySamplerForCombinedImageHandler handler(*this);
+ traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
+ if (handler.need_dummy_sampler)
+ {
+ uint32_t offset = ir.increase_bound_by(3);
+ auto type_id = offset + 0;
+ auto ptr_type_id = offset + 1;
+ auto var_id = offset + 2;
+
+ SPIRType sampler_type;
+ auto &sampler = set<SPIRType>(type_id);
+ sampler.basetype = SPIRType::Sampler;
+
+ auto &ptr_sampler = set<SPIRType>(ptr_type_id);
+ ptr_sampler = sampler;
+ ptr_sampler.self = type_id;
+ ptr_sampler.storage = StorageClassUniformConstant;
+ ptr_sampler.pointer = true;
+ ptr_sampler.parent_type = type_id;
+
+ set<SPIRVariable>(var_id, ptr_type_id, StorageClassUniformConstant, 0);
+ set_name(var_id, "SPIRV_Cross_DummySampler");
+ dummy_sampler_id = var_id;
+ return var_id;
+ }
+ else
+ return 0;
+}
+
+void Compiler::build_combined_image_samplers()
+{
+ ir.for_each_typed_id<SPIRFunction>([&](uint32_t, SPIRFunction &func) {
+ func.combined_parameters.clear();
+ func.shadow_arguments.clear();
+ func.do_combined_parameters = true;
+ });
+
+ combined_image_samplers.clear();
+ CombinedImageSamplerHandler handler(*this);
+ traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
+}
+
+vector<SpecializationConstant> Compiler::get_specialization_constants() const
+{
+ vector<SpecializationConstant> spec_consts;
+ ir.for_each_typed_id<SPIRConstant>([&](uint32_t, const SPIRConstant &c) {
+ if (c.specialization && has_decoration(c.self, DecorationSpecId))
+ spec_consts.push_back({ c.self, get_decoration(c.self, DecorationSpecId) });
+ });
+ return spec_consts;
+}
+
+SPIRConstant &Compiler::get_constant(uint32_t id)
+{
+ return get<SPIRConstant>(id);
+}
+
+const SPIRConstant &Compiler::get_constant(uint32_t id) const
+{
+ return get<SPIRConstant>(id);
+}
+
+static bool exists_unaccessed_path_to_return(const CFG &cfg, uint32_t block, const unordered_set<uint32_t> &blocks)
+{
+ // This block accesses the variable.
+ if (blocks.find(block) != end(blocks))
+ return false;
+
+ // We are at the end of the CFG.
+ if (cfg.get_succeeding_edges(block).empty())
+ return true;
+
+ // If any of our successors have a path to the end, there exists a path from block.
+ for (auto &succ : cfg.get_succeeding_edges(block))
+ if (exists_unaccessed_path_to_return(cfg, succ, blocks))
+ return true;
+
+ return false;
+}
+
+void Compiler::analyze_parameter_preservation(
+ SPIRFunction &entry, const CFG &cfg, const unordered_map<uint32_t, unordered_set<uint32_t>> &variable_to_blocks,
+ const unordered_map<uint32_t, unordered_set<uint32_t>> &complete_write_blocks)
+{
+ for (auto &arg : entry.arguments)
+ {
+ // Non-pointers are always inputs.
+ auto &type = get<SPIRType>(arg.type);
+ if (!type.pointer)
+ continue;
+
+ // Opaque argument types are always in
+ bool potential_preserve;
+ switch (type.basetype)
+ {
+ case SPIRType::Sampler:
+ case SPIRType::Image:
+ case SPIRType::SampledImage:
+ case SPIRType::AtomicCounter:
+ potential_preserve = false;
+ break;
+
+ default:
+ potential_preserve = true;
+ break;
+ }
+
+ if (!potential_preserve)
+ continue;
+
+ auto itr = variable_to_blocks.find(arg.id);
+ if (itr == end(variable_to_blocks))
+ {
+ // Variable is never accessed.
+ continue;
+ }
+
+ // We have accessed a variable, but there was no complete writes to that variable.
+ // We deduce that we must preserve the argument.
+ itr = complete_write_blocks.find(arg.id);
+ if (itr == end(complete_write_blocks))
+ {
+ arg.read_count++;
+ continue;
+ }
+
+ // If there is a path through the CFG where no block completely writes to the variable, the variable will be in an undefined state
+ // when the function returns. We therefore need to implicitly preserve the variable in case there are writers in the function.
+ // Major case here is if a function is
+ // void foo(int &var) { if (cond) var = 10; }
+ // Using read/write counts, we will think it's just an out variable, but it really needs to be inout,
+ // because if we don't write anything whatever we put into the function must return back to the caller.
+ if (exists_unaccessed_path_to_return(cfg, entry.entry_block, itr->second))
+ arg.read_count++;
+ }
+}
+
+Compiler::AnalyzeVariableScopeAccessHandler::AnalyzeVariableScopeAccessHandler(Compiler &compiler_,
+ SPIRFunction &entry_)
+ : compiler(compiler_)
+ , entry(entry_)
+{
+}
+
+bool Compiler::AnalyzeVariableScopeAccessHandler::follow_function_call(const SPIRFunction &)
+{
+ // Only analyze within this function.
+ return false;
+}
+
+void Compiler::AnalyzeVariableScopeAccessHandler::set_current_block(const SPIRBlock &block)
+{
+ current_block = &block;
+
+ // If we're branching to a block which uses OpPhi, in GLSL
+ // this will be a variable write when we branch,
+ // so we need to track access to these variables as well to
+ // have a complete picture.
+ const auto test_phi = [this, &block](uint32_t to) {
+ auto &next = compiler.get<SPIRBlock>(to);
+ for (auto &phi : next.phi_variables)
+ {
+ if (phi.parent == block.self)
+ {
+ accessed_variables_to_block[phi.function_variable].insert(block.self);
+ // Phi variables are also accessed in our target branch block.
+ accessed_variables_to_block[phi.function_variable].insert(next.self);
+
+ notify_variable_access(phi.local_variable, block.self);
+ }
+ }
+ };
+
+ switch (block.terminator)
+ {
+ case SPIRBlock::Direct:
+ notify_variable_access(block.condition, block.self);
+ test_phi(block.next_block);
+ break;
+
+ case SPIRBlock::Select:
+ notify_variable_access(block.condition, block.self);
+ test_phi(block.true_block);
+ test_phi(block.false_block);
+ break;
+
+ case SPIRBlock::MultiSelect:
+ notify_variable_access(block.condition, block.self);
+ for (auto &target : block.cases)
+ test_phi(target.block);
+ if (block.default_block)
+ test_phi(block.default_block);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void Compiler::AnalyzeVariableScopeAccessHandler::notify_variable_access(uint32_t id, uint32_t block)
+{
+ if (id_is_phi_variable(id))
+ accessed_variables_to_block[id].insert(block);
+ else if (id_is_potential_temporary(id))
+ accessed_temporaries_to_block[id].insert(block);
+}
+
+bool Compiler::AnalyzeVariableScopeAccessHandler::id_is_phi_variable(uint32_t id) const
+{
+ if (id >= compiler.get_current_id_bound())
+ return false;
+ auto *var = compiler.maybe_get<SPIRVariable>(id);
+ return var && var->phi_variable;
+}
+
+bool Compiler::AnalyzeVariableScopeAccessHandler::id_is_potential_temporary(uint32_t id) const
+{
+ if (id >= compiler.get_current_id_bound())
+ return false;
+
+ // Temporaries are not created before we start emitting code.
+ return compiler.ir.ids[id].empty() || (compiler.ir.ids[id].get_type() == TypeExpression);
+}
+
+bool Compiler::AnalyzeVariableScopeAccessHandler::handle(spv::Op op, const uint32_t *args, uint32_t length)
+{
+ // Keep track of the types of temporaries, so we can hoist them out as necessary.
+ uint32_t result_type, result_id;
+ if (compiler.instruction_to_result_type(result_type, result_id, op, args, length))
+ result_id_to_type[result_id] = result_type;
+
+ switch (op)
+ {
+ case OpStore:
+ {
+ if (length < 2)
+ return false;
+
+ uint32_t ptr = args[0];
+ auto *var = compiler.maybe_get_backing_variable(ptr);
+
+ // If we store through an access chain, we have a partial write.
+ if (var)
+ {
+ accessed_variables_to_block[var->self].insert(current_block->self);
+ if (var->self == ptr)
+ complete_write_variables_to_block[var->self].insert(current_block->self);
+ else
+ partial_write_variables_to_block[var->self].insert(current_block->self);
+ }
+
+ // Might try to store a Phi variable here.
+ notify_variable_access(args[1], current_block->self);
+ break;
+ }
+
+ case OpAccessChain:
+ case OpInBoundsAccessChain:
+ case OpPtrAccessChain:
+ {
+ if (length < 3)
+ return false;
+
+ uint32_t ptr = args[2];
+ auto *var = compiler.maybe_get<SPIRVariable>(ptr);
+ if (var)
+ accessed_variables_to_block[var->self].insert(current_block->self);
+
+ for (uint32_t i = 3; i < length; i++)
+ notify_variable_access(args[i], current_block->self);
+
+ // The result of an access chain is a fixed expression and is not really considered a temporary.
+ auto &e = compiler.set<SPIRExpression>(args[1], "", args[0], true);
+ auto *backing_variable = compiler.maybe_get_backing_variable(ptr);
+ e.loaded_from = backing_variable ? backing_variable->self : 0;
+
+ // Other backends might use SPIRAccessChain for this later.
+ compiler.ir.ids[args[1]].set_allow_type_rewrite();
+ break;
+ }
+
+ case OpCopyMemory:
+ {
+ if (length < 2)
+ return false;
+
+ uint32_t lhs = args[0];
+ uint32_t rhs = args[1];
+ auto *var = compiler.maybe_get_backing_variable(lhs);
+
+ // If we store through an access chain, we have a partial write.
+ if (var)
+ {
+ accessed_variables_to_block[var->self].insert(current_block->self);
+ if (var->self == lhs)
+ complete_write_variables_to_block[var->self].insert(current_block->self);
+ else
+ partial_write_variables_to_block[var->self].insert(current_block->self);
+ }
+
+ var = compiler.maybe_get_backing_variable(rhs);
+ if (var)
+ accessed_variables_to_block[var->self].insert(current_block->self);
+ break;
+ }
+
+ case OpCopyObject:
+ {
+ if (length < 3)
+ return false;
+
+ auto *var = compiler.maybe_get_backing_variable(args[2]);
+ if (var)
+ accessed_variables_to_block[var->self].insert(current_block->self);
+
+ // Might try to copy a Phi variable here.
+ notify_variable_access(args[2], current_block->self);
+ break;
+ }
+
+ case OpLoad:
+ {
+ if (length < 3)
+ return false;
+ uint32_t ptr = args[2];
+ auto *var = compiler.maybe_get_backing_variable(ptr);
+ if (var)
+ accessed_variables_to_block[var->self].insert(current_block->self);
+
+ // Loaded value is a temporary.
+ notify_variable_access(args[1], current_block->self);
+ break;
+ }
+
+ case OpFunctionCall:
+ {
+ if (length < 3)
+ return false;
+
+ length -= 3;
+ args += 3;
+
+ for (uint32_t i = 0; i < length; i++)
+ {
+ auto *var = compiler.maybe_get_backing_variable(args[i]);
+ if (var)
+ {
+ accessed_variables_to_block[var->self].insert(current_block->self);
+ // Assume we can get partial writes to this variable.
+ partial_write_variables_to_block[var->self].insert(current_block->self);
+ }
+
+ // Cannot easily prove if argument we pass to a function is completely written.
+ // Usually, functions write to a dummy variable,
+ // which is then copied to in full to the real argument.
+
+ // Might try to copy a Phi variable here.
+ notify_variable_access(args[i], current_block->self);
+ }
+
+ // Return value may be a temporary.
+ notify_variable_access(args[1], current_block->self);
+ break;
+ }
+
+ case OpExtInst:
+ {
+ for (uint32_t i = 4; i < length; i++)
+ notify_variable_access(args[i], current_block->self);
+ notify_variable_access(args[1], current_block->self);
+ break;
+ }
+
+ case OpArrayLength:
+ // Uses literals, but cannot be a phi variable or temporary, so ignore.
+ break;
+
+ // Atomics shouldn't be able to access function-local variables.
+ // Some GLSL builtins access a pointer.
+
+ case OpCompositeInsert:
+ case OpVectorShuffle:
+ // Specialize for opcode which contains literals.
+ for (uint32_t i = 1; i < 4; i++)
+ notify_variable_access(args[i], current_block->self);
+ break;
+
+ case OpCompositeExtract:
+ // Specialize for opcode which contains literals.
+ for (uint32_t i = 1; i < 3; i++)
+ notify_variable_access(args[i], current_block->self);
+ break;
+
+ case OpImageWrite:
+ for (uint32_t i = 0; i < length; i++)
+ {
+ // Argument 3 is a literal.
+ if (i != 3)
+ notify_variable_access(args[i], current_block->self);
+ }
+ break;
+
+ case OpImageSampleImplicitLod:
+ case OpImageSampleExplicitLod:
+ case OpImageSparseSampleImplicitLod:
+ case OpImageSparseSampleExplicitLod:
+ case OpImageSampleProjImplicitLod:
+ case OpImageSampleProjExplicitLod:
+ case OpImageSparseSampleProjImplicitLod:
+ case OpImageSparseSampleProjExplicitLod:
+ case OpImageFetch:
+ case OpImageSparseFetch:
+ case OpImageRead:
+ case OpImageSparseRead:
+ for (uint32_t i = 1; i < length; i++)
+ {
+ // Argument 4 is a literal.
+ if (i != 4)
+ notify_variable_access(args[i], current_block->self);
+ }
+ break;
+
+ case OpImageSampleDrefImplicitLod:
+ case OpImageSampleDrefExplicitLod:
+ case OpImageSparseSampleDrefImplicitLod:
+ case OpImageSparseSampleDrefExplicitLod:
+ case OpImageSampleProjDrefImplicitLod:
+ case OpImageSampleProjDrefExplicitLod:
+ case OpImageSparseSampleProjDrefImplicitLod:
+ case OpImageSparseSampleProjDrefExplicitLod:
+ case OpImageGather:
+ case OpImageSparseGather:
+ case OpImageDrefGather:
+ case OpImageSparseDrefGather:
+ for (uint32_t i = 1; i < length; i++)
+ {
+ // Argument 5 is a literal.
+ if (i != 5)
+ notify_variable_access(args[i], current_block->self);
+ }
+ break;
+
+ default:
+ {
+ // Rather dirty way of figuring out where Phi variables are used.
+ // As long as only IDs are used, we can scan through instructions and try to find any evidence that
+ // the ID of a variable has been used.
+ // There are potential false positives here where a literal is used in-place of an ID,
+ // but worst case, it does not affect the correctness of the compile.
+ // Exhaustive analysis would be better here, but it's not worth it for now.
+ for (uint32_t i = 0; i < length; i++)
+ notify_variable_access(args[i], current_block->self);
+ break;
+ }
+ }
+ return true;
+}
+
+Compiler::StaticExpressionAccessHandler::StaticExpressionAccessHandler(Compiler &compiler_, uint32_t variable_id_)
+ : compiler(compiler_)
+ , variable_id(variable_id_)
+{
+}
+
+bool Compiler::StaticExpressionAccessHandler::follow_function_call(const SPIRFunction &)
+{
+ return false;
+}
+
+bool Compiler::StaticExpressionAccessHandler::handle(spv::Op op, const uint32_t *args, uint32_t length)
+{
+ switch (op)
+ {
+ case OpStore:
+ if (length < 2)
+ return false;
+ if (args[0] == variable_id)
+ {
+ static_expression = args[1];
+ write_count++;
+ }
+ break;
+
+ case OpLoad:
+ if (length < 3)
+ return false;
+ if (args[2] == variable_id && static_expression == 0) // Tried to read from variable before it was initialized.
+ return false;
+ break;
+
+ case OpAccessChain:
+ case OpInBoundsAccessChain:
+ case OpPtrAccessChain:
+ if (length < 3)
+ return false;
+ if (args[2] == variable_id) // If we try to access chain our candidate variable before we store to it, bail.
+ return false;
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+void Compiler::find_function_local_luts(SPIRFunction &entry, const AnalyzeVariableScopeAccessHandler &handler,
+ bool single_function)
+{
+ auto &cfg = *function_cfgs.find(entry.self)->second;
+
+ // For each variable which is statically accessed.
+ for (auto &accessed_var : handler.accessed_variables_to_block)
+ {
+ auto &blocks = accessed_var.second;
+ auto &var = get<SPIRVariable>(accessed_var.first);
+ auto &type = expression_type(accessed_var.first);
+
+ // Only consider function local variables here.
+ // If we only have a single function in our CFG, private storage is also fine,
+ // since it behaves like a function local variable.
+ bool allow_lut = var.storage == StorageClassFunction || (single_function && var.storage == StorageClassPrivate);
+ if (!allow_lut)
+ continue;
+
+ // We cannot be a phi variable.
+ if (var.phi_variable)
+ continue;
+
+ // Only consider arrays here.
+ if (type.array.empty())
+ continue;
+
+ // If the variable has an initializer, make sure it is a constant expression.
+ uint32_t static_constant_expression = 0;
+ if (var.initializer)
+ {
+ if (ir.ids[var.initializer].get_type() != TypeConstant)
+ continue;
+ static_constant_expression = var.initializer;
+
+ // There can be no stores to this variable, we have now proved we have a LUT.
+ if (handler.complete_write_variables_to_block.count(var.self) != 0 ||
+ handler.partial_write_variables_to_block.count(var.self) != 0)
+ continue;
+ }
+ else
+ {
+ // We can have one, and only one write to the variable, and that write needs to be a constant.
+
+ // No partial writes allowed.
+ if (handler.partial_write_variables_to_block.count(var.self) != 0)
+ continue;
+
+ auto itr = handler.complete_write_variables_to_block.find(var.self);
+
+ // No writes?
+ if (itr == end(handler.complete_write_variables_to_block))
+ continue;
+
+ // We write to the variable in more than one block.
+ auto &write_blocks = itr->second;
+ if (write_blocks.size() != 1)
+ continue;
+
+ // The write needs to happen in the dominating block.
+ DominatorBuilder builder(cfg);
+ for (auto &block : blocks)
+ builder.add_block(block);
+ uint32_t dominator = builder.get_dominator();
+
+ // The complete write happened in a branch or similar, cannot deduce static expression.
+ if (write_blocks.count(dominator) == 0)
+ continue;
+
+ // Find the static expression for this variable.
+ StaticExpressionAccessHandler static_expression_handler(*this, var.self);
+ traverse_all_reachable_opcodes(get<SPIRBlock>(dominator), static_expression_handler);
+
+ // We want one, and exactly one write
+ if (static_expression_handler.write_count != 1 || static_expression_handler.static_expression == 0)
+ continue;
+
+ // Is it a constant expression?
+ if (ir.ids[static_expression_handler.static_expression].get_type() != TypeConstant)
+ continue;
+
+ // We found a LUT!
+ static_constant_expression = static_expression_handler.static_expression;
+ }
+
+ get<SPIRConstant>(static_constant_expression).is_used_as_lut = true;
+ var.static_expression = static_constant_expression;
+ var.statically_assigned = true;
+ var.remapped_variable = true;
+ }
+}
+
+void Compiler::analyze_variable_scope(SPIRFunction &entry, AnalyzeVariableScopeAccessHandler &handler)
+{
+ // First, we map out all variable access within a function.
+ // Essentially a map of block -> { variables accessed in the basic block }
+ traverse_all_reachable_opcodes(entry, handler);
+
+ auto &cfg = *function_cfgs.find(entry.self)->second;
+
+ // Analyze if there are parameters which need to be implicitly preserved with an "in" qualifier.
+ analyze_parameter_preservation(entry, cfg, handler.accessed_variables_to_block,
+ handler.complete_write_variables_to_block);
+
+ unordered_map<uint32_t, uint32_t> potential_loop_variables;
+
+ // For each variable which is statically accessed.
+ for (auto &var : handler.accessed_variables_to_block)
+ {
+ // Only deal with variables which are considered local variables in this function.
+ if (find(begin(entry.local_variables), end(entry.local_variables), var.first) == end(entry.local_variables))
+ continue;
+
+ DominatorBuilder builder(cfg);
+ auto &blocks = var.second;
+ auto &type = expression_type(var.first);
+
+ // Figure out which block is dominating all accesses of those variables.
+ for (auto &block : blocks)
+ {
+ // If we're accessing a variable inside a continue block, this variable might be a loop variable.
+ // We can only use loop variables with scalars, as we cannot track static expressions for vectors.
+ if (is_continue(block))
+ {
+ // Potentially awkward case to check for.
+ // We might have a variable inside a loop, which is touched by the continue block,
+ // but is not actually a loop variable.
+ // The continue block is dominated by the inner part of the loop, which does not make sense in high-level
+ // language output because it will be declared before the body,
+ // so we will have to lift the dominator up to the relevant loop header instead.
+ builder.add_block(ir.continue_block_to_loop_header[block]);
+
+ // Arrays or structs cannot be loop variables.
+ if (type.vecsize == 1 && type.columns == 1 && type.basetype != SPIRType::Struct && type.array.empty())
+ {
+ // The variable is used in multiple continue blocks, this is not a loop
+ // candidate, signal that by setting block to -1u.
+ auto &potential = potential_loop_variables[var.first];
+
+ if (potential == 0)
+ potential = block;
+ else
+ potential = ~(0u);
+ }
+ }
+ builder.add_block(block);
+ }
+
+ builder.lift_continue_block_dominator();
+
+ // Add it to a per-block list of variables.
+ uint32_t dominating_block = builder.get_dominator();
+
+ // If all blocks here are dead code, this will be 0, so the variable in question
+ // will be completely eliminated.
+ if (dominating_block)
+ {
+ auto &block = get<SPIRBlock>(dominating_block);
+ block.dominated_variables.push_back(var.first);
+ get<SPIRVariable>(var.first).dominator = dominating_block;
+ }
+ }
+
+ for (auto &var : handler.accessed_temporaries_to_block)
+ {
+ auto itr = handler.result_id_to_type.find(var.first);
+
+ if (itr == end(handler.result_id_to_type))
+ {
+ // We found a false positive ID being used, ignore.
+ // This should probably be an assert.
+ continue;
+ }
+
+ // There is no point in doing domination analysis for opaque types.
+ auto &type = get<SPIRType>(itr->second);
+ if (type_is_opaque_value(type))
+ continue;
+
+ DominatorBuilder builder(cfg);
+ bool force_temporary = false;
+
+ // Figure out which block is dominating all accesses of those temporaries.
+ auto &blocks = var.second;
+ for (auto &block : blocks)
+ {
+ builder.add_block(block);
+
+ // If a temporary is used in more than one block, we might have to lift continue block
+ // access up to loop header like we did for variables.
+ if (blocks.size() != 1 && is_continue(block))
+ builder.add_block(ir.continue_block_to_loop_header[block]);
+ else if (blocks.size() != 1 && is_single_block_loop(block))
+ {
+ // Awkward case, because the loop header is also the continue block.
+ force_temporary = true;
+ }
+ }
+
+ uint32_t dominating_block = builder.get_dominator();
+ if (dominating_block)
+ {
+ // If we touch a variable in the dominating block, this is the expected setup.
+ // SPIR-V normally mandates this, but we have extra cases for temporary use inside loops.
+ bool first_use_is_dominator = blocks.count(dominating_block) != 0;
+
+ if (!first_use_is_dominator || force_temporary)
+ {
+ // This should be very rare, but if we try to declare a temporary inside a loop,
+ // and that temporary is used outside the loop as well (spirv-opt inliner likes this)
+ // we should actually emit the temporary outside the loop.
+ hoisted_temporaries.insert(var.first);
+ forced_temporaries.insert(var.first);
+
+ auto &block_temporaries = get<SPIRBlock>(dominating_block).declare_temporary;
+ block_temporaries.emplace_back(handler.result_id_to_type[var.first], var.first);
+ }
+ else if (blocks.size() > 1)
+ {
+ // Keep track of the temporary as we might have to declare this temporary.
+ // This can happen if the loop header dominates a temporary, but we have a complex fallback loop.
+ // In this case, the header is actually inside the for (;;) {} block, and we have problems.
+ // What we need to do is hoist the temporaries outside the for (;;) {} block in case the header block
+ // declares the temporary.
+ auto &block_temporaries = get<SPIRBlock>(dominating_block).potential_declare_temporary;
+ block_temporaries.emplace_back(handler.result_id_to_type[var.first], var.first);
+ }
+ }
+ }
+
+ unordered_set<uint32_t> seen_blocks;
+
+ // Now, try to analyze whether or not these variables are actually loop variables.
+ for (auto &loop_variable : potential_loop_variables)
+ {
+ auto &var = get<SPIRVariable>(loop_variable.first);
+ auto dominator = var.dominator;
+ auto block = loop_variable.second;
+
+ // The variable was accessed in multiple continue blocks, ignore.
+ if (block == ~(0u) || block == 0)
+ continue;
+
+ // Dead code.
+ if (dominator == 0)
+ continue;
+
+ uint32_t header = 0;
+
+ // Find the loop header for this block if we are a continue block.
+ {
+ auto itr = ir.continue_block_to_loop_header.find(block);
+ if (itr != end(ir.continue_block_to_loop_header))
+ {
+ header = itr->second;
+ }
+ else if (get<SPIRBlock>(block).continue_block == block)
+ {
+ // Also check for self-referential continue block.
+ header = block;
+ }
+ }
+
+ assert(header);
+ auto &header_block = get<SPIRBlock>(header);
+ auto &blocks = handler.accessed_variables_to_block[loop_variable.first];
+
+ // If a loop variable is not used before the loop, it's probably not a loop variable.
+ bool has_accessed_variable = blocks.count(header) != 0;
+
+ // Now, there are two conditions we need to meet for the variable to be a loop variable.
+ // 1. The dominating block must have a branch-free path to the loop header,
+ // this way we statically know which expression should be part of the loop variable initializer.
+
+ // Walk from the dominator, if there is one straight edge connecting
+ // dominator and loop header, we statically know the loop initializer.
+ bool static_loop_init = true;
+ while (dominator != header)
+ {
+ if (blocks.count(dominator) != 0)
+ has_accessed_variable = true;
+
+ auto &succ = cfg.get_succeeding_edges(dominator);
+ if (succ.size() != 1)
+ {
+ static_loop_init = false;
+ break;
+ }
+
+ auto &pred = cfg.get_preceding_edges(succ.front());
+ if (pred.size() != 1 || pred.front() != dominator)
+ {
+ static_loop_init = false;
+ break;
+ }
+
+ dominator = succ.front();
+ }
+
+ if (!static_loop_init || !has_accessed_variable)
+ continue;
+
+ // The second condition we need to meet is that no access after the loop
+ // merge can occur. Walk the CFG to see if we find anything.
+
+ seen_blocks.clear();
+ cfg.walk_from(seen_blocks, header_block.merge_block, [&](uint32_t walk_block) {
+ // We found a block which accesses the variable outside the loop.
+ if (blocks.find(walk_block) != end(blocks))
+ static_loop_init = false;
+ });
+
+ if (!static_loop_init)
+ continue;
+
+ // We have a loop variable.
+ header_block.loop_variables.push_back(loop_variable.first);
+ // Need to sort here as variables come from an unordered container, and pushing stuff in wrong order
+ // will break reproducability in regression runs.
+ sort(begin(header_block.loop_variables), end(header_block.loop_variables));
+ get<SPIRVariable>(loop_variable.first).loop_variable = true;
+ }
+}
+
+Bitset Compiler::get_buffer_block_flags(uint32_t id) const
+{
+ return ir.get_buffer_block_flags(get<SPIRVariable>(id));
+}
+
+bool Compiler::get_common_basic_type(const SPIRType &type, SPIRType::BaseType &base_type)
+{
+ if (type.basetype == SPIRType::Struct)
+ {
+ base_type = SPIRType::Unknown;
+ for (auto &member_type : type.member_types)
+ {
+ SPIRType::BaseType member_base;
+ if (!get_common_basic_type(get<SPIRType>(member_type), member_base))
+ return false;
+
+ if (base_type == SPIRType::Unknown)
+ base_type = member_base;
+ else if (base_type != member_base)
+ return false;
+ }
+ return true;
+ }
+ else
+ {
+ base_type = type.basetype;
+ return true;
+ }
+}
+
+void Compiler::ActiveBuiltinHandler::handle_builtin(const SPIRType &type, BuiltIn builtin,
+ const Bitset &decoration_flags)
+{
+ // If used, we will need to explicitly declare a new array size for these builtins.
+
+ if (builtin == BuiltInClipDistance)
+ {
+ if (!type.array_size_literal[0])
+ SPIRV_CROSS_THROW("Array size for ClipDistance must be a literal.");
+ uint32_t array_size = type.array[0];
+ if (array_size == 0)
+ SPIRV_CROSS_THROW("Array size for ClipDistance must not be unsized.");
+ compiler.clip_distance_count = array_size;
+ }
+ else if (builtin == BuiltInCullDistance)
+ {
+ if (!type.array_size_literal[0])
+ SPIRV_CROSS_THROW("Array size for CullDistance must be a literal.");
+ uint32_t array_size = type.array[0];
+ if (array_size == 0)
+ SPIRV_CROSS_THROW("Array size for CullDistance must not be unsized.");
+ compiler.cull_distance_count = array_size;
+ }
+ else if (builtin == BuiltInPosition)
+ {
+ if (decoration_flags.get(DecorationInvariant))
+ compiler.position_invariant = true;
+ }
+}
+
+bool Compiler::ActiveBuiltinHandler::handle(spv::Op opcode, const uint32_t *args, uint32_t length)
+{
+ const auto add_if_builtin = [&](uint32_t id) {
+ // Only handles variables here.
+ // Builtins which are part of a block are handled in AccessChain.
+ auto *var = compiler.maybe_get<SPIRVariable>(id);
+ auto &decorations = compiler.ir.meta[id].decoration;
+ if (var && decorations.builtin)
+ {
+ auto &type = compiler.get<SPIRType>(var->basetype);
+ auto &flags =
+ type.storage == StorageClassInput ? compiler.active_input_builtins : compiler.active_output_builtins;
+ flags.set(decorations.builtin_type);
+ handle_builtin(type, decorations.builtin_type, decorations.decoration_flags);
+ }
+ };
+
+ switch (opcode)
+ {
+ case OpStore:
+ if (length < 1)
+ return false;
+
+ add_if_builtin(args[0]);
+ break;
+
+ case OpCopyMemory:
+ if (length < 2)
+ return false;
+
+ add_if_builtin(args[0]);
+ add_if_builtin(args[1]);
+ break;
+
+ case OpCopyObject:
+ case OpLoad:
+ if (length < 3)
+ return false;
+
+ add_if_builtin(args[2]);
+ break;
+
+ case OpSelect:
+ if (length < 5)
+ return false;
+
+ add_if_builtin(args[3]);
+ add_if_builtin(args[4]);
+ break;
+
+ case OpPhi:
+ {
+ if (length < 2)
+ return false;
+
+ uint32_t count = length - 2;
+ args += 2;
+ for (uint32_t i = 0; i < count; i += 2)
+ add_if_builtin(args[i]);
+ break;
+ }
+
+ case OpFunctionCall:
+ {
+ if (length < 3)
+ return false;
+
+ uint32_t count = length - 3;
+ args += 3;
+ for (uint32_t i = 0; i < count; i++)
+ add_if_builtin(args[i]);
+ break;
+ }
+
+ case OpAccessChain:
+ case OpInBoundsAccessChain:
+ case OpPtrAccessChain:
+ {
+ if (length < 4)
+ return false;
+
+ // Only consider global variables, cannot consider variables in functions yet, or other
+ // access chains as they have not been created yet.
+ auto *var = compiler.maybe_get<SPIRVariable>(args[2]);
+ if (!var)
+ break;
+
+ // Required if we access chain into builtins like gl_GlobalInvocationID.
+ add_if_builtin(args[2]);
+
+ // Start traversing type hierarchy at the proper non-pointer types.
+ auto *type = &compiler.get_variable_data_type(*var);
+
+ auto &flags =
+ var->storage == StorageClassInput ? compiler.active_input_builtins : compiler.active_output_builtins;
+
+ uint32_t count = length - 3;
+ args += 3;
+ for (uint32_t i = 0; i < count; i++)
+ {
+ // Pointers
+ if (opcode == OpPtrAccessChain && i == 0)
+ {
+ type = &compiler.get<SPIRType>(type->parent_type);
+ continue;
+ }
+
+ // Arrays
+ if (!type->array.empty())
+ {
+ type = &compiler.get<SPIRType>(type->parent_type);
+ }
+ // Structs
+ else if (type->basetype == SPIRType::Struct)
+ {
+ uint32_t index = compiler.get<SPIRConstant>(args[i]).scalar();
+
+ if (index < uint32_t(compiler.ir.meta[type->self].members.size()))
+ {
+ auto &decorations = compiler.ir.meta[type->self].members[index];
+ if (decorations.builtin)
+ {
+ flags.set(decorations.builtin_type);
+ handle_builtin(compiler.get<SPIRType>(type->member_types[index]), decorations.builtin_type,
+ decorations.decoration_flags);
+ }
+ }
+
+ type = &compiler.get<SPIRType>(type->member_types[index]);
+ }
+ else
+ {
+ // No point in traversing further. We won't find any extra builtins.
+ break;
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+void Compiler::update_active_builtins()
+{
+ active_input_builtins.reset();
+ active_output_builtins.reset();
+ cull_distance_count = 0;
+ clip_distance_count = 0;
+ ActiveBuiltinHandler handler(*this);
+ traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
+}
+
+// Returns whether this shader uses a builtin of the storage class
+bool Compiler::has_active_builtin(BuiltIn builtin, StorageClass storage)
+{
+ const Bitset *flags;
+ switch (storage)
+ {
+ case StorageClassInput:
+ flags = &active_input_builtins;
+ break;
+ case StorageClassOutput:
+ flags = &active_output_builtins;
+ break;
+
+ default:
+ return false;
+ }
+ return flags->get(builtin);
+}
+
+void Compiler::analyze_image_and_sampler_usage()
+{
+ CombinedImageSamplerDrefHandler dref_handler(*this);
+ traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), dref_handler);
+
+ CombinedImageSamplerUsageHandler handler(*this, dref_handler.dref_combined_samplers);
+ traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
+ comparison_ids = move(handler.comparison_ids);
+ need_subpass_input = handler.need_subpass_input;
+
+ // Forward information from separate images and samplers into combined image samplers.
+ for (auto &combined : combined_image_samplers)
+ if (comparison_ids.count(combined.sampler_id))
+ comparison_ids.insert(combined.combined_id);
+}
+
+bool Compiler::CombinedImageSamplerDrefHandler::handle(spv::Op opcode, const uint32_t *args, uint32_t)
+{
+ // Mark all sampled images which are used with Dref.
+ switch (opcode)
+ {
+ case OpImageSampleDrefExplicitLod:
+ case OpImageSampleDrefImplicitLod:
+ case OpImageSampleProjDrefExplicitLod:
+ case OpImageSampleProjDrefImplicitLod:
+ case OpImageSparseSampleProjDrefImplicitLod:
+ case OpImageSparseSampleDrefImplicitLod:
+ case OpImageSparseSampleProjDrefExplicitLod:
+ case OpImageSparseSampleDrefExplicitLod:
+ case OpImageDrefGather:
+ case OpImageSparseDrefGather:
+ dref_combined_samplers.insert(args[2]);
+ return true;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+void Compiler::build_function_control_flow_graphs_and_analyze()
+{
+ CFGBuilder handler(*this);
+ handler.function_cfgs[ir.default_entry_point].reset(new CFG(*this, get<SPIRFunction>(ir.default_entry_point)));
+ traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
+ function_cfgs = move(handler.function_cfgs);
+ bool single_function = function_cfgs.size() <= 1;
+
+ for (auto &f : function_cfgs)
+ {
+ auto &func = get<SPIRFunction>(f.first);
+ AnalyzeVariableScopeAccessHandler scope_handler(*this, func);
+ analyze_variable_scope(func, scope_handler);
+ find_function_local_luts(func, scope_handler, single_function);
+
+ // Check if we can actually use the loop variables we found in analyze_variable_scope.
+ // To use multiple initializers, we need the same type and qualifiers.
+ for (auto block : func.blocks)
+ {
+ auto &b = get<SPIRBlock>(block);
+ if (b.loop_variables.size() < 2)
+ continue;
+
+ auto &flags = get_decoration_bitset(b.loop_variables.front());
+ uint32_t type = get<SPIRVariable>(b.loop_variables.front()).basetype;
+ bool invalid_initializers = false;
+ for (auto loop_variable : b.loop_variables)
+ {
+ if (flags != get_decoration_bitset(loop_variable) ||
+ type != get<SPIRVariable>(b.loop_variables.front()).basetype)
+ {
+ invalid_initializers = true;
+ break;
+ }
+ }
+
+ if (invalid_initializers)
+ {
+ for (auto loop_variable : b.loop_variables)
+ get<SPIRVariable>(loop_variable).loop_variable = false;
+ b.loop_variables.clear();
+ }
+ }
+ }
+}
+
+Compiler::CFGBuilder::CFGBuilder(spirv_cross::Compiler &compiler_)
+ : compiler(compiler_)
+{
+}
+
+bool Compiler::CFGBuilder::handle(spv::Op, const uint32_t *, uint32_t)
+{
+ return true;
+}
+
+bool Compiler::CFGBuilder::follow_function_call(const SPIRFunction &func)
+{
+ if (function_cfgs.find(func.self) == end(function_cfgs))
+ {
+ function_cfgs[func.self].reset(new CFG(compiler, func));
+ return true;
+ }
+ else
+ return false;
+}
+
+bool Compiler::CombinedImageSamplerUsageHandler::begin_function_scope(const uint32_t *args, uint32_t length)
+{
+ if (length < 3)
+ return false;
+
+ auto &func = compiler.get<SPIRFunction>(args[2]);
+ const auto *arg = &args[3];
+ length -= 3;
+
+ for (uint32_t i = 0; i < length; i++)
+ {
+ auto &argument = func.arguments[i];
+ dependency_hierarchy[argument.id].insert(arg[i]);
+ }
+
+ return true;
+}
+
+void Compiler::CombinedImageSamplerUsageHandler::add_hierarchy_to_comparison_ids(uint32_t id)
+{
+ // Traverse the variable dependency hierarchy and tag everything in its path with comparison ids.
+ comparison_ids.insert(id);
+ for (auto &dep_id : dependency_hierarchy[id])
+ add_hierarchy_to_comparison_ids(dep_id);
+}
+
+bool Compiler::CombinedImageSamplerUsageHandler::handle(Op opcode, const uint32_t *args, uint32_t length)
+{
+ switch (opcode)
+ {
+ case OpAccessChain:
+ case OpInBoundsAccessChain:
+ case OpPtrAccessChain:
+ case OpLoad:
+ {
+ if (length < 3)
+ return false;
+ dependency_hierarchy[args[1]].insert(args[2]);
+
+ // Ideally defer this to OpImageRead, but then we'd need to track loaded IDs.
+ // If we load an image, we're going to use it and there is little harm in declaring an unused gl_FragCoord.
+ auto &type = compiler.get<SPIRType>(args[0]);
+ if (type.image.dim == DimSubpassData)
+ need_subpass_input = true;
+
+ // If we load a SampledImage and it will be used with Dref, propagate the state up.
+ if (dref_combined_samplers.count(args[1]) != 0)
+ add_hierarchy_to_comparison_ids(args[1]);
+ break;
+ }
+
+ case OpSampledImage:
+ {
+ if (length < 4)
+ return false;
+
+ uint32_t result_type = args[0];
+ uint32_t result_id = args[1];
+ auto &type = compiler.get<SPIRType>(result_type);
+ if (type.image.depth || dref_combined_samplers.count(result_id) != 0)
+ {
+ // This image must be a depth image.
+ uint32_t image = args[2];
+ add_hierarchy_to_comparison_ids(image);
+
+ // This sampler must be a SamplerComparisonState, and not a regular SamplerState.
+ uint32_t sampler = args[3];
+ add_hierarchy_to_comparison_ids(sampler);
+
+ // Mark the OpSampledImage itself as being comparison state.
+ comparison_ids.insert(result_id);
+ }
+ return true;
+ }
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool Compiler::buffer_is_hlsl_counter_buffer(uint32_t id) const
+{
+ auto *m = ir.find_meta(id);
+ return m && m->hlsl_is_magic_counter_buffer;
+}
+
+bool Compiler::buffer_get_hlsl_counter_buffer(uint32_t id, uint32_t &counter_id) const
+{
+ auto *m = ir.find_meta(id);
+
+ // First, check for the proper decoration.
+ if (m && m->hlsl_magic_counter_buffer != 0)
+ {
+ counter_id = m->hlsl_magic_counter_buffer;
+ return true;
+ }
+ else
+ return false;
+}
+
+void Compiler::make_constant_null(uint32_t id, uint32_t type)
+{
+ auto &constant_type = get<SPIRType>(type);
+
+ if (constant_type.pointer)
+ {
+ auto &constant = set<SPIRConstant>(id, type);
+ constant.make_null(constant_type);
+ }
+ else if (!constant_type.array.empty())
+ {
+ assert(constant_type.parent_type);
+ uint32_t parent_id = ir.increase_bound_by(1);
+ make_constant_null(parent_id, constant_type.parent_type);
+
+ if (!constant_type.array_size_literal.back())
+ SPIRV_CROSS_THROW("Array size of OpConstantNull must be a literal.");
+
+ vector<uint32_t> elements(constant_type.array.back());
+ for (uint32_t i = 0; i < constant_type.array.back(); i++)
+ elements[i] = parent_id;
+ set<SPIRConstant>(id, type, elements.data(), uint32_t(elements.size()), false);
+ }
+ else if (!constant_type.member_types.empty())
+ {
+ uint32_t member_ids = ir.increase_bound_by(uint32_t(constant_type.member_types.size()));
+ vector<uint32_t> elements(constant_type.member_types.size());
+ for (uint32_t i = 0; i < constant_type.member_types.size(); i++)
+ {
+ make_constant_null(member_ids + i, constant_type.member_types[i]);
+ elements[i] = member_ids + i;
+ }
+ set<SPIRConstant>(id, type, elements.data(), uint32_t(elements.size()), false);
+ }
+ else
+ {
+ auto &constant = set<SPIRConstant>(id, type);
+ constant.make_null(constant_type);
+ }
+}
+
+const std::vector<spv::Capability> &Compiler::get_declared_capabilities() const
+{
+ return ir.declared_capabilities;
+}
+
+const std::vector<std::string> &Compiler::get_declared_extensions() const
+{
+ return ir.declared_extensions;
+}
+
+std::string Compiler::get_remapped_declared_block_name(uint32_t id) const
+{
+ auto itr = declared_block_names.find(id);
+ if (itr != end(declared_block_names))
+ return itr->second;
+ else
+ {
+ auto &var = get<SPIRVariable>(id);
+ auto &type = get<SPIRType>(var.basetype);
+
+ auto *type_meta = ir.find_meta(type.self);
+ auto *block_name = type_meta ? &type_meta->decoration.alias : nullptr;
+ return (!block_name || block_name->empty()) ? get_block_fallback_name(id) : *block_name;
+ }
+}
+
+bool Compiler::instruction_to_result_type(uint32_t &result_type, uint32_t &result_id, spv::Op op, const uint32_t *args,
+ uint32_t length)
+{
+ // Most instructions follow the pattern of <result-type> <result-id> <arguments>.
+ // There are some exceptions.
+ switch (op)
+ {
+ case OpStore:
+ case OpCopyMemory:
+ case OpCopyMemorySized:
+ case OpImageWrite:
+ case OpAtomicStore:
+ case OpAtomicFlagClear:
+ case OpEmitStreamVertex:
+ case OpEndStreamPrimitive:
+ case OpControlBarrier:
+ case OpMemoryBarrier:
+ case OpGroupWaitEvents:
+ case OpRetainEvent:
+ case OpReleaseEvent:
+ case OpSetUserEventStatus:
+ case OpCaptureEventProfilingInfo:
+ case OpCommitReadPipe:
+ case OpCommitWritePipe:
+ case OpGroupCommitReadPipe:
+ case OpGroupCommitWritePipe:
+ return false;
+
+ default:
+ if (length > 1 && maybe_get<SPIRType>(args[0]) != nullptr)
+ {
+ result_type = args[0];
+ result_id = args[1];
+ return true;
+ }
+ else
+ return false;
+ }
+}
+
+Bitset Compiler::combined_decoration_for_member(const SPIRType &type, uint32_t index) const
+{
+ Bitset flags;
+ auto *type_meta = ir.find_meta(type.self);
+
+ if (type_meta)
+ {
+ auto &memb = type_meta->members;
+ if (index >= memb.size())
+ return flags;
+ auto &dec = memb[index];
+
+ // If our type is a struct, traverse all the members as well recursively.
+ flags.merge_or(dec.decoration_flags);
+ for (uint32_t i = 0; i < type.member_types.size(); i++)
+ flags.merge_or(combined_decoration_for_member(get<SPIRType>(type.member_types[i]), i));
+ }
+
+ return flags;
+}
+
+bool Compiler::is_desktop_only_format(spv::ImageFormat format)
+{
+ switch (format)
+ {
+ // Desktop-only formats
+ case ImageFormatR11fG11fB10f:
+ case ImageFormatR16f:
+ case ImageFormatRgb10A2:
+ case ImageFormatR8:
+ case ImageFormatRg8:
+ case ImageFormatR16:
+ case ImageFormatRg16:
+ case ImageFormatRgba16:
+ case ImageFormatR16Snorm:
+ case ImageFormatRg16Snorm:
+ case ImageFormatRgba16Snorm:
+ case ImageFormatR8Snorm:
+ case ImageFormatRg8Snorm:
+ case ImageFormatR8ui:
+ case ImageFormatRg8ui:
+ case ImageFormatR16ui:
+ case ImageFormatRgb10a2ui:
+ case ImageFormatR8i:
+ case ImageFormatRg8i:
+ case ImageFormatR16i:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool Compiler::image_is_comparison(const spirv_cross::SPIRType &type, uint32_t id) const
+{
+ return type.image.depth || (comparison_ids.count(id) != 0);
+}
+
+bool Compiler::type_is_opaque_value(const spirv_cross::SPIRType &type) const
+{
+ return !type.pointer && (type.basetype == SPIRType::SampledImage || type.basetype == SPIRType::Image ||
+ type.basetype == SPIRType::Sampler);
+}
diff --git a/src/3rdparty/SPIRV-Cross/spirv_cross.hpp b/src/3rdparty/SPIRV-Cross/spirv_cross.hpp
new file mode 100644
index 0000000..4edc836
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_cross.hpp
@@ -0,0 +1,969 @@
+/*
+ * Copyright 2015-2019 Arm Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SPIRV_CROSS_HPP
+#define SPIRV_CROSS_HPP
+
+#include "spirv.hpp"
+#include "spirv_cfg.hpp"
+#include "spirv_cross_parsed_ir.hpp"
+
+namespace spirv_cross
+{
+struct Resource
+{
+ // Resources are identified with their SPIR-V ID.
+ // This is the ID of the OpVariable.
+ uint32_t id;
+
+ // The type ID of the variable which includes arrays and all type modifications.
+ // This type ID is not suitable for parsing OpMemberDecoration of a struct and other decorations in general
+ // since these modifications typically happen on the base_type_id.
+ uint32_t type_id;
+
+ // The base type of the declared resource.
+ // This type is the base type which ignores pointers and arrays of the type_id.
+ // This is mostly useful to parse decorations of the underlying type.
+ // base_type_id can also be obtained with get_type(get_type(type_id).self).
+ uint32_t base_type_id;
+
+ // The declared name (OpName) of the resource.
+ // For Buffer blocks, the name actually reflects the externally
+ // visible Block name.
+ //
+ // This name can be retrieved again by using either
+ // get_name(id) or get_name(base_type_id) depending if it's a buffer block or not.
+ //
+ // This name can be an empty string in which case get_fallback_name(id) can be
+ // used which obtains a suitable fallback identifier for an ID.
+ std::string name;
+};
+
+struct ShaderResources
+{
+ std::vector<Resource> uniform_buffers;
+ std::vector<Resource> storage_buffers;
+ std::vector<Resource> stage_inputs;
+ std::vector<Resource> stage_outputs;
+ std::vector<Resource> subpass_inputs;
+ std::vector<Resource> storage_images;
+ std::vector<Resource> sampled_images;
+ std::vector<Resource> atomic_counters;
+ std::vector<Resource> acceleration_structures;
+
+ // There can only be one push constant block,
+ // but keep the vector in case this restriction is lifted in the future.
+ std::vector<Resource> push_constant_buffers;
+
+ // For Vulkan GLSL and HLSL source,
+ // these correspond to separate texture2D and samplers respectively.
+ std::vector<Resource> separate_images;
+ std::vector<Resource> separate_samplers;
+};
+
+struct CombinedImageSampler
+{
+ // The ID of the sampler2D variable.
+ uint32_t combined_id;
+ // The ID of the texture2D variable.
+ uint32_t image_id;
+ // The ID of the sampler variable.
+ uint32_t sampler_id;
+};
+
+struct SpecializationConstant
+{
+ // The ID of the specialization constant.
+ uint32_t id;
+ // The constant ID of the constant, used in Vulkan during pipeline creation.
+ uint32_t constant_id;
+};
+
+struct BufferRange
+{
+ unsigned index;
+ size_t offset;
+ size_t range;
+};
+
+enum BufferPackingStandard
+{
+ BufferPackingStd140,
+ BufferPackingStd430,
+ BufferPackingStd140EnhancedLayout,
+ BufferPackingStd430EnhancedLayout,
+ BufferPackingHLSLCbuffer,
+ BufferPackingHLSLCbufferPackOffset
+};
+
+struct EntryPoint
+{
+ std::string name;
+ spv::ExecutionModel execution_model;
+};
+
+enum ExtendedDecorations
+{
+ SPIRVCrossDecorationPacked,
+ SPIRVCrossDecorationPackedType,
+ SPIRVCrossDecorationInterfaceMemberIndex,
+ SPIRVCrossDecorationInterfaceOrigID,
+ SPIRVCrossDecorationArgumentBufferID
+};
+
+class Compiler
+{
+public:
+ friend class CFG;
+ friend class DominatorBuilder;
+
+ // The constructor takes a buffer of SPIR-V words and parses it.
+ // It will create its own parser, parse the SPIR-V and move the parsed IR
+ // as if you had called the constructors taking ParsedIR directly.
+ explicit Compiler(std::vector<uint32_t> ir);
+ Compiler(const uint32_t *ir, size_t word_count);
+
+ // This is more modular. We can also consume a ParsedIR structure directly, either as a move, or copy.
+ // With copy, we can reuse the same parsed IR for multiple Compiler instances.
+ explicit Compiler(const ParsedIR &ir);
+ explicit Compiler(ParsedIR &&ir);
+
+ virtual ~Compiler() = default;
+
+ // After parsing, API users can modify the SPIR-V via reflection and call this
+ // to disassemble the SPIR-V into the desired langauage.
+ // Sub-classes actually implement this.
+ virtual std::string compile();
+
+ // Gets the identifier (OpName) of an ID. If not defined, an empty string will be returned.
+ const std::string &get_name(uint32_t id) const;
+
+ // Applies a decoration to an ID. Effectively injects OpDecorate.
+ void set_decoration(uint32_t id, spv::Decoration decoration, uint32_t argument = 0);
+ void set_decoration_string(uint32_t id, spv::Decoration decoration, const std::string &argument);
+
+ // Overrides the identifier OpName of an ID.
+ // Identifiers beginning with underscores or identifiers which contain double underscores
+ // are reserved by the implementation.
+ void set_name(uint32_t id, const std::string &name);
+
+ // Gets a bitmask for the decorations which are applied to ID.
+ // I.e. (1ull << spv::DecorationFoo) | (1ull << spv::DecorationBar)
+ const Bitset &get_decoration_bitset(uint32_t id) const;
+
+ // Returns whether the decoration has been applied to the ID.
+ bool has_decoration(uint32_t id, spv::Decoration decoration) const;
+
+ // Gets the value for decorations which take arguments.
+ // If the decoration is a boolean (i.e. spv::DecorationNonWritable),
+ // 1 will be returned.
+ // If decoration doesn't exist or decoration is not recognized,
+ // 0 will be returned.
+ uint32_t get_decoration(uint32_t id, spv::Decoration decoration) const;
+ const std::string &get_decoration_string(uint32_t id, spv::Decoration decoration) const;
+
+ // Removes the decoration for an ID.
+ void unset_decoration(uint32_t id, spv::Decoration decoration);
+
+ // Gets the SPIR-V type associated with ID.
+ // Mostly used with Resource::type_id and Resource::base_type_id to parse the underlying type of a resource.
+ const SPIRType &get_type(uint32_t id) const;
+
+ // Gets the SPIR-V type of a variable.
+ const SPIRType &get_type_from_variable(uint32_t id) const;
+
+ // Gets the underlying storage class for an OpVariable.
+ spv::StorageClass get_storage_class(uint32_t id) const;
+
+ // If get_name() is an empty string, get the fallback name which will be used
+ // instead in the disassembled source.
+ virtual const std::string get_fallback_name(uint32_t id) const;
+
+ // If get_name() of a Block struct is an empty string, get the fallback name.
+ // This needs to be per-variable as multiple variables can use the same block type.
+ virtual const std::string get_block_fallback_name(uint32_t id) const;
+
+ // Given an OpTypeStruct in ID, obtain the identifier for member number "index".
+ // This may be an empty string.
+ const std::string &get_member_name(uint32_t id, uint32_t index) const;
+
+ // Given an OpTypeStruct in ID, obtain the OpMemberDecoration for member number "index".
+ uint32_t get_member_decoration(uint32_t id, uint32_t index, spv::Decoration decoration) const;
+ const std::string &get_member_decoration_string(uint32_t id, uint32_t index, spv::Decoration decoration) const;
+
+ // Sets the member identifier for OpTypeStruct ID, member number "index".
+ void set_member_name(uint32_t id, uint32_t index, const std::string &name);
+
+ // Returns the qualified member identifier for OpTypeStruct ID, member number "index",
+ // or an empty string if no qualified alias exists
+ const std::string &get_member_qualified_name(uint32_t type_id, uint32_t index) const;
+
+ // Gets the decoration mask for a member of a struct, similar to get_decoration_mask.
+ const Bitset &get_member_decoration_bitset(uint32_t id, uint32_t index) const;
+
+ // Returns whether the decoration has been applied to a member of a struct.
+ bool has_member_decoration(uint32_t id, uint32_t index, spv::Decoration decoration) const;
+
+ // Similar to set_decoration, but for struct members.
+ void set_member_decoration(uint32_t id, uint32_t index, spv::Decoration decoration, uint32_t argument = 0);
+ void set_member_decoration_string(uint32_t id, uint32_t index, spv::Decoration decoration,
+ const std::string &argument);
+
+ // Unsets a member decoration, similar to unset_decoration.
+ void unset_member_decoration(uint32_t id, uint32_t index, spv::Decoration decoration);
+
+ // Gets the fallback name for a member, similar to get_fallback_name.
+ virtual const std::string get_fallback_member_name(uint32_t index) const
+ {
+ return join("_", index);
+ }
+
+ // Returns a vector of which members of a struct are potentially in use by a
+ // SPIR-V shader. The granularity of this analysis is per-member of a struct.
+ // This can be used for Buffer (UBO), BufferBlock/StorageBuffer (SSBO) and PushConstant blocks.
+ // ID is the Resource::id obtained from get_shader_resources().
+ std::vector<BufferRange> get_active_buffer_ranges(uint32_t id) const;
+
+ // Returns the effective size of a buffer block.
+ size_t get_declared_struct_size(const SPIRType &struct_type) const;
+
+ // Returns the effective size of a buffer block, with a given array size
+ // for a runtime array.
+ // SSBOs are typically declared as runtime arrays. get_declared_struct_size() will return 0 for the size.
+ // This is not very helpful for applications which might need to know the array stride of its last member.
+ // This can be done through the API, but it is not very intuitive how to accomplish this, so here we provide a helper function
+ // to query the size of the buffer, assuming that the last member has a certain size.
+ // If the buffer does not contain a runtime array, array_size is ignored, and the function will behave as
+ // get_declared_struct_size().
+ // To get the array stride of the last member, something like:
+ // get_declared_struct_size_runtime_array(type, 1) - get_declared_struct_size_runtime_array(type, 0) will work.
+ size_t get_declared_struct_size_runtime_array(const SPIRType &struct_type, size_t array_size) const;
+
+ // Returns the effective size of a buffer block struct member.
+ virtual size_t get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const;
+
+ // Returns a set of all global variables which are statically accessed
+ // by the control flow graph from the current entry point.
+ // Only variables which change the interface for a shader are returned, that is,
+ // variables with storage class of Input, Output, Uniform, UniformConstant, PushConstant and AtomicCounter
+ // storage classes are returned.
+ //
+ // To use the returned set as the filter for which variables are used during compilation,
+ // this set can be moved to set_enabled_interface_variables().
+ std::unordered_set<uint32_t> get_active_interface_variables() const;
+
+ // Sets the interface variables which are used during compilation.
+ // By default, all variables are used.
+ // Once set, compile() will only consider the set in active_variables.
+ void set_enabled_interface_variables(std::unordered_set<uint32_t> active_variables);
+
+ // Query shader resources, use ids with reflection interface to modify or query binding points, etc.
+ ShaderResources get_shader_resources() const;
+
+ // Query shader resources, but only return the variables which are part of active_variables.
+ // E.g.: get_shader_resources(get_active_variables()) to only return the variables which are statically
+ // accessed.
+ ShaderResources get_shader_resources(const std::unordered_set<uint32_t> &active_variables) const;
+
+ // Remapped variables are considered built-in variables and a backend will
+ // not emit a declaration for this variable.
+ // This is mostly useful for making use of builtins which are dependent on extensions.
+ void set_remapped_variable_state(uint32_t id, bool remap_enable);
+ bool get_remapped_variable_state(uint32_t id) const;
+
+ // For subpassInput variables which are remapped to plain variables,
+ // the number of components in the remapped
+ // variable must be specified as the backing type of subpass inputs are opaque.
+ void set_subpass_input_remapped_components(uint32_t id, uint32_t components);
+ uint32_t get_subpass_input_remapped_components(uint32_t id) const;
+
+ // All operations work on the current entry point.
+ // Entry points can be swapped out with set_entry_point().
+ // Entry points should be set right after the constructor completes as some reflection functions traverse the graph from the entry point.
+ // Resource reflection also depends on the entry point.
+ // By default, the current entry point is set to the first OpEntryPoint which appears in the SPIR-V module.
+
+ // Some shader languages restrict the names that can be given to entry points, and the
+ // corresponding backend will automatically rename an entry point name, during the call
+ // to compile() if it is illegal. For example, the common entry point name main() is
+ // illegal in MSL, and is renamed to an alternate name by the MSL backend.
+ // Given the original entry point name contained in the SPIR-V, this function returns
+ // the name, as updated by the backend during the call to compile(). If the name is not
+ // illegal, and has not been renamed, or if this function is called before compile(),
+ // this function will simply return the same name.
+
+ // New variants of entry point query and reflection.
+ // Names for entry points in the SPIR-V module may alias if they belong to different execution models.
+ // To disambiguate, we must pass along with the entry point names the execution model.
+ std::vector<EntryPoint> get_entry_points_and_stages() const;
+ void set_entry_point(const std::string &entry, spv::ExecutionModel execution_model);
+
+ // Renames an entry point from old_name to new_name.
+ // If old_name is currently selected as the current entry point, it will continue to be the current entry point,
+ // albeit with a new name.
+ // get_entry_points() is essentially invalidated at this point.
+ void rename_entry_point(const std::string &old_name, const std::string &new_name,
+ spv::ExecutionModel execution_model);
+ const SPIREntryPoint &get_entry_point(const std::string &name, spv::ExecutionModel execution_model) const;
+ SPIREntryPoint &get_entry_point(const std::string &name, spv::ExecutionModel execution_model);
+ const std::string &get_cleansed_entry_point_name(const std::string &name,
+ spv::ExecutionModel execution_model) const;
+
+ // Query and modify OpExecutionMode.
+ const Bitset &get_execution_mode_bitset() const;
+
+ void unset_execution_mode(spv::ExecutionMode mode);
+ void set_execution_mode(spv::ExecutionMode mode, uint32_t arg0 = 0, uint32_t arg1 = 0, uint32_t arg2 = 0);
+
+ // Gets argument for an execution mode (LocalSize, Invocations, OutputVertices).
+ // For LocalSize, the index argument is used to select the dimension (X = 0, Y = 1, Z = 2).
+ // For execution modes which do not have arguments, 0 is returned.
+ uint32_t get_execution_mode_argument(spv::ExecutionMode mode, uint32_t index = 0) const;
+ spv::ExecutionModel get_execution_model() const;
+
+ bool is_tessellation_shader() const;
+
+ // In SPIR-V, the compute work group size can be represented by a constant vector, in which case
+ // the LocalSize execution mode is ignored.
+ //
+ // This constant vector can be a constant vector, specialization constant vector, or partly specialized constant vector.
+ // To modify and query work group dimensions which are specialization constants, SPIRConstant values must be modified
+ // directly via get_constant() rather than using LocalSize directly. This function will return which constants should be modified.
+ //
+ // To modify dimensions which are *not* specialization constants, set_execution_mode should be used directly.
+ // Arguments to set_execution_mode which are specialization constants are effectively ignored during compilation.
+ // NOTE: This is somewhat different from how SPIR-V works. In SPIR-V, the constant vector will completely replace LocalSize,
+ // while in this interface, LocalSize is only ignored for specialization constants.
+ //
+ // The specialization constant will be written to x, y and z arguments.
+ // If the component is not a specialization constant, a zeroed out struct will be written.
+ // The return value is the constant ID of the builtin WorkGroupSize, but this is not expected to be useful
+ // for most use cases.
+ uint32_t get_work_group_size_specialization_constants(SpecializationConstant &x, SpecializationConstant &y,
+ SpecializationConstant &z) const;
+
+ // Analyzes all OpImageFetch (texelFetch) opcodes and checks if there are instances where
+ // said instruction is used without a combined image sampler.
+ // GLSL targets do not support the use of texelFetch without a sampler.
+ // To workaround this, we must inject a dummy sampler which can be used to form a sampler2D at the call-site of
+ // texelFetch as necessary.
+ //
+ // This must be called before build_combined_image_samplers().
+ // build_combined_image_samplers() may refer to the ID returned by this method if the returned ID is non-zero.
+ // The return value will be the ID of a sampler object if a dummy sampler is necessary, or 0 if no sampler object
+ // is required.
+ //
+ // If the returned ID is non-zero, it can be decorated with set/bindings as desired before calling compile().
+ // Calling this function also invalidates get_active_interface_variables(), so this should be called
+ // before that function.
+ uint32_t build_dummy_sampler_for_combined_images();
+
+ // Analyzes all separate image and samplers used from the currently selected entry point,
+ // and re-routes them all to a combined image sampler instead.
+ // This is required to "support" separate image samplers in targets which do not natively support
+ // this feature, like GLSL/ESSL.
+ //
+ // This must be called before compile() if such remapping is desired.
+ // This call will add new sampled images to the SPIR-V,
+ // so it will appear in reflection if get_shader_resources() is called after build_combined_image_samplers.
+ //
+ // If any image/sampler remapping was found, no separate image/samplers will appear in the decompiled output,
+ // but will still appear in reflection.
+ //
+ // The resulting samplers will be void of any decorations like name, descriptor sets and binding points,
+ // so this can be added before compile() if desired.
+ //
+ // Combined image samplers originating from this set are always considered active variables.
+ // Arrays of separate samplers are not supported, but arrays of separate images are supported.
+ // Array of images + sampler -> Array of combined image samplers.
+ void build_combined_image_samplers();
+
+ // Gets a remapping for the combined image samplers.
+ const std::vector<CombinedImageSampler> &get_combined_image_samplers() const
+ {
+ return combined_image_samplers;
+ }
+
+ // Set a new variable type remap callback.
+ // The type remapping is designed to allow global interface variable to assume more special types.
+ // A typical example here is to remap sampler2D into samplerExternalOES, which currently isn't supported
+ // directly by SPIR-V.
+ //
+ // In compile() while emitting code,
+ // for every variable that is declared, including function parameters, the callback will be called
+ // and the API user has a chance to change the textual representation of the type used to declare the variable.
+ // The API user can detect special patterns in names to guide the remapping.
+ void set_variable_type_remap_callback(VariableTypeRemapCallback cb)
+ {
+ variable_remap_callback = std::move(cb);
+ }
+
+ // API for querying which specialization constants exist.
+ // To modify a specialization constant before compile(), use get_constant(constant.id),
+ // then update constants directly in the SPIRConstant data structure.
+ // For composite types, the subconstants can be iterated over and modified.
+ // constant_type is the SPIRType for the specialization constant,
+ // which can be queried to determine which fields in the unions should be poked at.
+ std::vector<SpecializationConstant> get_specialization_constants() const;
+ SPIRConstant &get_constant(uint32_t id);
+ const SPIRConstant &get_constant(uint32_t id) const;
+
+ uint32_t get_current_id_bound() const
+ {
+ return uint32_t(ir.ids.size());
+ }
+
+ // API for querying buffer objects.
+ // The type passed in here should be the base type of a resource, i.e.
+ // get_type(resource.base_type_id)
+ // as decorations are set in the basic Block type.
+ // The type passed in here must have these decorations set, or an exception is raised.
+ // Only UBOs and SSBOs or sub-structs which are part of these buffer types will have these decorations set.
+ uint32_t type_struct_member_offset(const SPIRType &type, uint32_t index) const;
+ uint32_t type_struct_member_array_stride(const SPIRType &type, uint32_t index) const;
+ uint32_t type_struct_member_matrix_stride(const SPIRType &type, uint32_t index) const;
+
+ // Gets the offset in SPIR-V words (uint32_t) for a decoration which was originally declared in the SPIR-V binary.
+ // The offset will point to one or more uint32_t literals which can be modified in-place before using the SPIR-V binary.
+ // Note that adding or removing decorations using the reflection API will not change the behavior of this function.
+ // If the decoration was declared, sets the word_offset to an offset into the provided SPIR-V binary buffer and returns true,
+ // otherwise, returns false.
+ // If the decoration does not have any value attached to it (e.g. DecorationRelaxedPrecision), this function will also return false.
+ bool get_binary_offset_for_decoration(uint32_t id, spv::Decoration decoration, uint32_t &word_offset) const;
+
+ // HLSL counter buffer reflection interface.
+ // Append/Consume/Increment/Decrement in HLSL is implemented as two "neighbor" buffer objects where
+ // one buffer implements the storage, and a single buffer containing just a lone "int" implements the counter.
+ // To SPIR-V these will be exposed as two separate buffers, but glslang HLSL frontend emits a special indentifier
+ // which lets us link the two buffers together.
+
+ // Queries if a variable ID is a counter buffer which "belongs" to a regular buffer object.
+
+ // If SPV_GOOGLE_hlsl_functionality1 is used, this can be used even with a stripped SPIR-V module.
+ // Otherwise, this query is purely based on OpName identifiers as found in the SPIR-V module, and will
+ // only return true if OpSource was reported HLSL.
+ // To rely on this functionality, ensure that the SPIR-V module is not stripped.
+
+ bool buffer_is_hlsl_counter_buffer(uint32_t id) const;
+
+ // Queries if a buffer object has a neighbor "counter" buffer.
+ // If so, the ID of that counter buffer will be returned in counter_id.
+ // If SPV_GOOGLE_hlsl_functionality1 is used, this can be used even with a stripped SPIR-V module.
+ // Otherwise, this query is purely based on OpName identifiers as found in the SPIR-V module, and will
+ // only return true if OpSource was reported HLSL.
+ // To rely on this functionality, ensure that the SPIR-V module is not stripped.
+ bool buffer_get_hlsl_counter_buffer(uint32_t id, uint32_t &counter_id) const;
+
+ // Gets the list of all SPIR-V Capabilities which were declared in the SPIR-V module.
+ const std::vector<spv::Capability> &get_declared_capabilities() const;
+
+ // Gets the list of all SPIR-V extensions which were declared in the SPIR-V module.
+ const std::vector<std::string> &get_declared_extensions() const;
+
+ // When declaring buffer blocks in GLSL, the name declared in the GLSL source
+ // might not be the same as the name declared in the SPIR-V module due to naming conflicts.
+ // In this case, SPIRV-Cross needs to find a fallback-name, and it might only
+ // be possible to know this name after compiling to GLSL.
+ // This is particularly important for HLSL input and UAVs which tends to reuse the same block type
+ // for multiple distinct blocks. For these cases it is not possible to modify the name of the type itself
+ // because it might be unique. Instead, you can use this interface to check after compilation which
+ // name was actually used if your input SPIR-V tends to have this problem.
+ // For other names like remapped names for variables, etc, it's generally enough to query the name of the variables
+ // after compiling, block names are an exception to this rule.
+ // ID is the name of a variable as returned by Resource::id, and must be a variable with a Block-like type.
+ //
+ // This also applies to HLSL cbuffers.
+ std::string get_remapped_declared_block_name(uint32_t id) const;
+
+ // For buffer block variables, get the decorations for that variable.
+ // Sometimes, decorations for buffer blocks are found in member decorations instead
+ // of direct decorations on the variable itself.
+ // The most common use here is to check if a buffer is readonly or writeonly.
+ Bitset get_buffer_block_flags(uint32_t id) const;
+
+protected:
+ const uint32_t *stream(const Instruction &instr) const
+ {
+ // If we're not going to use any arguments, just return nullptr.
+ // We want to avoid case where we return an out of range pointer
+ // that trips debug assertions on some platforms.
+ if (!instr.length)
+ return nullptr;
+
+ if (instr.offset + instr.length > ir.spirv.size())
+ SPIRV_CROSS_THROW("Compiler::stream() out of range.");
+ return &ir.spirv[instr.offset];
+ }
+
+ ParsedIR ir;
+ // Marks variables which have global scope and variables which can alias with other variables
+ // (SSBO, image load store, etc)
+ std::vector<uint32_t> global_variables;
+ std::vector<uint32_t> aliased_variables;
+
+ SPIRFunction *current_function = nullptr;
+ SPIRBlock *current_block = nullptr;
+ std::unordered_set<uint32_t> active_interface_variables;
+ bool check_active_interface_variables = false;
+
+ // If our IDs are out of range here as part of opcodes, throw instead of
+ // undefined behavior.
+ template <typename T, typename... P>
+ T &set(uint32_t id, P &&... args)
+ {
+ ir.add_typed_id(static_cast<Types>(T::type), id);
+ auto &var = variant_set<T>(ir.ids[id], std::forward<P>(args)...);
+ var.self = id;
+ return var;
+ }
+
+ template <typename T>
+ T &get(uint32_t id)
+ {
+ return variant_get<T>(ir.ids[id]);
+ }
+
+ template <typename T>
+ T *maybe_get(uint32_t id)
+ {
+ if (id >= ir.ids.size())
+ return nullptr;
+ else if (ir.ids[id].get_type() == static_cast<Types>(T::type))
+ return &get<T>(id);
+ else
+ return nullptr;
+ }
+
+ template <typename T>
+ const T &get(uint32_t id) const
+ {
+ return variant_get<T>(ir.ids[id]);
+ }
+
+ template <typename T>
+ const T *maybe_get(uint32_t id) const
+ {
+ if (ir.ids[id].get_type() == static_cast<Types>(T::type))
+ return &get<T>(id);
+ else
+ return nullptr;
+ }
+
+ // Gets the id of SPIR-V type underlying the given type_id, which might be a pointer.
+ uint32_t get_pointee_type_id(uint32_t type_id) const;
+
+ // Gets the SPIR-V type underlying the given type, which might be a pointer.
+ const SPIRType &get_pointee_type(const SPIRType &type) const;
+
+ // Gets the SPIR-V type underlying the given type_id, which might be a pointer.
+ const SPIRType &get_pointee_type(uint32_t type_id) const;
+
+ // Gets the ID of the SPIR-V type underlying a variable.
+ uint32_t get_variable_data_type_id(const SPIRVariable &var) const;
+
+ // Gets the SPIR-V type underlying a variable.
+ SPIRType &get_variable_data_type(const SPIRVariable &var);
+
+ // Gets the SPIR-V type underlying a variable.
+ const SPIRType &get_variable_data_type(const SPIRVariable &var) const;
+
+ // Gets the SPIR-V element type underlying an array variable.
+ SPIRType &get_variable_element_type(const SPIRVariable &var);
+
+ // Gets the SPIR-V element type underlying an array variable.
+ const SPIRType &get_variable_element_type(const SPIRVariable &var) const;
+
+ // Sets the qualified member identifier for OpTypeStruct ID, member number "index".
+ void set_member_qualified_name(uint32_t type_id, uint32_t index, const std::string &name);
+ void set_qualified_name(uint32_t id, const std::string &name);
+
+ // Returns if the given type refers to a sampled image.
+ bool is_sampled_image_type(const SPIRType &type);
+
+ const SPIREntryPoint &get_entry_point() const;
+ SPIREntryPoint &get_entry_point();
+ static bool is_tessellation_shader(spv::ExecutionModel model);
+
+ virtual std::string to_name(uint32_t id, bool allow_alias = true) const;
+ bool is_builtin_variable(const SPIRVariable &var) const;
+ bool is_builtin_type(const SPIRType &type) const;
+ bool is_hidden_variable(const SPIRVariable &var, bool include_builtins = false) const;
+ bool is_immutable(uint32_t id) const;
+ bool is_member_builtin(const SPIRType &type, uint32_t index, spv::BuiltIn *builtin) const;
+ bool is_scalar(const SPIRType &type) const;
+ bool is_vector(const SPIRType &type) const;
+ bool is_matrix(const SPIRType &type) const;
+ bool is_array(const SPIRType &type) const;
+ uint32_t expression_type_id(uint32_t id) const;
+ const SPIRType &expression_type(uint32_t id) const;
+ bool expression_is_lvalue(uint32_t id) const;
+ bool variable_storage_is_aliased(const SPIRVariable &var);
+ SPIRVariable *maybe_get_backing_variable(uint32_t chain);
+
+ void register_read(uint32_t expr, uint32_t chain, bool forwarded);
+ void register_write(uint32_t chain);
+
+ inline bool is_continue(uint32_t next) const
+ {
+ return (ir.block_meta[next] & ParsedIR::BLOCK_META_CONTINUE_BIT) != 0;
+ }
+
+ inline bool is_single_block_loop(uint32_t next) const
+ {
+ auto &block = get<SPIRBlock>(next);
+ return block.merge == SPIRBlock::MergeLoop && block.continue_block == next;
+ }
+
+ inline bool is_break(uint32_t next) const
+ {
+ return (ir.block_meta[next] &
+ (ParsedIR::BLOCK_META_LOOP_MERGE_BIT | ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT)) != 0;
+ }
+
+ inline bool is_loop_break(uint32_t next) const
+ {
+ return (ir.block_meta[next] & ParsedIR::BLOCK_META_LOOP_MERGE_BIT) != 0;
+ }
+
+ inline bool is_conditional(uint32_t next) const
+ {
+ return (ir.block_meta[next] &
+ (ParsedIR::BLOCK_META_SELECTION_MERGE_BIT | ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT)) != 0;
+ }
+
+ // Dependency tracking for temporaries read from variables.
+ void flush_dependees(SPIRVariable &var);
+ void flush_all_active_variables();
+ void flush_control_dependent_expressions(uint32_t block);
+ void flush_all_atomic_capable_variables();
+ void flush_all_aliased_variables();
+ void register_global_read_dependencies(const SPIRBlock &func, uint32_t id);
+ void register_global_read_dependencies(const SPIRFunction &func, uint32_t id);
+ std::unordered_set<uint32_t> invalid_expressions;
+
+ void update_name_cache(std::unordered_set<std::string> &cache, std::string &name);
+
+ // A variant which takes two sets of names. The secondary is only used to verify there are no collisions,
+ // but the set is not updated when we have found a new name.
+ // Used primarily when adding block interface names.
+ void update_name_cache(std::unordered_set<std::string> &cache_primary,
+ const std::unordered_set<std::string> &cache_secondary, std::string &name);
+
+ bool function_is_pure(const SPIRFunction &func);
+ bool block_is_pure(const SPIRBlock &block);
+ bool block_is_outside_flow_control_from_block(const SPIRBlock &from, const SPIRBlock &to);
+
+ bool execution_is_branchless(const SPIRBlock &from, const SPIRBlock &to) const;
+ bool execution_is_noop(const SPIRBlock &from, const SPIRBlock &to) const;
+ SPIRBlock::ContinueBlockType continue_block_type(const SPIRBlock &continue_block) const;
+
+ bool force_recompile = false;
+
+ bool block_is_loop_candidate(const SPIRBlock &block, SPIRBlock::Method method) const;
+
+ bool types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const;
+ void inherit_expression_dependencies(uint32_t dst, uint32_t source);
+ void add_implied_read_expression(SPIRExpression &e, uint32_t source);
+ void add_implied_read_expression(SPIRAccessChain &e, uint32_t source);
+
+ // For proper multiple entry point support, allow querying if an Input or Output
+ // variable is part of that entry points interface.
+ bool interface_variable_exists_in_entry_point(uint32_t id) const;
+
+ std::vector<CombinedImageSampler> combined_image_samplers;
+
+ void remap_variable_type_name(const SPIRType &type, const std::string &var_name, std::string &type_name) const
+ {
+ if (variable_remap_callback)
+ variable_remap_callback(type, var_name, type_name);
+ }
+
+ void set_ir(const ParsedIR &parsed);
+ void set_ir(ParsedIR &&parsed);
+ void parse_fixup();
+
+ // Used internally to implement various traversals for queries.
+ struct OpcodeHandler
+ {
+ virtual ~OpcodeHandler() = default;
+
+ // Return true if traversal should continue.
+ // If false, traversal will end immediately.
+ virtual bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) = 0;
+
+ virtual bool follow_function_call(const SPIRFunction &)
+ {
+ return true;
+ }
+
+ virtual void set_current_block(const SPIRBlock &)
+ {
+ }
+
+ virtual bool begin_function_scope(const uint32_t *, uint32_t)
+ {
+ return true;
+ }
+
+ virtual bool end_function_scope(const uint32_t *, uint32_t)
+ {
+ return true;
+ }
+ };
+
+ struct BufferAccessHandler : OpcodeHandler
+ {
+ BufferAccessHandler(const Compiler &compiler_, std::vector<BufferRange> &ranges_, uint32_t id_)
+ : compiler(compiler_)
+ , ranges(ranges_)
+ , id(id_)
+ {
+ }
+
+ bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override;
+
+ const Compiler &compiler;
+ std::vector<BufferRange> &ranges;
+ uint32_t id;
+
+ std::unordered_set<uint32_t> seen;
+ };
+
+ struct InterfaceVariableAccessHandler : OpcodeHandler
+ {
+ InterfaceVariableAccessHandler(const Compiler &compiler_, std::unordered_set<uint32_t> &variables_)
+ : compiler(compiler_)
+ , variables(variables_)
+ {
+ }
+
+ bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override;
+
+ const Compiler &compiler;
+ std::unordered_set<uint32_t> &variables;
+ };
+
+ struct CombinedImageSamplerHandler : OpcodeHandler
+ {
+ CombinedImageSamplerHandler(Compiler &compiler_)
+ : compiler(compiler_)
+ {
+ }
+ bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override;
+ bool begin_function_scope(const uint32_t *args, uint32_t length) override;
+ bool end_function_scope(const uint32_t *args, uint32_t length) override;
+
+ Compiler &compiler;
+
+ // Each function in the call stack needs its own remapping for parameters so we can deduce which global variable each texture/sampler the parameter is statically bound to.
+ std::stack<std::unordered_map<uint32_t, uint32_t>> parameter_remapping;
+ std::stack<SPIRFunction *> functions;
+
+ uint32_t remap_parameter(uint32_t id);
+ void push_remap_parameters(const SPIRFunction &func, const uint32_t *args, uint32_t length);
+ void pop_remap_parameters();
+ void register_combined_image_sampler(SPIRFunction &caller, uint32_t texture_id, uint32_t sampler_id,
+ bool depth);
+ };
+
+ struct DummySamplerForCombinedImageHandler : OpcodeHandler
+ {
+ DummySamplerForCombinedImageHandler(Compiler &compiler_)
+ : compiler(compiler_)
+ {
+ }
+ bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override;
+
+ Compiler &compiler;
+ bool need_dummy_sampler = false;
+ };
+
+ struct ActiveBuiltinHandler : OpcodeHandler
+ {
+ ActiveBuiltinHandler(Compiler &compiler_)
+ : compiler(compiler_)
+ {
+ }
+
+ bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override;
+ Compiler &compiler;
+
+ void handle_builtin(const SPIRType &type, spv::BuiltIn builtin, const Bitset &decoration_flags);
+ };
+
+ bool traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHandler &handler) const;
+ bool traverse_all_reachable_opcodes(const SPIRFunction &block, OpcodeHandler &handler) const;
+ // This must be an ordered data structure so we always pick the same type aliases.
+ std::vector<uint32_t> global_struct_cache;
+
+ ShaderResources get_shader_resources(const std::unordered_set<uint32_t> *active_variables) const;
+
+ VariableTypeRemapCallback variable_remap_callback;
+
+ bool get_common_basic_type(const SPIRType &type, SPIRType::BaseType &base_type);
+
+ std::unordered_set<uint32_t> forced_temporaries;
+ std::unordered_set<uint32_t> forwarded_temporaries;
+ std::unordered_set<uint32_t> hoisted_temporaries;
+
+ Bitset active_input_builtins;
+ Bitset active_output_builtins;
+ uint32_t clip_distance_count = 0;
+ uint32_t cull_distance_count = 0;
+ bool position_invariant = false;
+
+ // Traverses all reachable opcodes and sets active_builtins to a bitmask of all builtin variables which are accessed in the shader.
+ void update_active_builtins();
+ bool has_active_builtin(spv::BuiltIn builtin, spv::StorageClass storage);
+
+ void analyze_parameter_preservation(
+ SPIRFunction &entry, const CFG &cfg,
+ const std::unordered_map<uint32_t, std::unordered_set<uint32_t>> &variable_to_blocks,
+ const std::unordered_map<uint32_t, std::unordered_set<uint32_t>> &complete_write_blocks);
+
+ // If a variable ID or parameter ID is found in this set, a sampler is actually a shadow/comparison sampler.
+ // SPIR-V does not support this distinction, so we must keep track of this information outside the type system.
+ // There might be unrelated IDs found in this set which do not correspond to actual variables.
+ // This set should only be queried for the existence of samplers which are already known to be variables or parameter IDs.
+ // Similar is implemented for images, as well as if subpass inputs are needed.
+ std::unordered_set<uint32_t> comparison_ids;
+ bool need_subpass_input = false;
+
+ // In certain backends, we will need to use a dummy sampler to be able to emit code.
+ // GLSL does not support texelFetch on texture2D objects, but SPIR-V does,
+ // so we need to workaround by having the application inject a dummy sampler.
+ uint32_t dummy_sampler_id = 0;
+
+ void analyze_image_and_sampler_usage();
+
+ struct CombinedImageSamplerDrefHandler : OpcodeHandler
+ {
+ CombinedImageSamplerDrefHandler(Compiler &compiler_)
+ : compiler(compiler_)
+ {
+ }
+ bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override;
+
+ Compiler &compiler;
+ std::unordered_set<uint32_t> dref_combined_samplers;
+ };
+
+ struct CombinedImageSamplerUsageHandler : OpcodeHandler
+ {
+ CombinedImageSamplerUsageHandler(Compiler &compiler_,
+ const std::unordered_set<uint32_t> &dref_combined_samplers_)
+ : compiler(compiler_)
+ , dref_combined_samplers(dref_combined_samplers_)
+ {
+ }
+
+ bool begin_function_scope(const uint32_t *args, uint32_t length) override;
+ bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override;
+ Compiler &compiler;
+ const std::unordered_set<uint32_t> &dref_combined_samplers;
+
+ std::unordered_map<uint32_t, std::unordered_set<uint32_t>> dependency_hierarchy;
+ std::unordered_set<uint32_t> comparison_ids;
+
+ void add_hierarchy_to_comparison_ids(uint32_t ids);
+ bool need_subpass_input = false;
+ };
+
+ void build_function_control_flow_graphs_and_analyze();
+ std::unordered_map<uint32_t, std::unique_ptr<CFG>> function_cfgs;
+ struct CFGBuilder : OpcodeHandler
+ {
+ CFGBuilder(Compiler &compiler_);
+
+ bool follow_function_call(const SPIRFunction &func) override;
+ bool handle(spv::Op op, const uint32_t *args, uint32_t length) override;
+ Compiler &compiler;
+ std::unordered_map<uint32_t, std::unique_ptr<CFG>> function_cfgs;
+ };
+
+ struct AnalyzeVariableScopeAccessHandler : OpcodeHandler
+ {
+ AnalyzeVariableScopeAccessHandler(Compiler &compiler_, SPIRFunction &entry_);
+
+ bool follow_function_call(const SPIRFunction &) override;
+ void set_current_block(const SPIRBlock &block) override;
+
+ void notify_variable_access(uint32_t id, uint32_t block);
+ bool id_is_phi_variable(uint32_t id) const;
+ bool id_is_potential_temporary(uint32_t id) const;
+ bool handle(spv::Op op, const uint32_t *args, uint32_t length) override;
+
+ Compiler &compiler;
+ SPIRFunction &entry;
+ std::unordered_map<uint32_t, std::unordered_set<uint32_t>> accessed_variables_to_block;
+ std::unordered_map<uint32_t, std::unordered_set<uint32_t>> accessed_temporaries_to_block;
+ std::unordered_map<uint32_t, uint32_t> result_id_to_type;
+ std::unordered_map<uint32_t, std::unordered_set<uint32_t>> complete_write_variables_to_block;
+ std::unordered_map<uint32_t, std::unordered_set<uint32_t>> partial_write_variables_to_block;
+ const SPIRBlock *current_block = nullptr;
+ };
+
+ struct StaticExpressionAccessHandler : OpcodeHandler
+ {
+ StaticExpressionAccessHandler(Compiler &compiler_, uint32_t variable_id_);
+ bool follow_function_call(const SPIRFunction &) override;
+ bool handle(spv::Op op, const uint32_t *args, uint32_t length) override;
+
+ Compiler &compiler;
+ uint32_t variable_id;
+ uint32_t static_expression = 0;
+ uint32_t write_count = 0;
+ };
+
+ void analyze_variable_scope(SPIRFunction &function, AnalyzeVariableScopeAccessHandler &handler);
+ void find_function_local_luts(SPIRFunction &function, const AnalyzeVariableScopeAccessHandler &handler,
+ bool single_function);
+
+ void make_constant_null(uint32_t id, uint32_t type);
+
+ std::unordered_map<uint32_t, std::string> declared_block_names;
+
+ bool instruction_to_result_type(uint32_t &result_type, uint32_t &result_id, spv::Op op, const uint32_t *args,
+ uint32_t length);
+
+ Bitset combined_decoration_for_member(const SPIRType &type, uint32_t index) const;
+ static bool is_desktop_only_format(spv::ImageFormat format);
+
+ bool image_is_comparison(const SPIRType &type, uint32_t id) const;
+
+ void set_extended_decoration(uint32_t id, ExtendedDecorations decoration, uint32_t value = 0);
+ uint32_t get_extended_decoration(uint32_t id, ExtendedDecorations decoration) const;
+ bool has_extended_decoration(uint32_t id, ExtendedDecorations decoration) const;
+ void unset_extended_decoration(uint32_t id, ExtendedDecorations decoration);
+
+ void set_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration,
+ uint32_t value = 0);
+ uint32_t get_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const;
+ bool has_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const;
+ void unset_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration);
+
+private:
+ // Used only to implement the old deprecated get_entry_point() interface.
+ const SPIREntryPoint &get_first_entry_point(const std::string &name) const;
+ SPIREntryPoint &get_first_entry_point(const std::string &name);
+
+ void fixup_type_alias();
+ bool type_is_block_like(const SPIRType &type) const;
+ bool type_is_opaque_value(const SPIRType &type) const;
+};
+} // namespace spirv_cross
+
+#endif
diff --git a/src/3rdparty/SPIRV-Cross/spirv_cross_c.cpp b/src/3rdparty/SPIRV-Cross/spirv_cross_c.cpp
new file mode 100644
index 0000000..f41d216
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_cross_c.cpp
@@ -0,0 +1,1864 @@
+/*
+ * Copyright 2019 Hans-Kristian Arntzen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "spirv_cross_c.h"
+
+#if SPIRV_CROSS_C_API_CPP
+#include "spirv_cpp.hpp"
+#endif
+#if SPIRV_CROSS_C_API_GLSL
+#include "spirv_glsl.hpp"
+#else
+#include "spirv_cross.hpp"
+#endif
+#if SPIRV_CROSS_C_API_HLSL
+#include "spirv_hlsl.hpp"
+#endif
+#if SPIRV_CROSS_C_API_MSL
+#include "spirv_msl.hpp"
+#endif
+#if SPIRV_CROSS_C_API_REFLECT
+#include "spirv_reflect.hpp"
+#endif
+#include "spirv_parser.hpp"
+#include <string.h>
+#include <memory>
+#include <new>
+
+// clang-format off
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4996)
+#endif
+
+#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
+#define SPVC_BEGIN_SAFE_SCOPE try
+#else
+#define SPVC_BEGIN_SAFE_SCOPE
+#endif
+
+#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
+#define SPVC_END_SAFE_SCOPE(context, error) \
+ catch (const std::exception &e) \
+ { \
+ (context)->report_error(e.what()); \
+ return (error); \
+ }
+#else
+#define SPVC_END_SAFE_SCOPE(context, error)
+#endif
+
+using namespace std;
+using namespace spirv_cross;
+
+struct ScratchMemoryAllocation
+{
+ virtual ~ScratchMemoryAllocation() = default;
+};
+
+struct StringAllocation : ScratchMemoryAllocation
+{
+ explicit StringAllocation(const char *name)
+ : str(name)
+ {
+ }
+
+ explicit StringAllocation(std::string name)
+ : str(std::move(name))
+ {
+ }
+
+ std::string str;
+};
+
+template <typename T>
+struct TemporaryBuffer : ScratchMemoryAllocation
+{
+ std::vector<T> buffer;
+};
+
+template <typename T, typename... Ts>
+static inline std::unique_ptr<T> spvc_allocate(Ts &&... ts)
+{
+ return std::unique_ptr<T>(new T(std::forward<Ts>(ts)...));
+}
+
+struct spvc_context_s
+{
+ string last_error;
+ vector<unique_ptr<ScratchMemoryAllocation>> allocations;
+ const char *allocate_name(const std::string &name);
+
+ spvc_error_callback callback = nullptr;
+ void *callback_userdata = nullptr;
+ void report_error(std::string msg);
+};
+
+void spvc_context_s::report_error(std::string msg)
+{
+ last_error = std::move(msg);
+ if (callback)
+ callback(callback_userdata, last_error.c_str());
+}
+
+const char *spvc_context_s::allocate_name(const std::string &name)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ auto alloc = spvc_allocate<StringAllocation>(name);
+ auto *ret = alloc->str.c_str();
+ allocations.emplace_back(std::move(alloc));
+ return ret;
+ }
+ SPVC_END_SAFE_SCOPE(this, nullptr)
+}
+
+struct spvc_parsed_ir_s : ScratchMemoryAllocation
+{
+ spvc_context context = nullptr;
+ ParsedIR parsed;
+};
+
+struct spvc_compiler_s : ScratchMemoryAllocation
+{
+ spvc_context context = nullptr;
+ unique_ptr<Compiler> compiler;
+ spvc_backend backend = SPVC_BACKEND_NONE;
+};
+
+struct spvc_compiler_options_s : ScratchMemoryAllocation
+{
+ spvc_context context = nullptr;
+ uint32_t backend_flags = 0;
+#if SPIRV_CROSS_C_API_GLSL
+ CompilerGLSL::Options glsl;
+#endif
+#if SPIRV_CROSS_C_API_MSL
+ CompilerMSL::Options msl;
+#endif
+#if SPIRV_CROSS_C_API_HLSL
+ CompilerHLSL::Options hlsl;
+#endif
+};
+
+struct spvc_set_s : ScratchMemoryAllocation
+{
+ std::unordered_set<uint32_t> set;
+};
+
+// Dummy-inherit to we can keep our opaque type handle type safe in C-land as well,
+// and avoid just throwing void * around.
+struct spvc_type_s : SPIRType
+{
+};
+
+struct spvc_constant_s : SPIRConstant
+{
+};
+
+struct spvc_resources_s : ScratchMemoryAllocation
+{
+ spvc_context context = nullptr;
+ std::vector<spvc_reflected_resource> uniform_buffers;
+ std::vector<spvc_reflected_resource> storage_buffers;
+ std::vector<spvc_reflected_resource> stage_inputs;
+ std::vector<spvc_reflected_resource> stage_outputs;
+ std::vector<spvc_reflected_resource> subpass_inputs;
+ std::vector<spvc_reflected_resource> storage_images;
+ std::vector<spvc_reflected_resource> sampled_images;
+ std::vector<spvc_reflected_resource> atomic_counters;
+ std::vector<spvc_reflected_resource> push_constant_buffers;
+ std::vector<spvc_reflected_resource> separate_images;
+ std::vector<spvc_reflected_resource> separate_samplers;
+ std::vector<spvc_reflected_resource> acceleration_structures;
+
+ bool copy_resources(std::vector<spvc_reflected_resource> &outputs, const std::vector<Resource> &inputs);
+ bool copy_resources(const ShaderResources &resources);
+};
+
+spvc_result spvc_context_create(spvc_context *context)
+{
+ auto *ctx = new (std::nothrow) spvc_context_s;
+ if (!ctx)
+ return SPVC_ERROR_OUT_OF_MEMORY;
+
+ *context = ctx;
+ return SPVC_SUCCESS;
+}
+
+void spvc_context_destroy(spvc_context context)
+{
+ delete context;
+}
+
+void spvc_context_release_allocations(spvc_context context)
+{
+ context->allocations.clear();
+}
+
+const char *spvc_context_get_last_error_string(spvc_context context)
+{
+ return context->last_error.c_str();
+}
+
+SPVC_PUBLIC_API void spvc_context_set_error_callback(spvc_context context, spvc_error_callback cb, void *userdata)
+{
+ context->callback = cb;
+ context->callback_userdata = userdata;
+}
+
+spvc_result spvc_context_parse_spirv(spvc_context context, const SpvId *spirv, size_t word_count,
+ spvc_parsed_ir *parsed_ir)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ std::unique_ptr<spvc_parsed_ir_s> pir(new (std::nothrow) spvc_parsed_ir_s);
+ if (!pir)
+ {
+ context->report_error("Out of memory.");
+ return SPVC_ERROR_OUT_OF_MEMORY;
+ }
+
+ pir->context = context;
+ Parser parser(spirv, word_count);
+ parser.parse();
+ pir->parsed = move(parser.get_parsed_ir());
+ *parsed_ir = pir.get();
+ context->allocations.push_back(std::move(pir));
+ }
+ SPVC_END_SAFE_SCOPE(context, SPVC_ERROR_INVALID_SPIRV)
+ return SPVC_SUCCESS;
+}
+
+spvc_result spvc_context_create_compiler(spvc_context context, spvc_backend backend, spvc_parsed_ir parsed_ir,
+ spvc_capture_mode mode, spvc_compiler *compiler)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ std::unique_ptr<spvc_compiler_s> comp(new (std::nothrow) spvc_compiler_s);
+ if (!comp)
+ {
+ context->report_error("Out of memory.");
+ return SPVC_ERROR_OUT_OF_MEMORY;
+ }
+ comp->backend = backend;
+ comp->context = context;
+
+ if (mode != SPVC_CAPTURE_MODE_COPY && mode != SPVC_CAPTURE_MODE_TAKE_OWNERSHIP)
+ {
+ context->report_error("Invalid argument for capture mode.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+ }
+
+ switch (backend)
+ {
+ case SPVC_BACKEND_NONE:
+ if (mode == SPVC_CAPTURE_MODE_TAKE_OWNERSHIP)
+ comp->compiler.reset(new Compiler(move(parsed_ir->parsed)));
+ else if (mode == SPVC_CAPTURE_MODE_COPY)
+ comp->compiler.reset(new Compiler(parsed_ir->parsed));
+ break;
+
+#if SPIRV_CROSS_C_API_GLSL
+ case SPVC_BACKEND_GLSL:
+ if (mode == SPVC_CAPTURE_MODE_TAKE_OWNERSHIP)
+ comp->compiler.reset(new CompilerGLSL(move(parsed_ir->parsed)));
+ else if (mode == SPVC_CAPTURE_MODE_COPY)
+ comp->compiler.reset(new CompilerGLSL(parsed_ir->parsed));
+ break;
+#endif
+
+#if SPIRV_CROSS_C_API_HLSL
+ case SPVC_BACKEND_HLSL:
+ if (mode == SPVC_CAPTURE_MODE_TAKE_OWNERSHIP)
+ comp->compiler.reset(new CompilerHLSL(move(parsed_ir->parsed)));
+ else if (mode == SPVC_CAPTURE_MODE_COPY)
+ comp->compiler.reset(new CompilerHLSL(parsed_ir->parsed));
+ break;
+#endif
+
+#if SPIRV_CROSS_C_API_MSL
+ case SPVC_BACKEND_MSL:
+ if (mode == SPVC_CAPTURE_MODE_TAKE_OWNERSHIP)
+ comp->compiler.reset(new CompilerMSL(move(parsed_ir->parsed)));
+ else if (mode == SPVC_CAPTURE_MODE_COPY)
+ comp->compiler.reset(new CompilerMSL(parsed_ir->parsed));
+ break;
+#endif
+
+#if SPIRV_CROSS_C_API_CPP
+ case SPVC_BACKEND_CPP:
+ if (mode == SPVC_CAPTURE_MODE_TAKE_OWNERSHIP)
+ comp->compiler.reset(new CompilerCPP(move(parsed_ir->parsed)));
+ else if (mode == SPVC_CAPTURE_MODE_COPY)
+ comp->compiler.reset(new CompilerCPP(parsed_ir->parsed));
+ break;
+#endif
+
+#if SPIRV_CROSS_C_API_REFLECT
+ case SPVC_BACKEND_JSON:
+ if (mode == SPVC_CAPTURE_MODE_TAKE_OWNERSHIP)
+ comp->compiler.reset(new CompilerReflection(move(parsed_ir->parsed)));
+ else if (mode == SPVC_CAPTURE_MODE_COPY)
+ comp->compiler.reset(new CompilerReflection(parsed_ir->parsed));
+ break;
+#endif
+
+ default:
+ context->report_error("Invalid backend.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+ }
+
+ *compiler = comp.get();
+ context->allocations.push_back(std::move(comp));
+ }
+ SPVC_END_SAFE_SCOPE(context, SPVC_ERROR_OUT_OF_MEMORY)
+ return SPVC_SUCCESS;
+}
+
+spvc_result spvc_compiler_create_compiler_options(spvc_compiler compiler, spvc_compiler_options *options)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ std::unique_ptr<spvc_compiler_options_s> opt(new (std::nothrow) spvc_compiler_options_s);
+ if (!opt)
+ {
+ compiler->context->report_error("Out of memory.");
+ return SPVC_ERROR_OUT_OF_MEMORY;
+ }
+
+ opt->context = compiler->context;
+ opt->backend_flags = 0;
+ switch (compiler->backend)
+ {
+#if SPIRV_CROSS_C_API_MSL
+ case SPVC_BACKEND_MSL:
+ opt->backend_flags |= SPVC_COMPILER_OPTION_MSL_BIT | SPVC_COMPILER_OPTION_COMMON_BIT;
+ opt->glsl = static_cast<CompilerMSL *>(compiler->compiler.get())->get_common_options();
+ opt->msl = static_cast<CompilerMSL *>(compiler->compiler.get())->get_msl_options();
+ break;
+#endif
+
+#if SPIRV_CROSS_C_API_HLSL
+ case SPVC_BACKEND_HLSL:
+ opt->backend_flags |= SPVC_COMPILER_OPTION_HLSL_BIT | SPVC_COMPILER_OPTION_COMMON_BIT;
+ opt->glsl = static_cast<CompilerHLSL *>(compiler->compiler.get())->get_common_options();
+ opt->hlsl = static_cast<CompilerHLSL *>(compiler->compiler.get())->get_hlsl_options();
+ break;
+#endif
+
+#if SPIRV_CROSS_C_API_GLSL
+ case SPVC_BACKEND_GLSL:
+ opt->backend_flags |= SPVC_COMPILER_OPTION_GLSL_BIT | SPVC_COMPILER_OPTION_COMMON_BIT;
+ opt->glsl = static_cast<CompilerGLSL *>(compiler->compiler.get())->get_common_options();
+ break;
+#endif
+
+ default:
+ break;
+ }
+
+ *options = opt.get();
+ compiler->context->allocations.push_back(std::move(opt));
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY)
+ return SPVC_SUCCESS;
+}
+
+spvc_result spvc_compiler_options_set_bool(spvc_compiler_options options, spvc_compiler_option option,
+ spvc_bool value)
+{
+ return spvc_compiler_options_set_uint(options, option, value ? 1 : 0);
+}
+
+spvc_result spvc_compiler_options_set_uint(spvc_compiler_options options, spvc_compiler_option option, unsigned value)
+{
+ (void)value;
+ (void)option;
+ uint32_t supported_mask = options->backend_flags;
+ uint32_t required_mask = option & SPVC_COMPILER_OPTION_LANG_BITS;
+ if ((required_mask | supported_mask) != supported_mask)
+ {
+ options->context->report_error("Option is not supported by current backend.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+ }
+
+ switch (option)
+ {
+#if SPIRV_CROSS_C_API_GLSL
+ case SPVC_COMPILER_OPTION_FORCE_TEMPORARY:
+ options->glsl.force_temporary = value != 0;
+ break;
+ case SPVC_COMPILER_OPTION_FLATTEN_MULTIDIMENSIONAL_ARRAYS:
+ options->glsl.flatten_multidimensional_arrays = value != 0;
+ break;
+ case SPVC_COMPILER_OPTION_FIXUP_DEPTH_CONVENTION:
+ options->glsl.vertex.fixup_clipspace = value != 0;
+ break;
+ case SPVC_COMPILER_OPTION_FLIP_VERTEX_Y:
+ options->glsl.vertex.flip_vert_y = value != 0;
+ break;
+
+ case SPVC_COMPILER_OPTION_GLSL_SUPPORT_NONZERO_BASE_INSTANCE:
+ options->glsl.vertex.support_nonzero_base_instance = value != 0;
+ break;
+ case SPVC_COMPILER_OPTION_GLSL_SEPARATE_SHADER_OBJECTS:
+ options->glsl.separate_shader_objects = value != 0;
+ break;
+ case SPVC_COMPILER_OPTION_GLSL_ENABLE_420PACK_EXTENSION:
+ options->glsl.enable_420pack_extension = value != 0;
+ break;
+ case SPVC_COMPILER_OPTION_GLSL_VERSION:
+ options->glsl.version = value;
+ break;
+ case SPVC_COMPILER_OPTION_GLSL_ES:
+ options->glsl.es = value != 0;
+ break;
+ case SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS:
+ options->glsl.vulkan_semantics = value != 0;
+ break;
+ case SPVC_COMPILER_OPTION_GLSL_ES_DEFAULT_FLOAT_PRECISION_HIGHP:
+ options->glsl.fragment.default_float_precision =
+ value != 0 ? CompilerGLSL::Options::Precision::Highp : CompilerGLSL::Options::Precision::Mediump;
+ break;
+ case SPVC_COMPILER_OPTION_GLSL_ES_DEFAULT_INT_PRECISION_HIGHP:
+ options->glsl.fragment.default_int_precision =
+ value != 0 ? CompilerGLSL::Options::Precision::Highp : CompilerGLSL::Options::Precision::Mediump;
+ break;
+ case SPVC_COMPILER_OPTION_GLSL_EMIT_PUSH_CONSTANT_AS_UNIFORM_BUFFER:
+ options->glsl.emit_push_constant_as_uniform_buffer = value != 0;
+ break;
+#endif
+
+#if SPIRV_CROSS_C_API_HLSL
+ case SPVC_COMPILER_OPTION_HLSL_SHADER_MODEL:
+ options->hlsl.shader_model = value;
+ break;
+
+ case SPVC_COMPILER_OPTION_HLSL_POINT_SIZE_COMPAT:
+ options->hlsl.point_size_compat = value != 0;
+ break;
+
+ case SPVC_COMPILER_OPTION_HLSL_POINT_COORD_COMPAT:
+ options->hlsl.point_coord_compat = value != 0;
+ break;
+
+ case SPVC_COMPILER_OPTION_HLSL_SUPPORT_NONZERO_BASE_VERTEX_BASE_INSTANCE:
+ options->hlsl.support_nonzero_base_vertex_base_instance = value != 0;
+ break;
+#endif
+
+#if SPIRV_CROSS_C_API_MSL
+ case SPVC_COMPILER_OPTION_MSL_VERSION:
+ options->msl.msl_version = value;
+ break;
+
+ case SPVC_COMPILER_OPTION_MSL_TEXEL_BUFFER_TEXTURE_WIDTH:
+ options->msl.texel_buffer_texture_width = value;
+ break;
+
+ case SPVC_COMPILER_OPTION_MSL_AUX_BUFFER_INDEX:
+ options->msl.aux_buffer_index = value;
+ break;
+
+ case SPVC_COMPILER_OPTION_MSL_INDIRECT_PARAMS_BUFFER_INDEX:
+ options->msl.indirect_params_buffer_index = value;
+ break;
+
+ case SPVC_COMPILER_OPTION_MSL_SHADER_OUTPUT_BUFFER_INDEX:
+ options->msl.shader_output_buffer_index = value;
+ break;
+
+ case SPVC_COMPILER_OPTION_MSL_SHADER_PATCH_OUTPUT_BUFFER_INDEX:
+ options->msl.shader_patch_output_buffer_index = value;
+ break;
+
+ case SPVC_COMPILER_OPTION_MSL_SHADER_TESS_FACTOR_OUTPUT_BUFFER_INDEX:
+ options->msl.shader_tess_factor_buffer_index = value;
+ break;
+
+ case SPVC_COMPILER_OPTION_MSL_SHADER_INPUT_WORKGROUP_INDEX:
+ options->msl.shader_input_wg_index = value;
+ break;
+
+ case SPVC_COMPILER_OPTION_MSL_ENABLE_POINT_SIZE_BUILTIN:
+ options->msl.enable_point_size_builtin = value != 0;
+ break;
+
+ case SPVC_COMPILER_OPTION_MSL_DISABLE_RASTERIZATION:
+ options->msl.disable_rasterization = value != 0;
+ break;
+
+ case SPVC_COMPILER_OPTION_MSL_CAPTURE_OUTPUT_TO_BUFFER:
+ options->msl.capture_output_to_buffer = value != 0;
+ break;
+
+ case SPVC_COMPILER_OPTION_MSL_SWIZZLE_TEXTURE_SAMPLES:
+ options->msl.swizzle_texture_samples = value != 0;
+ break;
+
+ case SPVC_COMPILER_OPTION_MSL_PAD_FRAGMENT_OUTPUT_COMPONENTS:
+ options->msl.pad_fragment_output_components = value != 0;
+ break;
+
+ case SPVC_COMPILER_OPTION_MSL_TESS_DOMAIN_ORIGIN_LOWER_LEFT:
+ options->msl.tess_domain_origin_lower_left = value != 0;
+ break;
+
+ case SPVC_COMPILER_OPTION_MSL_PLATFORM:
+ options->msl.platform = static_cast<CompilerMSL::Options::Platform>(value);
+ break;
+
+ case SPVC_COMPILER_OPTION_MSL_ARGUMENT_BUFFERS:
+ options->msl.argument_buffers = value != 0;
+ break;
+#endif
+
+ default:
+ options->context->report_error("Unknown option.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+ }
+
+ return SPVC_SUCCESS;
+}
+
+spvc_result spvc_compiler_install_compiler_options(spvc_compiler compiler, spvc_compiler_options options)
+{
+ (void)options;
+ switch (compiler->backend)
+ {
+#if SPIRV_CROSS_C_API_GLSL
+ case SPVC_BACKEND_GLSL:
+ static_cast<CompilerGLSL &>(*compiler->compiler).set_common_options(options->glsl);
+ break;
+#endif
+
+#if SPIRV_CROSS_C_API_HLSL
+ case SPVC_BACKEND_HLSL:
+ static_cast<CompilerHLSL &>(*compiler->compiler).set_common_options(options->glsl);
+ static_cast<CompilerHLSL &>(*compiler->compiler).set_hlsl_options(options->hlsl);
+ break;
+#endif
+
+#if SPIRV_CROSS_C_API_MSL
+ case SPVC_BACKEND_MSL:
+ static_cast<CompilerMSL &>(*compiler->compiler).set_common_options(options->glsl);
+ static_cast<CompilerMSL &>(*compiler->compiler).set_msl_options(options->msl);
+ break;
+#endif
+
+ default:
+ break;
+ }
+
+ return SPVC_SUCCESS;
+}
+
+spvc_result spvc_compiler_add_header_line(spvc_compiler compiler, const char *line)
+{
+#if SPIRV_CROSS_C_API_GLSL
+ if (compiler->backend == SPVC_BACKEND_NONE)
+ {
+ compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+ }
+
+ static_cast<CompilerGLSL *>(compiler->compiler.get())->add_header_line(line);
+ return SPVC_SUCCESS;
+#else
+ (void)line;
+ compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+#endif
+}
+
+spvc_result spvc_compiler_require_extension(spvc_compiler compiler, const char *line)
+{
+#if SPIRV_CROSS_C_API_GLSL
+ if (compiler->backend == SPVC_BACKEND_NONE)
+ {
+ compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+ }
+
+ static_cast<CompilerGLSL *>(compiler->compiler.get())->require_extension(line);
+ return SPVC_SUCCESS;
+#else
+ (void)line;
+ compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+#endif
+}
+
+spvc_result spvc_compiler_flatten_buffer_block(spvc_compiler compiler, spvc_variable_id id)
+{
+#if SPIRV_CROSS_C_API_GLSL
+ if (compiler->backend == SPVC_BACKEND_NONE)
+ {
+ compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+ }
+
+ static_cast<CompilerGLSL *>(compiler->compiler.get())->flatten_buffer_block(id);
+ return SPVC_SUCCESS;
+#else
+ (void)id;
+ compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+#endif
+}
+
+spvc_result spvc_compiler_hlsl_set_root_constants_layout(spvc_compiler compiler,
+ const spvc_hlsl_root_constants *constant_info,
+ size_t count)
+{
+#if SPIRV_CROSS_C_API_HLSL
+ if (compiler->backend != SPVC_BACKEND_HLSL)
+ {
+ compiler->context->report_error("HLSL function used on a non-HLSL backend.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+ }
+
+ auto &hlsl = *static_cast<CompilerHLSL *>(compiler->compiler.get());
+ std::vector<RootConstants> roots;
+ roots.reserve(count);
+ for (size_t i = 0; i < count; i++)
+ {
+ RootConstants root;
+ root.binding = constant_info[i].binding;
+ root.space = constant_info[i].space;
+ root.start = constant_info[i].start;
+ root.end = constant_info[i].end;
+ roots.push_back(root);
+ }
+
+ hlsl.set_root_constant_layouts(std::move(roots));
+ return SPVC_SUCCESS;
+#else
+ (void)constant_info;
+ (void)count;
+ compiler->context->report_error("HLSL function used on a non-HLSL backend.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+#endif
+}
+
+spvc_result spvc_compiler_hlsl_add_vertex_attribute_remap(spvc_compiler compiler,
+ const spvc_hlsl_vertex_attribute_remap *remap,
+ size_t count)
+{
+#if SPIRV_CROSS_C_API_HLSL
+ if (compiler->backend != SPVC_BACKEND_HLSL)
+ {
+ compiler->context->report_error("HLSL function used on a non-HLSL backend.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+ }
+
+ HLSLVertexAttributeRemap re;
+ auto &hlsl = *static_cast<CompilerHLSL *>(compiler->compiler.get());
+ for (size_t i = 0; i < count; i++)
+ {
+ re.location = remap[i].location;
+ re.semantic = remap[i].semantic;
+ hlsl.add_vertex_attribute_remap(re);
+ }
+
+ return SPVC_SUCCESS;
+#else
+ (void)remap;
+ (void)count;
+ compiler->context->report_error("HLSL function used on a non-HLSL backend.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+#endif
+}
+
+spvc_variable_id spvc_compiler_hlsl_remap_num_workgroups_builtin(spvc_compiler compiler)
+{
+#if SPIRV_CROSS_C_API_HLSL
+ if (compiler->backend != SPVC_BACKEND_HLSL)
+ {
+ compiler->context->report_error("HLSL function used on a non-HLSL backend.");
+ return 0;
+ }
+
+ auto &hlsl = *static_cast<CompilerHLSL *>(compiler->compiler.get());
+ return hlsl.remap_num_workgroups_builtin();
+#else
+ compiler->context->report_error("HLSL function used on a non-HLSL backend.");
+ return 0;
+#endif
+}
+
+spvc_bool spvc_compiler_msl_is_rasterization_disabled(spvc_compiler compiler)
+{
+#if SPIRV_CROSS_C_API_MSL
+ if (compiler->backend != SPVC_BACKEND_MSL)
+ {
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_FALSE;
+ }
+
+ auto &msl = *static_cast<CompilerMSL *>(compiler->compiler.get());
+ return msl.get_is_rasterization_disabled() ? SPVC_TRUE : SPVC_FALSE;
+#else
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_FALSE;
+#endif
+}
+
+spvc_bool spvc_compiler_msl_needs_aux_buffer(spvc_compiler compiler)
+{
+#if SPIRV_CROSS_C_API_MSL
+ if (compiler->backend != SPVC_BACKEND_MSL)
+ {
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_FALSE;
+ }
+
+ auto &msl = *static_cast<CompilerMSL *>(compiler->compiler.get());
+ return msl.needs_aux_buffer() ? SPVC_TRUE : SPVC_FALSE;
+#else
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_FALSE;
+#endif
+}
+
+spvc_bool spvc_compiler_msl_needs_output_buffer(spvc_compiler compiler)
+{
+#if SPIRV_CROSS_C_API_MSL
+ if (compiler->backend != SPVC_BACKEND_MSL)
+ {
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_FALSE;
+ }
+
+ auto &msl = *static_cast<CompilerMSL *>(compiler->compiler.get());
+ return msl.needs_output_buffer() ? SPVC_TRUE : SPVC_FALSE;
+#else
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_FALSE;
+#endif
+}
+
+spvc_bool spvc_compiler_msl_needs_patch_output_buffer(spvc_compiler compiler)
+{
+#if SPIRV_CROSS_C_API_MSL
+ if (compiler->backend != SPVC_BACKEND_MSL)
+ {
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_FALSE;
+ }
+
+ auto &msl = *static_cast<CompilerMSL *>(compiler->compiler.get());
+ return msl.needs_patch_output_buffer() ? SPVC_TRUE : SPVC_FALSE;
+#else
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_FALSE;
+#endif
+}
+
+spvc_bool spvc_compiler_msl_needs_input_threadgroup_mem(spvc_compiler compiler)
+{
+#if SPIRV_CROSS_C_API_MSL
+ if (compiler->backend != SPVC_BACKEND_MSL)
+ {
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_FALSE;
+ }
+
+ auto &msl = *static_cast<CompilerMSL *>(compiler->compiler.get());
+ return msl.needs_input_threadgroup_mem() ? SPVC_TRUE : SPVC_FALSE;
+#else
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_FALSE;
+#endif
+}
+
+spvc_result spvc_compiler_msl_add_vertex_attribute(spvc_compiler compiler, const spvc_msl_vertex_attribute *va)
+{
+#if SPIRV_CROSS_C_API_MSL
+ if (compiler->backend != SPVC_BACKEND_MSL)
+ {
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+ }
+
+ auto &msl = *static_cast<CompilerMSL *>(compiler->compiler.get());
+ MSLVertexAttr attr;
+ attr.location = va->location;
+ attr.msl_buffer = va->msl_buffer;
+ attr.msl_offset = va->msl_offset;
+ attr.msl_stride = va->msl_stride;
+ attr.format = static_cast<MSLVertexFormat>(va->format);
+ attr.builtin = static_cast<spv::BuiltIn>(va->builtin);
+ attr.per_instance = va->per_instance;
+ msl.add_msl_vertex_attribute(attr);
+ return SPVC_SUCCESS;
+#else
+ (void)va;
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+#endif
+}
+
+spvc_result spvc_compiler_msl_add_resource_binding(spvc_compiler compiler,
+ const spvc_msl_resource_binding *binding)
+{
+#if SPIRV_CROSS_C_API_MSL
+ if (compiler->backend != SPVC_BACKEND_MSL)
+ {
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+ }
+
+ auto &msl = *static_cast<CompilerMSL *>(compiler->compiler.get());
+ MSLResourceBinding bind;
+ bind.binding = binding->binding;
+ bind.desc_set = binding->desc_set;
+ bind.stage = static_cast<spv::ExecutionModel>(binding->stage);
+ bind.msl_buffer = binding->msl_buffer;
+ bind.msl_texture = binding->msl_texture;
+ bind.msl_sampler = binding->msl_sampler;
+ msl.add_msl_resource_binding(bind);
+ return SPVC_SUCCESS;
+#else
+ (void)binding;
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+#endif
+}
+
+spvc_result spvc_compiler_msl_add_discrete_descriptor_set(spvc_compiler compiler, unsigned desc_set)
+{
+#if SPIRV_CROSS_C_API_MSL
+ if (compiler->backend != SPVC_BACKEND_MSL)
+ {
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+ }
+
+ auto &msl = *static_cast<CompilerMSL *>(compiler->compiler.get());
+ msl.add_discrete_descriptor_set(desc_set);
+ return SPVC_SUCCESS;
+#else
+ (void)desc_set;
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+#endif
+}
+
+spvc_bool spvc_compiler_msl_is_vertex_attribute_used(spvc_compiler compiler, unsigned location)
+{
+#if SPIRV_CROSS_C_API_MSL
+ if (compiler->backend != SPVC_BACKEND_MSL)
+ {
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_FALSE;
+ }
+
+ auto &msl = *static_cast<CompilerMSL *>(compiler->compiler.get());
+ return msl.is_msl_vertex_attribute_used(location) ? SPVC_TRUE : SPVC_FALSE;
+#else
+ (void)location;
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_FALSE;
+#endif
+}
+
+spvc_bool spvc_compiler_msl_is_resource_used(spvc_compiler compiler, SpvExecutionModel model, unsigned set,
+ unsigned binding)
+{
+#if SPIRV_CROSS_C_API_MSL
+ if (compiler->backend != SPVC_BACKEND_MSL)
+ {
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_FALSE;
+ }
+
+ auto &msl = *static_cast<CompilerMSL *>(compiler->compiler.get());
+ return msl.is_msl_resource_binding_used(static_cast<spv::ExecutionModel>(model), set, binding) ? SPVC_TRUE :
+ SPVC_FALSE;
+#else
+ (void)model;
+ (void)set;
+ (void)binding;
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_FALSE;
+#endif
+}
+
+spvc_result spvc_compiler_msl_remap_constexpr_sampler(spvc_compiler compiler, spvc_variable_id id,
+ const spvc_msl_constexpr_sampler *sampler)
+{
+#if SPIRV_CROSS_C_API_MSL
+ if (compiler->backend != SPVC_BACKEND_MSL)
+ {
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+ }
+
+ auto &msl = *static_cast<CompilerMSL *>(compiler->compiler.get());
+ MSLConstexprSampler samp;
+ samp.s_address = static_cast<MSLSamplerAddress>(sampler->s_address);
+ samp.t_address = static_cast<MSLSamplerAddress>(sampler->t_address);
+ samp.r_address = static_cast<MSLSamplerAddress>(sampler->r_address);
+ samp.lod_clamp_min = sampler->lod_clamp_min;
+ samp.lod_clamp_max = sampler->lod_clamp_max;
+ samp.lod_clamp_enable = sampler->lod_clamp_enable;
+ samp.min_filter = static_cast<MSLSamplerFilter>(sampler->min_filter);
+ samp.mag_filter = static_cast<MSLSamplerFilter>(sampler->mag_filter);
+ samp.mip_filter = static_cast<MSLSamplerMipFilter>(sampler->mip_filter);
+ samp.compare_enable = sampler->compare_enable;
+ samp.anisotropy_enable = sampler->anisotropy_enable;
+ samp.max_anisotropy = sampler->max_anisotropy;
+ samp.compare_func = static_cast<MSLSamplerCompareFunc>(sampler->compare_func);
+ samp.coord = static_cast<MSLSamplerCoord>(sampler->coord);
+ samp.border_color = static_cast<MSLSamplerBorderColor>(sampler->border_color);
+ msl.remap_constexpr_sampler(id, samp);
+ return SPVC_SUCCESS;
+#else
+ (void)id;
+ (void)sampler;
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+#endif
+}
+
+spvc_result spvc_compiler_msl_set_fragment_output_components(spvc_compiler compiler, unsigned location,
+ unsigned components)
+{
+#if SPIRV_CROSS_C_API_MSL
+ if (compiler->backend != SPVC_BACKEND_MSL)
+ {
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+ }
+
+ auto &msl = *static_cast<CompilerMSL *>(compiler->compiler.get());
+ msl.set_fragment_output_components(location, components);
+ return SPVC_SUCCESS;
+#else
+ (void)location;
+ (void)components;
+ compiler->context->report_error("MSL function used on a non-MSL backend.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+#endif
+}
+
+spvc_result spvc_compiler_compile(spvc_compiler compiler, const char **source)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ auto result = compiler->compiler->compile();
+ if (result.empty())
+ {
+ compiler->context->report_error("Unsupported SPIR-V.");
+ return SPVC_ERROR_UNSUPPORTED_SPIRV;
+ }
+
+ *source = compiler->context->allocate_name(result);
+ if (!*source)
+ {
+ compiler->context->report_error("Out of memory.");
+ return SPVC_ERROR_OUT_OF_MEMORY;
+ }
+ return SPVC_SUCCESS;
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_UNSUPPORTED_SPIRV)
+}
+
+bool spvc_resources_s::copy_resources(std::vector<spvc_reflected_resource> &outputs,
+ const std::vector<Resource> &inputs)
+{
+ for (auto &i : inputs)
+ {
+ spvc_reflected_resource r;
+ r.base_type_id = i.base_type_id;
+ r.type_id = i.type_id;
+ r.id = i.id;
+ r.name = context->allocate_name(i.name);
+ if (!r.name)
+ return false;
+
+ outputs.push_back(r);
+ }
+
+ return true;
+}
+
+bool spvc_resources_s::copy_resources(const ShaderResources &resources)
+{
+ if (!copy_resources(uniform_buffers, resources.uniform_buffers))
+ return false;
+ if (!copy_resources(storage_buffers, resources.storage_buffers))
+ return false;
+ if (!copy_resources(stage_inputs, resources.stage_inputs))
+ return false;
+ if (!copy_resources(stage_outputs, resources.stage_outputs))
+ return false;
+ if (!copy_resources(subpass_inputs, resources.subpass_inputs))
+ return false;
+ if (!copy_resources(storage_images, resources.storage_images))
+ return false;
+ if (!copy_resources(sampled_images, resources.sampled_images))
+ return false;
+ if (!copy_resources(atomic_counters, resources.atomic_counters))
+ return false;
+ if (!copy_resources(push_constant_buffers, resources.push_constant_buffers))
+ return false;
+ if (!copy_resources(separate_images, resources.separate_images))
+ return false;
+ if (!copy_resources(separate_samplers, resources.separate_samplers))
+ return false;
+ if (!copy_resources(acceleration_structures, resources.acceleration_structures))
+ return false;
+
+ return true;
+}
+
+spvc_result spvc_compiler_get_active_interface_variables(spvc_compiler compiler, spvc_set *set)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ std::unique_ptr<spvc_set_s> ptr(new (std::nothrow) spvc_set_s);
+ if (!ptr)
+ {
+ compiler->context->report_error("Out of memory.");
+ return SPVC_ERROR_OUT_OF_MEMORY;
+ }
+
+ auto active = compiler->compiler->get_active_interface_variables();
+ ptr->set = std::move(active);
+ *set = ptr.get();
+ compiler->context->allocations.push_back(std::move(ptr));
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT)
+ return SPVC_SUCCESS;
+}
+
+spvc_result spvc_compiler_set_enabled_interface_variables(spvc_compiler compiler, spvc_set set)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ compiler->compiler->set_enabled_interface_variables(set->set);
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT)
+ return SPVC_SUCCESS;
+}
+
+spvc_result spvc_compiler_create_shader_resources_for_active_variables(spvc_compiler compiler, spvc_resources *resources,
+ spvc_set set)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ std::unique_ptr<spvc_resources_s> res(new (std::nothrow) spvc_resources_s);
+ if (!res)
+ {
+ compiler->context->report_error("Out of memory.");
+ return SPVC_ERROR_OUT_OF_MEMORY;
+ }
+
+ res->context = compiler->context;
+ auto accessed_resources = compiler->compiler->get_shader_resources(set->set);
+
+ if (!res->copy_resources(accessed_resources))
+ {
+ res->context->report_error("Out of memory.");
+ return SPVC_ERROR_OUT_OF_MEMORY;
+ }
+ *resources = res.get();
+ compiler->context->allocations.push_back(std::move(res));
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY)
+ return SPVC_SUCCESS;
+}
+
+spvc_result spvc_compiler_create_shader_resources(spvc_compiler compiler, spvc_resources *resources)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ std::unique_ptr<spvc_resources_s> res(new (std::nothrow) spvc_resources_s);
+ if (!res)
+ {
+ compiler->context->report_error("Out of memory.");
+ return SPVC_ERROR_OUT_OF_MEMORY;
+ }
+
+ res->context = compiler->context;
+ auto accessed_resources = compiler->compiler->get_shader_resources();
+
+ if (!res->copy_resources(accessed_resources))
+ {
+ res->context->report_error("Out of memory.");
+ return SPVC_ERROR_OUT_OF_MEMORY;
+ }
+
+ *resources = res.get();
+ compiler->context->allocations.push_back(std::move(res));
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY)
+ return SPVC_SUCCESS;
+}
+
+spvc_result spvc_resources_get_resource_list_for_type(spvc_resources resources, spvc_resource_type type,
+ const spvc_reflected_resource **resource_list,
+ size_t *resource_size)
+{
+ const std::vector<spvc_reflected_resource> *list = nullptr;
+ switch (type)
+ {
+ case SPVC_RESOURCE_TYPE_UNIFORM_BUFFER:
+ list = &resources->uniform_buffers;
+ break;
+
+ case SPVC_RESOURCE_TYPE_STORAGE_BUFFER:
+ list = &resources->storage_buffers;
+ break;
+
+ case SPVC_RESOURCE_TYPE_STAGE_INPUT:
+ list = &resources->stage_inputs;
+ break;
+
+ case SPVC_RESOURCE_TYPE_STAGE_OUTPUT:
+ list = &resources->stage_outputs;
+ break;
+
+ case SPVC_RESOURCE_TYPE_SUBPASS_INPUT:
+ list = &resources->subpass_inputs;
+ break;
+
+ case SPVC_RESOURCE_TYPE_STORAGE_IMAGE:
+ list = &resources->storage_images;
+ break;
+
+ case SPVC_RESOURCE_TYPE_SAMPLED_IMAGE:
+ list = &resources->sampled_images;
+ break;
+
+ case SPVC_RESOURCE_TYPE_ATOMIC_COUNTER:
+ list = &resources->atomic_counters;
+ break;
+
+ case SPVC_RESOURCE_TYPE_PUSH_CONSTANT:
+ list = &resources->push_constant_buffers;
+ break;
+
+ case SPVC_RESOURCE_TYPE_SEPARATE_IMAGE:
+ list = &resources->separate_images;
+ break;
+
+ case SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS:
+ list = &resources->separate_samplers;
+ break;
+
+ case SPVC_RESOURCE_TYPE_ACCELERATION_STRUCTURE:
+ list = &resources->acceleration_structures;
+ break;
+
+ default:
+ break;
+ }
+
+ if (!list)
+ {
+ resources->context->report_error("Invalid argument.");
+ return SPVC_ERROR_INVALID_ARGUMENT;
+ }
+
+ *resource_size = list->size();
+ *resource_list = list->data();
+ return SPVC_SUCCESS;
+}
+
+void spvc_compiler_set_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration, unsigned argument)
+{
+ compiler->compiler->set_decoration(id, static_cast<spv::Decoration>(decoration), argument);
+}
+
+void spvc_compiler_set_decoration_string(spvc_compiler compiler, SpvId id, SpvDecoration decoration,
+ const char *argument)
+{
+ compiler->compiler->set_decoration_string(id, static_cast<spv::Decoration>(decoration), argument);
+}
+
+void spvc_compiler_set_name(spvc_compiler compiler, SpvId id, const char *argument)
+{
+ compiler->compiler->set_name(id, argument);
+}
+
+void spvc_compiler_set_member_decoration(spvc_compiler compiler, spvc_type_id id, unsigned member_index,
+ SpvDecoration decoration, unsigned argument)
+{
+ compiler->compiler->set_member_decoration(id, member_index, static_cast<spv::Decoration>(decoration), argument);
+}
+
+void spvc_compiler_set_member_decoration_string(spvc_compiler compiler, spvc_type_id id, unsigned member_index,
+ SpvDecoration decoration, const char *argument)
+{
+ compiler->compiler->set_member_decoration_string(id, member_index, static_cast<spv::Decoration>(decoration),
+ argument);
+}
+
+void spvc_compiler_set_member_name(spvc_compiler compiler, spvc_type_id id, unsigned member_index, const char *argument)
+{
+ compiler->compiler->set_member_name(id, member_index, argument);
+}
+
+void spvc_compiler_unset_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration)
+{
+ compiler->compiler->unset_decoration(id, static_cast<spv::Decoration>(decoration));
+}
+
+void spvc_compiler_unset_member_decoration(spvc_compiler compiler, spvc_type_id id, unsigned member_index,
+ SpvDecoration decoration)
+{
+ compiler->compiler->unset_member_decoration(id, member_index, static_cast<spv::Decoration>(decoration));
+}
+
+spvc_bool spvc_compiler_has_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration)
+{
+ return compiler->compiler->has_decoration(id, static_cast<spv::Decoration>(decoration)) ? SPVC_TRUE : SPVC_FALSE;
+}
+
+spvc_bool spvc_compiler_has_member_decoration(spvc_compiler compiler, spvc_type_id id, unsigned member_index,
+ SpvDecoration decoration)
+{
+ return compiler->compiler->has_member_decoration(id, member_index, static_cast<spv::Decoration>(decoration)) ?
+ SPVC_TRUE :
+ SPVC_FALSE;
+}
+
+const char *spvc_compiler_get_name(spvc_compiler compiler, SpvId id)
+{
+ return compiler->compiler->get_name(id).c_str();
+}
+
+unsigned spvc_compiler_get_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration)
+{
+ return compiler->compiler->get_decoration(id, static_cast<spv::Decoration>(decoration));
+}
+
+const char *spvc_compiler_get_decoration_string(spvc_compiler compiler, SpvId id, SpvDecoration decoration)
+{
+ return compiler->compiler->get_decoration_string(id, static_cast<spv::Decoration>(decoration)).c_str();
+}
+
+unsigned spvc_compiler_get_member_decoration(spvc_compiler compiler, spvc_type_id id, unsigned member_index,
+ SpvDecoration decoration)
+{
+ return compiler->compiler->get_member_decoration(id, member_index, static_cast<spv::Decoration>(decoration));
+}
+
+const char *spvc_compiler_get_member_decoration_string(spvc_compiler compiler, spvc_type_id id, unsigned member_index,
+ SpvDecoration decoration)
+{
+ return compiler->compiler->get_member_decoration_string(id, member_index, static_cast<spv::Decoration>(decoration))
+ .c_str();
+}
+
+spvc_result spvc_compiler_get_entry_points(spvc_compiler compiler, const spvc_entry_point **entry_points,
+ size_t *num_entry_points)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ auto entries = compiler->compiler->get_entry_points_and_stages();
+ std::vector<spvc_entry_point> translated;
+ translated.reserve(entries.size());
+
+ for (auto &entry : entries)
+ {
+ spvc_entry_point new_entry;
+ new_entry.execution_model = static_cast<SpvExecutionModel>(entry.execution_model);
+ new_entry.name = compiler->context->allocate_name(entry.name);
+ if (!new_entry.name)
+ {
+ compiler->context->report_error("Out of memory.");
+ return SPVC_ERROR_OUT_OF_MEMORY;
+ }
+ translated.push_back(new_entry);
+ }
+
+ auto ptr = spvc_allocate<TemporaryBuffer<spvc_entry_point>>();
+ ptr->buffer = std::move(translated);
+ *entry_points = ptr->buffer.data();
+ *num_entry_points = ptr->buffer.size();
+ compiler->context->allocations.push_back(std::move(ptr));
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY)
+ return SPVC_SUCCESS;
+}
+
+spvc_result spvc_compiler_set_entry_point(spvc_compiler compiler, const char *name, SpvExecutionModel model)
+{
+ compiler->compiler->set_entry_point(name, static_cast<spv::ExecutionModel>(model));
+ return SPVC_SUCCESS;
+}
+
+spvc_result spvc_compiler_rename_entry_point(spvc_compiler compiler, const char *old_name, const char *new_name,
+ SpvExecutionModel model)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ compiler->compiler->rename_entry_point(old_name, new_name, static_cast<spv::ExecutionModel>(model));
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT)
+ return SPVC_SUCCESS;
+}
+
+const char *spvc_compiler_get_cleansed_entry_point_name(spvc_compiler compiler, const char *name,
+ SpvExecutionModel model)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ auto cleansed_name =
+ compiler->compiler->get_cleansed_entry_point_name(name, static_cast<spv::ExecutionModel>(model));
+ return compiler->context->allocate_name(cleansed_name);
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, nullptr)
+}
+
+void spvc_compiler_set_execution_mode(spvc_compiler compiler, SpvExecutionMode mode)
+{
+ compiler->compiler->set_execution_mode(static_cast<spv::ExecutionMode>(mode));
+}
+
+void spvc_compiler_set_execution_mode_with_arguments(spvc_compiler compiler, SpvExecutionMode mode, unsigned arg0,
+ unsigned arg1,
+ unsigned arg2)
+{
+ compiler->compiler->set_execution_mode(static_cast<spv::ExecutionMode>(mode), arg0, arg1, arg2);
+}
+
+void spvc_compiler_unset_execution_mode(spvc_compiler compiler, SpvExecutionMode mode)
+{
+ compiler->compiler->unset_execution_mode(static_cast<spv::ExecutionMode>(mode));
+}
+
+spvc_result spvc_compiler_get_execution_modes(spvc_compiler compiler, const SpvExecutionMode **modes, size_t *num_modes)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ auto ptr = spvc_allocate<TemporaryBuffer<SpvExecutionMode>>();
+
+ compiler->compiler->get_execution_mode_bitset().for_each_bit(
+ [&](uint32_t bit) { ptr->buffer.push_back(static_cast<SpvExecutionMode>(bit)); });
+
+ *modes = ptr->buffer.data();
+ *num_modes = ptr->buffer.size();
+ compiler->context->allocations.push_back(std::move(ptr));
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY)
+ return SPVC_SUCCESS;
+}
+
+unsigned spvc_compiler_get_execution_mode_argument(spvc_compiler compiler, SpvExecutionMode mode)
+{
+ return compiler->compiler->get_execution_mode_argument(static_cast<spv::ExecutionMode>(mode));
+}
+
+unsigned spvc_compiler_get_execution_mode_argument_by_index(spvc_compiler compiler, SpvExecutionMode mode,
+ unsigned index)
+{
+ return compiler->compiler->get_execution_mode_argument(static_cast<spv::ExecutionMode>(mode), index);
+}
+
+SpvExecutionModel spvc_compiler_get_execution_model(spvc_compiler compiler)
+{
+ return static_cast<SpvExecutionModel>(compiler->compiler->get_execution_model());
+}
+
+spvc_type spvc_compiler_get_type_handle(spvc_compiler compiler, spvc_type_id id)
+{
+ // Should only throw if an intentionally garbage ID is passed, but the IDs are not type-safe.
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ return static_cast<spvc_type>(&compiler->compiler->get_type(id));
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, nullptr)
+}
+
+static spvc_basetype convert_basetype(SPIRType::BaseType type)
+{
+ // For now the enums match up.
+ return static_cast<spvc_basetype>(type);
+}
+
+spvc_basetype spvc_type_get_basetype(spvc_type type)
+{
+ return convert_basetype(type->basetype);
+}
+
+unsigned spvc_type_get_bit_width(spvc_type type)
+{
+ return type->width;
+}
+
+unsigned spvc_type_get_vector_size(spvc_type type)
+{
+ return type->vecsize;
+}
+
+unsigned spvc_type_get_columns(spvc_type type)
+{
+ return type->columns;
+}
+
+unsigned spvc_type_get_num_array_dimensions(spvc_type type)
+{
+ return unsigned(type->array.size());
+}
+
+spvc_bool spvc_type_array_dimension_is_literal(spvc_type type, unsigned dimension)
+{
+ return type->array_size_literal[dimension] ? SPVC_TRUE : SPVC_FALSE;
+}
+
+SpvId spvc_type_get_array_dimension(spvc_type type, unsigned dimension)
+{
+ return type->array[dimension];
+}
+
+unsigned spvc_type_get_num_member_types(spvc_type type)
+{
+ return unsigned(type->member_types.size());
+}
+
+spvc_type_id spvc_type_get_member_type(spvc_type type, unsigned index)
+{
+ return type->member_types[index];
+}
+
+SpvStorageClass spvc_type_get_storage_class(spvc_type type)
+{
+ return static_cast<SpvStorageClass>(type->storage);
+}
+
+// Image type query.
+spvc_type_id spvc_type_get_image_sampled_type(spvc_type type)
+{
+ return type->image.type;
+}
+
+SpvDim spvc_type_get_image_dimension(spvc_type type)
+{
+ return static_cast<SpvDim>(type->image.dim);
+}
+
+spvc_bool spvc_type_get_image_is_depth(spvc_type type)
+{
+ return type->image.depth ? SPVC_TRUE : SPVC_FALSE;
+}
+
+spvc_bool spvc_type_get_image_arrayed(spvc_type type)
+{
+ return type->image.arrayed ? SPVC_TRUE : SPVC_FALSE;
+}
+
+spvc_bool spvc_type_get_image_multisampled(spvc_type type)
+{
+ return type->image.ms ? SPVC_TRUE : SPVC_FALSE;
+}
+
+spvc_bool spvc_type_get_image_is_storage(spvc_type type)
+{
+ return type->image.sampled == 2 ? SPVC_TRUE : SPVC_FALSE;
+}
+
+SpvImageFormat spvc_type_get_image_storage_format(spvc_type type)
+{
+ return static_cast<SpvImageFormat>(static_cast<const SPIRType *>(type)->image.format);
+}
+
+SpvAccessQualifier spvc_type_get_image_access_qualifier(spvc_type type)
+{
+ return static_cast<SpvAccessQualifier>(static_cast<const SPIRType *>(type)->image.access);
+}
+
+spvc_result spvc_compiler_get_declared_struct_size(spvc_compiler compiler, spvc_type struct_type, size_t *size)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ *size = compiler->compiler->get_declared_struct_size(*static_cast<const SPIRType *>(struct_type));
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT)
+ return SPVC_SUCCESS;
+}
+
+spvc_result spvc_compiler_get_declared_struct_size_runtime_array(spvc_compiler compiler, spvc_type struct_type,
+ size_t array_size, size_t *size)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ *size = compiler->compiler->get_declared_struct_size_runtime_array(*static_cast<const SPIRType *>(struct_type),
+ array_size);
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT)
+ return SPVC_SUCCESS;
+}
+
+spvc_result spvc_compiler_type_struct_member_offset(spvc_compiler compiler, spvc_type type, unsigned index, unsigned *offset)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ *offset = compiler->compiler->type_struct_member_offset(*static_cast<const SPIRType *>(type), index);
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT)
+ return SPVC_SUCCESS;
+}
+
+spvc_result spvc_compiler_type_struct_member_array_stride(spvc_compiler compiler, spvc_type type, unsigned index, unsigned *stride)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ *stride = compiler->compiler->type_struct_member_array_stride(*static_cast<const SPIRType *>(type), index);
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT)
+ return SPVC_SUCCESS;
+}
+
+spvc_result spvc_compiler_type_struct_member_matrix_stride(spvc_compiler compiler, spvc_type type, unsigned index, unsigned *stride)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ *stride = compiler->compiler->type_struct_member_matrix_stride(*static_cast<const SPIRType *>(type), index);
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT)
+ return SPVC_SUCCESS;
+}
+
+spvc_result spvc_compiler_build_dummy_sampler_for_combined_images(spvc_compiler compiler, spvc_variable_id *id)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ *id = compiler->compiler->build_dummy_sampler_for_combined_images();
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT)
+ return SPVC_SUCCESS;
+}
+
+spvc_result spvc_compiler_build_combined_image_samplers(spvc_compiler compiler)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ compiler->compiler->build_combined_image_samplers();
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_UNSUPPORTED_SPIRV)
+ return SPVC_SUCCESS;
+}
+
+spvc_result spvc_compiler_get_combined_image_samplers(spvc_compiler compiler,
+ const spvc_combined_image_sampler **samplers,
+ size_t *num_samplers)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ auto combined = compiler->compiler->get_combined_image_samplers();
+ std::vector<spvc_combined_image_sampler> translated;
+ translated.reserve(combined.size());
+ for (auto &c : combined)
+ {
+ spvc_combined_image_sampler trans = { c.combined_id, c.image_id, c.sampler_id };
+ translated.push_back(trans);
+ }
+
+ auto ptr = spvc_allocate<TemporaryBuffer<spvc_combined_image_sampler>>();
+ ptr->buffer = std::move(translated);
+ *samplers = ptr->buffer.data();
+ *num_samplers = ptr->buffer.size();
+ compiler->context->allocations.push_back(std::move(ptr));
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY)
+ return SPVC_SUCCESS;
+}
+
+spvc_result spvc_compiler_get_specialization_constants(spvc_compiler compiler,
+ const spvc_specialization_constant **constants,
+ size_t *num_constants)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ auto spec_constants = compiler->compiler->get_specialization_constants();
+ std::vector<spvc_specialization_constant> translated;
+ translated.reserve(spec_constants.size());
+ for (auto &c : spec_constants)
+ {
+ spvc_specialization_constant trans = { c.id, c.constant_id };
+ translated.push_back(trans);
+ }
+
+ auto ptr = spvc_allocate<TemporaryBuffer<spvc_specialization_constant>>();
+ ptr->buffer = std::move(translated);
+ *constants = ptr->buffer.data();
+ *num_constants = ptr->buffer.size();
+ compiler->context->allocations.push_back(std::move(ptr));
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY)
+ return SPVC_SUCCESS;
+}
+
+spvc_constant spvc_compiler_get_constant_handle(spvc_compiler compiler, spvc_variable_id id)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ return static_cast<spvc_constant>(&compiler->compiler->get_constant(id));
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, nullptr)
+}
+
+spvc_constant_id spvc_compiler_get_work_group_size_specialization_constants(spvc_compiler compiler,
+ spvc_specialization_constant *x,
+ spvc_specialization_constant *y,
+ spvc_specialization_constant *z)
+{
+ SpecializationConstant tmpx;
+ SpecializationConstant tmpy;
+ SpecializationConstant tmpz;
+ spvc_constant_id ret = compiler->compiler->get_work_group_size_specialization_constants(tmpx, tmpy, tmpz);
+ x->id = tmpx.id;
+ x->constant_id = tmpx.constant_id;
+ y->id = tmpy.id;
+ y->constant_id = tmpy.constant_id;
+ z->id = tmpz.id;
+ z->constant_id = tmpz.constant_id;
+ return ret;
+}
+
+float spvc_constant_get_scalar_fp16(spvc_constant constant, unsigned column, unsigned row)
+{
+ return constant->scalar_f16(column, row);
+}
+
+float spvc_constant_get_scalar_fp32(spvc_constant constant, unsigned column, unsigned row)
+{
+ return constant->scalar_f32(column, row);
+}
+
+double spvc_constant_get_scalar_fp64(spvc_constant constant, unsigned column, unsigned row)
+{
+ return constant->scalar_f64(column, row);
+}
+
+unsigned spvc_constant_get_scalar_u32(spvc_constant constant, unsigned column, unsigned row)
+{
+ return constant->scalar(column, row);
+}
+
+int spvc_constant_get_scalar_i32(spvc_constant constant, unsigned column, unsigned row)
+{
+ return constant->scalar_i32(column, row);
+}
+
+unsigned spvc_constant_get_scalar_u16(spvc_constant constant, unsigned column, unsigned row)
+{
+ return constant->scalar_u16(column, row);
+}
+
+int spvc_constant_get_scalar_i16(spvc_constant constant, unsigned column, unsigned row)
+{
+ return constant->scalar_i16(column, row);
+}
+
+unsigned spvc_constant_get_scalar_u8(spvc_constant constant, unsigned column, unsigned row)
+{
+ return constant->scalar_u8(column, row);
+}
+
+int spvc_constant_get_scalar_i8(spvc_constant constant, unsigned column, unsigned row)
+{
+ return constant->scalar_i8(column, row);
+}
+
+void spvc_constant_get_subconstants(spvc_constant constant, const spvc_constant_id **constituents, size_t *count)
+{
+ static_assert(sizeof(spvc_constant_id) == sizeof(constant->subconstants.front()), "ID size is not consistent.");
+ *constituents = reinterpret_cast<spvc_constant_id *>(constant->subconstants.data());
+ *count = constant->subconstants.size();
+}
+
+spvc_type_id spvc_constant_get_type(spvc_constant constant)
+{
+ return constant->constant_type;
+}
+
+spvc_bool spvc_compiler_get_binary_offset_for_decoration(spvc_compiler compiler, spvc_variable_id id,
+ SpvDecoration decoration,
+ unsigned *word_offset)
+{
+ uint32_t off = 0;
+ bool ret = compiler->compiler->get_binary_offset_for_decoration(id, static_cast<spv::Decoration>(decoration), off);
+ if (ret)
+ {
+ *word_offset = off;
+ return SPVC_TRUE;
+ }
+ else
+ return SPVC_FALSE;
+}
+
+spvc_bool spvc_compiler_buffer_is_hlsl_counter_buffer(spvc_compiler compiler, spvc_variable_id id)
+{
+ return compiler->compiler->buffer_is_hlsl_counter_buffer(id) ? SPVC_TRUE : SPVC_FALSE;
+}
+
+spvc_bool spvc_compiler_buffer_get_hlsl_counter_buffer(spvc_compiler compiler, spvc_variable_id id,
+ spvc_variable_id *counter_id)
+{
+ uint32_t buffer;
+ bool ret = compiler->compiler->buffer_get_hlsl_counter_buffer(id, buffer);
+ if (ret)
+ {
+ *counter_id = buffer;
+ return SPVC_TRUE;
+ }
+ else
+ return SPVC_FALSE;
+}
+
+spvc_result spvc_compiler_get_declared_capabilities(spvc_compiler compiler, const SpvCapability **capabilities,
+ size_t *num_capabilities)
+{
+ auto &caps = compiler->compiler->get_declared_capabilities();
+ static_assert(sizeof(SpvCapability) == sizeof(spv::Capability), "Enum size mismatch.");
+ *capabilities = reinterpret_cast<const SpvCapability *>(caps.data());
+ *num_capabilities = caps.size();
+ return SPVC_SUCCESS;
+}
+
+spvc_result spvc_compiler_get_declared_extensions(spvc_compiler compiler, const char ***extensions,
+ size_t *num_extensions)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ auto &exts = compiler->compiler->get_declared_extensions();
+ std::vector<const char *> duped;
+ duped.reserve(exts.size());
+ for (auto &ext : exts)
+ duped.push_back(compiler->context->allocate_name(ext));
+
+ auto ptr = spvc_allocate<TemporaryBuffer<const char *>>();
+ ptr->buffer = std::move(duped);
+ *extensions = ptr->buffer.data();
+ *num_extensions = ptr->buffer.size();
+ compiler->context->allocations.push_back(std::move(ptr));
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY)
+ return SPVC_SUCCESS;
+}
+
+const char *spvc_compiler_get_remapped_declared_block_name(spvc_compiler compiler, spvc_variable_id id)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ auto name = compiler->compiler->get_remapped_declared_block_name(id);
+ return compiler->context->allocate_name(name);
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, nullptr)
+}
+
+spvc_result spvc_compiler_get_buffer_block_decorations(spvc_compiler compiler, spvc_variable_id id,
+ const SpvDecoration **decorations, size_t *num_decorations)
+{
+ SPVC_BEGIN_SAFE_SCOPE
+ {
+ auto flags = compiler->compiler->get_buffer_block_flags(id);
+ auto bitset = spvc_allocate<TemporaryBuffer<SpvDecoration>>();
+
+ flags.for_each_bit([&](uint32_t bit) { bitset->buffer.push_back(static_cast<SpvDecoration>(bit)); });
+
+ *decorations = bitset->buffer.data();
+ *num_decorations = bitset->buffer.size();
+ compiler->context->allocations.push_back(std::move(bitset));
+ }
+ SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT)
+ return SPVC_SUCCESS;
+}
+
+unsigned spvc_msl_get_aux_buffer_struct_version(void)
+{
+ return SPVC_MSL_AUX_BUFFER_STRUCT_VERSION;
+}
+
+void spvc_msl_vertex_attribute_init(spvc_msl_vertex_attribute *attr)
+{
+#if SPIRV_CROSS_C_API_MSL
+ // Crude, but works.
+ MSLVertexAttr attr_default;
+ attr->location = attr_default.location;
+ attr->per_instance = attr_default.per_instance ? SPVC_TRUE : SPVC_FALSE;
+ attr->format = static_cast<spvc_msl_vertex_format>(attr_default.format);
+ attr->builtin = static_cast<SpvBuiltIn>(attr_default.builtin);
+ attr->msl_buffer = attr_default.msl_buffer;
+ attr->msl_offset = attr_default.msl_offset;
+ attr->msl_stride = attr_default.msl_stride;
+#else
+ memset(attr, 0, sizeof(*attr));
+#endif
+}
+
+void spvc_msl_resource_binding_init(spvc_msl_resource_binding *binding)
+{
+#if SPIRV_CROSS_C_API_MSL
+ MSLResourceBinding binding_default;
+ binding->desc_set = binding_default.desc_set;
+ binding->binding = binding_default.binding;
+ binding->msl_buffer = binding_default.msl_buffer;
+ binding->msl_texture = binding_default.msl_texture;
+ binding->msl_sampler = binding_default.msl_sampler;
+ binding->stage = static_cast<SpvExecutionModel>(binding_default.stage);
+#else
+ memset(binding, 0, sizeof(*binding));
+#endif
+}
+
+void spvc_msl_constexpr_sampler_init(spvc_msl_constexpr_sampler *sampler)
+{
+#if SPIRV_CROSS_C_API_MSL
+ MSLConstexprSampler defaults;
+ sampler->anisotropy_enable = defaults.anisotropy_enable ? SPVC_TRUE : SPVC_FALSE;
+ sampler->border_color = static_cast<spvc_msl_sampler_border_color>(defaults.border_color);
+ sampler->compare_enable = defaults.compare_enable ? SPVC_TRUE : SPVC_FALSE;
+ sampler->coord = static_cast<spvc_msl_sampler_coord>(defaults.coord);
+ sampler->compare_func = static_cast<spvc_msl_sampler_compare_func>(defaults.compare_func);
+ sampler->lod_clamp_enable = defaults.lod_clamp_enable ? SPVC_TRUE : SPVC_FALSE;
+ sampler->lod_clamp_max = defaults.lod_clamp_max;
+ sampler->lod_clamp_min = defaults.lod_clamp_min;
+ sampler->mag_filter = static_cast<spvc_msl_sampler_filter>(defaults.mag_filter);
+ sampler->min_filter = static_cast<spvc_msl_sampler_filter>(defaults.min_filter);
+ sampler->mip_filter = static_cast<spvc_msl_sampler_mip_filter>(defaults.mip_filter);
+ sampler->max_anisotropy = defaults.max_anisotropy;
+ sampler->s_address = static_cast<spvc_msl_sampler_address>(defaults.s_address);
+ sampler->t_address = static_cast<spvc_msl_sampler_address>(defaults.t_address);
+ sampler->r_address = static_cast<spvc_msl_sampler_address>(defaults.r_address);
+#else
+ memset(sampler, 0, sizeof(*sampler));
+#endif
+}
+
+unsigned spvc_compiler_get_current_id_bound(spvc_compiler compiler)
+{
+ return compiler->compiler->get_current_id_bound();
+}
+
+void spvc_get_version(unsigned *major, unsigned *minor, unsigned *patch)
+{
+ *major = SPVC_C_API_VERSION_MAJOR;
+ *minor = SPVC_C_API_VERSION_MINOR;
+ *patch = SPVC_C_API_VERSION_PATCH;
+}
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
diff --git a/src/3rdparty/SPIRV-Cross/spirv_cross_c.h b/src/3rdparty/SPIRV-Cross/spirv_cross_c.h
new file mode 100644
index 0000000..5491a2e
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_cross_c.h
@@ -0,0 +1,703 @@
+/*
+ * Copyright 2019 Hans-Kristian Arntzen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SPIRV_CROSS_C_API_H
+#define SPIRV_CROSS_C_API_H
+
+#include <stddef.h>
+#include "spirv.h"
+
+/*
+ * C89-compatible wrapper for SPIRV-Cross' API.
+ * Documentation here is sparse unless the behavior does not map 1:1 with C++ API.
+ * It is recommended to look at the canonical C++ API for more detailed information.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Bumped if ABI or API breaks backwards compatibility. */
+#define SPVC_C_API_VERSION_MAJOR 0
+/* Bumped if APIs or enumerations are added in a backwards compatible way. */
+#define SPVC_C_API_VERSION_MINOR 5
+/* Bumped if internal implementation details change. */
+#define SPVC_C_API_VERSION_PATCH 0
+
+#if !defined(SPVC_PUBLIC_API)
+#if defined(SPVC_EXPORT_SYMBOLS)
+/* Exports symbols. Standard C calling convention is used. */
+#if defined(__GNUC__)
+#define SPVC_PUBLIC_API __attribute__((visibility("default")))
+#elif defined(_MSC_VER)
+#define SPVC_PUBLIC_API __declspec(dllexport)
+#else
+#define SPVC_PUBLIC_API
+#endif
+#else
+#define SPVC_PUBLIC_API
+#endif
+#endif
+
+/*
+ * Gets the SPVC_C_API_VERSION_* used to build this library.
+ * Can be used to check for ABI mismatch if so-versioning did not catch it.
+ */
+SPVC_PUBLIC_API void spvc_get_version(unsigned *major, unsigned *minor, unsigned *patch);
+
+/* These types are opaque to the user. */
+typedef struct spvc_context_s *spvc_context;
+typedef struct spvc_parsed_ir_s *spvc_parsed_ir;
+typedef struct spvc_compiler_s *spvc_compiler;
+typedef struct spvc_compiler_options_s *spvc_compiler_options;
+typedef struct spvc_resources_s *spvc_resources;
+struct spvc_type_s;
+typedef const struct spvc_type_s *spvc_type;
+typedef struct spvc_constant_s *spvc_constant;
+struct spvc_set_s;
+typedef const struct spvc_set_s *spvc_set;
+
+/*
+ * Shallow typedefs. All SPIR-V IDs are plain 32-bit numbers, but this helps communicate which data is used.
+ * Maps to a SPIRType.
+ */
+typedef SpvId spvc_type_id;
+/* Maps to a SPIRVariable. */
+typedef SpvId spvc_variable_id;
+/* Maps to a SPIRConstant. */
+typedef SpvId spvc_constant_id;
+
+/* See C++ API. */
+typedef struct spvc_reflected_resource
+{
+ spvc_variable_id id;
+ spvc_type_id base_type_id;
+ spvc_type_id type_id;
+ const char *name;
+} spvc_reflected_resource;
+
+/* See C++ API. */
+typedef struct spvc_entry_point
+{
+ SpvExecutionModel execution_model;
+ const char *name;
+} spvc_entry_point;
+
+/* See C++ API. */
+typedef struct spvc_combined_image_sampler
+{
+ spvc_variable_id combined_id;
+ spvc_variable_id image_id;
+ spvc_variable_id sampler_id;
+} spvc_combined_image_sampler;
+
+/* See C++ API. */
+typedef struct spvc_specialization_constant
+{
+ spvc_constant_id id;
+ unsigned constant_id;
+} spvc_specialization_constant;
+
+/* See C++ API. */
+typedef struct spvc_hlsl_root_constants
+{
+ unsigned start;
+ unsigned end;
+ unsigned binding;
+ unsigned space;
+} spvc_hlsl_root_constants;
+
+/* See C++ API. */
+typedef struct spvc_hlsl_vertex_attribute_remap
+{
+ unsigned location;
+ const char *semantic;
+} spvc_hlsl_vertex_attribute_remap;
+
+/*
+ * Be compatible with non-C99 compilers, which do not have stdbool.
+ * Only recent MSVC compilers supports this for example, and ideally SPIRV-Cross should be linkable
+ * from a wide range of compilers in its C wrapper.
+ */
+typedef unsigned char spvc_bool;
+#define SPVC_TRUE ((spvc_bool)1)
+#define SPVC_FALSE ((spvc_bool)0)
+
+typedef enum spvc_result
+{
+ /* Success. */
+ SPVC_SUCCESS = 0,
+
+ /* The SPIR-V is invalid. Should have been caught by validation ideally. */
+ SPVC_ERROR_INVALID_SPIRV = -1,
+
+ /* The SPIR-V might be valid or invalid, but SPIRV-Cross currently cannot correctly translate this to your target language. */
+ SPVC_ERROR_UNSUPPORTED_SPIRV = -2,
+
+ /* If for some reason we hit this, new or malloc failed. */
+ SPVC_ERROR_OUT_OF_MEMORY = -3,
+
+ /* Invalid API argument. */
+ SPVC_ERROR_INVALID_ARGUMENT = -4,
+
+ SPVC_ERROR_INT_MAX = 0x7fffffff
+} spvc_result;
+
+typedef enum spvc_capture_mode
+{
+ /* The Parsed IR payload will be copied, and the handle can be reused to create other compiler instances. */
+ SPVC_CAPTURE_MODE_COPY = 0,
+
+ /*
+ * The payload will now be owned by the compiler.
+ * parsed_ir should now be considered a dead blob and must not be used further.
+ * This is optimal for performance and should be the go-to option.
+ */
+ SPVC_CAPTURE_MODE_TAKE_OWNERSHIP = 1,
+
+ SPVC_CAPTURE_MODE_INT_MAX = 0x7fffffff
+} spvc_capture_mode;
+
+typedef enum spvc_backend
+{
+ /* This backend can only perform reflection, no compiler options are supported. Maps to spirv_cross::Compiler. */
+ SPVC_BACKEND_NONE = 0,
+ SPVC_BACKEND_GLSL = 1, /* spirv_cross::CompilerGLSL */
+ SPVC_BACKEND_HLSL = 2, /* CompilerHLSL */
+ SPVC_BACKEND_MSL = 3, /* CompilerMSL */
+ SPVC_BACKEND_CPP = 4, /* CompilerCPP */
+ SPVC_BACKEND_JSON = 5, /* CompilerReflection w/ JSON backend */
+ SPVC_BACKEND_INT_MAX = 0x7fffffff
+} spvc_backend;
+
+/* Maps to C++ API. */
+typedef enum spvc_resource_type
+{
+ SPVC_RESOURCE_TYPE_UNKNOWN = 0,
+ SPVC_RESOURCE_TYPE_UNIFORM_BUFFER = 1,
+ SPVC_RESOURCE_TYPE_STORAGE_BUFFER = 2,
+ SPVC_RESOURCE_TYPE_STAGE_INPUT = 3,
+ SPVC_RESOURCE_TYPE_STAGE_OUTPUT = 4,
+ SPVC_RESOURCE_TYPE_SUBPASS_INPUT = 5,
+ SPVC_RESOURCE_TYPE_STORAGE_IMAGE = 6,
+ SPVC_RESOURCE_TYPE_SAMPLED_IMAGE = 7,
+ SPVC_RESOURCE_TYPE_ATOMIC_COUNTER = 8,
+ SPVC_RESOURCE_TYPE_PUSH_CONSTANT = 9,
+ SPVC_RESOURCE_TYPE_SEPARATE_IMAGE = 10,
+ SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS = 11,
+ SPVC_RESOURCE_TYPE_ACCELERATION_STRUCTURE = 12,
+ SPVC_RESOURCE_TYPE_INT_MAX = 0x7fffffff
+} spvc_resource_type;
+
+/* Maps to spirv_cross::SPIRType::BaseType. */
+typedef enum spvc_basetype
+{
+ SPVC_BASETYPE_UNKNOWN = 0,
+ SPVC_BASETYPE_VOID = 1,
+ SPVC_BASETYPE_BOOLEAN = 2,
+ SPVC_BASETYPE_INT8 = 3,
+ SPVC_BASETYPE_UINT8 = 4,
+ SPVC_BASETYPE_INT16 = 5,
+ SPVC_BASETYPE_UINT16 = 6,
+ SPVC_BASETYPE_INT32 = 7,
+ SPVC_BASETYPE_UINT32 = 8,
+ SPVC_BASETYPE_INT64 = 9,
+ SPVC_BASETYPE_UINT64 = 10,
+ SPVC_BASETYPE_ATOMIC_COUNTER = 11,
+ SPVC_BASETYPE_FP16 = 12,
+ SPVC_BASETYPE_FP32 = 13,
+ SPVC_BASETYPE_FP64 = 14,
+ SPVC_BASETYPE_STRUCT = 15,
+ SPVC_BASETYPE_IMAGE = 16,
+ SPVC_BASETYPE_SAMPLED_IMAGE = 17,
+ SPVC_BASETYPE_SAMPLER = 18,
+ SPVC_BASETYPE_ACCELERATION_STRUCTURE = 19,
+
+ SPVC_BASETYPE_INT_MAX = 0x7fffffff
+} spvc_basetype;
+
+#define SPVC_COMPILER_OPTION_COMMON_BIT 0x1000000
+#define SPVC_COMPILER_OPTION_GLSL_BIT 0x2000000
+#define SPVC_COMPILER_OPTION_HLSL_BIT 0x4000000
+#define SPVC_COMPILER_OPTION_MSL_BIT 0x8000000
+#define SPVC_COMPILER_OPTION_LANG_BITS 0x0f000000
+#define SPVC_COMPILER_OPTION_ENUM_BITS 0xffffff
+
+#define SPVC_MAKE_MSL_VERSION(major, minor, patch) ((major) * 10000 + (minor) * 100 + (patch))
+
+/* Maps to C++ API. */
+typedef enum spvc_msl_platform
+{
+ SPVC_MSL_PLATFORM_IOS = 0,
+ SPVC_MSL_PLATFORM_MACOS = 1,
+ SPVC_MSL_PLATFORM_MAX_INT = 0x7fffffff
+} spvc_msl_platform;
+
+/* Maps to C++ API. */
+typedef enum spvc_msl_vertex_format
+{
+ SPVC_MSL_VERTEX_FORMAT_OTHER = 0,
+ SPVC_MSL_VERTEX_FORMAT_UINT8 = 1,
+ SPVC_MSL_VERTEX_FORMAT_UINT16 = 2
+} spvc_msl_vertex_format;
+
+/* Maps to C++ API. */
+typedef struct spvc_msl_vertex_attribute
+{
+ unsigned location;
+ unsigned msl_buffer;
+ unsigned msl_offset;
+ unsigned msl_stride;
+ spvc_bool per_instance;
+ spvc_msl_vertex_format format;
+ SpvBuiltIn builtin;
+} spvc_msl_vertex_attribute;
+
+/*
+ * Initializes the vertex attribute struct.
+ */
+SPVC_PUBLIC_API void spvc_msl_vertex_attribute_init(spvc_msl_vertex_attribute *attr);
+
+/* Maps to C++ API. */
+typedef struct spvc_msl_resource_binding
+{
+ SpvExecutionModel stage;
+ unsigned desc_set;
+ unsigned binding;
+ unsigned msl_buffer;
+ unsigned msl_texture;
+ unsigned msl_sampler;
+} spvc_msl_resource_binding;
+
+/*
+ * Initializes the resource binding struct.
+ * The defaults are non-zero.
+ */
+SPVC_PUBLIC_API void spvc_msl_resource_binding_init(spvc_msl_resource_binding *binding);
+
+#define SPVC_MSL_PUSH_CONSTANT_DESC_SET (~(0u))
+#define SPVC_MSL_PUSH_CONSTANT_BINDING (0)
+#define SPVC_MSL_AUX_BUFFER_STRUCT_VERSION 1
+
+/* Runtime check for incompatibility. */
+SPVC_PUBLIC_API unsigned spvc_msl_get_aux_buffer_struct_version(void);
+
+/* Maps to C++ API. */
+typedef enum spvc_msl_sampler_coord
+{
+ SPVC_MSL_SAMPLER_COORD_NORMALIZED = 0,
+ SPVC_MSL_SAMPLER_COORD_PIXEL = 1,
+ SPVC_MSL_SAMPLER_INT_MAX = 0x7fffffff
+} spvc_msl_sampler_coord;
+
+/* Maps to C++ API. */
+typedef enum spvc_msl_sampler_filter
+{
+ SPVC_MSL_SAMPLER_FILTER_NEAREST = 0,
+ SPVC_MSL_SAMPLER_FILTER_LINEAR = 1,
+ SPVC_MSL_SAMPLER_FILTER_INT_MAX = 0x7fffffff
+} spvc_msl_sampler_filter;
+
+/* Maps to C++ API. */
+typedef enum spvc_msl_sampler_mip_filter
+{
+ SPVC_MSL_SAMPLER_MIP_FILTER_NONE = 0,
+ SPVC_MSL_SAMPLER_MIP_FILTER_NEAREST = 1,
+ SPVC_MSL_SAMPLER_MIP_FILTER_LINEAR = 2,
+ SPVC_MSL_SAMPLER_MIP_FILTER_INT_MAX = 0x7fffffff
+} spvc_msl_sampler_mip_filter;
+
+/* Maps to C++ API. */
+typedef enum spvc_msl_sampler_address
+{
+ SPVC_MSL_SAMPLER_ADDRESS_CLAMP_TO_ZERO = 0,
+ SPVC_MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE = 1,
+ SPVC_MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER = 2,
+ SPVC_MSL_SAMPLER_ADDRESS_REPEAT = 3,
+ SPVC_MSL_SAMPLER_ADDRESS_MIRRORED_REPEAT = 4,
+ SPVC_MSL_SAMPLER_ADDRESS_INT_MAX = 0x7fffffff
+} spvc_msl_sampler_address;
+
+/* Maps to C++ API. */
+typedef enum spvc_msl_sampler_compare_func
+{
+ SPVC_MSL_SAMPLER_COMPARE_FUNC_NEVER = 0,
+ SPVC_MSL_SAMPLER_COMPARE_FUNC_LESS = 1,
+ SPVC_MSL_SAMPLER_COMPARE_FUNC_LESS_EQUAL = 2,
+ SPVC_MSL_SAMPLER_COMPARE_FUNC_GREATER = 3,
+ SPVC_MSL_SAMPLER_COMPARE_FUNC_GREATER_EQUAL = 4,
+ SPVC_MSL_SAMPLER_COMPARE_FUNC_EQUAL = 5,
+ SPVC_MSL_SAMPLER_COMPARE_FUNC_NOT_EQUAL = 6,
+ SPVC_MSL_SAMPLER_COMPARE_FUNC_ALWAYS = 7,
+ SPVC_MSL_SAMPLER_COMPARE_FUNC_INT_MAX = 0x7fffffff
+} spvc_msl_sampler_compare_func;
+
+/* Maps to C++ API. */
+typedef enum spvc_msl_sampler_border_color
+{
+ SPVC_MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK = 0,
+ SPVC_MSL_SAMPLER_BORDER_COLOR_OPAQUE_BLACK = 1,
+ SPVC_MSL_SAMPLER_BORDER_COLOR_OPAQUE_WHITE = 2,
+ SPVC_MSL_SAMPLER_BORDER_COLOR_INT_MAX = 0x7fffffff
+} spvc_msl_sampler_border_color;
+
+/* Maps to C++ API. */
+typedef struct spvc_msl_constexpr_sampler
+{
+ spvc_msl_sampler_coord coord;
+ spvc_msl_sampler_filter min_filter;
+ spvc_msl_sampler_filter mag_filter;
+ spvc_msl_sampler_mip_filter mip_filter;
+ spvc_msl_sampler_address s_address;
+ spvc_msl_sampler_address t_address;
+ spvc_msl_sampler_address r_address;
+ spvc_msl_sampler_compare_func compare_func;
+ spvc_msl_sampler_border_color border_color;
+ float lod_clamp_min;
+ float lod_clamp_max;
+ int max_anisotropy;
+
+ spvc_bool compare_enable;
+ spvc_bool lod_clamp_enable;
+ spvc_bool anisotropy_enable;
+} spvc_msl_constexpr_sampler;
+
+/*
+ * Initializes the constexpr sampler struct.
+ * The defaults are non-zero.
+ */
+SPVC_PUBLIC_API void spvc_msl_constexpr_sampler_init(spvc_msl_constexpr_sampler *sampler);
+
+/* Maps to the various spirv_cross::Compiler*::Option structures. See C++ API for defaults and details. */
+typedef enum spvc_compiler_option
+{
+ SPVC_COMPILER_OPTION_UNKNOWN = 0,
+
+ SPVC_COMPILER_OPTION_FORCE_TEMPORARY = 1 | SPVC_COMPILER_OPTION_COMMON_BIT,
+ SPVC_COMPILER_OPTION_FLATTEN_MULTIDIMENSIONAL_ARRAYS = 2 | SPVC_COMPILER_OPTION_COMMON_BIT,
+ SPVC_COMPILER_OPTION_FIXUP_DEPTH_CONVENTION = 3 | SPVC_COMPILER_OPTION_COMMON_BIT,
+ SPVC_COMPILER_OPTION_FLIP_VERTEX_Y = 4 | SPVC_COMPILER_OPTION_COMMON_BIT,
+
+ SPVC_COMPILER_OPTION_GLSL_SUPPORT_NONZERO_BASE_INSTANCE = 5 | SPVC_COMPILER_OPTION_GLSL_BIT,
+ SPVC_COMPILER_OPTION_GLSL_SEPARATE_SHADER_OBJECTS = 6 | SPVC_COMPILER_OPTION_GLSL_BIT,
+ SPVC_COMPILER_OPTION_GLSL_ENABLE_420PACK_EXTENSION = 7 | SPVC_COMPILER_OPTION_GLSL_BIT,
+ SPVC_COMPILER_OPTION_GLSL_VERSION = 8 | SPVC_COMPILER_OPTION_GLSL_BIT,
+ SPVC_COMPILER_OPTION_GLSL_ES = 9 | SPVC_COMPILER_OPTION_GLSL_BIT,
+ SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS = 10 | SPVC_COMPILER_OPTION_GLSL_BIT,
+ SPVC_COMPILER_OPTION_GLSL_ES_DEFAULT_FLOAT_PRECISION_HIGHP = 11 | SPVC_COMPILER_OPTION_GLSL_BIT,
+ SPVC_COMPILER_OPTION_GLSL_ES_DEFAULT_INT_PRECISION_HIGHP = 12 | SPVC_COMPILER_OPTION_GLSL_BIT,
+
+ SPVC_COMPILER_OPTION_HLSL_SHADER_MODEL = 13 | SPVC_COMPILER_OPTION_HLSL_BIT,
+ SPVC_COMPILER_OPTION_HLSL_POINT_SIZE_COMPAT = 14 | SPVC_COMPILER_OPTION_HLSL_BIT,
+ SPVC_COMPILER_OPTION_HLSL_POINT_COORD_COMPAT = 15 | SPVC_COMPILER_OPTION_HLSL_BIT,
+ SPVC_COMPILER_OPTION_HLSL_SUPPORT_NONZERO_BASE_VERTEX_BASE_INSTANCE = 16 | SPVC_COMPILER_OPTION_HLSL_BIT,
+
+ SPVC_COMPILER_OPTION_MSL_VERSION = 17 | SPVC_COMPILER_OPTION_MSL_BIT,
+ SPVC_COMPILER_OPTION_MSL_TEXEL_BUFFER_TEXTURE_WIDTH = 18 | SPVC_COMPILER_OPTION_MSL_BIT,
+ SPVC_COMPILER_OPTION_MSL_AUX_BUFFER_INDEX = 19 | SPVC_COMPILER_OPTION_MSL_BIT,
+ SPVC_COMPILER_OPTION_MSL_INDIRECT_PARAMS_BUFFER_INDEX = 20 | SPVC_COMPILER_OPTION_MSL_BIT,
+ SPVC_COMPILER_OPTION_MSL_SHADER_OUTPUT_BUFFER_INDEX = 21 | SPVC_COMPILER_OPTION_MSL_BIT,
+ SPVC_COMPILER_OPTION_MSL_SHADER_PATCH_OUTPUT_BUFFER_INDEX = 22 | SPVC_COMPILER_OPTION_MSL_BIT,
+ SPVC_COMPILER_OPTION_MSL_SHADER_TESS_FACTOR_OUTPUT_BUFFER_INDEX = 23 | SPVC_COMPILER_OPTION_MSL_BIT,
+ SPVC_COMPILER_OPTION_MSL_SHADER_INPUT_WORKGROUP_INDEX = 24 | SPVC_COMPILER_OPTION_MSL_BIT,
+ SPVC_COMPILER_OPTION_MSL_ENABLE_POINT_SIZE_BUILTIN = 25 | SPVC_COMPILER_OPTION_MSL_BIT,
+ SPVC_COMPILER_OPTION_MSL_DISABLE_RASTERIZATION = 26 | SPVC_COMPILER_OPTION_MSL_BIT,
+ SPVC_COMPILER_OPTION_MSL_CAPTURE_OUTPUT_TO_BUFFER = 27 | SPVC_COMPILER_OPTION_MSL_BIT,
+ SPVC_COMPILER_OPTION_MSL_SWIZZLE_TEXTURE_SAMPLES = 28 | SPVC_COMPILER_OPTION_MSL_BIT,
+ SPVC_COMPILER_OPTION_MSL_PAD_FRAGMENT_OUTPUT_COMPONENTS = 29 | SPVC_COMPILER_OPTION_MSL_BIT,
+ SPVC_COMPILER_OPTION_MSL_TESS_DOMAIN_ORIGIN_LOWER_LEFT = 30 | SPVC_COMPILER_OPTION_MSL_BIT,
+ SPVC_COMPILER_OPTION_MSL_PLATFORM = 31 | SPVC_COMPILER_OPTION_MSL_BIT,
+ SPVC_COMPILER_OPTION_MSL_ARGUMENT_BUFFERS = 32 | SPVC_COMPILER_OPTION_MSL_BIT,
+
+ SPVC_COMPILER_OPTION_GLSL_EMIT_PUSH_CONSTANT_AS_UNIFORM_BUFFER = 33 | SPVC_COMPILER_OPTION_GLSL_BIT,
+
+ SPVC_COMPILER_OPTION_INT_MAX = 0x7fffffff
+} spvc_compiler_option;
+
+/*
+ * Context is the highest-level API construct.
+ * The context owns all memory allocations made by its child object hierarchy, including various non-opaque structs and strings.
+ * This means that the API user only has to care about one "destroy" call ever when using the C API.
+ * All pointers handed out by the APIs are only valid as long as the context
+ * is alive and spvc_context_release_allocations has not been called.
+ */
+SPVC_PUBLIC_API spvc_result spvc_context_create(spvc_context *context);
+
+/* Frees all memory allocations and objects associated with the context and its child objects. */
+SPVC_PUBLIC_API void spvc_context_destroy(spvc_context context);
+
+/* Frees all memory allocations and objects associated with the context and its child objects, but keeps the context alive. */
+SPVC_PUBLIC_API void spvc_context_release_allocations(spvc_context context);
+
+/* Get the string for the last error which was logged. */
+SPVC_PUBLIC_API const char *spvc_context_get_last_error_string(spvc_context context);
+
+/* Get notified in a callback when an error triggers. Useful for debugging. */
+typedef void (*spvc_error_callback)(void *userdata, const char *error);
+SPVC_PUBLIC_API void spvc_context_set_error_callback(spvc_context context, spvc_error_callback cb, void *userdata);
+
+/* SPIR-V parsing interface. Maps to Parser which then creates a ParsedIR, and that IR is extracted into the handle. */
+SPVC_PUBLIC_API spvc_result spvc_context_parse_spirv(spvc_context context, const SpvId *spirv, size_t word_count,
+ spvc_parsed_ir *parsed_ir);
+
+/*
+ * Create a compiler backend. Capture mode controls if we construct by copy or move semantics.
+ * It is always recommended to use SPVC_CAPTURE_MODE_TAKE_OWNERSHIP if you only intend to cross-compile the IR once.
+ */
+SPVC_PUBLIC_API spvc_result spvc_context_create_compiler(spvc_context context, spvc_backend backend,
+ spvc_parsed_ir parsed_ir, spvc_capture_mode mode,
+ spvc_compiler *compiler);
+
+/* Maps directly to C++ API. */
+SPVC_PUBLIC_API unsigned spvc_compiler_get_current_id_bound(spvc_compiler compiler);
+
+/* Create compiler options, which will initialize defaults. */
+SPVC_PUBLIC_API spvc_result spvc_compiler_create_compiler_options(spvc_compiler compiler,
+ spvc_compiler_options *options);
+/* Override options. Will return error if e.g. MSL options are used for the HLSL backend, etc. */
+SPVC_PUBLIC_API spvc_result spvc_compiler_options_set_bool(spvc_compiler_options options,
+ spvc_compiler_option option, spvc_bool value);
+SPVC_PUBLIC_API spvc_result spvc_compiler_options_set_uint(spvc_compiler_options options,
+ spvc_compiler_option option, unsigned value);
+/* Set compiler options. */
+SPVC_PUBLIC_API spvc_result spvc_compiler_install_compiler_options(spvc_compiler compiler,
+ spvc_compiler_options options);
+
+/* Compile IR into a string. *source is owned by the context, and caller must not free it themselves. */
+SPVC_PUBLIC_API spvc_result spvc_compiler_compile(spvc_compiler compiler, const char **source);
+
+/* Maps to C++ API. */
+SPVC_PUBLIC_API spvc_result spvc_compiler_add_header_line(spvc_compiler compiler, const char *line);
+SPVC_PUBLIC_API spvc_result spvc_compiler_require_extension(spvc_compiler compiler, const char *ext);
+SPVC_PUBLIC_API spvc_result spvc_compiler_flatten_buffer_block(spvc_compiler compiler, spvc_variable_id id);
+
+/*
+ * HLSL specifics.
+ * Maps to C++ API.
+ */
+SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_set_root_constants_layout(spvc_compiler compiler,
+ const spvc_hlsl_root_constants *constant_info,
+ size_t count);
+SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_add_vertex_attribute_remap(spvc_compiler compiler,
+ const spvc_hlsl_vertex_attribute_remap *remap,
+ size_t remaps);
+SPVC_PUBLIC_API spvc_variable_id spvc_compiler_hlsl_remap_num_workgroups_builtin(spvc_compiler compiler);
+
+/*
+ * MSL specifics.
+ * Maps to C++ API.
+ */
+SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_rasterization_disabled(spvc_compiler compiler);
+SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_aux_buffer(spvc_compiler compiler);
+SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_output_buffer(spvc_compiler compiler);
+SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_patch_output_buffer(spvc_compiler compiler);
+SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_input_threadgroup_mem(spvc_compiler compiler);
+SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_vertex_attribute(spvc_compiler compiler,
+ const spvc_msl_vertex_attribute *attrs);
+SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_resource_binding(spvc_compiler compiler,
+ const spvc_msl_resource_binding *binding);
+SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_discrete_descriptor_set(spvc_compiler compiler, unsigned desc_set);
+SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_vertex_attribute_used(spvc_compiler compiler, unsigned location);
+SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_resource_used(spvc_compiler compiler,
+ SpvExecutionModel model,
+ unsigned set,
+ unsigned binding);
+SPVC_PUBLIC_API spvc_result spvc_compiler_msl_remap_constexpr_sampler(spvc_compiler compiler, spvc_variable_id id, const spvc_msl_constexpr_sampler *sampler);
+SPVC_PUBLIC_API spvc_result spvc_compiler_msl_set_fragment_output_components(spvc_compiler compiler, unsigned location, unsigned components);
+
+/*
+ * Reflect resources.
+ * Maps almost 1:1 to C++ API.
+ */
+SPVC_PUBLIC_API spvc_result spvc_compiler_get_active_interface_variables(spvc_compiler compiler, spvc_set *set);
+SPVC_PUBLIC_API spvc_result spvc_compiler_set_enabled_interface_variables(spvc_compiler compiler, spvc_set set);
+SPVC_PUBLIC_API spvc_result spvc_compiler_create_shader_resources(spvc_compiler compiler, spvc_resources *resources);
+SPVC_PUBLIC_API spvc_result spvc_compiler_create_shader_resources_for_active_variables(spvc_compiler compiler,
+ spvc_resources *resources,
+ spvc_set active);
+SPVC_PUBLIC_API spvc_result spvc_resources_get_resource_list_for_type(spvc_resources resources, spvc_resource_type type,
+ const spvc_reflected_resource **resource_list,
+ size_t *resource_size);
+
+/*
+ * Decorations.
+ * Maps to C++ API.
+ */
+SPVC_PUBLIC_API void spvc_compiler_set_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration,
+ unsigned argument);
+SPVC_PUBLIC_API void spvc_compiler_set_decoration_string(spvc_compiler compiler, SpvId id, SpvDecoration decoration,
+ const char *argument);
+SPVC_PUBLIC_API void spvc_compiler_set_name(spvc_compiler compiler, SpvId id, const char *argument);
+SPVC_PUBLIC_API void spvc_compiler_set_member_decoration(spvc_compiler compiler, spvc_type_id id, unsigned member_index,
+ SpvDecoration decoration, unsigned argument);
+SPVC_PUBLIC_API void spvc_compiler_set_member_decoration_string(spvc_compiler compiler, spvc_type_id id,
+ unsigned member_index, SpvDecoration decoration,
+ const char *argument);
+SPVC_PUBLIC_API void spvc_compiler_set_member_name(spvc_compiler compiler, spvc_type_id id, unsigned member_index,
+ const char *argument);
+SPVC_PUBLIC_API void spvc_compiler_unset_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration);
+SPVC_PUBLIC_API void spvc_compiler_unset_member_decoration(spvc_compiler compiler, spvc_type_id id,
+ unsigned member_index, SpvDecoration decoration);
+
+SPVC_PUBLIC_API spvc_bool spvc_compiler_has_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration);
+SPVC_PUBLIC_API spvc_bool spvc_compiler_has_member_decoration(spvc_compiler compiler, spvc_type_id id,
+ unsigned member_index, SpvDecoration decoration);
+SPVC_PUBLIC_API const char *spvc_compiler_get_name(spvc_compiler compiler, SpvId id);
+SPVC_PUBLIC_API unsigned spvc_compiler_get_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration);
+SPVC_PUBLIC_API const char *spvc_compiler_get_decoration_string(spvc_compiler compiler, SpvId id,
+ SpvDecoration decoration);
+SPVC_PUBLIC_API unsigned spvc_compiler_get_member_decoration(spvc_compiler compiler, spvc_type_id id,
+ unsigned member_index, SpvDecoration decoration);
+SPVC_PUBLIC_API const char *spvc_compiler_get_member_decoration_string(spvc_compiler compiler, spvc_type_id id,
+ unsigned member_index, SpvDecoration decoration);
+
+/*
+ * Entry points.
+ * Maps to C++ API.
+ */
+SPVC_PUBLIC_API spvc_result spvc_compiler_get_entry_points(spvc_compiler compiler,
+ const spvc_entry_point **entry_points,
+ size_t *num_entry_points);
+SPVC_PUBLIC_API spvc_result spvc_compiler_set_entry_point(spvc_compiler compiler, const char *name,
+ SpvExecutionModel model);
+SPVC_PUBLIC_API spvc_result spvc_compiler_rename_entry_point(spvc_compiler compiler, const char *old_name,
+ const char *new_name, SpvExecutionModel model);
+SPVC_PUBLIC_API const char *spvc_compiler_get_cleansed_entry_point_name(spvc_compiler compiler, const char *name,
+ SpvExecutionModel model);
+SPVC_PUBLIC_API void spvc_compiler_set_execution_mode(spvc_compiler compiler, SpvExecutionMode mode);
+SPVC_PUBLIC_API void spvc_compiler_unset_execution_mode(spvc_compiler compiler, SpvExecutionMode mode);
+SPVC_PUBLIC_API void spvc_compiler_set_execution_mode_with_arguments(spvc_compiler compiler, SpvExecutionMode mode,
+ unsigned arg0, unsigned arg1, unsigned arg2);
+SPVC_PUBLIC_API spvc_result spvc_compiler_get_execution_modes(spvc_compiler compiler, const SpvExecutionMode **modes,
+ size_t *num_modes);
+SPVC_PUBLIC_API unsigned spvc_compiler_get_execution_mode_argument(spvc_compiler compiler, SpvExecutionMode mode);
+SPVC_PUBLIC_API unsigned spvc_compiler_get_execution_mode_argument_by_index(spvc_compiler compiler,
+ SpvExecutionMode mode, unsigned index);
+SPVC_PUBLIC_API SpvExecutionModel spvc_compiler_get_execution_model(spvc_compiler compiler);
+
+/*
+ * Type query interface.
+ * Maps to C++ API, except it's read-only.
+ */
+SPVC_PUBLIC_API spvc_type spvc_compiler_get_type_handle(spvc_compiler compiler, spvc_type_id id);
+
+SPVC_PUBLIC_API spvc_basetype spvc_type_get_basetype(spvc_type type);
+SPVC_PUBLIC_API unsigned spvc_type_get_bit_width(spvc_type type);
+SPVC_PUBLIC_API unsigned spvc_type_get_vector_size(spvc_type type);
+SPVC_PUBLIC_API unsigned spvc_type_get_columns(spvc_type type);
+SPVC_PUBLIC_API unsigned spvc_type_get_num_array_dimensions(spvc_type type);
+SPVC_PUBLIC_API spvc_bool spvc_type_array_dimension_is_literal(spvc_type type, unsigned dimension);
+SPVC_PUBLIC_API SpvId spvc_type_get_array_dimension(spvc_type type, unsigned dimension);
+SPVC_PUBLIC_API unsigned spvc_type_get_num_member_types(spvc_type type);
+SPVC_PUBLIC_API spvc_type_id spvc_type_get_member_type(spvc_type type, unsigned index);
+SPVC_PUBLIC_API SpvStorageClass spvc_type_get_storage_class(spvc_type type);
+
+/* Image type query. */
+SPVC_PUBLIC_API spvc_type_id spvc_type_get_image_sampled_type(spvc_type type);
+SPVC_PUBLIC_API SpvDim spvc_type_get_image_dimension(spvc_type type);
+SPVC_PUBLIC_API spvc_bool spvc_type_get_image_is_depth(spvc_type type);
+SPVC_PUBLIC_API spvc_bool spvc_type_get_image_arrayed(spvc_type type);
+SPVC_PUBLIC_API spvc_bool spvc_type_get_image_multisampled(spvc_type type);
+SPVC_PUBLIC_API spvc_bool spvc_type_get_image_is_storage(spvc_type type);
+SPVC_PUBLIC_API SpvImageFormat spvc_type_get_image_storage_format(spvc_type type);
+SPVC_PUBLIC_API SpvAccessQualifier spvc_type_get_image_access_qualifier(spvc_type type);
+
+/*
+ * Buffer layout query.
+ * Maps to C++ API.
+ */
+SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_struct_size(spvc_compiler compiler, spvc_type struct_type, size_t *size);
+SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_struct_size_runtime_array(spvc_compiler compiler,
+ spvc_type struct_type, size_t array_size, size_t *size);
+
+SPVC_PUBLIC_API spvc_result spvc_compiler_type_struct_member_offset(spvc_compiler compiler,
+ spvc_type type, unsigned index, unsigned *offset);
+SPVC_PUBLIC_API spvc_result spvc_compiler_type_struct_member_array_stride(spvc_compiler compiler,
+ spvc_type type, unsigned index, unsigned *stride);
+SPVC_PUBLIC_API spvc_result spvc_compiler_type_struct_member_matrix_stride(spvc_compiler compiler,
+ spvc_type type, unsigned index, unsigned *stride);
+
+/*
+ * Workaround helper functions.
+ * Maps to C++ API.
+ */
+SPVC_PUBLIC_API spvc_result spvc_compiler_build_dummy_sampler_for_combined_images(spvc_compiler compiler, spvc_variable_id *id);
+SPVC_PUBLIC_API spvc_result spvc_compiler_build_combined_image_samplers(spvc_compiler compiler);
+SPVC_PUBLIC_API spvc_result spvc_compiler_get_combined_image_samplers(spvc_compiler compiler,
+ const spvc_combined_image_sampler **samplers,
+ size_t *num_samplers);
+
+/*
+ * Constants
+ * Maps to C++ API.
+ */
+SPVC_PUBLIC_API spvc_result spvc_compiler_get_specialization_constants(spvc_compiler compiler,
+ const spvc_specialization_constant **constants,
+ size_t *num_constants);
+SPVC_PUBLIC_API spvc_constant spvc_compiler_get_constant_handle(spvc_compiler compiler,
+ spvc_constant_id id);
+
+SPVC_PUBLIC_API spvc_constant_id spvc_compiler_get_work_group_size_specialization_constants(spvc_compiler compiler,
+ spvc_specialization_constant *x,
+ spvc_specialization_constant *y,
+ spvc_specialization_constant *z);
+
+/*
+ * No stdint.h until C99, sigh :(
+ * For smaller types, the result is sign or zero-extended as appropriate.
+ * Maps to C++ API.
+ * TODO: The SPIRConstant query interface and modification interface is not quite complete.
+ */
+SPVC_PUBLIC_API float spvc_constant_get_scalar_fp16(spvc_constant constant, unsigned column, unsigned row);
+SPVC_PUBLIC_API float spvc_constant_get_scalar_fp32(spvc_constant constant, unsigned column, unsigned row);
+SPVC_PUBLIC_API double spvc_constant_get_scalar_fp64(spvc_constant constant, unsigned column, unsigned row);
+SPVC_PUBLIC_API unsigned spvc_constant_get_scalar_u32(spvc_constant constant, unsigned column, unsigned row);
+SPVC_PUBLIC_API int spvc_constant_get_scalar_i32(spvc_constant constant, unsigned column, unsigned row);
+SPVC_PUBLIC_API unsigned spvc_constant_get_scalar_u16(spvc_constant constant, unsigned column, unsigned row);
+SPVC_PUBLIC_API int spvc_constant_get_scalar_i16(spvc_constant constant, unsigned column, unsigned row);
+SPVC_PUBLIC_API unsigned spvc_constant_get_scalar_u8(spvc_constant constant, unsigned column, unsigned row);
+SPVC_PUBLIC_API int spvc_constant_get_scalar_i8(spvc_constant constant, unsigned column, unsigned row);
+SPVC_PUBLIC_API void spvc_constant_get_subconstants(spvc_constant constant, const spvc_constant_id **constituents, size_t *count);
+SPVC_PUBLIC_API spvc_type_id spvc_constant_get_type(spvc_constant constant);
+
+/*
+ * Misc reflection
+ * Maps to C++ API.
+ */
+SPVC_PUBLIC_API spvc_bool spvc_compiler_get_binary_offset_for_decoration(spvc_compiler compiler,
+ spvc_variable_id id,
+ SpvDecoration decoration,
+ unsigned *word_offset);
+
+SPVC_PUBLIC_API spvc_bool spvc_compiler_buffer_is_hlsl_counter_buffer(spvc_compiler compiler, spvc_variable_id id);
+SPVC_PUBLIC_API spvc_bool spvc_compiler_buffer_get_hlsl_counter_buffer(spvc_compiler compiler, spvc_variable_id id,
+ spvc_variable_id *counter_id);
+
+SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_capabilities(spvc_compiler compiler,
+ const SpvCapability **capabilities,
+ size_t *num_capabilities);
+SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_extensions(spvc_compiler compiler, const char ***extensions,
+ size_t *num_extensions);
+
+SPVC_PUBLIC_API const char *spvc_compiler_get_remapped_declared_block_name(spvc_compiler compiler, spvc_variable_id id);
+SPVC_PUBLIC_API spvc_result spvc_compiler_get_buffer_block_decorations(spvc_compiler compiler, spvc_variable_id id,
+ const SpvDecoration **decorations,
+ size_t *num_decorations);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/3rdparty/SPIRV-Cross/spirv_cross_parsed_ir.cpp b/src/3rdparty/SPIRV-Cross/spirv_cross_parsed_ir.cpp
new file mode 100644
index 0000000..f17c2be
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_cross_parsed_ir.cpp
@@ -0,0 +1,648 @@
+/*
+ * Copyright 2018-2019 Arm Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "spirv_cross_parsed_ir.hpp"
+#include <algorithm>
+#include <assert.h>
+
+using namespace std;
+using namespace spv;
+
+namespace spirv_cross
+{
+void ParsedIR::set_id_bounds(uint32_t bounds)
+{
+ ids.resize(bounds);
+ block_meta.resize(bounds);
+}
+
+static string ensure_valid_identifier(const string &name, bool member)
+{
+ // Functions in glslangValidator are mangled with name(<mangled> stuff.
+ // Normally, we would never see '(' in any legal identifiers, so just strip them out.
+ auto str = name.substr(0, name.find('('));
+
+ for (uint32_t i = 0; i < str.size(); i++)
+ {
+ auto &c = str[i];
+
+ if (member)
+ {
+ // _m<num> variables are reserved by the internal implementation,
+ // otherwise, make sure the name is a valid identifier.
+ if (i == 0)
+ c = isalpha(c) ? c : '_';
+ else if (i == 2 && str[0] == '_' && str[1] == 'm')
+ c = isalpha(c) ? c : '_';
+ else
+ c = isalnum(c) ? c : '_';
+ }
+ else
+ {
+ // _<num> variables are reserved by the internal implementation,
+ // otherwise, make sure the name is a valid identifier.
+ if (i == 0 || (str[0] == '_' && i == 1))
+ c = isalpha(c) ? c : '_';
+ else
+ c = isalnum(c) ? c : '_';
+ }
+ }
+ return str;
+}
+
+const string &ParsedIR::get_name(uint32_t id) const
+{
+ auto *m = find_meta(id);
+ if (m)
+ return m->decoration.alias;
+ else
+ return empty_string;
+}
+
+const string &ParsedIR::get_member_name(uint32_t id, uint32_t index) const
+{
+ auto *m = find_meta(id);
+ if (m)
+ {
+ if (index >= m->members.size())
+ return empty_string;
+ return m->members[index].alias;
+ }
+ else
+ return empty_string;
+}
+
+void ParsedIR::set_name(uint32_t id, const string &name)
+{
+ auto &str = meta[id].decoration.alias;
+ str.clear();
+
+ if (name.empty())
+ return;
+
+ // Reserved for temporaries.
+ if (name[0] == '_' && name.size() >= 2 && isdigit(name[1]))
+ return;
+
+ str = ensure_valid_identifier(name, false);
+}
+
+void ParsedIR::set_member_name(uint32_t id, uint32_t index, const string &name)
+{
+ meta[id].members.resize(max(meta[id].members.size(), size_t(index) + 1));
+
+ auto &str = meta[id].members[index].alias;
+ str.clear();
+ if (name.empty())
+ return;
+
+ // Reserved for unnamed members.
+ if (name[0] == '_' && name.size() >= 3 && name[1] == 'm' && isdigit(name[2]))
+ return;
+
+ str = ensure_valid_identifier(name, true);
+}
+
+void ParsedIR::set_decoration_string(uint32_t id, Decoration decoration, const string &argument)
+{
+ auto &dec = meta[id].decoration;
+ dec.decoration_flags.set(decoration);
+
+ switch (decoration)
+ {
+ case DecorationHlslSemanticGOOGLE:
+ dec.hlsl_semantic = argument;
+ break;
+
+ default:
+ break;
+ }
+}
+
+void ParsedIR::set_decoration(uint32_t id, Decoration decoration, uint32_t argument)
+{
+ auto &dec = meta[id].decoration;
+ dec.decoration_flags.set(decoration);
+
+ switch (decoration)
+ {
+ case DecorationBuiltIn:
+ dec.builtin = true;
+ dec.builtin_type = static_cast<BuiltIn>(argument);
+ break;
+
+ case DecorationLocation:
+ dec.location = argument;
+ break;
+
+ case DecorationComponent:
+ dec.component = argument;
+ break;
+
+ case DecorationOffset:
+ dec.offset = argument;
+ break;
+
+ case DecorationArrayStride:
+ dec.array_stride = argument;
+ break;
+
+ case DecorationMatrixStride:
+ dec.matrix_stride = argument;
+ break;
+
+ case DecorationBinding:
+ dec.binding = argument;
+ break;
+
+ case DecorationDescriptorSet:
+ dec.set = argument;
+ break;
+
+ case DecorationInputAttachmentIndex:
+ dec.input_attachment = argument;
+ break;
+
+ case DecorationSpecId:
+ dec.spec_id = argument;
+ break;
+
+ case DecorationIndex:
+ dec.index = argument;
+ break;
+
+ case DecorationHlslCounterBufferGOOGLE:
+ meta[id].hlsl_magic_counter_buffer = argument;
+ meta[argument].hlsl_is_magic_counter_buffer = true;
+ break;
+
+ case DecorationFPRoundingMode:
+ dec.fp_rounding_mode = static_cast<FPRoundingMode>(argument);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void ParsedIR::set_member_decoration(uint32_t id, uint32_t index, Decoration decoration, uint32_t argument)
+{
+ meta[id].members.resize(max(meta[id].members.size(), size_t(index) + 1));
+ auto &dec = meta[id].members[index];
+ dec.decoration_flags.set(decoration);
+
+ switch (decoration)
+ {
+ case DecorationBuiltIn:
+ dec.builtin = true;
+ dec.builtin_type = static_cast<BuiltIn>(argument);
+ break;
+
+ case DecorationLocation:
+ dec.location = argument;
+ break;
+
+ case DecorationComponent:
+ dec.component = argument;
+ break;
+
+ case DecorationBinding:
+ dec.binding = argument;
+ break;
+
+ case DecorationOffset:
+ dec.offset = argument;
+ break;
+
+ case DecorationSpecId:
+ dec.spec_id = argument;
+ break;
+
+ case DecorationMatrixStride:
+ dec.matrix_stride = argument;
+ break;
+
+ case DecorationIndex:
+ dec.index = argument;
+ break;
+
+ default:
+ break;
+ }
+}
+
+// Recursively marks any constants referenced by the specified constant instruction as being used
+// as an array length. The id must be a constant instruction (SPIRConstant or SPIRConstantOp).
+void ParsedIR::mark_used_as_array_length(uint32_t id)
+{
+ switch (ids[id].get_type())
+ {
+ case TypeConstant:
+ get<SPIRConstant>(id).is_used_as_array_length = true;
+ break;
+
+ case TypeConstantOp:
+ {
+ auto &cop = get<SPIRConstantOp>(id);
+ for (uint32_t arg_id : cop.arguments)
+ mark_used_as_array_length(arg_id);
+ break;
+ }
+
+ case TypeUndef:
+ break;
+
+ default:
+ assert(0);
+ }
+}
+
+Bitset ParsedIR::get_buffer_block_flags(const SPIRVariable &var) const
+{
+ auto &type = get<SPIRType>(var.basetype);
+ assert(type.basetype == SPIRType::Struct);
+
+ // Some flags like non-writable, non-readable are actually found
+ // as member decorations. If all members have a decoration set, propagate
+ // the decoration up as a regular variable decoration.
+ Bitset base_flags;
+ auto *m = find_meta(var.self);
+ if (m)
+ base_flags = m->decoration.decoration_flags;
+
+ if (type.member_types.empty())
+ return base_flags;
+
+ Bitset all_members_flags = get_member_decoration_bitset(type.self, 0);
+ for (uint32_t i = 1; i < uint32_t(type.member_types.size()); i++)
+ all_members_flags.merge_and(get_member_decoration_bitset(type.self, i));
+
+ base_flags.merge_or(all_members_flags);
+ return base_flags;
+}
+
+const Bitset &ParsedIR::get_member_decoration_bitset(uint32_t id, uint32_t index) const
+{
+ auto *m = find_meta(id);
+ if (m)
+ {
+ if (index >= m->members.size())
+ return cleared_bitset;
+ return m->members[index].decoration_flags;
+ }
+ else
+ return cleared_bitset;
+}
+
+bool ParsedIR::has_decoration(uint32_t id, Decoration decoration) const
+{
+ return get_decoration_bitset(id).get(decoration);
+}
+
+uint32_t ParsedIR::get_decoration(uint32_t id, Decoration decoration) const
+{
+ auto *m = find_meta(id);
+ if (!m)
+ return 0;
+
+ auto &dec = m->decoration;
+ if (!dec.decoration_flags.get(decoration))
+ return 0;
+
+ switch (decoration)
+ {
+ case DecorationBuiltIn:
+ return dec.builtin_type;
+ case DecorationLocation:
+ return dec.location;
+ case DecorationComponent:
+ return dec.component;
+ case DecorationOffset:
+ return dec.offset;
+ case DecorationBinding:
+ return dec.binding;
+ case DecorationDescriptorSet:
+ return dec.set;
+ case DecorationInputAttachmentIndex:
+ return dec.input_attachment;
+ case DecorationSpecId:
+ return dec.spec_id;
+ case DecorationArrayStride:
+ return dec.array_stride;
+ case DecorationMatrixStride:
+ return dec.matrix_stride;
+ case DecorationIndex:
+ return dec.index;
+ case DecorationFPRoundingMode:
+ return dec.fp_rounding_mode;
+ default:
+ return 1;
+ }
+}
+
+const string &ParsedIR::get_decoration_string(uint32_t id, Decoration decoration) const
+{
+ auto *m = find_meta(id);
+ if (!m)
+ return empty_string;
+
+ auto &dec = m->decoration;
+
+ if (!dec.decoration_flags.get(decoration))
+ return empty_string;
+
+ switch (decoration)
+ {
+ case DecorationHlslSemanticGOOGLE:
+ return dec.hlsl_semantic;
+
+ default:
+ return empty_string;
+ }
+}
+
+void ParsedIR::unset_decoration(uint32_t id, Decoration decoration)
+{
+ auto &dec = meta[id].decoration;
+ dec.decoration_flags.clear(decoration);
+ switch (decoration)
+ {
+ case DecorationBuiltIn:
+ dec.builtin = false;
+ break;
+
+ case DecorationLocation:
+ dec.location = 0;
+ break;
+
+ case DecorationComponent:
+ dec.component = 0;
+ break;
+
+ case DecorationOffset:
+ dec.offset = 0;
+ break;
+
+ case DecorationBinding:
+ dec.binding = 0;
+ break;
+
+ case DecorationDescriptorSet:
+ dec.set = 0;
+ break;
+
+ case DecorationInputAttachmentIndex:
+ dec.input_attachment = 0;
+ break;
+
+ case DecorationSpecId:
+ dec.spec_id = 0;
+ break;
+
+ case DecorationHlslSemanticGOOGLE:
+ dec.hlsl_semantic.clear();
+ break;
+
+ case DecorationFPRoundingMode:
+ dec.fp_rounding_mode = FPRoundingModeMax;
+ break;
+
+ case DecorationHlslCounterBufferGOOGLE:
+ {
+ auto &counter = meta[id].hlsl_magic_counter_buffer;
+ if (counter)
+ {
+ meta[counter].hlsl_is_magic_counter_buffer = false;
+ counter = 0;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+bool ParsedIR::has_member_decoration(uint32_t id, uint32_t index, Decoration decoration) const
+{
+ return get_member_decoration_bitset(id, index).get(decoration);
+}
+
+uint32_t ParsedIR::get_member_decoration(uint32_t id, uint32_t index, Decoration decoration) const
+{
+ auto *m = find_meta(id);
+ if (!m)
+ return 0;
+
+ if (index >= m->members.size())
+ return 0;
+
+ auto &dec = m->members[index];
+ if (!dec.decoration_flags.get(decoration))
+ return 0;
+
+ switch (decoration)
+ {
+ case DecorationBuiltIn:
+ return dec.builtin_type;
+ case DecorationLocation:
+ return dec.location;
+ case DecorationComponent:
+ return dec.component;
+ case DecorationBinding:
+ return dec.binding;
+ case DecorationOffset:
+ return dec.offset;
+ case DecorationSpecId:
+ return dec.spec_id;
+ case DecorationIndex:
+ return dec.index;
+ default:
+ return 1;
+ }
+}
+
+const Bitset &ParsedIR::get_decoration_bitset(uint32_t id) const
+{
+ auto *m = find_meta(id);
+ if (m)
+ {
+ auto &dec = m->decoration;
+ return dec.decoration_flags;
+ }
+ else
+ return cleared_bitset;
+}
+
+void ParsedIR::set_member_decoration_string(uint32_t id, uint32_t index, Decoration decoration, const string &argument)
+{
+ meta[id].members.resize(max(meta[id].members.size(), size_t(index) + 1));
+ auto &dec = meta[id].members[index];
+ dec.decoration_flags.set(decoration);
+
+ switch (decoration)
+ {
+ case DecorationHlslSemanticGOOGLE:
+ dec.hlsl_semantic = argument;
+ break;
+
+ default:
+ break;
+ }
+}
+
+const string &ParsedIR::get_member_decoration_string(uint32_t id, uint32_t index, Decoration decoration) const
+{
+ auto *m = find_meta(id);
+ if (m)
+ {
+ if (!has_member_decoration(id, index, decoration))
+ return empty_string;
+
+ auto &dec = m->members[index];
+
+ switch (decoration)
+ {
+ case DecorationHlslSemanticGOOGLE:
+ return dec.hlsl_semantic;
+
+ default:
+ return empty_string;
+ }
+ }
+ else
+ return empty_string;
+}
+
+void ParsedIR::unset_member_decoration(uint32_t id, uint32_t index, Decoration decoration)
+{
+ auto &m = meta[id];
+ if (index >= m.members.size())
+ return;
+
+ auto &dec = m.members[index];
+
+ dec.decoration_flags.clear(decoration);
+ switch (decoration)
+ {
+ case DecorationBuiltIn:
+ dec.builtin = false;
+ break;
+
+ case DecorationLocation:
+ dec.location = 0;
+ break;
+
+ case DecorationComponent:
+ dec.component = 0;
+ break;
+
+ case DecorationOffset:
+ dec.offset = 0;
+ break;
+
+ case DecorationSpecId:
+ dec.spec_id = 0;
+ break;
+
+ case DecorationHlslSemanticGOOGLE:
+ dec.hlsl_semantic.clear();
+ break;
+
+ default:
+ break;
+ }
+}
+
+uint32_t ParsedIR::increase_bound_by(uint32_t incr_amount)
+{
+ auto curr_bound = ids.size();
+ auto new_bound = curr_bound + incr_amount;
+ ids.resize(new_bound);
+ block_meta.resize(new_bound);
+ return uint32_t(curr_bound);
+}
+
+void ParsedIR::remove_typed_id(Types type, uint32_t id)
+{
+ auto &type_ids = ids_for_type[type];
+ type_ids.erase(remove(begin(type_ids), end(type_ids), id), end(type_ids));
+}
+
+void ParsedIR::reset_all_of_type(Types type)
+{
+ for (auto &id : ids_for_type[type])
+ if (ids[id].get_type() == type)
+ ids[id].reset();
+
+ ids_for_type[type].clear();
+}
+
+void ParsedIR::add_typed_id(Types type, uint32_t id)
+{
+ if (loop_iteration_depth)
+ SPIRV_CROSS_THROW("Cannot add typed ID while looping over it.");
+
+ switch (type)
+ {
+ case TypeConstant:
+ ids_for_constant_or_variable.push_back(id);
+ ids_for_constant_or_type.push_back(id);
+ break;
+
+ case TypeVariable:
+ ids_for_constant_or_variable.push_back(id);
+ break;
+
+ case TypeType:
+ case TypeConstantOp:
+ ids_for_constant_or_type.push_back(id);
+ break;
+
+ default:
+ break;
+ }
+
+ if (ids[id].empty())
+ {
+ ids_for_type[type].push_back(id);
+ }
+ else if (ids[id].get_type() != type)
+ {
+ remove_typed_id(ids[id].get_type(), id);
+ ids_for_type[type].push_back(id);
+ }
+}
+
+const Meta *ParsedIR::find_meta(uint32_t id) const
+{
+ auto itr = meta.find(id);
+ if (itr != end(meta))
+ return &itr->second;
+ else
+ return nullptr;
+}
+
+Meta *ParsedIR::find_meta(uint32_t id)
+{
+ auto itr = meta.find(id);
+ if (itr != end(meta))
+ return &itr->second;
+ else
+ return nullptr;
+}
+
+} // namespace spirv_cross
diff --git a/src/3rdparty/SPIRV-Cross/spirv_cross_parsed_ir.hpp b/src/3rdparty/SPIRV-Cross/spirv_cross_parsed_ir.hpp
new file mode 100644
index 0000000..c3c4612
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_cross_parsed_ir.hpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2018-2019 Arm Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SPIRV_CROSS_PARSED_IR_HPP
+#define SPIRV_CROSS_PARSED_IR_HPP
+
+#include "spirv_common.hpp"
+#include <stdint.h>
+#include <unordered_map>
+#include <vector>
+
+namespace spirv_cross
+{
+
+// This data structure holds all information needed to perform cross-compilation and reflection.
+// It is the output of the Parser, but any implementation could create this structure.
+// It is intentionally very "open" and struct-like with some helper functions to deal with decorations.
+// Parser is the reference implementation of how this data structure should be filled in.
+
+class ParsedIR
+{
+public:
+ // Resizes ids, meta and block_meta.
+ void set_id_bounds(uint32_t bounds);
+
+ // The raw SPIR-V, instructions and opcodes refer to this by offset + count.
+ std::vector<uint32_t> spirv;
+
+ // Holds various data structures which inherit from IVariant.
+ std::vector<Variant> ids;
+
+ // Various meta data for IDs, decorations, names, etc.
+ std::unordered_map<uint32_t, Meta> meta;
+
+ // Holds all IDs which have a certain type.
+ // This is needed so we can iterate through a specific kind of resource quickly,
+ // and in-order of module declaration.
+ std::vector<uint32_t> ids_for_type[TypeCount];
+
+ // Special purpose lists which contain a union of types.
+ // This is needed so we can declare specialization constants and structs in an interleaved fashion,
+ // among other things.
+ // Constants can be of struct type, and struct array sizes can use specialization constants.
+ std::vector<uint32_t> ids_for_constant_or_type;
+ std::vector<uint32_t> ids_for_constant_or_variable;
+
+ // Declared capabilities and extensions in the SPIR-V module.
+ // Not really used except for reflection at the moment.
+ std::vector<spv::Capability> declared_capabilities;
+ std::vector<std::string> declared_extensions;
+
+ // Meta data about blocks. The cross-compiler needs to query if a block is either of these types.
+ // It is a bitset as there can be more than one tag per block.
+ enum BlockMetaFlagBits
+ {
+ BLOCK_META_LOOP_HEADER_BIT = 1 << 0,
+ BLOCK_META_CONTINUE_BIT = 1 << 1,
+ BLOCK_META_LOOP_MERGE_BIT = 1 << 2,
+ BLOCK_META_SELECTION_MERGE_BIT = 1 << 3,
+ BLOCK_META_MULTISELECT_MERGE_BIT = 1 << 4
+ };
+ using BlockMetaFlags = uint8_t;
+ std::vector<BlockMetaFlags> block_meta;
+ std::unordered_map<uint32_t, uint32_t> continue_block_to_loop_header;
+
+ // Normally, we'd stick SPIREntryPoint in ids array, but it conflicts with SPIRFunction.
+ // Entry points can therefore be seen as some sort of meta structure.
+ std::unordered_map<uint32_t, SPIREntryPoint> entry_points;
+ uint32_t default_entry_point = 0;
+
+ struct Source
+ {
+ uint32_t version = 0;
+ bool es = false;
+ bool known = false;
+ bool hlsl = false;
+
+ Source() = default;
+ };
+
+ Source source;
+
+ // Decoration handling methods.
+ // Can be useful for simple "raw" reflection.
+ // However, most members are here because the Parser needs most of these,
+ // and might as well just have the whole suite of decoration/name handling in one place.
+ void set_name(uint32_t id, const std::string &name);
+ const std::string &get_name(uint32_t id) const;
+ void set_decoration(uint32_t id, spv::Decoration decoration, uint32_t argument = 0);
+ void set_decoration_string(uint32_t id, spv::Decoration decoration, const std::string &argument);
+ bool has_decoration(uint32_t id, spv::Decoration decoration) const;
+ uint32_t get_decoration(uint32_t id, spv::Decoration decoration) const;
+ const std::string &get_decoration_string(uint32_t id, spv::Decoration decoration) const;
+ const Bitset &get_decoration_bitset(uint32_t id) const;
+ void unset_decoration(uint32_t id, spv::Decoration decoration);
+
+ // Decoration handling methods (for members of a struct).
+ void set_member_name(uint32_t id, uint32_t index, const std::string &name);
+ const std::string &get_member_name(uint32_t id, uint32_t index) const;
+ void set_member_decoration(uint32_t id, uint32_t index, spv::Decoration decoration, uint32_t argument = 0);
+ void set_member_decoration_string(uint32_t id, uint32_t index, spv::Decoration decoration,
+ const std::string &argument);
+ uint32_t get_member_decoration(uint32_t id, uint32_t index, spv::Decoration decoration) const;
+ const std::string &get_member_decoration_string(uint32_t id, uint32_t index, spv::Decoration decoration) const;
+ bool has_member_decoration(uint32_t id, uint32_t index, spv::Decoration decoration) const;
+ const Bitset &get_member_decoration_bitset(uint32_t id, uint32_t index) const;
+ void unset_member_decoration(uint32_t id, uint32_t index, spv::Decoration decoration);
+
+ void mark_used_as_array_length(uint32_t id);
+ uint32_t increase_bound_by(uint32_t count);
+ Bitset get_buffer_block_flags(const SPIRVariable &var) const;
+
+ void add_typed_id(Types type, uint32_t id);
+ void remove_typed_id(Types type, uint32_t id);
+
+ template <typename T, typename Op>
+ void for_each_typed_id(const Op &op)
+ {
+ loop_iteration_depth++;
+ for (auto &id : ids_for_type[T::type])
+ {
+ if (ids[id].get_type() == static_cast<Types>(T::type))
+ op(id, get<T>(id));
+ }
+ loop_iteration_depth--;
+ }
+
+ template <typename T, typename Op>
+ void for_each_typed_id(const Op &op) const
+ {
+ for (auto &id : ids_for_type[T::type])
+ {
+ if (ids[id].get_type() == static_cast<Types>(T::type))
+ op(id, get<T>(id));
+ }
+ }
+
+ template <typename T>
+ void reset_all_of_type()
+ {
+ reset_all_of_type(static_cast<Types>(T::type));
+ }
+
+ void reset_all_of_type(Types type);
+
+ Meta *find_meta(uint32_t id);
+ const Meta *find_meta(uint32_t id) const;
+
+ const std::string &get_empty_string() const
+ {
+ return empty_string;
+ }
+
+private:
+ template <typename T>
+ T &get(uint32_t id)
+ {
+ return variant_get<T>(ids[id]);
+ }
+
+ template <typename T>
+ const T &get(uint32_t id) const
+ {
+ return variant_get<T>(ids[id]);
+ }
+
+ uint32_t loop_iteration_depth = 0;
+ std::string empty_string;
+ Bitset cleared_bitset;
+};
+} // namespace spirv_cross
+
+#endif
diff --git a/src/3rdparty/SPIRV-Cross/spirv_cross_util.cpp b/src/3rdparty/SPIRV-Cross/spirv_cross_util.cpp
new file mode 100644
index 0000000..58c1ddc
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_cross_util.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2015-2019 Arm Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "spirv_cross_util.hpp"
+#include "spirv_common.hpp"
+
+using namespace spv;
+using namespace spirv_cross;
+
+namespace spirv_cross_util
+{
+void rename_interface_variable(spirv_cross::Compiler &compiler, const std::vector<spirv_cross::Resource> &resources,
+ uint32_t location, const std::string &name)
+{
+ for (auto &v : resources)
+ {
+ if (!compiler.has_decoration(v.id, spv::DecorationLocation))
+ continue;
+
+ auto loc = compiler.get_decoration(v.id, spv::DecorationLocation);
+ if (loc != location)
+ continue;
+
+ auto &type = compiler.get_type(v.base_type_id);
+
+ // This is more of a friendly variant. If we need to rename interface variables, we might have to rename
+ // structs as well and make sure all the names match up.
+ if (type.basetype == SPIRType::Struct)
+ {
+ compiler.set_name(v.base_type_id, join("SPIRV_Cross_Interface_Location", location));
+ for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
+ compiler.set_member_name(v.base_type_id, i, join("InterfaceMember", i));
+ }
+
+ compiler.set_name(v.id, name);
+ }
+}
+
+void inherit_combined_sampler_bindings(spirv_cross::Compiler &compiler)
+{
+ auto &samplers = compiler.get_combined_image_samplers();
+ for (auto &s : samplers)
+ {
+ if (compiler.has_decoration(s.image_id, spv::DecorationDescriptorSet))
+ {
+ uint32_t set = compiler.get_decoration(s.image_id, spv::DecorationDescriptorSet);
+ compiler.set_decoration(s.combined_id, spv::DecorationDescriptorSet, set);
+ }
+
+ if (compiler.has_decoration(s.image_id, spv::DecorationBinding))
+ {
+ uint32_t binding = compiler.get_decoration(s.image_id, spv::DecorationBinding);
+ compiler.set_decoration(s.combined_id, spv::DecorationBinding, binding);
+ }
+ }
+}
+} // namespace spirv_cross_util
diff --git a/src/3rdparty/SPIRV-Cross/spirv_cross_util.hpp b/src/3rdparty/SPIRV-Cross/spirv_cross_util.hpp
new file mode 100644
index 0000000..faf0f48
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_cross_util.hpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015-2019 Arm Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SPIRV_CROSS_UTIL_HPP
+#define SPIRV_CROSS_UTIL_HPP
+
+#include "spirv_cross.hpp"
+
+namespace spirv_cross_util
+{
+void rename_interface_variable(spirv_cross::Compiler &compiler, const std::vector<spirv_cross::Resource> &resources,
+ uint32_t location, const std::string &name);
+void inherit_combined_sampler_bindings(spirv_cross::Compiler &compiler);
+} // namespace spirv_cross_util
+
+#endif
diff --git a/src/3rdparty/SPIRV-Cross/spirv_glsl.cpp b/src/3rdparty/SPIRV-Cross/spirv_glsl.cpp
new file mode 100644
index 0000000..f35c7d8
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_glsl.cpp
@@ -0,0 +1,11451 @@
+/*
+ * Copyright 2015-2019 Arm Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "spirv_glsl.hpp"
+#include "GLSL.std.450.h"
+#include "spirv_common.hpp"
+#include <algorithm>
+#include <assert.h>
+#include <cmath>
+#include <limits>
+#include <locale.h>
+#include <utility>
+
+#ifndef _WIN32
+#include <langinfo.h>
+#endif
+#include <locale.h>
+
+using namespace spv;
+using namespace spirv_cross;
+using namespace std;
+
+static bool is_unsigned_opcode(Op op)
+{
+ // Don't have to be exhaustive, only relevant for legacy target checking ...
+ switch (op)
+ {
+ case OpShiftRightLogical:
+ case OpUGreaterThan:
+ case OpUGreaterThanEqual:
+ case OpULessThan:
+ case OpULessThanEqual:
+ case OpUConvert:
+ case OpUDiv:
+ case OpUMod:
+ case OpUMulExtended:
+ case OpConvertUToF:
+ case OpConvertFToU:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool is_unsigned_glsl_opcode(GLSLstd450 op)
+{
+ // Don't have to be exhaustive, only relevant for legacy target checking ...
+ switch (op)
+ {
+ case GLSLstd450UClamp:
+ case GLSLstd450UMin:
+ case GLSLstd450UMax:
+ case GLSLstd450FindUMsb:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool packing_is_vec4_padded(BufferPackingStandard packing)
+{
+ switch (packing)
+ {
+ case BufferPackingHLSLCbuffer:
+ case BufferPackingHLSLCbufferPackOffset:
+ case BufferPackingStd140:
+ case BufferPackingStd140EnhancedLayout:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool packing_is_hlsl(BufferPackingStandard packing)
+{
+ switch (packing)
+ {
+ case BufferPackingHLSLCbuffer:
+ case BufferPackingHLSLCbufferPackOffset:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool packing_has_flexible_offset(BufferPackingStandard packing)
+{
+ switch (packing)
+ {
+ case BufferPackingStd140:
+ case BufferPackingStd430:
+ case BufferPackingHLSLCbuffer:
+ return false;
+
+ default:
+ return true;
+ }
+}
+
+static BufferPackingStandard packing_to_substruct_packing(BufferPackingStandard packing)
+{
+ switch (packing)
+ {
+ case BufferPackingStd140EnhancedLayout:
+ return BufferPackingStd140;
+ case BufferPackingStd430EnhancedLayout:
+ return BufferPackingStd430;
+ case BufferPackingHLSLCbufferPackOffset:
+ return BufferPackingHLSLCbuffer;
+ default:
+ return packing;
+ }
+}
+
+// Sanitizes underscores for GLSL where multiple underscores in a row are not allowed.
+string CompilerGLSL::sanitize_underscores(const string &str)
+{
+ string res;
+ res.reserve(str.size());
+
+ bool last_underscore = false;
+ for (auto c : str)
+ {
+ if (c == '_')
+ {
+ if (last_underscore)
+ continue;
+
+ res += c;
+ last_underscore = true;
+ }
+ else
+ {
+ res += c;
+ last_underscore = false;
+ }
+ }
+ return res;
+}
+
+void CompilerGLSL::init()
+{
+ if (ir.source.known)
+ {
+ options.es = ir.source.es;
+ options.version = ir.source.version;
+ }
+
+ // Query the locale to see what the decimal point is.
+ // We'll rely on fixing it up ourselves in the rare case we have a comma-as-decimal locale
+ // rather than setting locales ourselves. Settings locales in a safe and isolated way is rather
+ // tricky.
+#ifdef _WIN32
+ // On Windows, localeconv uses thread-local storage, so it should be fine.
+ const struct lconv *conv = localeconv();
+ if (conv && conv->decimal_point)
+ current_locale_radix_character = *conv->decimal_point;
+#elif defined(__ANDROID__) && __ANDROID_API__ < 26
+ // nl_langinfo is not supported on this platform, fall back to the worse alternative.
+ const struct lconv *conv = localeconv();
+ if (conv && conv->decimal_point)
+ current_locale_radix_character = *conv->decimal_point;
+#else
+ // localeconv, the portable function is not MT safe ...
+ const char *decimal_point = nl_langinfo(RADIXCHAR);
+ if (decimal_point && *decimal_point != '\0')
+ current_locale_radix_character = *decimal_point;
+#endif
+}
+
+static const char *to_pls_layout(PlsFormat format)
+{
+ switch (format)
+ {
+ case PlsR11FG11FB10F:
+ return "layout(r11f_g11f_b10f) ";
+ case PlsR32F:
+ return "layout(r32f) ";
+ case PlsRG16F:
+ return "layout(rg16f) ";
+ case PlsRGB10A2:
+ return "layout(rgb10_a2) ";
+ case PlsRGBA8:
+ return "layout(rgba8) ";
+ case PlsRG16:
+ return "layout(rg16) ";
+ case PlsRGBA8I:
+ return "layout(rgba8i)";
+ case PlsRG16I:
+ return "layout(rg16i) ";
+ case PlsRGB10A2UI:
+ return "layout(rgb10_a2ui) ";
+ case PlsRGBA8UI:
+ return "layout(rgba8ui) ";
+ case PlsRG16UI:
+ return "layout(rg16ui) ";
+ case PlsR32UI:
+ return "layout(r32ui) ";
+ default:
+ return "";
+ }
+}
+
+static SPIRType::BaseType pls_format_to_basetype(PlsFormat format)
+{
+ switch (format)
+ {
+ default:
+ case PlsR11FG11FB10F:
+ case PlsR32F:
+ case PlsRG16F:
+ case PlsRGB10A2:
+ case PlsRGBA8:
+ case PlsRG16:
+ return SPIRType::Float;
+
+ case PlsRGBA8I:
+ case PlsRG16I:
+ return SPIRType::Int;
+
+ case PlsRGB10A2UI:
+ case PlsRGBA8UI:
+ case PlsRG16UI:
+ case PlsR32UI:
+ return SPIRType::UInt;
+ }
+}
+
+static uint32_t pls_format_to_components(PlsFormat format)
+{
+ switch (format)
+ {
+ default:
+ case PlsR32F:
+ case PlsR32UI:
+ return 1;
+
+ case PlsRG16F:
+ case PlsRG16:
+ case PlsRG16UI:
+ case PlsRG16I:
+ return 2;
+
+ case PlsR11FG11FB10F:
+ return 3;
+
+ case PlsRGB10A2:
+ case PlsRGBA8:
+ case PlsRGBA8I:
+ case PlsRGB10A2UI:
+ case PlsRGBA8UI:
+ return 4;
+ }
+}
+
+static const char *vector_swizzle(int vecsize, int index)
+{
+ static const char *swizzle[4][4] = {
+ { ".x", ".y", ".z", ".w" }, { ".xy", ".yz", ".zw" }, { ".xyz", ".yzw" }, { "" }
+ };
+
+ assert(vecsize >= 1 && vecsize <= 4);
+ assert(index >= 0 && index < 4);
+ assert(swizzle[vecsize - 1][index]);
+
+ return swizzle[vecsize - 1][index];
+}
+
+void CompilerGLSL::reset()
+{
+ // We do some speculative optimizations which should pretty much always work out,
+ // but just in case the SPIR-V is rather weird, recompile until it's happy.
+ // This typically only means one extra pass.
+ force_recompile = false;
+
+ // Clear invalid expression tracking.
+ invalid_expressions.clear();
+ current_function = nullptr;
+
+ // Clear temporary usage tracking.
+ expression_usage_counts.clear();
+ forwarded_temporaries.clear();
+
+ reset_name_caches();
+
+ ir.for_each_typed_id<SPIRFunction>([&](uint32_t, SPIRFunction &func) {
+ func.active = false;
+ func.flush_undeclared = true;
+ });
+
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) { var.dependees.clear(); });
+
+ ir.reset_all_of_type<SPIRExpression>();
+ ir.reset_all_of_type<SPIRAccessChain>();
+
+ statement_count = 0;
+ indent = 0;
+}
+
+void CompilerGLSL::remap_pls_variables()
+{
+ for (auto &input : pls_inputs)
+ {
+ auto &var = get<SPIRVariable>(input.id);
+
+ bool input_is_target = false;
+ if (var.storage == StorageClassUniformConstant)
+ {
+ auto &type = get<SPIRType>(var.basetype);
+ input_is_target = type.image.dim == DimSubpassData;
+ }
+
+ if (var.storage != StorageClassInput && !input_is_target)
+ SPIRV_CROSS_THROW("Can only use in and target variables for PLS inputs.");
+ var.remapped_variable = true;
+ }
+
+ for (auto &output : pls_outputs)
+ {
+ auto &var = get<SPIRVariable>(output.id);
+ if (var.storage != StorageClassOutput)
+ SPIRV_CROSS_THROW("Can only use out variables for PLS outputs.");
+ var.remapped_variable = true;
+ }
+}
+
+void CompilerGLSL::find_static_extensions()
+{
+ ir.for_each_typed_id<SPIRType>([&](uint32_t, const SPIRType &type) {
+ if (type.basetype == SPIRType::Double)
+ {
+ if (options.es)
+ SPIRV_CROSS_THROW("FP64 not supported in ES profile.");
+ if (!options.es && options.version < 400)
+ require_extension_internal("GL_ARB_gpu_shader_fp64");
+ }
+ else if (type.basetype == SPIRType::Int64 || type.basetype == SPIRType::UInt64)
+ {
+ if (options.es)
+ SPIRV_CROSS_THROW("64-bit integers not supported in ES profile.");
+ if (!options.es)
+ require_extension_internal("GL_ARB_gpu_shader_int64");
+ }
+ else if (type.basetype == SPIRType::Half)
+ {
+ require_extension_internal("GL_EXT_shader_explicit_arithmetic_types_float16");
+ if (options.vulkan_semantics)
+ require_extension_internal("GL_EXT_shader_16bit_storage");
+ }
+ else if (type.basetype == SPIRType::SByte || type.basetype == SPIRType::UByte)
+ {
+ require_extension_internal("GL_EXT_shader_explicit_arithmetic_types_int8");
+ if (options.vulkan_semantics)
+ require_extension_internal("GL_EXT_shader_8bit_storage");
+ }
+ else if (type.basetype == SPIRType::Short || type.basetype == SPIRType::UShort)
+ {
+ require_extension_internal("GL_EXT_shader_explicit_arithmetic_types_int16");
+ if (options.vulkan_semantics)
+ require_extension_internal("GL_EXT_shader_16bit_storage");
+ }
+ });
+
+ auto &execution = get_entry_point();
+ switch (execution.model)
+ {
+ case ExecutionModelGLCompute:
+ if (!options.es && options.version < 430)
+ require_extension_internal("GL_ARB_compute_shader");
+ if (options.es && options.version < 310)
+ SPIRV_CROSS_THROW("At least ESSL 3.10 required for compute shaders.");
+ break;
+
+ case ExecutionModelGeometry:
+ if (options.es && options.version < 320)
+ require_extension_internal("GL_EXT_geometry_shader");
+ if (!options.es && options.version < 150)
+ require_extension_internal("GL_ARB_geometry_shader4");
+
+ if (execution.flags.get(ExecutionModeInvocations) && execution.invocations != 1)
+ {
+ // Instanced GS is part of 400 core or this extension.
+ if (!options.es && options.version < 400)
+ require_extension_internal("GL_ARB_gpu_shader5");
+ }
+ break;
+
+ case ExecutionModelTessellationEvaluation:
+ case ExecutionModelTessellationControl:
+ if (options.es && options.version < 320)
+ require_extension_internal("GL_EXT_tessellation_shader");
+ if (!options.es && options.version < 400)
+ require_extension_internal("GL_ARB_tessellation_shader");
+ break;
+
+ case ExecutionModelRayGenerationNV:
+ case ExecutionModelIntersectionNV:
+ case ExecutionModelAnyHitNV:
+ case ExecutionModelClosestHitNV:
+ case ExecutionModelMissNV:
+ case ExecutionModelCallableNV:
+ if (options.es || options.version < 460)
+ SPIRV_CROSS_THROW("Ray tracing shaders require non-es profile with version 460 or above.");
+ require_extension_internal("GL_NV_ray_tracing");
+ break;
+
+ default:
+ break;
+ }
+
+ if (!pls_inputs.empty() || !pls_outputs.empty())
+ require_extension_internal("GL_EXT_shader_pixel_local_storage");
+
+ if (options.separate_shader_objects && !options.es && options.version < 410)
+ require_extension_internal("GL_ARB_separate_shader_objects");
+}
+
+string CompilerGLSL::compile()
+{
+ if (options.vulkan_semantics)
+ backend.allow_precision_qualifiers = true;
+ backend.force_gl_in_out_block = true;
+ backend.supports_extensions = true;
+
+ // Scan the SPIR-V to find trivial uses of extensions.
+ build_function_control_flow_graphs_and_analyze();
+ find_static_extensions();
+ fixup_image_load_store_access();
+ update_active_builtins();
+ analyze_image_and_sampler_usage();
+
+ uint32_t pass_count = 0;
+ do
+ {
+ if (pass_count >= 3)
+ SPIRV_CROSS_THROW("Over 3 compilation loops detected. Must be a bug!");
+
+ reset();
+
+ // Move constructor for this type is broken on GCC 4.9 ...
+ buffer = unique_ptr<ostringstream>(new ostringstream());
+
+ emit_header();
+ emit_resources();
+
+ emit_function(get<SPIRFunction>(ir.default_entry_point), Bitset());
+
+ pass_count++;
+ } while (force_recompile);
+
+ // Entry point in GLSL is always main().
+ get_entry_point().name = "main";
+
+ return buffer->str();
+}
+
+std::string CompilerGLSL::get_partial_source()
+{
+ return buffer ? buffer->str() : "No compiled source available yet.";
+}
+
+void CompilerGLSL::build_workgroup_size(vector<string> &arguments, const SpecializationConstant &wg_x,
+ const SpecializationConstant &wg_y, const SpecializationConstant &wg_z)
+{
+ auto &execution = get_entry_point();
+
+ if (wg_x.id)
+ {
+ if (options.vulkan_semantics)
+ arguments.push_back(join("local_size_x_id = ", wg_x.constant_id));
+ else
+ arguments.push_back(join("local_size_x = ", get<SPIRConstant>(wg_x.id).specialization_constant_macro_name));
+ }
+ else
+ arguments.push_back(join("local_size_x = ", execution.workgroup_size.x));
+
+ if (wg_y.id)
+ {
+ if (options.vulkan_semantics)
+ arguments.push_back(join("local_size_y_id = ", wg_y.constant_id));
+ else
+ arguments.push_back(join("local_size_y = ", get<SPIRConstant>(wg_y.id).specialization_constant_macro_name));
+ }
+ else
+ arguments.push_back(join("local_size_y = ", execution.workgroup_size.y));
+
+ if (wg_z.id)
+ {
+ if (options.vulkan_semantics)
+ arguments.push_back(join("local_size_z_id = ", wg_z.constant_id));
+ else
+ arguments.push_back(join("local_size_z = ", get<SPIRConstant>(wg_z.id).specialization_constant_macro_name));
+ }
+ else
+ arguments.push_back(join("local_size_z = ", execution.workgroup_size.z));
+}
+
+void CompilerGLSL::emit_header()
+{
+ auto &execution = get_entry_point();
+ statement("#version ", options.version, options.es && options.version > 100 ? " es" : "");
+
+ if (!options.es && options.version < 420)
+ {
+ // Needed for binding = # on UBOs, etc.
+ if (options.enable_420pack_extension)
+ {
+ statement("#ifdef GL_ARB_shading_language_420pack");
+ statement("#extension GL_ARB_shading_language_420pack : require");
+ statement("#endif");
+ }
+ // Needed for: layout(early_fragment_tests) in;
+ if (execution.flags.get(ExecutionModeEarlyFragmentTests))
+ require_extension_internal("GL_ARB_shader_image_load_store");
+ }
+
+ for (auto &ext : forced_extensions)
+ {
+ if (ext == "GL_EXT_shader_explicit_arithmetic_types_float16")
+ {
+ // Special case, this extension has a potential fallback to another vendor extension in normal GLSL.
+ // GL_AMD_gpu_shader_half_float is a superset, so try that first.
+ statement("#if defined(GL_AMD_gpu_shader_half_float)");
+ statement("#extension GL_AMD_gpu_shader_half_float : require");
+ if (!options.vulkan_semantics)
+ {
+ statement("#elif defined(GL_NV_gpu_shader5)");
+ statement("#extension GL_NV_gpu_shader5 : require");
+ }
+ else
+ {
+ statement("#elif defined(GL_EXT_shader_explicit_arithmetic_types_float16)");
+ statement("#extension GL_EXT_shader_explicit_arithmetic_types_float16 : require");
+ }
+ statement("#else");
+ statement("#error No extension available for FP16.");
+ statement("#endif");
+ }
+ else if (ext == "GL_EXT_shader_explicit_arithmetic_types_int16")
+ {
+ if (options.vulkan_semantics)
+ statement("#extension GL_EXT_shader_explicit_arithmetic_types_int16 : require");
+ else
+ {
+ statement("#if defined(GL_AMD_gpu_shader_int16)");
+ statement("#extension GL_AMD_gpu_shader_int16 : require");
+ statement("#else");
+ statement("#error No extension available for Int16.");
+ statement("#endif");
+ }
+ }
+ else
+ statement("#extension ", ext, " : require");
+ }
+
+ for (auto &header : header_lines)
+ statement(header);
+
+ vector<string> inputs;
+ vector<string> outputs;
+
+ switch (execution.model)
+ {
+ case ExecutionModelGeometry:
+ outputs.push_back(join("max_vertices = ", execution.output_vertices));
+ if ((execution.flags.get(ExecutionModeInvocations)) && execution.invocations != 1)
+ inputs.push_back(join("invocations = ", execution.invocations));
+ if (execution.flags.get(ExecutionModeInputPoints))
+ inputs.push_back("points");
+ if (execution.flags.get(ExecutionModeInputLines))
+ inputs.push_back("lines");
+ if (execution.flags.get(ExecutionModeInputLinesAdjacency))
+ inputs.push_back("lines_adjacency");
+ if (execution.flags.get(ExecutionModeTriangles))
+ inputs.push_back("triangles");
+ if (execution.flags.get(ExecutionModeInputTrianglesAdjacency))
+ inputs.push_back("triangles_adjacency");
+ if (execution.flags.get(ExecutionModeOutputTriangleStrip))
+ outputs.push_back("triangle_strip");
+ if (execution.flags.get(ExecutionModeOutputPoints))
+ outputs.push_back("points");
+ if (execution.flags.get(ExecutionModeOutputLineStrip))
+ outputs.push_back("line_strip");
+ break;
+
+ case ExecutionModelTessellationControl:
+ if (execution.flags.get(ExecutionModeOutputVertices))
+ outputs.push_back(join("vertices = ", execution.output_vertices));
+ break;
+
+ case ExecutionModelTessellationEvaluation:
+ if (execution.flags.get(ExecutionModeQuads))
+ inputs.push_back("quads");
+ if (execution.flags.get(ExecutionModeTriangles))
+ inputs.push_back("triangles");
+ if (execution.flags.get(ExecutionModeIsolines))
+ inputs.push_back("isolines");
+ if (execution.flags.get(ExecutionModePointMode))
+ inputs.push_back("point_mode");
+
+ if (!execution.flags.get(ExecutionModeIsolines))
+ {
+ if (execution.flags.get(ExecutionModeVertexOrderCw))
+ inputs.push_back("cw");
+ if (execution.flags.get(ExecutionModeVertexOrderCcw))
+ inputs.push_back("ccw");
+ }
+
+ if (execution.flags.get(ExecutionModeSpacingFractionalEven))
+ inputs.push_back("fractional_even_spacing");
+ if (execution.flags.get(ExecutionModeSpacingFractionalOdd))
+ inputs.push_back("fractional_odd_spacing");
+ if (execution.flags.get(ExecutionModeSpacingEqual))
+ inputs.push_back("equal_spacing");
+ break;
+
+ case ExecutionModelGLCompute:
+ {
+ if (execution.workgroup_size.constant != 0)
+ {
+ SpecializationConstant wg_x, wg_y, wg_z;
+ get_work_group_size_specialization_constants(wg_x, wg_y, wg_z);
+
+ // If there are any spec constants on legacy GLSL, defer declaration, we need to set up macro
+ // declarations before we can emit the work group size.
+ if (options.vulkan_semantics || ((wg_x.id == 0) && (wg_y.id == 0) && (wg_z.id == 0)))
+ build_workgroup_size(inputs, wg_x, wg_y, wg_z);
+ }
+ else
+ {
+ inputs.push_back(join("local_size_x = ", execution.workgroup_size.x));
+ inputs.push_back(join("local_size_y = ", execution.workgroup_size.y));
+ inputs.push_back(join("local_size_z = ", execution.workgroup_size.z));
+ }
+ break;
+ }
+
+ case ExecutionModelFragment:
+ if (options.es)
+ {
+ switch (options.fragment.default_float_precision)
+ {
+ case Options::Lowp:
+ statement("precision lowp float;");
+ break;
+
+ case Options::Mediump:
+ statement("precision mediump float;");
+ break;
+
+ case Options::Highp:
+ statement("precision highp float;");
+ break;
+
+ default:
+ break;
+ }
+
+ switch (options.fragment.default_int_precision)
+ {
+ case Options::Lowp:
+ statement("precision lowp int;");
+ break;
+
+ case Options::Mediump:
+ statement("precision mediump int;");
+ break;
+
+ case Options::Highp:
+ statement("precision highp int;");
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (execution.flags.get(ExecutionModeEarlyFragmentTests))
+ inputs.push_back("early_fragment_tests");
+
+ if (!options.es && execution.flags.get(ExecutionModeDepthGreater))
+ statement("layout(depth_greater) out float gl_FragDepth;");
+ else if (!options.es && execution.flags.get(ExecutionModeDepthLess))
+ statement("layout(depth_less) out float gl_FragDepth;");
+
+ break;
+
+ default:
+ break;
+ }
+
+ if (!inputs.empty())
+ statement("layout(", merge(inputs), ") in;");
+ if (!outputs.empty())
+ statement("layout(", merge(outputs), ") out;");
+
+ statement("");
+}
+
+bool CompilerGLSL::type_is_empty(const SPIRType &type)
+{
+ return type.basetype == SPIRType::Struct && type.member_types.empty();
+}
+
+void CompilerGLSL::emit_struct(SPIRType &type)
+{
+ // Struct types can be stamped out multiple times
+ // with just different offsets, matrix layouts, etc ...
+ // Type-punning with these types is legal, which complicates things
+ // when we are storing struct and array types in an SSBO for example.
+ // If the type master is packed however, we can no longer assume that the struct declaration will be redundant.
+ if (type.type_alias != 0 && !has_extended_decoration(type.type_alias, SPIRVCrossDecorationPacked))
+ return;
+
+ add_resource_name(type.self);
+ auto name = type_to_glsl(type);
+
+ statement(!backend.explicit_struct_type ? "struct " : "", name);
+ begin_scope();
+
+ type.member_name_cache.clear();
+
+ uint32_t i = 0;
+ bool emitted = false;
+ for (auto &member : type.member_types)
+ {
+ add_member_name(type, i);
+ emit_struct_member(type, member, i);
+ i++;
+ emitted = true;
+ }
+
+ // Don't declare empty structs in GLSL, this is not allowed.
+ if (type_is_empty(type) && !backend.supports_empty_struct)
+ {
+ statement("int empty_struct_member;");
+ emitted = true;
+ }
+
+ end_scope_decl();
+
+ if (emitted)
+ statement("");
+}
+
+string CompilerGLSL::to_interpolation_qualifiers(const Bitset &flags)
+{
+ string res;
+ //if (flags & (1ull << DecorationSmooth))
+ // res += "smooth ";
+ if (flags.get(DecorationFlat))
+ res += "flat ";
+ if (flags.get(DecorationNoPerspective))
+ res += "noperspective ";
+ if (flags.get(DecorationCentroid))
+ res += "centroid ";
+ if (flags.get(DecorationPatch))
+ res += "patch ";
+ if (flags.get(DecorationSample))
+ res += "sample ";
+ if (flags.get(DecorationInvariant))
+ res += "invariant ";
+ if (flags.get(DecorationExplicitInterpAMD))
+ res += "__explicitInterpAMD ";
+
+ return res;
+}
+
+string CompilerGLSL::layout_for_member(const SPIRType &type, uint32_t index)
+{
+ if (is_legacy())
+ return "";
+
+ bool is_block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) ||
+ ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock);
+ if (!is_block)
+ return "";
+
+ auto &memb = ir.meta[type.self].members;
+ if (index >= memb.size())
+ return "";
+ auto &dec = memb[index];
+
+ vector<string> attr;
+
+ // We can only apply layouts on members in block interfaces.
+ // This is a bit problematic because in SPIR-V decorations are applied on the struct types directly.
+ // This is not supported on GLSL, so we have to make the assumption that if a struct within our buffer block struct
+ // has a decoration, it was originally caused by a top-level layout() qualifier in GLSL.
+ //
+ // We would like to go from (SPIR-V style):
+ //
+ // struct Foo { layout(row_major) mat4 matrix; };
+ // buffer UBO { Foo foo; };
+ //
+ // to
+ //
+ // struct Foo { mat4 matrix; }; // GLSL doesn't support any layout shenanigans in raw struct declarations.
+ // buffer UBO { layout(row_major) Foo foo; }; // Apply the layout on top-level.
+ auto flags = combined_decoration_for_member(type, index);
+
+ if (flags.get(DecorationRowMajor))
+ attr.push_back("row_major");
+ // We don't emit any global layouts, so column_major is default.
+ //if (flags & (1ull << DecorationColMajor))
+ // attr.push_back("column_major");
+
+ if (dec.decoration_flags.get(DecorationLocation) && can_use_io_location(type.storage, true))
+ attr.push_back(join("location = ", dec.location));
+
+ // Can only declare component if we can declare location.
+ if (dec.decoration_flags.get(DecorationComponent) && can_use_io_location(type.storage, true))
+ {
+ if (!options.es)
+ {
+ if (options.version < 440 && options.version >= 140)
+ require_extension_internal("GL_ARB_enhanced_layouts");
+ else if (options.version < 140)
+ SPIRV_CROSS_THROW("Component decoration is not supported in targets below GLSL 1.40.");
+ attr.push_back(join("component = ", dec.component));
+ }
+ else
+ SPIRV_CROSS_THROW("Component decoration is not supported in ES targets.");
+ }
+
+ // SPIRVCrossDecorationPacked is set by layout_for_variable earlier to mark that we need to emit offset qualifiers.
+ // This is only done selectively in GLSL as needed.
+ if (has_extended_decoration(type.self, SPIRVCrossDecorationPacked) && dec.decoration_flags.get(DecorationOffset))
+ attr.push_back(join("offset = ", dec.offset));
+
+ if (attr.empty())
+ return "";
+
+ string res = "layout(";
+ res += merge(attr);
+ res += ") ";
+ return res;
+}
+
+const char *CompilerGLSL::format_to_glsl(spv::ImageFormat format)
+{
+ if (options.es && is_desktop_only_format(format))
+ SPIRV_CROSS_THROW("Attempting to use image format not supported in ES profile.");
+
+ switch (format)
+ {
+ case ImageFormatRgba32f:
+ return "rgba32f";
+ case ImageFormatRgba16f:
+ return "rgba16f";
+ case ImageFormatR32f:
+ return "r32f";
+ case ImageFormatRgba8:
+ return "rgba8";
+ case ImageFormatRgba8Snorm:
+ return "rgba8_snorm";
+ case ImageFormatRg32f:
+ return "rg32f";
+ case ImageFormatRg16f:
+ return "rg16f";
+ case ImageFormatRgba32i:
+ return "rgba32i";
+ case ImageFormatRgba16i:
+ return "rgba16i";
+ case ImageFormatR32i:
+ return "r32i";
+ case ImageFormatRgba8i:
+ return "rgba8i";
+ case ImageFormatRg32i:
+ return "rg32i";
+ case ImageFormatRg16i:
+ return "rg16i";
+ case ImageFormatRgba32ui:
+ return "rgba32ui";
+ case ImageFormatRgba16ui:
+ return "rgba16ui";
+ case ImageFormatR32ui:
+ return "r32ui";
+ case ImageFormatRgba8ui:
+ return "rgba8ui";
+ case ImageFormatRg32ui:
+ return "rg32ui";
+ case ImageFormatRg16ui:
+ return "rg16ui";
+ case ImageFormatR11fG11fB10f:
+ return "r11f_g11f_b10f";
+ case ImageFormatR16f:
+ return "r16f";
+ case ImageFormatRgb10A2:
+ return "rgb10_a2";
+ case ImageFormatR8:
+ return "r8";
+ case ImageFormatRg8:
+ return "rg8";
+ case ImageFormatR16:
+ return "r16";
+ case ImageFormatRg16:
+ return "rg16";
+ case ImageFormatRgba16:
+ return "rgba16";
+ case ImageFormatR16Snorm:
+ return "r16_snorm";
+ case ImageFormatRg16Snorm:
+ return "rg16_snorm";
+ case ImageFormatRgba16Snorm:
+ return "rgba16_snorm";
+ case ImageFormatR8Snorm:
+ return "r8_snorm";
+ case ImageFormatRg8Snorm:
+ return "rg8_snorm";
+ case ImageFormatR8ui:
+ return "r8ui";
+ case ImageFormatRg8ui:
+ return "rg8ui";
+ case ImageFormatR16ui:
+ return "r16ui";
+ case ImageFormatRgb10a2ui:
+ return "rgb10_a2ui";
+ case ImageFormatR8i:
+ return "r8i";
+ case ImageFormatRg8i:
+ return "rg8i";
+ case ImageFormatR16i:
+ return "r16i";
+ default:
+ case ImageFormatUnknown:
+ return nullptr;
+ }
+}
+
+uint32_t CompilerGLSL::type_to_packed_base_size(const SPIRType &type, BufferPackingStandard)
+{
+ switch (type.basetype)
+ {
+ case SPIRType::Double:
+ case SPIRType::Int64:
+ case SPIRType::UInt64:
+ return 8;
+ case SPIRType::Float:
+ case SPIRType::Int:
+ case SPIRType::UInt:
+ return 4;
+ case SPIRType::Half:
+ case SPIRType::Short:
+ case SPIRType::UShort:
+ return 2;
+ case SPIRType::SByte:
+ case SPIRType::UByte:
+ return 1;
+
+ default:
+ SPIRV_CROSS_THROW("Unrecognized type in type_to_packed_base_size.");
+ }
+}
+
+uint32_t CompilerGLSL::type_to_packed_alignment(const SPIRType &type, const Bitset &flags,
+ BufferPackingStandard packing)
+{
+ if (!type.array.empty())
+ {
+ uint32_t minimum_alignment = 1;
+ if (packing_is_vec4_padded(packing))
+ minimum_alignment = 16;
+
+ auto *tmp = &get<SPIRType>(type.parent_type);
+ while (!tmp->array.empty())
+ tmp = &get<SPIRType>(tmp->parent_type);
+
+ // Get the alignment of the base type, then maybe round up.
+ return max(minimum_alignment, type_to_packed_alignment(*tmp, flags, packing));
+ }
+
+ if (type.basetype == SPIRType::Struct)
+ {
+ // Rule 9. Structs alignments are maximum alignment of its members.
+ uint32_t alignment = 1;
+ for (uint32_t i = 0; i < type.member_types.size(); i++)
+ {
+ auto member_flags = ir.meta[type.self].members[i].decoration_flags;
+ alignment =
+ max(alignment, type_to_packed_alignment(get<SPIRType>(type.member_types[i]), member_flags, packing));
+ }
+
+ // In std140, struct alignment is rounded up to 16.
+ if (packing_is_vec4_padded(packing))
+ alignment = max(alignment, 16u);
+
+ return alignment;
+ }
+ else
+ {
+ const uint32_t base_alignment = type_to_packed_base_size(type, packing);
+
+ // Vectors are *not* aligned in HLSL, but there's an extra rule where vectors cannot straddle
+ // a vec4, this is handled outside since that part knows our current offset.
+ if (type.columns == 1 && packing_is_hlsl(packing))
+ return base_alignment;
+
+ // From 7.6.2.2 in GL 4.5 core spec.
+ // Rule 1
+ if (type.vecsize == 1 && type.columns == 1)
+ return base_alignment;
+
+ // Rule 2
+ if ((type.vecsize == 2 || type.vecsize == 4) && type.columns == 1)
+ return type.vecsize * base_alignment;
+
+ // Rule 3
+ if (type.vecsize == 3 && type.columns == 1)
+ return 4 * base_alignment;
+
+ // Rule 4 implied. Alignment does not change in std430.
+
+ // Rule 5. Column-major matrices are stored as arrays of
+ // vectors.
+ if (flags.get(DecorationColMajor) && type.columns > 1)
+ {
+ if (packing_is_vec4_padded(packing))
+ return 4 * base_alignment;
+ else if (type.vecsize == 3)
+ return 4 * base_alignment;
+ else
+ return type.vecsize * base_alignment;
+ }
+
+ // Rule 6 implied.
+
+ // Rule 7.
+ if (flags.get(DecorationRowMajor) && type.vecsize > 1)
+ {
+ if (packing_is_vec4_padded(packing))
+ return 4 * base_alignment;
+ else if (type.columns == 3)
+ return 4 * base_alignment;
+ else
+ return type.columns * base_alignment;
+ }
+
+ // Rule 8 implied.
+ }
+
+ SPIRV_CROSS_THROW("Did not find suitable rule for type. Bogus decorations?");
+}
+
+uint32_t CompilerGLSL::type_to_packed_array_stride(const SPIRType &type, const Bitset &flags,
+ BufferPackingStandard packing)
+{
+ // Array stride is equal to aligned size of the underlying type.
+ uint32_t parent = type.parent_type;
+ assert(parent);
+
+ auto &tmp = get<SPIRType>(parent);
+
+ uint32_t size = type_to_packed_size(tmp, flags, packing);
+ if (tmp.array.empty())
+ {
+ uint32_t alignment = type_to_packed_alignment(type, flags, packing);
+ return (size + alignment - 1) & ~(alignment - 1);
+ }
+ else
+ {
+ // For multidimensional arrays, array stride always matches size of subtype.
+ // The alignment cannot change because multidimensional arrays are basically N * M array elements.
+ return size;
+ }
+}
+
+uint32_t CompilerGLSL::type_to_packed_size(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing)
+{
+ if (!type.array.empty())
+ {
+ return to_array_size_literal(type) * type_to_packed_array_stride(type, flags, packing);
+ }
+
+ uint32_t size = 0;
+
+ if (type.basetype == SPIRType::Struct)
+ {
+ uint32_t pad_alignment = 1;
+
+ for (uint32_t i = 0; i < type.member_types.size(); i++)
+ {
+ auto member_flags = ir.meta[type.self].members[i].decoration_flags;
+ auto &member_type = get<SPIRType>(type.member_types[i]);
+
+ uint32_t packed_alignment = type_to_packed_alignment(member_type, member_flags, packing);
+ uint32_t alignment = max(packed_alignment, pad_alignment);
+
+ // The next member following a struct member is aligned to the base alignment of the struct that came before.
+ // GL 4.5 spec, 7.6.2.2.
+ if (member_type.basetype == SPIRType::Struct)
+ pad_alignment = packed_alignment;
+ else
+ pad_alignment = 1;
+
+ size = (size + alignment - 1) & ~(alignment - 1);
+ size += type_to_packed_size(member_type, member_flags, packing);
+ }
+ }
+ else
+ {
+ const uint32_t base_alignment = type_to_packed_base_size(type, packing);
+
+ if (type.columns == 1)
+ size = type.vecsize * base_alignment;
+
+ if (flags.get(DecorationColMajor) && type.columns > 1)
+ {
+ if (packing_is_vec4_padded(packing))
+ size = type.columns * 4 * base_alignment;
+ else if (type.vecsize == 3)
+ size = type.columns * 4 * base_alignment;
+ else
+ size = type.columns * type.vecsize * base_alignment;
+ }
+
+ if (flags.get(DecorationRowMajor) && type.vecsize > 1)
+ {
+ if (packing_is_vec4_padded(packing))
+ size = type.vecsize * 4 * base_alignment;
+ else if (type.columns == 3)
+ size = type.vecsize * 4 * base_alignment;
+ else
+ size = type.vecsize * type.columns * base_alignment;
+ }
+ }
+
+ return size;
+}
+
+bool CompilerGLSL::buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing,
+ uint32_t start_offset, uint32_t end_offset)
+{
+ // This is very tricky and error prone, but try to be exhaustive and correct here.
+ // SPIR-V doesn't directly say if we're using std430 or std140.
+ // SPIR-V communicates this using Offset and ArrayStride decorations (which is what really matters),
+ // so we have to try to infer whether or not the original GLSL source was std140 or std430 based on this information.
+ // We do not have to consider shared or packed since these layouts are not allowed in Vulkan SPIR-V (they are useless anyways, and custom offsets would do the same thing).
+ //
+ // It is almost certain that we're using std430, but it gets tricky with arrays in particular.
+ // We will assume std430, but infer std140 if we can prove the struct is not compliant with std430.
+ //
+ // The only two differences between std140 and std430 are related to padding alignment/array stride
+ // in arrays and structs. In std140 they take minimum vec4 alignment.
+ // std430 only removes the vec4 requirement.
+
+ uint32_t offset = 0;
+ uint32_t pad_alignment = 1;
+
+ bool is_top_level_block =
+ has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock);
+
+ for (uint32_t i = 0; i < type.member_types.size(); i++)
+ {
+ auto &memb_type = get<SPIRType>(type.member_types[i]);
+ auto member_flags = ir.meta[type.self].members[i].decoration_flags;
+
+ // Verify alignment rules.
+ uint32_t packed_alignment = type_to_packed_alignment(memb_type, member_flags, packing);
+
+ // This is a rather dirty workaround to deal with some cases of OpSpecConstantOp used as array size, e.g:
+ // layout(constant_id = 0) const int s = 10;
+ // const int S = s + 5; // SpecConstantOp
+ // buffer Foo { int data[S]; }; // <-- Very hard for us to deduce a fixed value here,
+ // we would need full implementation of compile-time constant folding. :(
+ // If we are the last member of a struct, there might be cases where the actual size of that member is irrelevant
+ // for our analysis (e.g. unsized arrays).
+ // This lets us simply ignore that there are spec constant op sized arrays in our buffers.
+ // Querying size of this member will fail, so just don't call it unless we have to.
+ //
+ // This is likely "best effort" we can support without going into unacceptably complicated workarounds.
+ bool member_can_be_unsized =
+ is_top_level_block && size_t(i + 1) == type.member_types.size() && !memb_type.array.empty();
+
+ uint32_t packed_size = 0;
+ if (!member_can_be_unsized)
+ packed_size = type_to_packed_size(memb_type, member_flags, packing);
+
+ // We only need to care about this if we have non-array types which can straddle the vec4 boundary.
+ if (packing_is_hlsl(packing))
+ {
+ // If a member straddles across a vec4 boundary, alignment is actually vec4.
+ uint32_t begin_word = offset / 16;
+ uint32_t end_word = (offset + packed_size - 1) / 16;
+ if (begin_word != end_word)
+ packed_alignment = max(packed_alignment, 16u);
+ }
+
+ uint32_t alignment = max(packed_alignment, pad_alignment);
+ offset = (offset + alignment - 1) & ~(alignment - 1);
+
+ // Field is not in the specified range anymore and we can ignore any further fields.
+ if (offset >= end_offset)
+ break;
+
+ // The next member following a struct member is aligned to the base alignment of the struct that came before.
+ // GL 4.5 spec, 7.6.2.2.
+ if (memb_type.basetype == SPIRType::Struct)
+ pad_alignment = packed_alignment;
+ else
+ pad_alignment = 1;
+
+ // Only care about packing if we are in the given range
+ if (offset >= start_offset)
+ {
+ // We only care about offsets in std140, std430, etc ...
+ // For EnhancedLayout variants, we have the flexibility to choose our own offsets.
+ if (!packing_has_flexible_offset(packing))
+ {
+ uint32_t actual_offset = type_struct_member_offset(type, i);
+ if (actual_offset != offset) // This cannot be the packing we're looking for.
+ return false;
+ }
+
+ // Verify array stride rules.
+ if (!memb_type.array.empty() && type_to_packed_array_stride(memb_type, member_flags, packing) !=
+ type_struct_member_array_stride(type, i))
+ return false;
+
+ // Verify that sub-structs also follow packing rules.
+ // We cannot use enhanced layouts on substructs, so they better be up to spec.
+ auto substruct_packing = packing_to_substruct_packing(packing);
+
+ if (!memb_type.member_types.empty() && !buffer_is_packing_standard(memb_type, substruct_packing))
+ return false;
+ }
+
+ // Bump size.
+ offset += packed_size;
+ }
+
+ return true;
+}
+
+bool CompilerGLSL::can_use_io_location(StorageClass storage, bool block)
+{
+ // Location specifiers are must have in SPIR-V, but they aren't really supported in earlier versions of GLSL.
+ // Be very explicit here about how to solve the issue.
+ if ((get_execution_model() != ExecutionModelVertex && storage == StorageClassInput) ||
+ (get_execution_model() != ExecutionModelFragment && storage == StorageClassOutput))
+ {
+ uint32_t minimum_desktop_version = block ? 440 : 410;
+ // ARB_enhanced_layouts vs ARB_separate_shader_objects ...
+
+ if (!options.es && options.version < minimum_desktop_version && !options.separate_shader_objects)
+ return false;
+ else if (options.es && options.version < 310)
+ return false;
+ }
+
+ if ((get_execution_model() == ExecutionModelVertex && storage == StorageClassInput) ||
+ (get_execution_model() == ExecutionModelFragment && storage == StorageClassOutput))
+ {
+ if (options.es && options.version < 300)
+ return false;
+ else if (!options.es && options.version < 330)
+ return false;
+ }
+
+ if (storage == StorageClassUniform || storage == StorageClassUniformConstant || storage == StorageClassPushConstant)
+ {
+ if (options.es && options.version < 310)
+ return false;
+ else if (!options.es && options.version < 430)
+ return false;
+ }
+
+ return true;
+}
+
+string CompilerGLSL::layout_for_variable(const SPIRVariable &var)
+{
+ // FIXME: Come up with a better solution for when to disable layouts.
+ // Having layouts depend on extensions as well as which types
+ // of layouts are used. For now, the simple solution is to just disable
+ // layouts for legacy versions.
+ if (is_legacy())
+ return "";
+
+ vector<string> attr;
+
+ auto &dec = ir.meta[var.self].decoration;
+ auto &type = get<SPIRType>(var.basetype);
+ auto &flags = dec.decoration_flags;
+ auto typeflags = ir.meta[type.self].decoration.decoration_flags;
+
+ if (options.vulkan_semantics && var.storage == StorageClassPushConstant)
+ attr.push_back("push_constant");
+
+ if (flags.get(DecorationRowMajor))
+ attr.push_back("row_major");
+ if (flags.get(DecorationColMajor))
+ attr.push_back("column_major");
+
+ if (options.vulkan_semantics)
+ {
+ if (flags.get(DecorationInputAttachmentIndex))
+ attr.push_back(join("input_attachment_index = ", dec.input_attachment));
+ }
+
+ bool is_block = has_decoration(type.self, DecorationBlock);
+ if (flags.get(DecorationLocation) && can_use_io_location(var.storage, is_block))
+ {
+ Bitset combined_decoration;
+ for (uint32_t i = 0; i < ir.meta[type.self].members.size(); i++)
+ combined_decoration.merge_or(combined_decoration_for_member(type, i));
+
+ // If our members have location decorations, we don't need to
+ // emit location decorations at the top as well (looks weird).
+ if (!combined_decoration.get(DecorationLocation))
+ attr.push_back(join("location = ", dec.location));
+ }
+
+ // Can only declare Component if we can declare location.
+ if (flags.get(DecorationComponent) && can_use_io_location(var.storage, is_block))
+ {
+ if (!options.es)
+ {
+ if (options.version < 440 && options.version >= 140)
+ require_extension_internal("GL_ARB_enhanced_layouts");
+ else if (options.version < 140)
+ SPIRV_CROSS_THROW("Component decoration is not supported in targets below GLSL 1.40.");
+ attr.push_back(join("component = ", dec.component));
+ }
+ else
+ SPIRV_CROSS_THROW("Component decoration is not supported in ES targets.");
+ }
+
+ if (flags.get(DecorationIndex))
+ attr.push_back(join("index = ", dec.index));
+
+ // Do not emit set = decoration in regular GLSL output, but
+ // we need to preserve it in Vulkan GLSL mode.
+ if (var.storage != StorageClassPushConstant)
+ {
+ if (flags.get(DecorationDescriptorSet) && options.vulkan_semantics)
+ attr.push_back(join("set = ", dec.set));
+ }
+
+ // GL 3.0/GLSL 1.30 is not considered legacy, but it doesn't have UBOs ...
+ bool can_use_buffer_blocks = (options.es && options.version >= 300) || (!options.es && options.version >= 140);
+
+ bool can_use_binding;
+ if (options.es)
+ can_use_binding = options.version >= 310;
+ else
+ can_use_binding = options.enable_420pack_extension || (options.version >= 420);
+
+ // Make sure we don't emit binding layout for a classic uniform on GLSL 1.30.
+ if (!can_use_buffer_blocks && var.storage == StorageClassUniform)
+ can_use_binding = false;
+
+ if (can_use_binding && flags.get(DecorationBinding))
+ attr.push_back(join("binding = ", dec.binding));
+
+ if (flags.get(DecorationOffset))
+ attr.push_back(join("offset = ", dec.offset));
+
+ bool push_constant_block = options.vulkan_semantics && var.storage == StorageClassPushConstant;
+ bool ssbo_block = var.storage == StorageClassStorageBuffer ||
+ (var.storage == StorageClassUniform && typeflags.get(DecorationBufferBlock));
+ bool emulated_ubo = var.storage == StorageClassPushConstant && options.emit_push_constant_as_uniform_buffer;
+ bool ubo_block = var.storage == StorageClassUniform && typeflags.get(DecorationBlock);
+
+ // Instead of adding explicit offsets for every element here, just assume we're using std140 or std430.
+ // If SPIR-V does not comply with either layout, we cannot really work around it.
+ if (can_use_buffer_blocks && (ubo_block || emulated_ubo))
+ {
+ if (buffer_is_packing_standard(type, BufferPackingStd140))
+ attr.push_back("std140");
+ else if (buffer_is_packing_standard(type, BufferPackingStd140EnhancedLayout))
+ {
+ attr.push_back("std140");
+ // Fallback time. We might be able to use the ARB_enhanced_layouts to deal with this difference,
+ // however, we can only use layout(offset) on the block itself, not any substructs, so the substructs better be the appropriate layout.
+ // Enhanced layouts seem to always work in Vulkan GLSL, so no need for extensions there.
+ if (options.es && !options.vulkan_semantics)
+ SPIRV_CROSS_THROW("Uniform buffer block cannot be expressed as std140. ES-targets do "
+ "not support GL_ARB_enhanced_layouts.");
+ if (!options.es && !options.vulkan_semantics && options.version < 440)
+ require_extension_internal("GL_ARB_enhanced_layouts");
+
+ // This is a very last minute to check for this, but use this unused decoration to mark that we should emit
+ // explicit offsets for this block type.
+ // layout_for_variable() will be called before the actual buffer emit.
+ // The alternative is a full pass before codegen where we deduce this decoration,
+ // but then we are just doing the exact same work twice, and more complexity.
+ set_extended_decoration(type.self, SPIRVCrossDecorationPacked);
+ }
+ else
+ {
+ SPIRV_CROSS_THROW("Uniform buffer cannot be expressed as std140, even with enhanced layouts. You can try "
+ "flattening this block to "
+ "support a more flexible layout.");
+ }
+ }
+ else if (can_use_buffer_blocks && (push_constant_block || ssbo_block))
+ {
+ if (buffer_is_packing_standard(type, BufferPackingStd430))
+ attr.push_back("std430");
+ else if (buffer_is_packing_standard(type, BufferPackingStd140))
+ attr.push_back("std140");
+ else if (buffer_is_packing_standard(type, BufferPackingStd140EnhancedLayout))
+ {
+ attr.push_back("std140");
+
+ // Fallback time. We might be able to use the ARB_enhanced_layouts to deal with this difference,
+ // however, we can only use layout(offset) on the block itself, not any substructs, so the substructs better be the appropriate layout.
+ // Enhanced layouts seem to always work in Vulkan GLSL, so no need for extensions there.
+ if (options.es && !options.vulkan_semantics)
+ SPIRV_CROSS_THROW("Push constant block cannot be expressed as neither std430 nor std140. ES-targets do "
+ "not support GL_ARB_enhanced_layouts.");
+ if (!options.es && !options.vulkan_semantics && options.version < 440)
+ require_extension_internal("GL_ARB_enhanced_layouts");
+
+ set_extended_decoration(type.self, SPIRVCrossDecorationPacked);
+ }
+ else if (buffer_is_packing_standard(type, BufferPackingStd430EnhancedLayout))
+ {
+ attr.push_back("std430");
+ if (options.es && !options.vulkan_semantics)
+ SPIRV_CROSS_THROW("Push constant block cannot be expressed as neither std430 nor std140. ES-targets do "
+ "not support GL_ARB_enhanced_layouts.");
+ if (!options.es && !options.vulkan_semantics && options.version < 440)
+ require_extension_internal("GL_ARB_enhanced_layouts");
+
+ set_extended_decoration(type.self, SPIRVCrossDecorationPacked);
+ }
+ else
+ {
+ SPIRV_CROSS_THROW("Buffer block cannot be expressed as neither std430 nor std140, even with enhanced "
+ "layouts. You can try flattening this block to support a more flexible layout.");
+ }
+ }
+
+ // For images, the type itself adds a layout qualifer.
+ // Only emit the format for storage images.
+ if (type.basetype == SPIRType::Image && type.image.sampled == 2)
+ {
+ const char *fmt = format_to_glsl(type.image.format);
+ if (fmt)
+ attr.push_back(fmt);
+ }
+
+ if (attr.empty())
+ return "";
+
+ string res = "layout(";
+ res += merge(attr);
+ res += ") ";
+ return res;
+}
+
+void CompilerGLSL::emit_push_constant_block(const SPIRVariable &var)
+{
+ if (flattened_buffer_blocks.count(var.self))
+ emit_buffer_block_flattened(var);
+ else if (options.vulkan_semantics)
+ emit_push_constant_block_vulkan(var);
+ else if (options.emit_push_constant_as_uniform_buffer)
+ emit_buffer_block_native(var);
+ else
+ emit_push_constant_block_glsl(var);
+}
+
+void CompilerGLSL::emit_push_constant_block_vulkan(const SPIRVariable &var)
+{
+ emit_buffer_block(var);
+}
+
+void CompilerGLSL::emit_push_constant_block_glsl(const SPIRVariable &var)
+{
+ // OpenGL has no concept of push constant blocks, implement it as a uniform struct.
+ auto &type = get<SPIRType>(var.basetype);
+
+ auto &flags = ir.meta[var.self].decoration.decoration_flags;
+ flags.clear(DecorationBinding);
+ flags.clear(DecorationDescriptorSet);
+
+#if 0
+ if (flags & ((1ull << DecorationBinding) | (1ull << DecorationDescriptorSet)))
+ SPIRV_CROSS_THROW("Push constant blocks cannot be compiled to GLSL with Binding or Set syntax. "
+ "Remap to location with reflection API first or disable these decorations.");
+#endif
+
+ // We're emitting the push constant block as a regular struct, so disable the block qualifier temporarily.
+ // Otherwise, we will end up emitting layout() qualifiers on naked structs which is not allowed.
+ auto &block_flags = ir.meta[type.self].decoration.decoration_flags;
+ bool block_flag = block_flags.get(DecorationBlock);
+ block_flags.clear(DecorationBlock);
+
+ emit_struct(type);
+
+ if (block_flag)
+ block_flags.set(DecorationBlock);
+
+ emit_uniform(var);
+ statement("");
+}
+
+void CompilerGLSL::emit_buffer_block(const SPIRVariable &var)
+{
+ if (flattened_buffer_blocks.count(var.self))
+ emit_buffer_block_flattened(var);
+ else if (is_legacy() || (!options.es && options.version == 130))
+ emit_buffer_block_legacy(var);
+ else
+ emit_buffer_block_native(var);
+}
+
+void CompilerGLSL::emit_buffer_block_legacy(const SPIRVariable &var)
+{
+ auto &type = get<SPIRType>(var.basetype);
+ bool ssbo = var.storage == StorageClassStorageBuffer ||
+ ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock);
+ if (ssbo)
+ SPIRV_CROSS_THROW("SSBOs not supported in legacy targets.");
+
+ // We're emitting the push constant block as a regular struct, so disable the block qualifier temporarily.
+ // Otherwise, we will end up emitting layout() qualifiers on naked structs which is not allowed.
+ auto &block_flags = ir.meta[type.self].decoration.decoration_flags;
+ bool block_flag = block_flags.get(DecorationBlock);
+ block_flags.clear(DecorationBlock);
+ emit_struct(type);
+ if (block_flag)
+ block_flags.set(DecorationBlock);
+ emit_uniform(var);
+ statement("");
+}
+
+void CompilerGLSL::emit_buffer_block_native(const SPIRVariable &var)
+{
+ auto &type = get<SPIRType>(var.basetype);
+
+ Bitset flags = ir.get_buffer_block_flags(var);
+ bool ssbo = var.storage == StorageClassStorageBuffer ||
+ ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock);
+ bool is_restrict = ssbo && flags.get(DecorationRestrict);
+ bool is_writeonly = ssbo && flags.get(DecorationNonReadable);
+ bool is_readonly = ssbo && flags.get(DecorationNonWritable);
+ bool is_coherent = ssbo && flags.get(DecorationCoherent);
+
+ // Block names should never alias, but from HLSL input they kind of can because block types are reused for UAVs ...
+ auto buffer_name = to_name(type.self, false);
+
+ auto &block_namespace = ssbo ? block_ssbo_names : block_ubo_names;
+
+ // Shaders never use the block by interface name, so we don't
+ // have to track this other than updating name caches.
+ // If we have a collision for any reason, just fallback immediately.
+ if (ir.meta[type.self].decoration.alias.empty() || block_namespace.find(buffer_name) != end(block_namespace) ||
+ resource_names.find(buffer_name) != end(resource_names))
+ {
+ buffer_name = get_block_fallback_name(var.self);
+ }
+
+ // Make sure we get something unique for both global name scope and block name scope.
+ // See GLSL 4.5 spec: section 4.3.9 for details.
+ add_variable(block_namespace, resource_names, buffer_name);
+
+ // If for some reason buffer_name is an illegal name, make a final fallback to a workaround name.
+ // This cannot conflict with anything else, so we're safe now.
+ // We cannot reuse this fallback name in neither global scope (blocked by block_names) nor block name scope.
+ if (buffer_name.empty())
+ buffer_name = join("_", get<SPIRType>(var.basetype).self, "_", var.self);
+
+ block_names.insert(buffer_name);
+ block_namespace.insert(buffer_name);
+
+ // Save for post-reflection later.
+ declared_block_names[var.self] = buffer_name;
+
+ statement(layout_for_variable(var), is_coherent ? "coherent " : "", is_restrict ? "restrict " : "",
+ is_writeonly ? "writeonly " : "", is_readonly ? "readonly " : "", ssbo ? "buffer " : "uniform ",
+ buffer_name);
+
+ begin_scope();
+
+ type.member_name_cache.clear();
+
+ uint32_t i = 0;
+ for (auto &member : type.member_types)
+ {
+ add_member_name(type, i);
+ emit_struct_member(type, member, i);
+ i++;
+ }
+
+ // var.self can be used as a backup name for the block name,
+ // so we need to make sure we don't disturb the name here on a recompile.
+ // It will need to be reset if we have to recompile.
+ preserve_alias_on_reset(var.self);
+ add_resource_name(var.self);
+ end_scope_decl(to_name(var.self) + type_to_array_glsl(type));
+ statement("");
+}
+
+void CompilerGLSL::emit_buffer_block_flattened(const SPIRVariable &var)
+{
+ auto &type = get<SPIRType>(var.basetype);
+
+ // Block names should never alias.
+ auto buffer_name = to_name(type.self, false);
+ size_t buffer_size = (get_declared_struct_size(type) + 15) / 16;
+
+ SPIRType::BaseType basic_type;
+ if (get_common_basic_type(type, basic_type))
+ {
+ SPIRType tmp;
+ tmp.basetype = basic_type;
+ tmp.vecsize = 4;
+ if (basic_type != SPIRType::Float && basic_type != SPIRType::Int && basic_type != SPIRType::UInt)
+ SPIRV_CROSS_THROW("Basic types in a flattened UBO must be float, int or uint.");
+
+ auto flags = ir.get_buffer_block_flags(var);
+ statement("uniform ", flags_to_precision_qualifiers_glsl(tmp, flags), type_to_glsl(tmp), " ", buffer_name, "[",
+ buffer_size, "];");
+ }
+ else
+ SPIRV_CROSS_THROW("All basic types in a flattened block must be the same.");
+}
+
+const char *CompilerGLSL::to_storage_qualifiers_glsl(const SPIRVariable &var)
+{
+ auto &execution = get_entry_point();
+
+ if (var.storage == StorageClassInput || var.storage == StorageClassOutput)
+ {
+ if (is_legacy() && execution.model == ExecutionModelVertex)
+ return var.storage == StorageClassInput ? "attribute " : "varying ";
+ else if (is_legacy() && execution.model == ExecutionModelFragment)
+ return "varying "; // Fragment outputs are renamed so they never hit this case.
+ else
+ return var.storage == StorageClassInput ? "in " : "out ";
+ }
+ else if (var.storage == StorageClassUniformConstant || var.storage == StorageClassUniform ||
+ var.storage == StorageClassPushConstant)
+ {
+ return "uniform ";
+ }
+ else if (var.storage == StorageClassRayPayloadNV)
+ {
+ return "rayPayloadNV ";
+ }
+ else if (var.storage == StorageClassIncomingRayPayloadNV)
+ {
+ return "rayPayloadInNV ";
+ }
+ else if (var.storage == StorageClassHitAttributeNV)
+ {
+ return "hitAttributeNV ";
+ }
+
+ return "";
+}
+
+void CompilerGLSL::emit_flattened_io_block(const SPIRVariable &var, const char *qual)
+{
+ auto &type = get<SPIRType>(var.basetype);
+ if (!type.array.empty())
+ SPIRV_CROSS_THROW("Array of varying structs cannot be flattened to legacy-compatible varyings.");
+
+ auto old_flags = ir.meta[type.self].decoration.decoration_flags;
+ // Emit the members as if they are part of a block to get all qualifiers.
+ ir.meta[type.self].decoration.decoration_flags.set(DecorationBlock);
+
+ type.member_name_cache.clear();
+
+ uint32_t i = 0;
+ for (auto &member : type.member_types)
+ {
+ add_member_name(type, i);
+ auto &membertype = get<SPIRType>(member);
+
+ if (membertype.basetype == SPIRType::Struct)
+ SPIRV_CROSS_THROW("Cannot flatten struct inside structs in I/O variables.");
+
+ // Pass in the varying qualifier here so it will appear in the correct declaration order.
+ // Replace member name while emitting it so it encodes both struct name and member name.
+ // Sanitize underscores because joining the two identifiers might create more than 1 underscore in a row,
+ // which is not allowed.
+ auto backup_name = get_member_name(type.self, i);
+ auto member_name = to_member_name(type, i);
+ set_member_name(type.self, i, sanitize_underscores(join(to_name(var.self), "_", member_name)));
+ emit_struct_member(type, member, i, qual);
+ // Restore member name.
+ set_member_name(type.self, i, member_name);
+ i++;
+ }
+
+ ir.meta[type.self].decoration.decoration_flags = old_flags;
+
+ // Treat this variable as flattened from now on.
+ flattened_structs.insert(var.self);
+}
+
+void CompilerGLSL::emit_interface_block(const SPIRVariable &var)
+{
+ auto &type = get<SPIRType>(var.basetype);
+
+ // Either make it plain in/out or in/out blocks depending on what shader is doing ...
+ bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock);
+ const char *qual = to_storage_qualifiers_glsl(var);
+
+ if (block)
+ {
+ // ESSL earlier than 310 and GLSL earlier than 150 did not support
+ // I/O variables which are struct types.
+ // To support this, flatten the struct into separate varyings instead.
+ if ((options.es && options.version < 310) || (!options.es && options.version < 150))
+ {
+ // I/O blocks on ES require version 310 with Android Extension Pack extensions, or core version 320.
+ // On desktop, I/O blocks were introduced with geometry shaders in GL 3.2 (GLSL 150).
+ emit_flattened_io_block(var, qual);
+ }
+ else
+ {
+ if (options.es && options.version < 320)
+ {
+ // Geometry and tessellation extensions imply this extension.
+ if (!has_extension("GL_EXT_geometry_shader") && !has_extension("GL_EXT_tessellation_shader"))
+ require_extension_internal("GL_EXT_shader_io_blocks");
+ }
+
+ // Block names should never alias.
+ auto block_name = to_name(type.self, false);
+
+ // The namespace for I/O blocks is separate from other variables in GLSL.
+ auto &block_namespace = type.storage == StorageClassInput ? block_input_names : block_output_names;
+
+ // Shaders never use the block by interface name, so we don't
+ // have to track this other than updating name caches.
+ if (block_name.empty() || block_namespace.find(block_name) != end(block_namespace))
+ block_name = get_fallback_name(type.self);
+ else
+ block_namespace.insert(block_name);
+
+ // If for some reason buffer_name is an illegal name, make a final fallback to a workaround name.
+ // This cannot conflict with anything else, so we're safe now.
+ if (block_name.empty())
+ block_name = join("_", get<SPIRType>(var.basetype).self, "_", var.self);
+
+ // Instance names cannot alias block names.
+ resource_names.insert(block_name);
+
+ statement(layout_for_variable(var), qual, block_name);
+ begin_scope();
+
+ type.member_name_cache.clear();
+
+ uint32_t i = 0;
+ for (auto &member : type.member_types)
+ {
+ add_member_name(type, i);
+ emit_struct_member(type, member, i);
+ i++;
+ }
+
+ add_resource_name(var.self);
+ end_scope_decl(join(to_name(var.self), type_to_array_glsl(type)));
+ statement("");
+ }
+ }
+ else
+ {
+ // ESSL earlier than 310 and GLSL earlier than 150 did not support
+ // I/O variables which are struct types.
+ // To support this, flatten the struct into separate varyings instead.
+ if (type.basetype == SPIRType::Struct &&
+ ((options.es && options.version < 310) || (!options.es && options.version < 150)))
+ {
+ emit_flattened_io_block(var, qual);
+ }
+ else
+ {
+ add_resource_name(var.self);
+ statement(layout_for_variable(var), to_qualifiers_glsl(var.self),
+ variable_decl(type, to_name(var.self), var.self), ";");
+
+ // If a StorageClassOutput variable has an initializer, we need to initialize it in main().
+ if (var.storage == StorageClassOutput && var.initializer)
+ {
+ auto &entry_func = this->get<SPIRFunction>(ir.default_entry_point);
+ entry_func.fixup_hooks_in.push_back(
+ [&]() { statement(to_name(var.self), " = ", to_expression(var.initializer), ";"); });
+ }
+ }
+ }
+}
+
+void CompilerGLSL::emit_uniform(const SPIRVariable &var)
+{
+ auto &type = get<SPIRType>(var.basetype);
+ if (type.basetype == SPIRType::Image && type.image.sampled == 2)
+ {
+ if (!options.es && options.version < 420)
+ require_extension_internal("GL_ARB_shader_image_load_store");
+ else if (options.es && options.version < 310)
+ SPIRV_CROSS_THROW("At least ESSL 3.10 required for shader image load store.");
+ }
+
+ add_resource_name(var.self);
+ statement(layout_for_variable(var), variable_decl(var), ";");
+}
+
+string CompilerGLSL::constant_value_macro_name(uint32_t id)
+{
+ return join("SPIRV_CROSS_CONSTANT_ID_", id);
+}
+
+void CompilerGLSL::emit_specialization_constant_op(const SPIRConstantOp &constant)
+{
+ auto &type = get<SPIRType>(constant.basetype);
+ auto name = to_name(constant.self);
+ statement("const ", variable_decl(type, name), " = ", constant_op_expression(constant), ";");
+}
+
+void CompilerGLSL::emit_constant(const SPIRConstant &constant)
+{
+ auto &type = get<SPIRType>(constant.constant_type);
+ auto name = to_name(constant.self);
+
+ SpecializationConstant wg_x, wg_y, wg_z;
+ uint32_t workgroup_size_id = get_work_group_size_specialization_constants(wg_x, wg_y, wg_z);
+
+ // This specialization constant is implicitly declared by emitting layout() in;
+ if (constant.self == workgroup_size_id)
+ return;
+
+ // These specialization constants are implicitly declared by emitting layout() in;
+ // In legacy GLSL, we will still need to emit macros for these, so a layout() in; declaration
+ // later can use macro overrides for work group size.
+ bool is_workgroup_size_constant = constant.self == wg_x.id || constant.self == wg_y.id || constant.self == wg_z.id;
+
+ if (options.vulkan_semantics && is_workgroup_size_constant)
+ {
+ // Vulkan GLSL does not need to declare workgroup spec constants explicitly, it is handled in layout().
+ return;
+ }
+ else if (!options.vulkan_semantics && is_workgroup_size_constant &&
+ !has_decoration(constant.self, DecorationSpecId))
+ {
+ // Only bother declaring a workgroup size if it is actually a specialization constant, because we need macros.
+ return;
+ }
+
+ // Only scalars have constant IDs.
+ if (has_decoration(constant.self, DecorationSpecId))
+ {
+ if (options.vulkan_semantics)
+ {
+ statement("layout(constant_id = ", get_decoration(constant.self, DecorationSpecId), ") const ",
+ variable_decl(type, name), " = ", constant_expression(constant), ";");
+ }
+ else
+ {
+ const string &macro_name = constant.specialization_constant_macro_name;
+ statement("#ifndef ", macro_name);
+ statement("#define ", macro_name, " ", constant_expression(constant));
+ statement("#endif");
+
+ // For workgroup size constants, only emit the macros.
+ if (!is_workgroup_size_constant)
+ statement("const ", variable_decl(type, name), " = ", macro_name, ";");
+ }
+ }
+ else
+ {
+ statement("const ", variable_decl(type, name), " = ", constant_expression(constant), ";");
+ }
+}
+
+void CompilerGLSL::emit_entry_point_declarations()
+{
+}
+
+void CompilerGLSL::replace_illegal_names()
+{
+ // clang-format off
+ static const unordered_set<string> keywords = {
+ "abs", "acos", "acosh", "all", "any", "asin", "asinh", "atan", "atanh",
+ "atomicAdd", "atomicCompSwap", "atomicCounter", "atomicCounterDecrement", "atomicCounterIncrement",
+ "atomicExchange", "atomicMax", "atomicMin", "atomicOr", "atomicXor",
+ "bitCount", "bitfieldExtract", "bitfieldInsert", "bitfieldReverse",
+ "ceil", "cos", "cosh", "cross", "degrees",
+ "dFdx", "dFdxCoarse", "dFdxFine",
+ "dFdy", "dFdyCoarse", "dFdyFine",
+ "distance", "dot", "EmitStreamVertex", "EmitVertex", "EndPrimitive", "EndStreamPrimitive", "equal", "exp", "exp2",
+ "faceforward", "findLSB", "findMSB", "float16BitsToInt16", "float16BitsToUint16", "floatBitsToInt", "floatBitsToUint", "floor", "fma", "fract",
+ "frexp", "fwidth", "fwidthCoarse", "fwidthFine",
+ "greaterThan", "greaterThanEqual", "groupMemoryBarrier",
+ "imageAtomicAdd", "imageAtomicAnd", "imageAtomicCompSwap", "imageAtomicExchange", "imageAtomicMax", "imageAtomicMin", "imageAtomicOr", "imageAtomicXor",
+ "imageLoad", "imageSamples", "imageSize", "imageStore", "imulExtended", "int16BitsToFloat16", "intBitsToFloat", "interpolateAtOffset", "interpolateAtCentroid", "interpolateAtSample",
+ "inverse", "inversesqrt", "isinf", "isnan", "ldexp", "length", "lessThan", "lessThanEqual", "log", "log2",
+ "matrixCompMult", "max", "memoryBarrier", "memoryBarrierAtomicCounter", "memoryBarrierBuffer", "memoryBarrierImage", "memoryBarrierShared",
+ "min", "mix", "mod", "modf", "noise", "noise1", "noise2", "noise3", "noise4", "normalize", "not", "notEqual",
+ "outerProduct", "packDouble2x32", "packHalf2x16", "packInt2x16", "packInt4x16", "packSnorm2x16", "packSnorm4x8",
+ "packUint2x16", "packUint4x16", "packUnorm2x16", "packUnorm4x8", "pow",
+ "radians", "reflect", "refract", "round", "roundEven", "sign", "sin", "sinh", "smoothstep", "sqrt", "step",
+ "tan", "tanh", "texelFetch", "texelFetchOffset", "texture", "textureGather", "textureGatherOffset", "textureGatherOffsets",
+ "textureGrad", "textureGradOffset", "textureLod", "textureLodOffset", "textureOffset", "textureProj", "textureProjGrad",
+ "textureProjGradOffset", "textureProjLod", "textureProjLodOffset", "textureProjOffset", "textureQueryLevels", "textureQueryLod", "textureSamples", "textureSize",
+ "transpose", "trunc", "uaddCarry", "uint16BitsToFloat16", "uintBitsToFloat", "umulExtended", "unpackDouble2x32", "unpackHalf2x16", "unpackInt2x16", "unpackInt4x16",
+ "unpackSnorm2x16", "unpackSnorm4x8", "unpackUint2x16", "unpackUint4x16", "unpackUnorm2x16", "unpackUnorm4x8", "usubBorrow",
+
+ "active", "asm", "atomic_uint", "attribute", "bool", "break", "buffer",
+ "bvec2", "bvec3", "bvec4", "case", "cast", "centroid", "class", "coherent", "common", "const", "continue", "default", "discard",
+ "dmat2", "dmat2x2", "dmat2x3", "dmat2x4", "dmat3", "dmat3x2", "dmat3x3", "dmat3x4", "dmat4", "dmat4x2", "dmat4x3", "dmat4x4",
+ "do", "double", "dvec2", "dvec3", "dvec4", "else", "enum", "extern", "external", "false", "filter", "fixed", "flat", "float",
+ "for", "fvec2", "fvec3", "fvec4", "goto", "half", "highp", "hvec2", "hvec3", "hvec4", "if", "iimage1D", "iimage1DArray",
+ "iimage2D", "iimage2DArray", "iimage2DMS", "iimage2DMSArray", "iimage2DRect", "iimage3D", "iimageBuffer", "iimageCube",
+ "iimageCubeArray", "image1D", "image1DArray", "image2D", "image2DArray", "image2DMS", "image2DMSArray", "image2DRect",
+ "image3D", "imageBuffer", "imageCube", "imageCubeArray", "in", "inline", "inout", "input", "int", "interface", "invariant",
+ "isampler1D", "isampler1DArray", "isampler2D", "isampler2DArray", "isampler2DMS", "isampler2DMSArray", "isampler2DRect",
+ "isampler3D", "isamplerBuffer", "isamplerCube", "isamplerCubeArray", "ivec2", "ivec3", "ivec4", "layout", "long", "lowp",
+ "mat2", "mat2x2", "mat2x3", "mat2x4", "mat3", "mat3x2", "mat3x3", "mat3x4", "mat4", "mat4x2", "mat4x3", "mat4x4", "mediump",
+ "namespace", "noinline", "noperspective", "out", "output", "packed", "partition", "patch", "precise", "precision", "public", "readonly",
+ "resource", "restrict", "return", "sample", "sampler1D", "sampler1DArray", "sampler1DArrayShadow",
+ "sampler1DShadow", "sampler2D", "sampler2DArray", "sampler2DArrayShadow", "sampler2DMS", "sampler2DMSArray",
+ "sampler2DRect", "sampler2DRectShadow", "sampler2DShadow", "sampler3D", "sampler3DRect", "samplerBuffer",
+ "samplerCube", "samplerCubeArray", "samplerCubeArrayShadow", "samplerCubeShadow", "shared", "short", "sizeof", "smooth", "static",
+ "struct", "subroutine", "superp", "switch", "template", "this", "true", "typedef", "uimage1D", "uimage1DArray", "uimage2D",
+ "uimage2DArray", "uimage2DMS", "uimage2DMSArray", "uimage2DRect", "uimage3D", "uimageBuffer", "uimageCube",
+ "uimageCubeArray", "uint", "uniform", "union", "unsigned", "usampler1D", "usampler1DArray", "usampler2D", "usampler2DArray",
+ "usampler2DMS", "usampler2DMSArray", "usampler2DRect", "usampler3D", "usamplerBuffer", "usamplerCube",
+ "usamplerCubeArray", "using", "uvec2", "uvec3", "uvec4", "varying", "vec2", "vec3", "vec4", "void", "volatile",
+ "while", "writeonly",
+ };
+ // clang-format on
+
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, const SPIRVariable &var) {
+ if (!is_hidden_variable(var))
+ {
+ auto &m = ir.meta[var.self].decoration;
+ if (m.alias.compare(0, 3, "gl_") == 0 || keywords.find(m.alias) != end(keywords))
+ m.alias = join("_", m.alias);
+ }
+ });
+}
+
+void CompilerGLSL::replace_fragment_output(SPIRVariable &var)
+{
+ auto &m = ir.meta[var.self].decoration;
+ uint32_t location = 0;
+ if (m.decoration_flags.get(DecorationLocation))
+ location = m.location;
+
+ // If our variable is arrayed, we must not emit the array part of this as the SPIR-V will
+ // do the access chain part of this for us.
+ auto &type = get<SPIRType>(var.basetype);
+
+ if (type.array.empty())
+ {
+ // Redirect the write to a specific render target in legacy GLSL.
+ m.alias = join("gl_FragData[", location, "]");
+
+ if (is_legacy_es() && location != 0)
+ require_extension_internal("GL_EXT_draw_buffers");
+ }
+ else if (type.array.size() == 1)
+ {
+ // If location is non-zero, we probably have to add an offset.
+ // This gets really tricky since we'd have to inject an offset in the access chain.
+ // FIXME: This seems like an extremely odd-ball case, so it's probably fine to leave it like this for now.
+ m.alias = "gl_FragData";
+ if (location != 0)
+ SPIRV_CROSS_THROW("Arrayed output variable used, but location is not 0. "
+ "This is unimplemented in SPIRV-Cross.");
+
+ if (is_legacy_es())
+ require_extension_internal("GL_EXT_draw_buffers");
+ }
+ else
+ SPIRV_CROSS_THROW("Array-of-array output variable used. This cannot be implemented in legacy GLSL.");
+
+ var.compat_builtin = true; // We don't want to declare this variable, but use the name as-is.
+}
+
+void CompilerGLSL::replace_fragment_outputs()
+{
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ auto &type = this->get<SPIRType>(var.basetype);
+
+ if (!is_builtin_variable(var) && !var.remapped_variable && type.pointer && var.storage == StorageClassOutput)
+ replace_fragment_output(var);
+ });
+}
+
+string CompilerGLSL::remap_swizzle(const SPIRType &out_type, uint32_t input_components, const string &expr)
+{
+ if (out_type.vecsize == input_components)
+ return expr;
+ else if (input_components == 1 && !backend.can_swizzle_scalar)
+ return join(type_to_glsl(out_type), "(", expr, ")");
+ else
+ {
+ // FIXME: This will not work with packed expressions.
+ auto e = enclose_expression(expr) + ".";
+ // Just clamp the swizzle index if we have more outputs than inputs.
+ for (uint32_t c = 0; c < out_type.vecsize; c++)
+ e += index_to_swizzle(min(c, input_components - 1));
+ if (backend.swizzle_is_function && out_type.vecsize > 1)
+ e += "()";
+
+ remove_duplicate_swizzle(e);
+ return e;
+ }
+}
+
+void CompilerGLSL::emit_pls()
+{
+ auto &execution = get_entry_point();
+ if (execution.model != ExecutionModelFragment)
+ SPIRV_CROSS_THROW("Pixel local storage only supported in fragment shaders.");
+
+ if (!options.es)
+ SPIRV_CROSS_THROW("Pixel local storage only supported in OpenGL ES.");
+
+ if (options.version < 300)
+ SPIRV_CROSS_THROW("Pixel local storage only supported in ESSL 3.0 and above.");
+
+ if (!pls_inputs.empty())
+ {
+ statement("__pixel_local_inEXT _PLSIn");
+ begin_scope();
+ for (auto &input : pls_inputs)
+ statement(pls_decl(input), ";");
+ end_scope_decl();
+ statement("");
+ }
+
+ if (!pls_outputs.empty())
+ {
+ statement("__pixel_local_outEXT _PLSOut");
+ begin_scope();
+ for (auto &output : pls_outputs)
+ statement(pls_decl(output), ";");
+ end_scope_decl();
+ statement("");
+ }
+}
+
+void CompilerGLSL::fixup_image_load_store_access()
+{
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t var, const SPIRVariable &) {
+ auto &vartype = expression_type(var);
+ if (vartype.basetype == SPIRType::Image)
+ {
+ // Older glslangValidator does not emit required qualifiers here.
+ // Solve this by making the image access as restricted as possible and loosen up if we need to.
+ // If any no-read/no-write flags are actually set, assume that the compiler knows what it's doing.
+
+ auto &flags = ir.meta[var].decoration.decoration_flags;
+ if (!flags.get(DecorationNonWritable) && !flags.get(DecorationNonReadable))
+ {
+ flags.set(DecorationNonWritable);
+ flags.set(DecorationNonReadable);
+ }
+ }
+ });
+}
+
+void CompilerGLSL::emit_declared_builtin_block(StorageClass storage, ExecutionModel model)
+{
+ Bitset emitted_builtins;
+ Bitset global_builtins;
+ const SPIRVariable *block_var = nullptr;
+ bool emitted_block = false;
+ bool builtin_array = false;
+
+ // Need to use declared size in the type.
+ // These variables might have been declared, but not statically used, so we haven't deduced their size yet.
+ uint32_t cull_distance_size = 0;
+ uint32_t clip_distance_size = 0;
+
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ auto &type = this->get<SPIRType>(var.basetype);
+ bool block = has_decoration(type.self, DecorationBlock);
+ Bitset builtins;
+
+ if (var.storage == storage && block && is_builtin_variable(var))
+ {
+ uint32_t index = 0;
+ for (auto &m : ir.meta[type.self].members)
+ {
+ if (m.builtin)
+ {
+ builtins.set(m.builtin_type);
+ if (m.builtin_type == BuiltInCullDistance)
+ cull_distance_size = this->get<SPIRType>(type.member_types[index]).array.front();
+ else if (m.builtin_type == BuiltInClipDistance)
+ clip_distance_size = this->get<SPIRType>(type.member_types[index]).array.front();
+ }
+ index++;
+ }
+ }
+ else if (var.storage == storage && !block && is_builtin_variable(var))
+ {
+ // While we're at it, collect all declared global builtins (HLSL mostly ...).
+ auto &m = ir.meta[var.self].decoration;
+ if (m.builtin)
+ {
+ global_builtins.set(m.builtin_type);
+ if (m.builtin_type == BuiltInCullDistance)
+ cull_distance_size = type.array.front();
+ else if (m.builtin_type == BuiltInClipDistance)
+ clip_distance_size = type.array.front();
+ }
+ }
+
+ if (builtins.empty())
+ return;
+
+ if (emitted_block)
+ SPIRV_CROSS_THROW("Cannot use more than one builtin I/O block.");
+
+ emitted_builtins = builtins;
+ emitted_block = true;
+ builtin_array = !type.array.empty();
+ block_var = &var;
+ });
+
+ global_builtins =
+ Bitset(global_builtins.get_lower() & ((1ull << BuiltInPosition) | (1ull << BuiltInPointSize) |
+ (1ull << BuiltInClipDistance) | (1ull << BuiltInCullDistance)));
+
+ // Try to collect all other declared builtins.
+ if (!emitted_block)
+ emitted_builtins = global_builtins;
+
+ // Can't declare an empty interface block.
+ if (emitted_builtins.empty())
+ return;
+
+ if (storage == StorageClassOutput)
+ statement("out gl_PerVertex");
+ else
+ statement("in gl_PerVertex");
+
+ begin_scope();
+ if (emitted_builtins.get(BuiltInPosition))
+ statement("vec4 gl_Position;");
+ if (emitted_builtins.get(BuiltInPointSize))
+ statement("float gl_PointSize;");
+ if (emitted_builtins.get(BuiltInClipDistance))
+ statement("float gl_ClipDistance[", clip_distance_size, "];");
+ if (emitted_builtins.get(BuiltInCullDistance))
+ statement("float gl_CullDistance[", cull_distance_size, "];");
+
+ bool tessellation = model == ExecutionModelTessellationEvaluation || model == ExecutionModelTessellationControl;
+ if (builtin_array)
+ {
+ // Make sure the array has a supported name in the code.
+ if (storage == StorageClassOutput)
+ set_name(block_var->self, "gl_out");
+ else if (storage == StorageClassInput)
+ set_name(block_var->self, "gl_in");
+
+ if (model == ExecutionModelTessellationControl && storage == StorageClassOutput)
+ end_scope_decl(join(to_name(block_var->self), "[", get_entry_point().output_vertices, "]"));
+ else
+ end_scope_decl(join(to_name(block_var->self), tessellation ? "[gl_MaxPatchVertices]" : "[]"));
+ }
+ else
+ end_scope_decl();
+ statement("");
+}
+
+void CompilerGLSL::declare_undefined_values()
+{
+ bool emitted = false;
+ ir.for_each_typed_id<SPIRUndef>([&](uint32_t, const SPIRUndef &undef) {
+ statement(variable_decl(this->get<SPIRType>(undef.basetype), to_name(undef.self), undef.self), ";");
+ emitted = true;
+ });
+
+ if (emitted)
+ statement("");
+}
+
+bool CompilerGLSL::variable_is_lut(const SPIRVariable &var) const
+{
+ bool statically_assigned = var.statically_assigned && var.static_expression != 0 && var.remapped_variable;
+
+ if (statically_assigned)
+ {
+ auto *constant = maybe_get<SPIRConstant>(var.static_expression);
+ if (constant && constant->is_used_as_lut)
+ return true;
+ }
+
+ return false;
+}
+
+void CompilerGLSL::emit_resources()
+{
+ auto &execution = get_entry_point();
+
+ replace_illegal_names();
+
+ // Legacy GL uses gl_FragData[], redeclare all fragment outputs
+ // with builtins.
+ if (execution.model == ExecutionModelFragment && is_legacy())
+ replace_fragment_outputs();
+
+ // Emit PLS blocks if we have such variables.
+ if (!pls_inputs.empty() || !pls_outputs.empty())
+ emit_pls();
+
+ // Emit custom gl_PerVertex for SSO compatibility.
+ if (options.separate_shader_objects && !options.es && execution.model != ExecutionModelFragment)
+ {
+ switch (execution.model)
+ {
+ case ExecutionModelGeometry:
+ case ExecutionModelTessellationControl:
+ case ExecutionModelTessellationEvaluation:
+ emit_declared_builtin_block(StorageClassInput, execution.model);
+ emit_declared_builtin_block(StorageClassOutput, execution.model);
+ break;
+
+ case ExecutionModelVertex:
+ emit_declared_builtin_block(StorageClassOutput, execution.model);
+ break;
+
+ default:
+ break;
+ }
+ }
+ else
+ {
+ // Need to redeclare clip/cull distance with explicit size to use them.
+ // SPIR-V mandates these builtins have a size declared.
+ const char *storage = execution.model == ExecutionModelFragment ? "in" : "out";
+ if (clip_distance_count != 0)
+ statement(storage, " float gl_ClipDistance[", clip_distance_count, "];");
+ if (cull_distance_count != 0)
+ statement(storage, " float gl_CullDistance[", cull_distance_count, "];");
+ if (clip_distance_count != 0 || cull_distance_count != 0)
+ statement("");
+ }
+
+ if (position_invariant)
+ {
+ statement("invariant gl_Position;");
+ statement("");
+ }
+
+ bool emitted = false;
+
+ // If emitted Vulkan GLSL,
+ // emit specialization constants as actual floats,
+ // spec op expressions will redirect to the constant name.
+ //
+ for (auto &id_ : ir.ids_for_constant_or_type)
+ {
+ auto &id = ir.ids[id_];
+
+ if (id.get_type() == TypeConstant)
+ {
+ auto &c = id.get<SPIRConstant>();
+
+ bool needs_declaration = c.specialization || c.is_used_as_lut;
+
+ if (needs_declaration)
+ {
+ if (!options.vulkan_semantics && c.specialization)
+ {
+ c.specialization_constant_macro_name =
+ constant_value_macro_name(get_decoration(c.self, DecorationSpecId));
+ }
+ emit_constant(c);
+ emitted = true;
+ }
+ }
+ else if (id.get_type() == TypeConstantOp)
+ {
+ emit_specialization_constant_op(id.get<SPIRConstantOp>());
+ emitted = true;
+ }
+ else if (id.get_type() == TypeType)
+ {
+ auto &type = id.get<SPIRType>();
+ if (type.basetype == SPIRType::Struct && type.array.empty() && !type.pointer &&
+ (!ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) &&
+ !ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock)))
+ {
+ if (emitted)
+ statement("");
+ emitted = false;
+
+ emit_struct(type);
+ }
+ }
+ }
+
+ if (emitted)
+ statement("");
+
+ // If we needed to declare work group size late, check here.
+ // If the work group size depends on a specialization constant, we need to declare the layout() block
+ // after constants (and their macros) have been declared.
+ if (execution.model == ExecutionModelGLCompute && !options.vulkan_semantics &&
+ execution.workgroup_size.constant != 0)
+ {
+ SpecializationConstant wg_x, wg_y, wg_z;
+ get_work_group_size_specialization_constants(wg_x, wg_y, wg_z);
+
+ if ((wg_x.id != 0) || (wg_y.id != 0) || (wg_z.id != 0))
+ {
+ vector<string> inputs;
+ build_workgroup_size(inputs, wg_x, wg_y, wg_z);
+ statement("layout(", merge(inputs), ") in;");
+ statement("");
+ }
+ }
+
+ emitted = false;
+
+ // Output UBOs and SSBOs
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ auto &type = this->get<SPIRType>(var.basetype);
+
+ bool is_block_storage = type.storage == StorageClassStorageBuffer || type.storage == StorageClassUniform;
+ bool has_block_flags = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) ||
+ ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock);
+
+ if (var.storage != StorageClassFunction && type.pointer && is_block_storage && !is_hidden_variable(var) &&
+ has_block_flags)
+ {
+ emit_buffer_block(var);
+ }
+ });
+
+ // Output push constant blocks
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ auto &type = this->get<SPIRType>(var.basetype);
+ if (var.storage != StorageClassFunction && type.pointer && type.storage == StorageClassPushConstant &&
+ !is_hidden_variable(var))
+ {
+ emit_push_constant_block(var);
+ }
+ });
+
+ bool skip_separate_image_sampler = !combined_image_samplers.empty() || !options.vulkan_semantics;
+
+ // Output Uniform Constants (values, samplers, images, etc).
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ auto &type = this->get<SPIRType>(var.basetype);
+
+ // If we're remapping separate samplers and images, only emit the combined samplers.
+ if (skip_separate_image_sampler)
+ {
+ // Sampler buffers are always used without a sampler, and they will also work in regular GL.
+ bool sampler_buffer = type.basetype == SPIRType::Image && type.image.dim == DimBuffer;
+ bool separate_image = type.basetype == SPIRType::Image && type.image.sampled == 1;
+ bool separate_sampler = type.basetype == SPIRType::Sampler;
+ if (!sampler_buffer && (separate_image || separate_sampler))
+ return;
+ }
+
+ if (var.storage != StorageClassFunction && type.pointer &&
+ (type.storage == StorageClassUniformConstant || type.storage == StorageClassAtomicCounter ||
+ type.storage == StorageClassRayPayloadNV || type.storage == StorageClassHitAttributeNV ||
+ type.storage == StorageClassIncomingRayPayloadNV) &&
+ !is_hidden_variable(var))
+ {
+ emit_uniform(var);
+ emitted = true;
+ }
+ });
+
+ if (emitted)
+ statement("");
+ emitted = false;
+
+ // Output in/out interfaces.
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ auto &type = this->get<SPIRType>(var.basetype);
+
+ if (var.storage != StorageClassFunction && type.pointer &&
+ (var.storage == StorageClassInput || var.storage == StorageClassOutput) &&
+ interface_variable_exists_in_entry_point(var.self) && !is_hidden_variable(var))
+ {
+ emit_interface_block(var);
+ emitted = true;
+ }
+ else if (is_builtin_variable(var))
+ {
+ // For gl_InstanceIndex emulation on GLES, the API user needs to
+ // supply this uniform.
+ if (options.vertex.support_nonzero_base_instance &&
+ ir.meta[var.self].decoration.builtin_type == BuiltInInstanceIndex && !options.vulkan_semantics)
+ {
+ statement("uniform int SPIRV_Cross_BaseInstance;");
+ emitted = true;
+ }
+ }
+ });
+
+ // Global variables.
+ for (auto global : global_variables)
+ {
+ auto &var = get<SPIRVariable>(global);
+ if (var.storage != StorageClassOutput)
+ {
+ if (!variable_is_lut(var))
+ {
+ add_resource_name(var.self);
+ statement(variable_decl(var), ";");
+ emitted = true;
+ }
+ }
+ }
+
+ if (emitted)
+ statement("");
+
+ declare_undefined_values();
+}
+
+// Returns a string representation of the ID, usable as a function arg.
+// Default is to simply return the expression representation fo the arg ID.
+// Subclasses may override to modify the return value.
+string CompilerGLSL::to_func_call_arg(uint32_t id)
+{
+ // Make sure that we use the name of the original variable, and not the parameter alias.
+ uint32_t name_id = id;
+ auto *var = maybe_get<SPIRVariable>(id);
+ if (var && var->basevariable)
+ name_id = var->basevariable;
+ return to_expression(name_id);
+}
+
+void CompilerGLSL::handle_invalid_expression(uint32_t id)
+{
+ // We tried to read an invalidated expression.
+ // This means we need another pass at compilation, but next time, force temporary variables so that they cannot be invalidated.
+ forced_temporaries.insert(id);
+ force_recompile = true;
+}
+
+// Converts the format of the current expression from packed to unpacked,
+// by wrapping the expression in a constructor of the appropriate type.
+// GLSL does not support packed formats, so simply return the expression.
+// Subclasses that do will override
+string CompilerGLSL::unpack_expression_type(string expr_str, const SPIRType &, uint32_t)
+{
+ return expr_str;
+}
+
+// Sometimes we proactively enclosed an expression where it turns out we might have not needed it after all.
+void CompilerGLSL::strip_enclosed_expression(string &expr)
+{
+ if (expr.size() < 2 || expr.front() != '(' || expr.back() != ')')
+ return;
+
+ // Have to make sure that our first and last parens actually enclose everything inside it.
+ uint32_t paren_count = 0;
+ for (auto &c : expr)
+ {
+ if (c == '(')
+ paren_count++;
+ else if (c == ')')
+ {
+ paren_count--;
+
+ // If we hit 0 and this is not the final char, our first and final parens actually don't
+ // enclose the expression, and we cannot strip, e.g.: (a + b) * (c + d).
+ if (paren_count == 0 && &c != &expr.back())
+ return;
+ }
+ }
+ expr.erase(expr.size() - 1, 1);
+ expr.erase(begin(expr));
+}
+
+string CompilerGLSL::enclose_expression(const string &expr)
+{
+ bool need_parens = false;
+
+ // If the expression starts with a unary we need to enclose to deal with cases where we have back-to-back
+ // unary expressions.
+ if (!expr.empty())
+ {
+ auto c = expr.front();
+ if (c == '-' || c == '+' || c == '!' || c == '~' || c == '&' || c == '*')
+ need_parens = true;
+ }
+
+ if (!need_parens)
+ {
+ uint32_t paren_count = 0;
+ for (auto c : expr)
+ {
+ if (c == '(' || c == '[')
+ paren_count++;
+ else if (c == ')' || c == ']')
+ {
+ assert(paren_count);
+ paren_count--;
+ }
+ else if (c == ' ' && paren_count == 0)
+ {
+ need_parens = true;
+ break;
+ }
+ }
+ assert(paren_count == 0);
+ }
+
+ // If this expression contains any spaces which are not enclosed by parentheses,
+ // we need to enclose it so we can treat the whole string as an expression.
+ // This happens when two expressions have been part of a binary op earlier.
+ if (need_parens)
+ return join('(', expr, ')');
+ else
+ return expr;
+}
+
+string CompilerGLSL::dereference_expression(const std::string &expr)
+{
+ // If this expression starts with an address-of operator ('&'), then
+ // just return the part after the operator.
+ // TODO: Strip parens if unnecessary?
+ if (expr.front() == '&')
+ return expr.substr(1);
+ else
+ return join('*', expr);
+}
+
+string CompilerGLSL::address_of_expression(const std::string &expr)
+{
+ // If this expression starts with a dereference operator ('*'), then
+ // just return the part after the operator.
+ // TODO: Strip parens if unnecessary?
+ if (expr.front() == '*')
+ return expr.substr(1);
+ else
+ return join('&', expr);
+}
+
+// Just like to_expression except that we enclose the expression inside parentheses if needed.
+string CompilerGLSL::to_enclosed_expression(uint32_t id, bool register_expression_read)
+{
+ return enclose_expression(to_expression(id, register_expression_read));
+}
+
+string CompilerGLSL::to_unpacked_expression(uint32_t id, bool register_expression_read)
+{
+ // If we need to transpose, it will also take care of unpacking rules.
+ auto *e = maybe_get<SPIRExpression>(id);
+ bool need_transpose = e && e->need_transpose;
+ if (!need_transpose && has_extended_decoration(id, SPIRVCrossDecorationPacked))
+ return unpack_expression_type(to_expression(id, register_expression_read), expression_type(id),
+ get_extended_decoration(id, SPIRVCrossDecorationPackedType));
+ else
+ return to_expression(id, register_expression_read);
+}
+
+string CompilerGLSL::to_enclosed_unpacked_expression(uint32_t id, bool register_expression_read)
+{
+ // If we need to transpose, it will also take care of unpacking rules.
+ auto *e = maybe_get<SPIRExpression>(id);
+ bool need_transpose = e && e->need_transpose;
+ if (!need_transpose && has_extended_decoration(id, SPIRVCrossDecorationPacked))
+ return unpack_expression_type(to_expression(id, register_expression_read), expression_type(id),
+ get_extended_decoration(id, SPIRVCrossDecorationPackedType));
+ else
+ return to_enclosed_expression(id, register_expression_read);
+}
+
+string CompilerGLSL::to_dereferenced_expression(uint32_t id, bool register_expression_read)
+{
+ auto &type = expression_type(id);
+ if (type.pointer && should_dereference(id))
+ return dereference_expression(to_enclosed_expression(id, register_expression_read));
+ else
+ return to_expression(id, register_expression_read);
+}
+
+string CompilerGLSL::to_pointer_expression(uint32_t id, bool register_expression_read)
+{
+ auto &type = expression_type(id);
+ if (type.pointer && expression_is_lvalue(id) && !should_dereference(id))
+ return address_of_expression(to_enclosed_expression(id, register_expression_read));
+ else
+ return to_unpacked_expression(id, register_expression_read);
+}
+
+string CompilerGLSL::to_enclosed_pointer_expression(uint32_t id, bool register_expression_read)
+{
+ auto &type = expression_type(id);
+ if (type.pointer && expression_is_lvalue(id) && !should_dereference(id))
+ return address_of_expression(to_enclosed_expression(id, register_expression_read));
+ else
+ return to_enclosed_unpacked_expression(id, register_expression_read);
+}
+
+string CompilerGLSL::to_extract_component_expression(uint32_t id, uint32_t index)
+{
+ auto expr = to_enclosed_expression(id);
+ if (has_extended_decoration(id, SPIRVCrossDecorationPacked))
+ return join(expr, "[", index, "]");
+ else
+ return join(expr, ".", index_to_swizzle(index));
+}
+
+string CompilerGLSL::to_expression(uint32_t id, bool register_expression_read)
+{
+ auto itr = invalid_expressions.find(id);
+ if (itr != end(invalid_expressions))
+ handle_invalid_expression(id);
+
+ if (ir.ids[id].get_type() == TypeExpression)
+ {
+ // We might have a more complex chain of dependencies.
+ // A possible scenario is that we
+ //
+ // %1 = OpLoad
+ // %2 = OpDoSomething %1 %1. here %2 will have a dependency on %1.
+ // %3 = OpDoSomethingAgain %2 %2. Here %3 will lose the link to %1 since we don't propagate the dependencies like that.
+ // OpStore %1 %foo // Here we can invalidate %1, and hence all expressions which depend on %1. Only %2 will know since it's part of invalid_expressions.
+ // %4 = OpDoSomethingAnotherTime %3 %3 // If we forward all expressions we will see %1 expression after store, not before.
+ //
+ // However, we can propagate up a list of depended expressions when we used %2, so we can check if %2 is invalid when reading %3 after the store,
+ // and see that we should not forward reads of the original variable.
+ auto &expr = get<SPIRExpression>(id);
+ for (uint32_t dep : expr.expression_dependencies)
+ if (invalid_expressions.find(dep) != end(invalid_expressions))
+ handle_invalid_expression(dep);
+ }
+
+ if (register_expression_read)
+ track_expression_read(id);
+
+ switch (ir.ids[id].get_type())
+ {
+ case TypeExpression:
+ {
+ auto &e = get<SPIRExpression>(id);
+ if (e.base_expression)
+ return to_enclosed_expression(e.base_expression) + e.expression;
+ else if (e.need_transpose)
+ {
+ bool is_packed = has_extended_decoration(id, SPIRVCrossDecorationPacked);
+ return convert_row_major_matrix(e.expression, get<SPIRType>(e.expression_type), is_packed);
+ }
+ else
+ {
+ if (force_recompile)
+ {
+ // During first compilation phase, certain expression patterns can trigger exponential growth of memory.
+ // Avoid this by returning dummy expressions during this phase.
+ // Do not use empty expressions here, because those are sentinels for other cases.
+ return "_";
+ }
+ else
+ return e.expression;
+ }
+ }
+
+ case TypeConstant:
+ {
+ auto &c = get<SPIRConstant>(id);
+ auto &type = get<SPIRType>(c.constant_type);
+
+ // WorkGroupSize may be a constant.
+ auto &dec = ir.meta[c.self].decoration;
+ if (dec.builtin)
+ return builtin_to_glsl(dec.builtin_type, StorageClassGeneric);
+ else if (c.specialization)
+ return to_name(id);
+ else if (c.is_used_as_lut)
+ return to_name(id);
+ else if (type.basetype == SPIRType::Struct && !backend.can_declare_struct_inline)
+ return to_name(id);
+ else if (!type.array.empty() && !backend.can_declare_arrays_inline)
+ return to_name(id);
+ else
+ return constant_expression(c);
+ }
+
+ case TypeConstantOp:
+ return to_name(id);
+
+ case TypeVariable:
+ {
+ auto &var = get<SPIRVariable>(id);
+ // If we try to use a loop variable before the loop header, we have to redirect it to the static expression,
+ // the variable has not been declared yet.
+ if (var.statically_assigned || (var.loop_variable && !var.loop_variable_enable))
+ return to_expression(var.static_expression);
+ else if (var.deferred_declaration)
+ {
+ var.deferred_declaration = false;
+ return variable_decl(var);
+ }
+ else if (flattened_structs.count(id))
+ {
+ return load_flattened_struct(var);
+ }
+ else
+ {
+ auto &dec = ir.meta[var.self].decoration;
+ if (dec.builtin)
+ return builtin_to_glsl(dec.builtin_type, var.storage);
+ else
+ return to_name(id);
+ }
+ }
+
+ case TypeCombinedImageSampler:
+ // This type should never be taken the expression of directly.
+ // The intention is that texture sampling functions will extract the image and samplers
+ // separately and take their expressions as needed.
+ // GLSL does not use this type because OpSampledImage immediately creates a combined image sampler
+ // expression ala sampler2D(texture, sampler).
+ SPIRV_CROSS_THROW("Combined image samplers have no default expression representation.");
+
+ case TypeAccessChain:
+ // We cannot express this type. They only have meaning in other OpAccessChains, OpStore or OpLoad.
+ SPIRV_CROSS_THROW("Access chains have no default expression representation.");
+
+ default:
+ return to_name(id);
+ }
+}
+
+string CompilerGLSL::constant_op_expression(const SPIRConstantOp &cop)
+{
+ auto &type = get<SPIRType>(cop.basetype);
+ bool binary = false;
+ bool unary = false;
+ string op;
+
+ if (is_legacy() && is_unsigned_opcode(cop.opcode))
+ SPIRV_CROSS_THROW("Unsigned integers are not supported on legacy targets.");
+
+ // TODO: Find a clean way to reuse emit_instruction.
+ switch (cop.opcode)
+ {
+ case OpSConvert:
+ case OpUConvert:
+ case OpFConvert:
+ op = type_to_glsl_constructor(type);
+ break;
+
+#define GLSL_BOP(opname, x) \
+ case Op##opname: \
+ binary = true; \
+ op = x; \
+ break
+
+#define GLSL_UOP(opname, x) \
+ case Op##opname: \
+ unary = true; \
+ op = x; \
+ break
+
+ GLSL_UOP(SNegate, "-");
+ GLSL_UOP(Not, "~");
+ GLSL_BOP(IAdd, "+");
+ GLSL_BOP(ISub, "-");
+ GLSL_BOP(IMul, "*");
+ GLSL_BOP(SDiv, "/");
+ GLSL_BOP(UDiv, "/");
+ GLSL_BOP(UMod, "%");
+ GLSL_BOP(SMod, "%");
+ GLSL_BOP(ShiftRightLogical, ">>");
+ GLSL_BOP(ShiftRightArithmetic, ">>");
+ GLSL_BOP(ShiftLeftLogical, "<<");
+ GLSL_BOP(BitwiseOr, "|");
+ GLSL_BOP(BitwiseXor, "^");
+ GLSL_BOP(BitwiseAnd, "&");
+ GLSL_BOP(LogicalOr, "||");
+ GLSL_BOP(LogicalAnd, "&&");
+ GLSL_UOP(LogicalNot, "!");
+ GLSL_BOP(LogicalEqual, "==");
+ GLSL_BOP(LogicalNotEqual, "!=");
+ GLSL_BOP(IEqual, "==");
+ GLSL_BOP(INotEqual, "!=");
+ GLSL_BOP(ULessThan, "<");
+ GLSL_BOP(SLessThan, "<");
+ GLSL_BOP(ULessThanEqual, "<=");
+ GLSL_BOP(SLessThanEqual, "<=");
+ GLSL_BOP(UGreaterThan, ">");
+ GLSL_BOP(SGreaterThan, ">");
+ GLSL_BOP(UGreaterThanEqual, ">=");
+ GLSL_BOP(SGreaterThanEqual, ">=");
+
+ case OpSelect:
+ {
+ if (cop.arguments.size() < 3)
+ SPIRV_CROSS_THROW("Not enough arguments to OpSpecConstantOp.");
+
+ // This one is pretty annoying. It's triggered from
+ // uint(bool), int(bool) from spec constants.
+ // In order to preserve its compile-time constness in Vulkan GLSL,
+ // we need to reduce the OpSelect expression back to this simplified model.
+ // If we cannot, fail.
+ if (to_trivial_mix_op(type, op, cop.arguments[2], cop.arguments[1], cop.arguments[0]))
+ {
+ // Implement as a simple cast down below.
+ }
+ else
+ {
+ // Implement a ternary and pray the compiler understands it :)
+ return to_ternary_expression(type, cop.arguments[0], cop.arguments[1], cop.arguments[2]);
+ }
+ break;
+ }
+
+ case OpVectorShuffle:
+ {
+ string expr = type_to_glsl_constructor(type);
+ expr += "(";
+
+ uint32_t left_components = expression_type(cop.arguments[0]).vecsize;
+ string left_arg = to_enclosed_expression(cop.arguments[0]);
+ string right_arg = to_enclosed_expression(cop.arguments[1]);
+
+ for (uint32_t i = 2; i < uint32_t(cop.arguments.size()); i++)
+ {
+ uint32_t index = cop.arguments[i];
+ if (index >= left_components)
+ expr += right_arg + "." + "xyzw"[index - left_components];
+ else
+ expr += left_arg + "." + "xyzw"[index];
+
+ if (i + 1 < uint32_t(cop.arguments.size()))
+ expr += ", ";
+ }
+
+ expr += ")";
+ return expr;
+ }
+
+ case OpCompositeExtract:
+ {
+ auto expr = access_chain_internal(cop.arguments[0], &cop.arguments[1], uint32_t(cop.arguments.size() - 1),
+ ACCESS_CHAIN_INDEX_IS_LITERAL_BIT, nullptr);
+ return expr;
+ }
+
+ case OpCompositeInsert:
+ SPIRV_CROSS_THROW("OpCompositeInsert spec constant op is not supported.");
+
+ default:
+ // Some opcodes are unimplemented here, these are currently not possible to test from glslang.
+ SPIRV_CROSS_THROW("Unimplemented spec constant op.");
+ }
+
+ uint32_t bit_width = 0;
+ if (unary || binary)
+ bit_width = expression_type(cop.arguments[0]).width;
+
+ SPIRType::BaseType input_type;
+ bool skip_cast_if_equal_type = opcode_is_sign_invariant(cop.opcode);
+
+ switch (cop.opcode)
+ {
+ case OpIEqual:
+ case OpINotEqual:
+ input_type = to_signed_basetype(bit_width);
+ break;
+
+ case OpSLessThan:
+ case OpSLessThanEqual:
+ case OpSGreaterThan:
+ case OpSGreaterThanEqual:
+ case OpSMod:
+ case OpSDiv:
+ case OpShiftRightArithmetic:
+ input_type = to_signed_basetype(bit_width);
+ break;
+
+ case OpULessThan:
+ case OpULessThanEqual:
+ case OpUGreaterThan:
+ case OpUGreaterThanEqual:
+ case OpUMod:
+ case OpUDiv:
+ case OpShiftRightLogical:
+ input_type = to_unsigned_basetype(bit_width);
+ break;
+
+ default:
+ input_type = type.basetype;
+ break;
+ }
+
+#undef GLSL_BOP
+#undef GLSL_UOP
+ if (binary)
+ {
+ if (cop.arguments.size() < 2)
+ SPIRV_CROSS_THROW("Not enough arguments to OpSpecConstantOp.");
+
+ string cast_op0;
+ string cast_op1;
+ auto expected_type = binary_op_bitcast_helper(cast_op0, cast_op1, input_type, cop.arguments[0],
+ cop.arguments[1], skip_cast_if_equal_type);
+
+ if (type.basetype != input_type && type.basetype != SPIRType::Boolean)
+ {
+ expected_type.basetype = input_type;
+ auto expr = bitcast_glsl_op(type, expected_type);
+ expr += '(';
+ expr += join(cast_op0, " ", op, " ", cast_op1);
+ expr += ')';
+ return expr;
+ }
+ else
+ return join("(", cast_op0, " ", op, " ", cast_op1, ")");
+ }
+ else if (unary)
+ {
+ if (cop.arguments.size() < 1)
+ SPIRV_CROSS_THROW("Not enough arguments to OpSpecConstantOp.");
+
+ // Auto-bitcast to result type as needed.
+ // Works around various casting scenarios in glslang as there is no OpBitcast for specialization constants.
+ return join("(", op, bitcast_glsl(type, cop.arguments[0]), ")");
+ }
+ else
+ {
+ if (cop.arguments.size() < 1)
+ SPIRV_CROSS_THROW("Not enough arguments to OpSpecConstantOp.");
+ return join(op, "(", to_expression(cop.arguments[0]), ")");
+ }
+}
+
+string CompilerGLSL::constant_expression(const SPIRConstant &c)
+{
+ auto &type = get<SPIRType>(c.constant_type);
+
+ if (type.pointer)
+ {
+ return backend.null_pointer_literal;
+ }
+ else if (!c.subconstants.empty())
+ {
+ // Handles Arrays and structures.
+ string res;
+ if (backend.use_initializer_list && backend.use_typed_initializer_list && type.basetype == SPIRType::Struct &&
+ type.array.empty())
+ {
+ res = type_to_glsl_constructor(type) + "{ ";
+ }
+ else if (backend.use_initializer_list)
+ {
+ res = "{ ";
+ }
+ else
+ {
+ res = type_to_glsl_constructor(type) + "(";
+ }
+
+ for (auto &elem : c.subconstants)
+ {
+ auto &subc = get<SPIRConstant>(elem);
+ if (subc.specialization)
+ res += to_name(elem);
+ else
+ res += constant_expression(subc);
+
+ if (&elem != &c.subconstants.back())
+ res += ", ";
+ }
+
+ res += backend.use_initializer_list ? " }" : ")";
+ return res;
+ }
+ else if (c.columns() == 1)
+ {
+ return constant_expression_vector(c, 0);
+ }
+ else
+ {
+ string res = type_to_glsl(get<SPIRType>(c.constant_type)) + "(";
+ for (uint32_t col = 0; col < c.columns(); col++)
+ {
+ if (c.specialization_constant_id(col) != 0)
+ res += to_name(c.specialization_constant_id(col));
+ else
+ res += constant_expression_vector(c, col);
+
+ if (col + 1 < c.columns())
+ res += ", ";
+ }
+ res += ")";
+ return res;
+ }
+}
+
+#ifdef _MSC_VER
+// sprintf warning.
+// We cannot rely on snprintf existing because, ..., MSVC.
+#pragma warning(push)
+#pragma warning(disable : 4996)
+#endif
+
+string CompilerGLSL::convert_half_to_string(const SPIRConstant &c, uint32_t col, uint32_t row)
+{
+ string res;
+ float float_value = c.scalar_f16(col, row);
+
+ // There is no literal "hf" in GL_NV_gpu_shader5, so to avoid lots
+ // of complicated workarounds, just value-cast to the half type always.
+ if (std::isnan(float_value) || std::isinf(float_value))
+ {
+ SPIRType type;
+ type.basetype = SPIRType::Half;
+ type.vecsize = 1;
+ type.columns = 1;
+
+ if (float_value == numeric_limits<float>::infinity())
+ res = join(type_to_glsl(type), "(1.0 / 0.0)");
+ else if (float_value == -numeric_limits<float>::infinity())
+ res = join(type_to_glsl(type), "(-1.0 / 0.0)");
+ else if (std::isnan(float_value))
+ res = join(type_to_glsl(type), "(0.0 / 0.0)");
+ else
+ SPIRV_CROSS_THROW("Cannot represent non-finite floating point constant.");
+ }
+ else
+ {
+ SPIRType type;
+ type.basetype = SPIRType::Half;
+ type.vecsize = 1;
+ type.columns = 1;
+ res = join(type_to_glsl(type), "(", convert_to_string(float_value, current_locale_radix_character), ")");
+ }
+
+ return res;
+}
+
+string CompilerGLSL::convert_float_to_string(const SPIRConstant &c, uint32_t col, uint32_t row)
+{
+ string res;
+ float float_value = c.scalar_f32(col, row);
+
+ if (std::isnan(float_value) || std::isinf(float_value))
+ {
+ // Use special representation.
+ if (!is_legacy())
+ {
+ SPIRType out_type;
+ SPIRType in_type;
+ out_type.basetype = SPIRType::Float;
+ in_type.basetype = SPIRType::UInt;
+ out_type.vecsize = 1;
+ in_type.vecsize = 1;
+ out_type.width = 32;
+ in_type.width = 32;
+
+ char print_buffer[32];
+ sprintf(print_buffer, "0x%xu", c.scalar(col, row));
+ res = join(bitcast_glsl_op(out_type, in_type), "(", print_buffer, ")");
+ }
+ else
+ {
+ if (float_value == numeric_limits<float>::infinity())
+ {
+ if (backend.float_literal_suffix)
+ res = "(1.0f / 0.0f)";
+ else
+ res = "(1.0 / 0.0)";
+ }
+ else if (float_value == -numeric_limits<float>::infinity())
+ {
+ if (backend.float_literal_suffix)
+ res = "(-1.0f / 0.0f)";
+ else
+ res = "(-1.0 / 0.0)";
+ }
+ else if (std::isnan(float_value))
+ {
+ if (backend.float_literal_suffix)
+ res = "(0.0f / 0.0f)";
+ else
+ res = "(0.0 / 0.0)";
+ }
+ else
+ SPIRV_CROSS_THROW("Cannot represent non-finite floating point constant.");
+ }
+ }
+ else
+ {
+ res = convert_to_string(float_value, current_locale_radix_character);
+ if (backend.float_literal_suffix)
+ res += "f";
+ }
+
+ return res;
+}
+
+std::string CompilerGLSL::convert_double_to_string(const SPIRConstant &c, uint32_t col, uint32_t row)
+{
+ string res;
+ double double_value = c.scalar_f64(col, row);
+
+ if (std::isnan(double_value) || std::isinf(double_value))
+ {
+ // Use special representation.
+ if (!is_legacy())
+ {
+ SPIRType out_type;
+ SPIRType in_type;
+ out_type.basetype = SPIRType::Double;
+ in_type.basetype = SPIRType::UInt64;
+ out_type.vecsize = 1;
+ in_type.vecsize = 1;
+ out_type.width = 64;
+ in_type.width = 64;
+
+ uint64_t u64_value = c.scalar_u64(col, row);
+
+ if (options.es)
+ SPIRV_CROSS_THROW("64-bit integers/float not supported in ES profile.");
+ require_extension_internal("GL_ARB_gpu_shader_int64");
+
+ char print_buffer[64];
+ sprintf(print_buffer, "0x%llx%s", static_cast<unsigned long long>(u64_value),
+ backend.long_long_literal_suffix ? "ull" : "ul");
+ res = join(bitcast_glsl_op(out_type, in_type), "(", print_buffer, ")");
+ }
+ else
+ {
+ if (options.es)
+ SPIRV_CROSS_THROW("FP64 not supported in ES profile.");
+ if (options.version < 400)
+ require_extension_internal("GL_ARB_gpu_shader_fp64");
+
+ if (double_value == numeric_limits<double>::infinity())
+ {
+ if (backend.double_literal_suffix)
+ res = "(1.0lf / 0.0lf)";
+ else
+ res = "(1.0 / 0.0)";
+ }
+ else if (double_value == -numeric_limits<double>::infinity())
+ {
+ if (backend.double_literal_suffix)
+ res = "(-1.0lf / 0.0lf)";
+ else
+ res = "(-1.0 / 0.0)";
+ }
+ else if (std::isnan(double_value))
+ {
+ if (backend.double_literal_suffix)
+ res = "(0.0lf / 0.0lf)";
+ else
+ res = "(0.0 / 0.0)";
+ }
+ else
+ SPIRV_CROSS_THROW("Cannot represent non-finite floating point constant.");
+ }
+ }
+ else
+ {
+ res = convert_to_string(double_value, current_locale_radix_character);
+ if (backend.double_literal_suffix)
+ res += "lf";
+ }
+
+ return res;
+}
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+string CompilerGLSL::constant_expression_vector(const SPIRConstant &c, uint32_t vector)
+{
+ auto type = get<SPIRType>(c.constant_type);
+ type.columns = 1;
+
+ auto scalar_type = type;
+ scalar_type.vecsize = 1;
+
+ string res;
+ bool splat = backend.use_constructor_splatting && c.vector_size() > 1;
+ bool swizzle_splat = backend.can_swizzle_scalar && c.vector_size() > 1;
+
+ if (!type_is_floating_point(type))
+ {
+ // Cannot swizzle literal integers as a special case.
+ swizzle_splat = false;
+ }
+
+ if (splat || swizzle_splat)
+ {
+ // Cannot use constant splatting if we have specialization constants somewhere in the vector.
+ for (uint32_t i = 0; i < c.vector_size(); i++)
+ {
+ if (c.specialization_constant_id(vector, i) != 0)
+ {
+ splat = false;
+ swizzle_splat = false;
+ break;
+ }
+ }
+ }
+
+ if (splat || swizzle_splat)
+ {
+ if (type.width == 64)
+ {
+ uint64_t ident = c.scalar_u64(vector, 0);
+ for (uint32_t i = 1; i < c.vector_size(); i++)
+ {
+ if (ident != c.scalar_u64(vector, i))
+ {
+ splat = false;
+ swizzle_splat = false;
+ break;
+ }
+ }
+ }
+ else
+ {
+ uint32_t ident = c.scalar(vector, 0);
+ for (uint32_t i = 1; i < c.vector_size(); i++)
+ {
+ if (ident != c.scalar(vector, i))
+ {
+ splat = false;
+ swizzle_splat = false;
+ }
+ }
+ }
+ }
+
+ if (c.vector_size() > 1 && !swizzle_splat)
+ res += type_to_glsl(type) + "(";
+
+ switch (type.basetype)
+ {
+ case SPIRType::Half:
+ if (splat || swizzle_splat)
+ {
+ res += convert_half_to_string(c, vector, 0);
+ if (swizzle_splat)
+ res = remap_swizzle(get<SPIRType>(c.constant_type), 1, res);
+ }
+ else
+ {
+ for (uint32_t i = 0; i < c.vector_size(); i++)
+ {
+ if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0)
+ res += to_name(c.specialization_constant_id(vector, i));
+ else
+ res += convert_half_to_string(c, vector, i);
+
+ if (i + 1 < c.vector_size())
+ res += ", ";
+ }
+ }
+ break;
+
+ case SPIRType::Float:
+ if (splat || swizzle_splat)
+ {
+ res += convert_float_to_string(c, vector, 0);
+ if (swizzle_splat)
+ res = remap_swizzle(get<SPIRType>(c.constant_type), 1, res);
+ }
+ else
+ {
+ for (uint32_t i = 0; i < c.vector_size(); i++)
+ {
+ if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0)
+ res += to_name(c.specialization_constant_id(vector, i));
+ else
+ res += convert_float_to_string(c, vector, i);
+
+ if (i + 1 < c.vector_size())
+ res += ", ";
+ }
+ }
+ break;
+
+ case SPIRType::Double:
+ if (splat || swizzle_splat)
+ {
+ res += convert_double_to_string(c, vector, 0);
+ if (swizzle_splat)
+ res = remap_swizzle(get<SPIRType>(c.constant_type), 1, res);
+ }
+ else
+ {
+ for (uint32_t i = 0; i < c.vector_size(); i++)
+ {
+ if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0)
+ res += to_name(c.specialization_constant_id(vector, i));
+ else
+ res += convert_double_to_string(c, vector, i);
+
+ if (i + 1 < c.vector_size())
+ res += ", ";
+ }
+ }
+ break;
+
+ case SPIRType::Int64:
+ if (splat)
+ {
+ res += convert_to_string(c.scalar_i64(vector, 0));
+ if (backend.long_long_literal_suffix)
+ res += "ll";
+ else
+ res += "l";
+ }
+ else
+ {
+ for (uint32_t i = 0; i < c.vector_size(); i++)
+ {
+ if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0)
+ res += to_name(c.specialization_constant_id(vector, i));
+ else
+ {
+ res += convert_to_string(c.scalar_i64(vector, i));
+ if (backend.long_long_literal_suffix)
+ res += "ll";
+ else
+ res += "l";
+ }
+
+ if (i + 1 < c.vector_size())
+ res += ", ";
+ }
+ }
+ break;
+
+ case SPIRType::UInt64:
+ if (splat)
+ {
+ res += convert_to_string(c.scalar_u64(vector, 0));
+ if (backend.long_long_literal_suffix)
+ res += "ull";
+ else
+ res += "ul";
+ }
+ else
+ {
+ for (uint32_t i = 0; i < c.vector_size(); i++)
+ {
+ if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0)
+ res += to_name(c.specialization_constant_id(vector, i));
+ else
+ {
+ res += convert_to_string(c.scalar_u64(vector, i));
+ if (backend.long_long_literal_suffix)
+ res += "ull";
+ else
+ res += "ul";
+ }
+
+ if (i + 1 < c.vector_size())
+ res += ", ";
+ }
+ }
+ break;
+
+ case SPIRType::UInt:
+ if (splat)
+ {
+ res += convert_to_string(c.scalar(vector, 0));
+ if (is_legacy())
+ {
+ // Fake unsigned constant literals with signed ones if possible.
+ // Things like array sizes, etc, tend to be unsigned even though they could just as easily be signed.
+ if (c.scalar_i32(vector, 0) < 0)
+ SPIRV_CROSS_THROW("Tried to convert uint literal into int, but this made the literal negative.");
+ }
+ else if (backend.uint32_t_literal_suffix)
+ res += "u";
+ }
+ else
+ {
+ for (uint32_t i = 0; i < c.vector_size(); i++)
+ {
+ if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0)
+ res += to_name(c.specialization_constant_id(vector, i));
+ else
+ {
+ res += convert_to_string(c.scalar(vector, i));
+ if (is_legacy())
+ {
+ // Fake unsigned constant literals with signed ones if possible.
+ // Things like array sizes, etc, tend to be unsigned even though they could just as easily be signed.
+ if (c.scalar_i32(vector, i) < 0)
+ SPIRV_CROSS_THROW(
+ "Tried to convert uint literal into int, but this made the literal negative.");
+ }
+ else if (backend.uint32_t_literal_suffix)
+ res += "u";
+ }
+
+ if (i + 1 < c.vector_size())
+ res += ", ";
+ }
+ }
+ break;
+
+ case SPIRType::Int:
+ if (splat)
+ res += convert_to_string(c.scalar_i32(vector, 0));
+ else
+ {
+ for (uint32_t i = 0; i < c.vector_size(); i++)
+ {
+ if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0)
+ res += to_name(c.specialization_constant_id(vector, i));
+ else
+ res += convert_to_string(c.scalar_i32(vector, i));
+ if (i + 1 < c.vector_size())
+ res += ", ";
+ }
+ }
+ break;
+
+ case SPIRType::UShort:
+ if (splat)
+ {
+ res += convert_to_string(c.scalar(vector, 0));
+ if (is_legacy())
+ {
+ // Fake unsigned constant literals with signed ones if possible.
+ // Things like array sizes, etc, tend to be unsigned even though they could just as easily be signed.
+ if (c.scalar_i16(vector, 0) < 0)
+ SPIRV_CROSS_THROW("Tried to convert uint literal into int, but this made the literal negative.");
+ }
+ else
+ res += backend.uint16_t_literal_suffix;
+ }
+ else
+ {
+ for (uint32_t i = 0; i < c.vector_size(); i++)
+ {
+ if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0)
+ res += to_name(c.specialization_constant_id(vector, i));
+ else
+ {
+ res += convert_to_string(c.scalar(vector, i));
+ if (is_legacy())
+ {
+ // Fake unsigned constant literals with signed ones if possible.
+ // Things like array sizes, etc, tend to be unsigned even though they could just as easily be signed.
+ if (c.scalar_i16(vector, i) < 0)
+ SPIRV_CROSS_THROW(
+ "Tried to convert uint literal into int, but this made the literal negative.");
+ }
+ else
+ res += backend.uint16_t_literal_suffix;
+ }
+
+ if (i + 1 < c.vector_size())
+ res += ", ";
+ }
+ }
+ break;
+
+ case SPIRType::Short:
+ if (splat)
+ {
+ res += convert_to_string(c.scalar_i16(vector, 0));
+ res += backend.int16_t_literal_suffix;
+ }
+ else
+ {
+ for (uint32_t i = 0; i < c.vector_size(); i++)
+ {
+ if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0)
+ res += to_name(c.specialization_constant_id(vector, i));
+ else
+ {
+ res += convert_to_string(c.scalar_i16(vector, i));
+ res += backend.int16_t_literal_suffix;
+ }
+ if (i + 1 < c.vector_size())
+ res += ", ";
+ }
+ }
+ break;
+
+ case SPIRType::UByte:
+ if (splat)
+ {
+ res += convert_to_string(c.scalar_u8(vector, 0));
+ }
+ else
+ {
+ for (uint32_t i = 0; i < c.vector_size(); i++)
+ {
+ if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0)
+ res += to_name(c.specialization_constant_id(vector, i));
+ else
+ {
+ res += type_to_glsl(scalar_type);
+ res += "(";
+ res += convert_to_string(c.scalar_u8(vector, i));
+ res += ")";
+ }
+
+ if (i + 1 < c.vector_size())
+ res += ", ";
+ }
+ }
+ break;
+
+ case SPIRType::SByte:
+ if (splat)
+ {
+ res += convert_to_string(c.scalar_i8(vector, 0));
+ }
+ else
+ {
+ for (uint32_t i = 0; i < c.vector_size(); i++)
+ {
+ if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0)
+ res += to_name(c.specialization_constant_id(vector, i));
+ else
+ {
+ res += type_to_glsl(scalar_type);
+ res += "(";
+ res += convert_to_string(c.scalar_i8(vector, i));
+ res += ")";
+ }
+
+ if (i + 1 < c.vector_size())
+ res += ", ";
+ }
+ }
+ break;
+
+ case SPIRType::Boolean:
+ if (splat)
+ res += c.scalar(vector, 0) ? "true" : "false";
+ else
+ {
+ for (uint32_t i = 0; i < c.vector_size(); i++)
+ {
+ if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0)
+ res += to_name(c.specialization_constant_id(vector, i));
+ else
+ res += c.scalar(vector, i) ? "true" : "false";
+
+ if (i + 1 < c.vector_size())
+ res += ", ";
+ }
+ }
+ break;
+
+ default:
+ SPIRV_CROSS_THROW("Invalid constant expression basetype.");
+ }
+
+ if (c.vector_size() > 1 && !swizzle_splat)
+ res += ")";
+
+ return res;
+}
+
+string CompilerGLSL::declare_temporary(uint32_t result_type, uint32_t result_id)
+{
+ auto &type = get<SPIRType>(result_type);
+ auto &flags = ir.meta[result_id].decoration.decoration_flags;
+
+ // If we're declaring temporaries inside continue blocks,
+ // we must declare the temporary in the loop header so that the continue block can avoid declaring new variables.
+ if (current_continue_block && !hoisted_temporaries.count(result_id))
+ {
+ auto &header = get<SPIRBlock>(current_continue_block->loop_dominator);
+ if (find_if(begin(header.declare_temporary), end(header.declare_temporary),
+ [result_type, result_id](const pair<uint32_t, uint32_t> &tmp) {
+ return tmp.first == result_type && tmp.second == result_id;
+ }) == end(header.declare_temporary))
+ {
+ header.declare_temporary.emplace_back(result_type, result_id);
+ hoisted_temporaries.insert(result_id);
+ force_recompile = true;
+ }
+
+ return join(to_name(result_id), " = ");
+ }
+ else if (hoisted_temporaries.count(result_id))
+ {
+ // The temporary has already been declared earlier, so just "declare" the temporary by writing to it.
+ return join(to_name(result_id), " = ");
+ }
+ else
+ {
+ // The result_id has not been made into an expression yet, so use flags interface.
+ add_local_variable_name(result_id);
+ return join(flags_to_precision_qualifiers_glsl(type, flags), variable_decl(type, to_name(result_id)), " = ");
+ }
+}
+
+bool CompilerGLSL::expression_is_forwarded(uint32_t id)
+{
+ return forwarded_temporaries.find(id) != end(forwarded_temporaries);
+}
+
+SPIRExpression &CompilerGLSL::emit_op(uint32_t result_type, uint32_t result_id, const string &rhs, bool forwarding,
+ bool suppress_usage_tracking)
+{
+ if (forwarding && (forced_temporaries.find(result_id) == end(forced_temporaries)))
+ {
+ // Just forward it without temporary.
+ // If the forward is trivial, we do not force flushing to temporary for this expression.
+ if (!suppress_usage_tracking)
+ forwarded_temporaries.insert(result_id);
+
+ return set<SPIRExpression>(result_id, rhs, result_type, true);
+ }
+ else
+ {
+ // If expression isn't immutable, bind it to a temporary and make the new temporary immutable (they always are).
+ statement(declare_temporary(result_type, result_id), rhs, ";");
+ return set<SPIRExpression>(result_id, to_name(result_id), result_type, true);
+ }
+}
+
+void CompilerGLSL::emit_unary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op)
+{
+ bool forward = should_forward(op0);
+ emit_op(result_type, result_id, join(op, to_enclosed_unpacked_expression(op0)), forward);
+ inherit_expression_dependencies(result_id, op0);
+}
+
+void CompilerGLSL::emit_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op)
+{
+ bool forward = should_forward(op0) && should_forward(op1);
+ emit_op(result_type, result_id,
+ join(to_enclosed_unpacked_expression(op0), " ", op, " ", to_enclosed_unpacked_expression(op1)), forward);
+
+ inherit_expression_dependencies(result_id, op0);
+ inherit_expression_dependencies(result_id, op1);
+}
+
+void CompilerGLSL::emit_unrolled_unary_op(uint32_t result_type, uint32_t result_id, uint32_t operand, const char *op)
+{
+ auto &type = get<SPIRType>(result_type);
+ auto expr = type_to_glsl_constructor(type);
+ expr += '(';
+ for (uint32_t i = 0; i < type.vecsize; i++)
+ {
+ // Make sure to call to_expression multiple times to ensure
+ // that these expressions are properly flushed to temporaries if needed.
+ expr += op;
+ expr += to_extract_component_expression(operand, i);
+
+ if (i + 1 < type.vecsize)
+ expr += ", ";
+ }
+ expr += ')';
+ emit_op(result_type, result_id, expr, should_forward(operand));
+
+ inherit_expression_dependencies(result_id, operand);
+}
+
+void CompilerGLSL::emit_unrolled_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1,
+ const char *op)
+{
+ auto &type = get<SPIRType>(result_type);
+ auto expr = type_to_glsl_constructor(type);
+ expr += '(';
+ for (uint32_t i = 0; i < type.vecsize; i++)
+ {
+ // Make sure to call to_expression multiple times to ensure
+ // that these expressions are properly flushed to temporaries if needed.
+ expr += to_extract_component_expression(op0, i);
+ expr += ' ';
+ expr += op;
+ expr += ' ';
+ expr += to_extract_component_expression(op1, i);
+
+ if (i + 1 < type.vecsize)
+ expr += ", ";
+ }
+ expr += ')';
+ emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1));
+
+ inherit_expression_dependencies(result_id, op0);
+ inherit_expression_dependencies(result_id, op1);
+}
+
+SPIRType CompilerGLSL::binary_op_bitcast_helper(string &cast_op0, string &cast_op1, SPIRType::BaseType &input_type,
+ uint32_t op0, uint32_t op1, bool skip_cast_if_equal_type)
+{
+ auto &type0 = expression_type(op0);
+ auto &type1 = expression_type(op1);
+
+ // We have to bitcast if our inputs are of different type, or if our types are not equal to expected inputs.
+ // For some functions like OpIEqual and INotEqual, we don't care if inputs are of different types than expected
+ // since equality test is exactly the same.
+ bool cast = (type0.basetype != type1.basetype) || (!skip_cast_if_equal_type && type0.basetype != input_type);
+
+ // Create a fake type so we can bitcast to it.
+ // We only deal with regular arithmetic types here like int, uints and so on.
+ SPIRType expected_type;
+ expected_type.basetype = input_type;
+ expected_type.vecsize = type0.vecsize;
+ expected_type.columns = type0.columns;
+ expected_type.width = type0.width;
+
+ if (cast)
+ {
+ cast_op0 = bitcast_glsl(expected_type, op0);
+ cast_op1 = bitcast_glsl(expected_type, op1);
+ }
+ else
+ {
+ // If we don't cast, our actual input type is that of the first (or second) argument.
+ cast_op0 = to_enclosed_unpacked_expression(op0);
+ cast_op1 = to_enclosed_unpacked_expression(op1);
+ input_type = type0.basetype;
+ }
+
+ return expected_type;
+}
+
+void CompilerGLSL::emit_binary_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1,
+ const char *op, SPIRType::BaseType input_type, bool skip_cast_if_equal_type)
+{
+ string cast_op0, cast_op1;
+ auto expected_type = binary_op_bitcast_helper(cast_op0, cast_op1, input_type, op0, op1, skip_cast_if_equal_type);
+ auto &out_type = get<SPIRType>(result_type);
+
+ // We might have casted away from the result type, so bitcast again.
+ // For example, arithmetic right shift with uint inputs.
+ // Special case boolean outputs since relational opcodes output booleans instead of int/uint.
+ string expr;
+ if (out_type.basetype != input_type && out_type.basetype != SPIRType::Boolean)
+ {
+ expected_type.basetype = input_type;
+ expr = bitcast_glsl_op(out_type, expected_type);
+ expr += '(';
+ expr += join(cast_op0, " ", op, " ", cast_op1);
+ expr += ')';
+ }
+ else
+ expr += join(cast_op0, " ", op, " ", cast_op1);
+
+ emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1));
+ inherit_expression_dependencies(result_id, op0);
+ inherit_expression_dependencies(result_id, op1);
+}
+
+void CompilerGLSL::emit_unary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op)
+{
+ bool forward = should_forward(op0);
+ emit_op(result_type, result_id, join(op, "(", to_unpacked_expression(op0), ")"), forward);
+ inherit_expression_dependencies(result_id, op0);
+}
+
+void CompilerGLSL::emit_binary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1,
+ const char *op)
+{
+ bool forward = should_forward(op0) && should_forward(op1);
+ emit_op(result_type, result_id, join(op, "(", to_unpacked_expression(op0), ", ", to_unpacked_expression(op1), ")"),
+ forward);
+ inherit_expression_dependencies(result_id, op0);
+ inherit_expression_dependencies(result_id, op1);
+}
+
+void CompilerGLSL::emit_unary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op,
+ SPIRType::BaseType input_type, SPIRType::BaseType expected_result_type)
+{
+ auto &out_type = get<SPIRType>(result_type);
+ auto expected_type = out_type;
+ expected_type.basetype = input_type;
+ string cast_op = expression_type(op0).basetype != input_type ? bitcast_glsl(expected_type, op0) : to_expression(op0);
+
+ string expr;
+ if (out_type.basetype != expected_result_type)
+ {
+ expected_type.basetype = expected_result_type;
+ expr = bitcast_glsl_op(out_type, expected_type);
+ expr += '(';
+ expr += join(op, "(", cast_op, ")");
+ expr += ')';
+ }
+ else
+ {
+ expr += join(op, "(", cast_op, ")");
+ }
+
+ emit_op(result_type, result_id, expr, should_forward(op0));
+ inherit_expression_dependencies(result_id, op0);
+}
+
+void CompilerGLSL::emit_trinary_func_op_cast(uint32_t result_type, uint32_t result_id,
+ uint32_t op0, uint32_t op1, uint32_t op2,
+ const char *op,
+ SPIRType::BaseType input_type)
+{
+ auto &out_type = get<SPIRType>(result_type);
+ auto expected_type = out_type;
+ expected_type.basetype = input_type;
+ string cast_op0 = expression_type(op0).basetype != input_type ? bitcast_glsl(expected_type, op0) : to_expression(op0);
+ string cast_op1 = expression_type(op1).basetype != input_type ? bitcast_glsl(expected_type, op1) : to_expression(op1);
+ string cast_op2 = expression_type(op2).basetype != input_type ? bitcast_glsl(expected_type, op2) : to_expression(op2);
+
+ string expr;
+ if (out_type.basetype != input_type)
+ {
+ expr = bitcast_glsl_op(out_type, expected_type);
+ expr += '(';
+ expr += join(op, "(", cast_op0, ", ", cast_op1, ", ", cast_op2, ")");
+ expr += ')';
+ }
+ else
+ {
+ expr += join(op, "(", cast_op0, ", ", cast_op1, ", ", cast_op2, ")");
+ }
+
+ emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1) && should_forward(op2));
+ inherit_expression_dependencies(result_id, op0);
+ inherit_expression_dependencies(result_id, op1);
+ inherit_expression_dependencies(result_id, op2);
+}
+
+void CompilerGLSL::emit_binary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1,
+ const char *op, SPIRType::BaseType input_type, bool skip_cast_if_equal_type)
+{
+ string cast_op0, cast_op1;
+ auto expected_type = binary_op_bitcast_helper(cast_op0, cast_op1, input_type, op0, op1, skip_cast_if_equal_type);
+ auto &out_type = get<SPIRType>(result_type);
+
+ // Special case boolean outputs since relational opcodes output booleans instead of int/uint.
+ string expr;
+ if (out_type.basetype != input_type && out_type.basetype != SPIRType::Boolean)
+ {
+ expected_type.basetype = input_type;
+ expr = bitcast_glsl_op(out_type, expected_type);
+ expr += '(';
+ expr += join(op, "(", cast_op0, ", ", cast_op1, ")");
+ expr += ')';
+ }
+ else
+ {
+ expr += join(op, "(", cast_op0, ", ", cast_op1, ")");
+ }
+
+ emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1));
+ inherit_expression_dependencies(result_id, op0);
+ inherit_expression_dependencies(result_id, op1);
+}
+
+void CompilerGLSL::emit_trinary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1,
+ uint32_t op2, const char *op)
+{
+ bool forward = should_forward(op0) && should_forward(op1) && should_forward(op2);
+ emit_op(result_type, result_id,
+ join(op, "(", to_unpacked_expression(op0), ", ", to_unpacked_expression(op1), ", ",
+ to_unpacked_expression(op2), ")"),
+ forward);
+
+ inherit_expression_dependencies(result_id, op0);
+ inherit_expression_dependencies(result_id, op1);
+ inherit_expression_dependencies(result_id, op2);
+}
+
+void CompilerGLSL::emit_quaternary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1,
+ uint32_t op2, uint32_t op3, const char *op)
+{
+ bool forward = should_forward(op0) && should_forward(op1) && should_forward(op2) && should_forward(op3);
+ emit_op(result_type, result_id,
+ join(op, "(", to_unpacked_expression(op0), ", ", to_unpacked_expression(op1), ", ",
+ to_unpacked_expression(op2), ", ", to_unpacked_expression(op3), ")"),
+ forward);
+
+ inherit_expression_dependencies(result_id, op0);
+ inherit_expression_dependencies(result_id, op1);
+ inherit_expression_dependencies(result_id, op2);
+ inherit_expression_dependencies(result_id, op3);
+}
+
+// EXT_shader_texture_lod only concerns fragment shaders so lod tex functions
+// are not allowed in ES 2 vertex shaders. But SPIR-V only supports lod tex
+// functions in vertex shaders so we revert those back to plain calls when
+// the lod is a constant value of zero.
+bool CompilerGLSL::check_explicit_lod_allowed(uint32_t lod)
+{
+ auto &execution = get_entry_point();
+ bool allowed = !is_legacy_es() || execution.model == ExecutionModelFragment;
+ if (!allowed && lod != 0)
+ {
+ auto *lod_constant = maybe_get<SPIRConstant>(lod);
+ if (!lod_constant || lod_constant->scalar_f32() != 0.0f)
+ {
+ SPIRV_CROSS_THROW("Explicit lod not allowed in legacy ES non-fragment shaders.");
+ }
+ }
+ return allowed;
+}
+
+string CompilerGLSL::legacy_tex_op(const std::string &op, const SPIRType &imgtype, uint32_t lod, uint32_t tex)
+{
+ const char *type;
+ switch (imgtype.image.dim)
+ {
+ case spv::Dim1D:
+ type = (imgtype.image.arrayed && !options.es) ? "1DArray" : "1D";
+ break;
+ case spv::Dim2D:
+ type = (imgtype.image.arrayed && !options.es) ? "2DArray" : "2D";
+ break;
+ case spv::Dim3D:
+ type = "3D";
+ break;
+ case spv::DimCube:
+ type = "Cube";
+ break;
+ case spv::DimRect:
+ type = "2DRect";
+ break;
+ case spv::DimBuffer:
+ type = "Buffer";
+ break;
+ case spv::DimSubpassData:
+ type = "2D";
+ break;
+ default:
+ type = "";
+ break;
+ }
+
+ bool use_explicit_lod = check_explicit_lod_allowed(lod);
+
+ if (op == "textureLod" || op == "textureProjLod" || op == "textureGrad" || op == "textureProjGrad")
+ {
+ if (is_legacy_es())
+ {
+ if (use_explicit_lod)
+ require_extension_internal("GL_EXT_shader_texture_lod");
+ }
+ else if (is_legacy())
+ require_extension_internal("GL_ARB_shader_texture_lod");
+ }
+
+ if (op == "textureLodOffset" || op == "textureProjLodOffset")
+ {
+ if (is_legacy_es())
+ SPIRV_CROSS_THROW(join(op, " not allowed in legacy ES"));
+
+ require_extension_internal("GL_EXT_gpu_shader4");
+ }
+
+ // GLES has very limited support for shadow samplers.
+ // Basically shadow2D and shadow2DProj work through EXT_shadow_samplers,
+ // everything else can just throw
+ if (image_is_comparison(imgtype, tex) && is_legacy_es())
+ {
+ if (op == "texture" || op == "textureProj")
+ require_extension_internal("GL_EXT_shadow_samplers");
+ else
+ SPIRV_CROSS_THROW(join(op, " not allowed on depth samplers in legacy ES"));
+ }
+
+ bool is_es_and_depth = is_legacy_es() && image_is_comparison(imgtype, tex);
+ std::string type_prefix = image_is_comparison(imgtype, tex) ? "shadow" : "texture";
+
+ if (op == "texture")
+ return is_es_and_depth ? join(type_prefix, type, "EXT") : join(type_prefix, type);
+ else if (op == "textureLod")
+ {
+ if (use_explicit_lod)
+ return join(type_prefix, type, is_legacy_es() ? "LodEXT" : "Lod");
+ else
+ return join(type_prefix, type);
+ }
+ else if (op == "textureProj")
+ return join(type_prefix, type, is_es_and_depth ? "ProjEXT" : "Proj");
+ else if (op == "textureGrad")
+ return join(type_prefix, type, is_legacy_es() ? "GradEXT" : is_legacy_desktop() ? "GradARB" : "Grad");
+ else if (op == "textureProjLod")
+ {
+ if (use_explicit_lod)
+ return join(type_prefix, type, is_legacy_es() ? "ProjLodEXT" : "ProjLod");
+ else
+ return join(type_prefix, type, "Proj");
+ }
+ else if (op == "textureLodOffset")
+ {
+ if (use_explicit_lod)
+ return join(type_prefix, type, "LodOffset");
+ else
+ return join(type_prefix, type);
+ }
+ else if (op == "textureProjGrad")
+ return join(type_prefix, type,
+ is_legacy_es() ? "ProjGradEXT" : is_legacy_desktop() ? "ProjGradARB" : "ProjGrad");
+ else if (op == "textureProjLodOffset")
+ {
+ if (use_explicit_lod)
+ return join(type_prefix, type, "ProjLodOffset");
+ else
+ return join(type_prefix, type, "ProjOffset");
+ }
+ else
+ {
+ SPIRV_CROSS_THROW(join("Unsupported legacy texture op: ", op));
+ }
+}
+
+bool CompilerGLSL::to_trivial_mix_op(const SPIRType &type, string &op, uint32_t left, uint32_t right, uint32_t lerp)
+{
+ auto *cleft = maybe_get<SPIRConstant>(left);
+ auto *cright = maybe_get<SPIRConstant>(right);
+ auto &lerptype = expression_type(lerp);
+
+ // If our targets aren't constants, we cannot use construction.
+ if (!cleft || !cright)
+ return false;
+
+ // If our targets are spec constants, we cannot use construction.
+ if (cleft->specialization || cright->specialization)
+ return false;
+
+ // We can only use trivial construction if we have a scalar
+ // (should be possible to do it for vectors as well, but that is overkill for now).
+ if (lerptype.basetype != SPIRType::Boolean || lerptype.vecsize > 1)
+ return false;
+
+ // If our bool selects between 0 and 1, we can cast from bool instead, making our trivial constructor.
+ bool ret = false;
+ switch (type.basetype)
+ {
+ case SPIRType::Short:
+ case SPIRType::UShort:
+ ret = cleft->scalar_u16() == 0 && cright->scalar_u16() == 1;
+ break;
+
+ case SPIRType::Int:
+ case SPIRType::UInt:
+ ret = cleft->scalar() == 0 && cright->scalar() == 1;
+ break;
+
+ case SPIRType::Half:
+ ret = cleft->scalar_f16() == 0.0f && cright->scalar_f16() == 1.0f;
+ break;
+
+ case SPIRType::Float:
+ ret = cleft->scalar_f32() == 0.0f && cright->scalar_f32() == 1.0f;
+ break;
+
+ case SPIRType::Double:
+ ret = cleft->scalar_f64() == 0.0 && cright->scalar_f64() == 1.0;
+ break;
+
+ case SPIRType::Int64:
+ case SPIRType::UInt64:
+ ret = cleft->scalar_u64() == 0 && cright->scalar_u64() == 1;
+ break;
+
+ default:
+ break;
+ }
+
+ if (ret)
+ op = type_to_glsl_constructor(type);
+ return ret;
+}
+
+string CompilerGLSL::to_ternary_expression(const SPIRType &restype, uint32_t select, uint32_t true_value,
+ uint32_t false_value)
+{
+ string expr;
+ auto &lerptype = expression_type(select);
+
+ if (lerptype.vecsize == 1)
+ expr = join(to_enclosed_expression(select), " ? ", to_enclosed_pointer_expression(true_value), " : ",
+ to_enclosed_pointer_expression(false_value));
+ else
+ {
+ auto swiz = [this](uint32_t expression, uint32_t i) { return to_extract_component_expression(expression, i); };
+
+ expr = type_to_glsl_constructor(restype);
+ expr += "(";
+ for (uint32_t i = 0; i < restype.vecsize; i++)
+ {
+ expr += swiz(select, i);
+ expr += " ? ";
+ expr += swiz(true_value, i);
+ expr += " : ";
+ expr += swiz(false_value, i);
+ if (i + 1 < restype.vecsize)
+ expr += ", ";
+ }
+ expr += ")";
+ }
+
+ return expr;
+}
+
+void CompilerGLSL::emit_mix_op(uint32_t result_type, uint32_t id, uint32_t left, uint32_t right, uint32_t lerp)
+{
+ auto &lerptype = expression_type(lerp);
+ auto &restype = get<SPIRType>(result_type);
+
+ // If this results in a variable pointer, assume it may be written through.
+ if (restype.pointer)
+ {
+ register_write(left);
+ register_write(right);
+ }
+
+ string mix_op;
+ bool has_boolean_mix = backend.boolean_mix_support &&
+ ((options.es && options.version >= 310) || (!options.es && options.version >= 450));
+ bool trivial_mix = to_trivial_mix_op(restype, mix_op, left, right, lerp);
+
+ // Cannot use boolean mix when the lerp argument is just one boolean,
+ // fall back to regular trinary statements.
+ if (lerptype.vecsize == 1)
+ has_boolean_mix = false;
+
+ // If we can reduce the mix to a simple cast, do so.
+ // This helps for cases like int(bool), uint(bool) which is implemented with
+ // OpSelect bool 1 0.
+ if (trivial_mix)
+ {
+ emit_unary_func_op(result_type, id, lerp, mix_op.c_str());
+ }
+ else if (!has_boolean_mix && lerptype.basetype == SPIRType::Boolean)
+ {
+ // Boolean mix not supported on desktop without extension.
+ // Was added in OpenGL 4.5 with ES 3.1 compat.
+ //
+ // Could use GL_EXT_shader_integer_mix on desktop at least,
+ // but Apple doesn't support it. :(
+ // Just implement it as ternary expressions.
+ auto expr = to_ternary_expression(get<SPIRType>(result_type), lerp, right, left);
+ emit_op(result_type, id, expr, should_forward(left) && should_forward(right) && should_forward(lerp));
+ inherit_expression_dependencies(id, left);
+ inherit_expression_dependencies(id, right);
+ inherit_expression_dependencies(id, lerp);
+ }
+ else
+ emit_trinary_func_op(result_type, id, left, right, lerp, "mix");
+}
+
+string CompilerGLSL::to_combined_image_sampler(uint32_t image_id, uint32_t samp_id)
+{
+ // Keep track of the array indices we have used to load the image.
+ // We'll need to use the same array index into the combined image sampler array.
+ auto image_expr = to_expression(image_id);
+ string array_expr;
+ auto array_index = image_expr.find_first_of('[');
+ if (array_index != string::npos)
+ array_expr = image_expr.substr(array_index, string::npos);
+
+ auto &args = current_function->arguments;
+
+ // For GLSL and ESSL targets, we must enumerate all possible combinations for sampler2D(texture2D, sampler) and redirect
+ // all possible combinations into new sampler2D uniforms.
+ auto *image = maybe_get_backing_variable(image_id);
+ auto *samp = maybe_get_backing_variable(samp_id);
+ if (image)
+ image_id = image->self;
+ if (samp)
+ samp_id = samp->self;
+
+ auto image_itr = find_if(begin(args), end(args),
+ [image_id](const SPIRFunction::Parameter &param) { return param.id == image_id; });
+
+ auto sampler_itr = find_if(begin(args), end(args),
+ [samp_id](const SPIRFunction::Parameter &param) { return param.id == samp_id; });
+
+ if (image_itr != end(args) || sampler_itr != end(args))
+ {
+ // If any parameter originates from a parameter, we will find it in our argument list.
+ bool global_image = image_itr == end(args);
+ bool global_sampler = sampler_itr == end(args);
+ uint32_t iid = global_image ? image_id : uint32_t(image_itr - begin(args));
+ uint32_t sid = global_sampler ? samp_id : uint32_t(sampler_itr - begin(args));
+
+ auto &combined = current_function->combined_parameters;
+ auto itr = find_if(begin(combined), end(combined), [=](const SPIRFunction::CombinedImageSamplerParameter &p) {
+ return p.global_image == global_image && p.global_sampler == global_sampler && p.image_id == iid &&
+ p.sampler_id == sid;
+ });
+
+ if (itr != end(combined))
+ return to_expression(itr->id) + array_expr;
+ else
+ {
+ SPIRV_CROSS_THROW(
+ "Cannot find mapping for combined sampler parameter, was build_combined_image_samplers() used "
+ "before compile() was called?");
+ }
+ }
+ else
+ {
+ // For global sampler2D, look directly at the global remapping table.
+ auto &mapping = combined_image_samplers;
+ auto itr = find_if(begin(mapping), end(mapping), [image_id, samp_id](const CombinedImageSampler &combined) {
+ return combined.image_id == image_id && combined.sampler_id == samp_id;
+ });
+
+ if (itr != end(combined_image_samplers))
+ return to_expression(itr->combined_id) + array_expr;
+ else
+ {
+ SPIRV_CROSS_THROW("Cannot find mapping for combined sampler, was build_combined_image_samplers() used "
+ "before compile() was called?");
+ }
+ }
+}
+
+void CompilerGLSL::emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id)
+{
+ if (options.vulkan_semantics && combined_image_samplers.empty())
+ {
+ emit_binary_func_op(result_type, result_id, image_id, samp_id,
+ type_to_glsl(get<SPIRType>(result_type), result_id).c_str());
+
+ // Make sure to suppress usage tracking. It is illegal to create temporaries of opaque types.
+ forwarded_temporaries.erase(result_id);
+ }
+ else
+ {
+ // Make sure to suppress usage tracking. It is illegal to create temporaries of opaque types.
+ emit_op(result_type, result_id, to_combined_image_sampler(image_id, samp_id), true, true);
+ }
+}
+
+static inline bool image_opcode_is_sample_no_dref(Op op)
+{
+ switch (op)
+ {
+ case OpImageSampleExplicitLod:
+ case OpImageSampleImplicitLod:
+ case OpImageSampleProjExplicitLod:
+ case OpImageSampleProjImplicitLod:
+ case OpImageFetch:
+ case OpImageRead:
+ case OpImageSparseSampleExplicitLod:
+ case OpImageSparseSampleImplicitLod:
+ case OpImageSparseSampleProjExplicitLod:
+ case OpImageSparseSampleProjImplicitLod:
+ case OpImageSparseFetch:
+ case OpImageSparseRead:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+void CompilerGLSL::emit_texture_op(const Instruction &i)
+{
+ auto *ops = stream(i);
+ auto op = static_cast<Op>(i.op);
+ uint32_t length = i.length;
+
+ vector<uint32_t> inherited_expressions;
+
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ uint32_t img = ops[2];
+ uint32_t coord = ops[3];
+ uint32_t dref = 0;
+ uint32_t comp = 0;
+ bool gather = false;
+ bool proj = false;
+ bool fetch = false;
+ const uint32_t *opt = nullptr;
+
+ inherited_expressions.push_back(coord);
+
+ switch (op)
+ {
+ case OpImageSampleDrefImplicitLod:
+ case OpImageSampleDrefExplicitLod:
+ dref = ops[4];
+ opt = &ops[5];
+ length -= 5;
+ break;
+
+ case OpImageSampleProjDrefImplicitLod:
+ case OpImageSampleProjDrefExplicitLod:
+ dref = ops[4];
+ opt = &ops[5];
+ length -= 5;
+ proj = true;
+ break;
+
+ case OpImageDrefGather:
+ dref = ops[4];
+ opt = &ops[5];
+ length -= 5;
+ gather = true;
+ break;
+
+ case OpImageGather:
+ comp = ops[4];
+ opt = &ops[5];
+ length -= 5;
+ gather = true;
+ break;
+
+ case OpImageFetch:
+ case OpImageRead: // Reads == fetches in Metal (other langs will not get here)
+ opt = &ops[4];
+ length -= 4;
+ fetch = true;
+ break;
+
+ case OpImageSampleProjImplicitLod:
+ case OpImageSampleProjExplicitLod:
+ opt = &ops[4];
+ length -= 4;
+ proj = true;
+ break;
+
+ default:
+ opt = &ops[4];
+ length -= 4;
+ break;
+ }
+
+ // Bypass pointers because we need the real image struct
+ auto &type = expression_type(img);
+ auto &imgtype = get<SPIRType>(type.self);
+
+ uint32_t coord_components = 0;
+ switch (imgtype.image.dim)
+ {
+ case spv::Dim1D:
+ coord_components = 1;
+ break;
+ case spv::Dim2D:
+ coord_components = 2;
+ break;
+ case spv::Dim3D:
+ coord_components = 3;
+ break;
+ case spv::DimCube:
+ coord_components = 3;
+ break;
+ case spv::DimBuffer:
+ coord_components = 1;
+ break;
+ default:
+ coord_components = 2;
+ break;
+ }
+
+ if (dref)
+ inherited_expressions.push_back(dref);
+
+ if (proj)
+ coord_components++;
+ if (imgtype.image.arrayed)
+ coord_components++;
+
+ uint32_t bias = 0;
+ uint32_t lod = 0;
+ uint32_t grad_x = 0;
+ uint32_t grad_y = 0;
+ uint32_t coffset = 0;
+ uint32_t offset = 0;
+ uint32_t coffsets = 0;
+ uint32_t sample = 0;
+ uint32_t flags = 0;
+
+ if (length)
+ {
+ flags = *opt++;
+ length--;
+ }
+
+ auto test = [&](uint32_t &v, uint32_t flag) {
+ if (length && (flags & flag))
+ {
+ v = *opt++;
+ inherited_expressions.push_back(v);
+ length--;
+ }
+ };
+
+ test(bias, ImageOperandsBiasMask);
+ test(lod, ImageOperandsLodMask);
+ test(grad_x, ImageOperandsGradMask);
+ test(grad_y, ImageOperandsGradMask);
+ test(coffset, ImageOperandsConstOffsetMask);
+ test(offset, ImageOperandsOffsetMask);
+ test(coffsets, ImageOperandsConstOffsetsMask);
+ test(sample, ImageOperandsSampleMask);
+
+ string expr;
+ bool forward = false;
+ expr += to_function_name(img, imgtype, !!fetch, !!gather, !!proj, !!coffsets, (!!coffset || !!offset),
+ (!!grad_x || !!grad_y), !!dref, lod);
+ expr += "(";
+ expr += to_function_args(img, imgtype, fetch, gather, proj, coord, coord_components, dref, grad_x, grad_y, lod,
+ coffset, offset, bias, comp, sample, &forward);
+ expr += ")";
+
+ // texture(samplerXShadow) returns float. shadowX() returns vec4. Swizzle here.
+ if (is_legacy() && image_is_comparison(imgtype, img))
+ expr += ".r";
+
+ // Sampling from a texture which was deduced to be a depth image, might actually return 1 component here.
+ // Remap back to 4 components as sampling opcodes expect.
+ bool image_is_depth;
+ const auto *combined = maybe_get<SPIRCombinedImageSampler>(img);
+ if (combined)
+ image_is_depth = image_is_comparison(imgtype, combined->image);
+ else
+ image_is_depth = image_is_comparison(imgtype, img);
+
+ if (image_is_depth && backend.comparison_image_samples_scalar && image_opcode_is_sample_no_dref(op))
+ {
+ expr = remap_swizzle(get<SPIRType>(result_type), 1, expr);
+ }
+
+ // Deals with reads from MSL. We might need to downconvert to fewer components.
+ if (op == OpImageRead)
+ expr = remap_swizzle(get<SPIRType>(result_type), 4, expr);
+
+ emit_op(result_type, id, expr, forward);
+ for (auto &inherit : inherited_expressions)
+ inherit_expression_dependencies(id, inherit);
+
+ switch (op)
+ {
+ case OpImageSampleDrefImplicitLod:
+ case OpImageSampleImplicitLod:
+ case OpImageSampleProjImplicitLod:
+ case OpImageSampleProjDrefImplicitLod:
+ register_control_dependent_expression(id);
+ break;
+
+ default:
+ break;
+ }
+}
+
+bool CompilerGLSL::expression_is_constant_null(uint32_t id) const
+{
+ auto *c = maybe_get<SPIRConstant>(id);
+ if (!c)
+ return false;
+ return c->constant_is_null();
+}
+
+// Returns the function name for a texture sampling function for the specified image and sampling characteristics.
+// For some subclasses, the function is a method on the specified image.
+string CompilerGLSL::to_function_name(uint32_t tex, const SPIRType &imgtype, bool is_fetch, bool is_gather,
+ bool is_proj, bool has_array_offsets, bool has_offset, bool has_grad, bool,
+ uint32_t lod)
+{
+ string fname;
+
+ // textureLod on sampler2DArrayShadow and samplerCubeShadow does not exist in GLSL for some reason.
+ // To emulate this, we will have to use textureGrad with a constant gradient of 0.
+ // The workaround will assert that the LOD is in fact constant 0, or we cannot emit correct code.
+ // This happens for HLSL SampleCmpLevelZero on Texture2DArray and TextureCube.
+ bool workaround_lod_array_shadow_as_grad = false;
+ if (((imgtype.image.arrayed && imgtype.image.dim == Dim2D) || imgtype.image.dim == DimCube) &&
+ image_is_comparison(imgtype, tex) && lod)
+ {
+ if (!expression_is_constant_null(lod))
+ {
+ SPIRV_CROSS_THROW(
+ "textureLod on sampler2DArrayShadow is not constant 0.0. This cannot be expressed in GLSL.");
+ }
+ workaround_lod_array_shadow_as_grad = true;
+ }
+
+ if (is_fetch)
+ fname += "texelFetch";
+ else
+ {
+ fname += "texture";
+
+ if (is_gather)
+ fname += "Gather";
+ if (has_array_offsets)
+ fname += "Offsets";
+ if (is_proj)
+ fname += "Proj";
+ if (has_grad || workaround_lod_array_shadow_as_grad)
+ fname += "Grad";
+ if (!!lod && !workaround_lod_array_shadow_as_grad)
+ fname += "Lod";
+ }
+
+ if (has_offset)
+ fname += "Offset";
+
+ return is_legacy() ? legacy_tex_op(fname, imgtype, lod, tex) : fname;
+}
+
+std::string CompilerGLSL::convert_separate_image_to_expression(uint32_t id)
+{
+ auto *var = maybe_get_backing_variable(id);
+
+ // If we are fetching from a plain OpTypeImage, we must combine with a dummy sampler in GLSL.
+ // In Vulkan GLSL, we can make use of the newer GL_EXT_samplerless_texture_functions.
+ if (var)
+ {
+ auto &type = get<SPIRType>(var->basetype);
+ if (type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer)
+ {
+ if (options.vulkan_semantics)
+ {
+ // Newer glslang supports this extension to deal with texture2D as argument to texture functions.
+ if (dummy_sampler_id)
+ SPIRV_CROSS_THROW("Vulkan GLSL should not have a dummy sampler for combining.");
+ require_extension_internal("GL_EXT_samplerless_texture_functions");
+ }
+ else
+ {
+ if (!dummy_sampler_id)
+ SPIRV_CROSS_THROW(
+ "Cannot find dummy sampler ID. Was build_dummy_sampler_for_combined_images() called?");
+
+ return to_combined_image_sampler(id, dummy_sampler_id);
+ }
+ }
+ }
+
+ return to_expression(id);
+}
+
+// Returns the function args for a texture sampling function for the specified image and sampling characteristics.
+string CompilerGLSL::to_function_args(uint32_t img, const SPIRType &imgtype, bool is_fetch, bool is_gather,
+ bool is_proj, uint32_t coord, uint32_t coord_components, uint32_t dref,
+ uint32_t grad_x, uint32_t grad_y, uint32_t lod, uint32_t coffset, uint32_t offset,
+ uint32_t bias, uint32_t comp, uint32_t sample, bool *p_forward)
+{
+ string farg_str;
+ if (is_fetch)
+ farg_str = convert_separate_image_to_expression(img);
+ else
+ farg_str = to_expression(img);
+
+ bool swizz_func = backend.swizzle_is_function;
+ auto swizzle = [swizz_func](uint32_t comps, uint32_t in_comps) -> const char * {
+ if (comps == in_comps)
+ return "";
+
+ switch (comps)
+ {
+ case 1:
+ return ".x";
+ case 2:
+ return swizz_func ? ".xy()" : ".xy";
+ case 3:
+ return swizz_func ? ".xyz()" : ".xyz";
+ default:
+ return "";
+ }
+ };
+
+ bool forward = should_forward(coord);
+
+ // The IR can give us more components than we need, so chop them off as needed.
+ auto swizzle_expr = swizzle(coord_components, expression_type(coord).vecsize);
+ // Only enclose the UV expression if needed.
+ auto coord_expr = (*swizzle_expr == '\0') ? to_expression(coord) : (to_enclosed_expression(coord) + swizzle_expr);
+
+ // texelFetch only takes int, not uint.
+ auto &coord_type = expression_type(coord);
+ if (coord_type.basetype == SPIRType::UInt)
+ {
+ auto expected_type = coord_type;
+ expected_type.basetype = SPIRType::Int;
+ coord_expr = bitcast_expression(expected_type, coord_type.basetype, coord_expr);
+ }
+
+ // textureLod on sampler2DArrayShadow and samplerCubeShadow does not exist in GLSL for some reason.
+ // To emulate this, we will have to use textureGrad with a constant gradient of 0.
+ // The workaround will assert that the LOD is in fact constant 0, or we cannot emit correct code.
+ // This happens for HLSL SampleCmpLevelZero on Texture2DArray and TextureCube.
+ bool workaround_lod_array_shadow_as_grad =
+ ((imgtype.image.arrayed && imgtype.image.dim == Dim2D) || imgtype.image.dim == DimCube) &&
+ image_is_comparison(imgtype, img) && lod;
+
+ if (dref)
+ {
+ forward = forward && should_forward(dref);
+
+ // SPIR-V splits dref and coordinate.
+ if (is_gather || coord_components == 4) // GLSL also splits the arguments in two. Same for textureGather.
+ {
+ farg_str += ", ";
+ farg_str += to_expression(coord);
+ farg_str += ", ";
+ farg_str += to_expression(dref);
+ }
+ else if (is_proj)
+ {
+ // Have to reshuffle so we get vec4(coord, dref, proj), special case.
+ // Other shading languages splits up the arguments for coord and compare value like SPIR-V.
+ // The coordinate type for textureProj shadow is always vec4 even for sampler1DShadow.
+ farg_str += ", vec4(";
+
+ if (imgtype.image.dim == Dim1D)
+ {
+ // Could reuse coord_expr, but we will mess up the temporary usage checking.
+ farg_str += to_enclosed_expression(coord) + ".x";
+ farg_str += ", ";
+ farg_str += "0.0, ";
+ farg_str += to_expression(dref);
+ farg_str += ", ";
+ farg_str += to_enclosed_expression(coord) + ".y)";
+ }
+ else if (imgtype.image.dim == Dim2D)
+ {
+ // Could reuse coord_expr, but we will mess up the temporary usage checking.
+ farg_str += to_enclosed_expression(coord) + (swizz_func ? ".xy()" : ".xy");
+ farg_str += ", ";
+ farg_str += to_expression(dref);
+ farg_str += ", ";
+ farg_str += to_enclosed_expression(coord) + ".z)";
+ }
+ else
+ SPIRV_CROSS_THROW("Invalid type for textureProj with shadow.");
+ }
+ else
+ {
+ // Create a composite which merges coord/dref into a single vector.
+ auto type = expression_type(coord);
+ type.vecsize = coord_components + 1;
+ farg_str += ", ";
+ farg_str += type_to_glsl_constructor(type);
+ farg_str += "(";
+ farg_str += coord_expr;
+ farg_str += ", ";
+ farg_str += to_expression(dref);
+ farg_str += ")";
+ }
+ }
+ else
+ {
+ farg_str += ", ";
+ farg_str += coord_expr;
+ }
+
+ if (grad_x || grad_y)
+ {
+ forward = forward && should_forward(grad_x);
+ forward = forward && should_forward(grad_y);
+ farg_str += ", ";
+ farg_str += to_expression(grad_x);
+ farg_str += ", ";
+ farg_str += to_expression(grad_y);
+ }
+
+ if (lod)
+ {
+ if (workaround_lod_array_shadow_as_grad)
+ {
+ // Implement textureGrad() instead. LOD == 0.0 is implemented as gradient of 0.0.
+ // Implementing this as plain texture() is not safe on some implementations.
+ if (imgtype.image.dim == Dim2D)
+ farg_str += ", vec2(0.0), vec2(0.0)";
+ else if (imgtype.image.dim == DimCube)
+ farg_str += ", vec3(0.0), vec3(0.0)";
+ }
+ else
+ {
+ if (check_explicit_lod_allowed(lod))
+ {
+ forward = forward && should_forward(lod);
+ farg_str += ", ";
+ farg_str += to_expression(lod);
+ }
+ }
+ }
+ else if (is_fetch && imgtype.image.dim != DimBuffer && !imgtype.image.ms)
+ {
+ // Lod argument is optional in OpImageFetch, but we require a LOD value, pick 0 as the default.
+ farg_str += ", 0";
+ }
+
+ if (coffset)
+ {
+ forward = forward && should_forward(coffset);
+ farg_str += ", ";
+ farg_str += to_expression(coffset);
+ }
+ else if (offset)
+ {
+ forward = forward && should_forward(offset);
+ farg_str += ", ";
+ farg_str += to_expression(offset);
+ }
+
+ if (bias)
+ {
+ forward = forward && should_forward(bias);
+ farg_str += ", ";
+ farg_str += to_expression(bias);
+ }
+
+ if (comp)
+ {
+ forward = forward && should_forward(comp);
+ farg_str += ", ";
+ farg_str += to_expression(comp);
+ }
+
+ if (sample)
+ {
+ farg_str += ", ";
+ farg_str += to_expression(sample);
+ }
+
+ *p_forward = forward;
+
+ return farg_str;
+}
+
+void CompilerGLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop, const uint32_t *args, uint32_t length)
+{
+ auto op = static_cast<GLSLstd450>(eop);
+
+ if (is_legacy() && is_unsigned_glsl_opcode(op))
+ SPIRV_CROSS_THROW("Unsigned integers are not supported on legacy GLSL targets.");
+
+ // If we need to do implicit bitcasts, make sure we do it with the correct type.
+ uint32_t integer_width = get_integer_width_for_glsl_instruction(op, args, length);
+ auto int_type = to_signed_basetype(integer_width);
+ auto uint_type = to_unsigned_basetype(integer_width);
+
+ switch (op)
+ {
+ // FP fiddling
+ case GLSLstd450Round:
+ emit_unary_func_op(result_type, id, args[0], "round");
+ break;
+
+ case GLSLstd450RoundEven:
+ if ((options.es && options.version >= 300) || (!options.es && options.version >= 130))
+ emit_unary_func_op(result_type, id, args[0], "roundEven");
+ else
+ SPIRV_CROSS_THROW("roundEven supported only in ESSL 300 and GLSL 130 and up.");
+ break;
+
+ case GLSLstd450Trunc:
+ emit_unary_func_op(result_type, id, args[0], "trunc");
+ break;
+ case GLSLstd450SAbs:
+ emit_unary_func_op_cast(result_type, id, args[0], "abs", int_type, int_type);
+ break;
+ case GLSLstd450FAbs:
+ emit_unary_func_op(result_type, id, args[0], "abs");
+ break;
+ case GLSLstd450SSign:
+ emit_unary_func_op_cast(result_type, id, args[0], "sign", int_type, int_type);
+ break;
+ case GLSLstd450FSign:
+ emit_unary_func_op(result_type, id, args[0], "sign");
+ break;
+ case GLSLstd450Floor:
+ emit_unary_func_op(result_type, id, args[0], "floor");
+ break;
+ case GLSLstd450Ceil:
+ emit_unary_func_op(result_type, id, args[0], "ceil");
+ break;
+ case GLSLstd450Fract:
+ emit_unary_func_op(result_type, id, args[0], "fract");
+ break;
+ case GLSLstd450Radians:
+ emit_unary_func_op(result_type, id, args[0], "radians");
+ break;
+ case GLSLstd450Degrees:
+ emit_unary_func_op(result_type, id, args[0], "degrees");
+ break;
+ case GLSLstd450Fma:
+ emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "fma");
+ break;
+ case GLSLstd450Modf:
+ register_call_out_argument(args[1]);
+ forced_temporaries.insert(id);
+ emit_binary_func_op(result_type, id, args[0], args[1], "modf");
+ break;
+
+ case GLSLstd450ModfStruct:
+ {
+ forced_temporaries.insert(id);
+ auto &type = get<SPIRType>(result_type);
+ auto &flags = ir.meta[id].decoration.decoration_flags;
+ statement(flags_to_precision_qualifiers_glsl(type, flags), variable_decl(type, to_name(id)), ";");
+ set<SPIRExpression>(id, to_name(id), result_type, true);
+
+ statement(to_expression(id), ".", to_member_name(type, 0), " = ", "modf(", to_expression(args[0]), ", ",
+ to_expression(id), ".", to_member_name(type, 1), ");");
+ break;
+ }
+
+ // Minmax
+ case GLSLstd450UMin:
+ emit_binary_func_op_cast(result_type, id, args[0], args[1], "min", uint_type, false);
+ break;
+
+ case GLSLstd450SMin:
+ emit_binary_func_op_cast(result_type, id, args[0], args[1], "min", int_type, false);
+ break;
+
+ case GLSLstd450FMin:
+ emit_binary_func_op(result_type, id, args[0], args[1], "min");
+ break;
+
+ case GLSLstd450FMax:
+ emit_binary_func_op(result_type, id, args[0], args[1], "max");
+ break;
+
+ case GLSLstd450UMax:
+ emit_binary_func_op_cast(result_type, id, args[0], args[1], "max", uint_type, false);
+ break;
+
+ case GLSLstd450SMax:
+ emit_binary_func_op_cast(result_type, id, args[0], args[1], "max", int_type, false);
+ break;
+
+ case GLSLstd450FClamp:
+ emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "clamp");
+ break;
+
+ case GLSLstd450UClamp:
+ emit_trinary_func_op_cast(result_type, id, args[0], args[1], args[2], "clamp", uint_type);
+ break;
+
+ case GLSLstd450SClamp:
+ emit_trinary_func_op_cast(result_type, id, args[0], args[1], args[2], "clamp", int_type);
+ break;
+
+ // Trig
+ case GLSLstd450Sin:
+ emit_unary_func_op(result_type, id, args[0], "sin");
+ break;
+ case GLSLstd450Cos:
+ emit_unary_func_op(result_type, id, args[0], "cos");
+ break;
+ case GLSLstd450Tan:
+ emit_unary_func_op(result_type, id, args[0], "tan");
+ break;
+ case GLSLstd450Asin:
+ emit_unary_func_op(result_type, id, args[0], "asin");
+ break;
+ case GLSLstd450Acos:
+ emit_unary_func_op(result_type, id, args[0], "acos");
+ break;
+ case GLSLstd450Atan:
+ emit_unary_func_op(result_type, id, args[0], "atan");
+ break;
+ case GLSLstd450Sinh:
+ emit_unary_func_op(result_type, id, args[0], "sinh");
+ break;
+ case GLSLstd450Cosh:
+ emit_unary_func_op(result_type, id, args[0], "cosh");
+ break;
+ case GLSLstd450Tanh:
+ emit_unary_func_op(result_type, id, args[0], "tanh");
+ break;
+ case GLSLstd450Asinh:
+ emit_unary_func_op(result_type, id, args[0], "asinh");
+ break;
+ case GLSLstd450Acosh:
+ emit_unary_func_op(result_type, id, args[0], "acosh");
+ break;
+ case GLSLstd450Atanh:
+ emit_unary_func_op(result_type, id, args[0], "atanh");
+ break;
+ case GLSLstd450Atan2:
+ emit_binary_func_op(result_type, id, args[0], args[1], "atan");
+ break;
+
+ // Exponentials
+ case GLSLstd450Pow:
+ emit_binary_func_op(result_type, id, args[0], args[1], "pow");
+ break;
+ case GLSLstd450Exp:
+ emit_unary_func_op(result_type, id, args[0], "exp");
+ break;
+ case GLSLstd450Log:
+ emit_unary_func_op(result_type, id, args[0], "log");
+ break;
+ case GLSLstd450Exp2:
+ emit_unary_func_op(result_type, id, args[0], "exp2");
+ break;
+ case GLSLstd450Log2:
+ emit_unary_func_op(result_type, id, args[0], "log2");
+ break;
+ case GLSLstd450Sqrt:
+ emit_unary_func_op(result_type, id, args[0], "sqrt");
+ break;
+ case GLSLstd450InverseSqrt:
+ emit_unary_func_op(result_type, id, args[0], "inversesqrt");
+ break;
+
+ // Matrix math
+ case GLSLstd450Determinant:
+ emit_unary_func_op(result_type, id, args[0], "determinant");
+ break;
+ case GLSLstd450MatrixInverse:
+ emit_unary_func_op(result_type, id, args[0], "inverse");
+ break;
+
+ // Lerping
+ case GLSLstd450FMix:
+ case GLSLstd450IMix:
+ {
+ emit_mix_op(result_type, id, args[0], args[1], args[2]);
+ break;
+ }
+ case GLSLstd450Step:
+ emit_binary_func_op(result_type, id, args[0], args[1], "step");
+ break;
+ case GLSLstd450SmoothStep:
+ emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "smoothstep");
+ break;
+
+ // Packing
+ case GLSLstd450Frexp:
+ register_call_out_argument(args[1]);
+ forced_temporaries.insert(id);
+ emit_binary_func_op(result_type, id, args[0], args[1], "frexp");
+ break;
+
+ case GLSLstd450FrexpStruct:
+ {
+ forced_temporaries.insert(id);
+ auto &type = get<SPIRType>(result_type);
+ auto &flags = ir.meta[id].decoration.decoration_flags;
+ statement(flags_to_precision_qualifiers_glsl(type, flags), variable_decl(type, to_name(id)), ";");
+ set<SPIRExpression>(id, to_name(id), result_type, true);
+
+ statement(to_expression(id), ".", to_member_name(type, 0), " = ", "frexp(", to_expression(args[0]), ", ",
+ to_expression(id), ".", to_member_name(type, 1), ");");
+ break;
+ }
+
+ case GLSLstd450Ldexp:
+ emit_binary_func_op(result_type, id, args[0], args[1], "ldexp");
+ break;
+ case GLSLstd450PackSnorm4x8:
+ emit_unary_func_op(result_type, id, args[0], "packSnorm4x8");
+ break;
+ case GLSLstd450PackUnorm4x8:
+ emit_unary_func_op(result_type, id, args[0], "packUnorm4x8");
+ break;
+ case GLSLstd450PackSnorm2x16:
+ emit_unary_func_op(result_type, id, args[0], "packSnorm2x16");
+ break;
+ case GLSLstd450PackUnorm2x16:
+ emit_unary_func_op(result_type, id, args[0], "packUnorm2x16");
+ break;
+ case GLSLstd450PackHalf2x16:
+ emit_unary_func_op(result_type, id, args[0], "packHalf2x16");
+ break;
+ case GLSLstd450UnpackSnorm4x8:
+ emit_unary_func_op(result_type, id, args[0], "unpackSnorm4x8");
+ break;
+ case GLSLstd450UnpackUnorm4x8:
+ emit_unary_func_op(result_type, id, args[0], "unpackUnorm4x8");
+ break;
+ case GLSLstd450UnpackSnorm2x16:
+ emit_unary_func_op(result_type, id, args[0], "unpackSnorm2x16");
+ break;
+ case GLSLstd450UnpackUnorm2x16:
+ emit_unary_func_op(result_type, id, args[0], "unpackUnorm2x16");
+ break;
+ case GLSLstd450UnpackHalf2x16:
+ emit_unary_func_op(result_type, id, args[0], "unpackHalf2x16");
+ break;
+
+ case GLSLstd450PackDouble2x32:
+ emit_unary_func_op(result_type, id, args[0], "packDouble2x32");
+ break;
+ case GLSLstd450UnpackDouble2x32:
+ emit_unary_func_op(result_type, id, args[0], "unpackDouble2x32");
+ break;
+
+ // Vector math
+ case GLSLstd450Length:
+ emit_unary_func_op(result_type, id, args[0], "length");
+ break;
+ case GLSLstd450Distance:
+ emit_binary_func_op(result_type, id, args[0], args[1], "distance");
+ break;
+ case GLSLstd450Cross:
+ emit_binary_func_op(result_type, id, args[0], args[1], "cross");
+ break;
+ case GLSLstd450Normalize:
+ emit_unary_func_op(result_type, id, args[0], "normalize");
+ break;
+ case GLSLstd450FaceForward:
+ emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "faceforward");
+ break;
+ case GLSLstd450Reflect:
+ emit_binary_func_op(result_type, id, args[0], args[1], "reflect");
+ break;
+ case GLSLstd450Refract:
+ emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "refract");
+ break;
+
+ // Bit-fiddling
+ case GLSLstd450FindILsb:
+ emit_unary_func_op(result_type, id, args[0], "findLSB");
+ break;
+
+ case GLSLstd450FindSMsb:
+ emit_unary_func_op_cast(result_type, id, args[0], "findMSB", int_type, int_type);
+ break;
+
+ case GLSLstd450FindUMsb:
+ emit_unary_func_op_cast(result_type, id, args[0], "findMSB", uint_type, int_type); // findMSB always returns int.
+ break;
+
+ // Multisampled varying
+ case GLSLstd450InterpolateAtCentroid:
+ emit_unary_func_op(result_type, id, args[0], "interpolateAtCentroid");
+ break;
+ case GLSLstd450InterpolateAtSample:
+ emit_binary_func_op(result_type, id, args[0], args[1], "interpolateAtSample");
+ break;
+ case GLSLstd450InterpolateAtOffset:
+ emit_binary_func_op(result_type, id, args[0], args[1], "interpolateAtOffset");
+ break;
+
+ case GLSLstd450NMin:
+ case GLSLstd450NMax:
+ {
+ emit_nminmax_op(result_type, id, args[0], args[1], op);
+ break;
+ }
+
+ case GLSLstd450NClamp:
+ {
+ // Make sure we have a unique ID here to avoid aliasing the extra sub-expressions between clamp and NMin sub-op.
+ // IDs cannot exceed 24 bits, so we can make use of the higher bits for some unique flags.
+ uint32_t &max_id = extra_sub_expressions[id | 0x80000000u];
+ if (!max_id)
+ max_id = ir.increase_bound_by(1);
+
+ // Inherit precision qualifiers.
+ ir.meta[max_id] = ir.meta[id];
+
+ emit_nminmax_op(result_type, max_id, args[0], args[1], GLSLstd450NMax);
+ emit_nminmax_op(result_type, id, max_id, args[2], GLSLstd450NMin);
+ break;
+ }
+
+ default:
+ statement("// unimplemented GLSL op ", eop);
+ break;
+ }
+}
+
+void CompilerGLSL::emit_nminmax_op(uint32_t result_type, uint32_t id, uint32_t op0, uint32_t op1, GLSLstd450 op)
+{
+ // Need to emulate this call.
+ uint32_t &ids = extra_sub_expressions[id];
+ if (!ids)
+ {
+ ids = ir.increase_bound_by(5);
+ auto btype = get<SPIRType>(result_type);
+ btype.basetype = SPIRType::Boolean;
+ set<SPIRType>(ids, btype);
+ }
+
+ uint32_t btype_id = ids + 0;
+ uint32_t left_nan_id = ids + 1;
+ uint32_t right_nan_id = ids + 2;
+ uint32_t tmp_id = ids + 3;
+ uint32_t mixed_first_id = ids + 4;
+
+ // Inherit precision qualifiers.
+ ir.meta[tmp_id] = ir.meta[id];
+ ir.meta[mixed_first_id] = ir.meta[id];
+
+ emit_unary_func_op(btype_id, left_nan_id, op0, "isnan");
+ emit_unary_func_op(btype_id, right_nan_id, op1, "isnan");
+ emit_binary_func_op(result_type, tmp_id, op0, op1, op == GLSLstd450NMin ? "min" : "max");
+ emit_mix_op(result_type, mixed_first_id, tmp_id, op1, left_nan_id);
+ emit_mix_op(result_type, id, mixed_first_id, op0, right_nan_id);
+}
+
+void CompilerGLSL::emit_spv_amd_shader_ballot_op(uint32_t result_type, uint32_t id, uint32_t eop, const uint32_t *args,
+ uint32_t)
+{
+ require_extension_internal("GL_AMD_shader_ballot");
+
+ enum AMDShaderBallot
+ {
+ SwizzleInvocationsAMD = 1,
+ SwizzleInvocationsMaskedAMD = 2,
+ WriteInvocationAMD = 3,
+ MbcntAMD = 4
+ };
+
+ auto op = static_cast<AMDShaderBallot>(eop);
+
+ switch (op)
+ {
+ case SwizzleInvocationsAMD:
+ emit_binary_func_op(result_type, id, args[0], args[1], "swizzleInvocationsAMD");
+ register_control_dependent_expression(id);
+ break;
+
+ case SwizzleInvocationsMaskedAMD:
+ emit_binary_func_op(result_type, id, args[0], args[1], "swizzleInvocationsMaskedAMD");
+ register_control_dependent_expression(id);
+ break;
+
+ case WriteInvocationAMD:
+ emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "writeInvocationAMD");
+ register_control_dependent_expression(id);
+ break;
+
+ case MbcntAMD:
+ emit_unary_func_op(result_type, id, args[0], "mbcntAMD");
+ register_control_dependent_expression(id);
+ break;
+
+ default:
+ statement("// unimplemented SPV AMD shader ballot op ", eop);
+ break;
+ }
+}
+
+void CompilerGLSL::emit_spv_amd_shader_explicit_vertex_parameter_op(uint32_t result_type, uint32_t id, uint32_t eop,
+ const uint32_t *args, uint32_t)
+{
+ require_extension_internal("GL_AMD_shader_explicit_vertex_parameter");
+
+ enum AMDShaderExplicitVertexParameter
+ {
+ InterpolateAtVertexAMD = 1
+ };
+
+ auto op = static_cast<AMDShaderExplicitVertexParameter>(eop);
+
+ switch (op)
+ {
+ case InterpolateAtVertexAMD:
+ emit_binary_func_op(result_type, id, args[0], args[1], "interpolateAtVertexAMD");
+ break;
+
+ default:
+ statement("// unimplemented SPV AMD shader explicit vertex parameter op ", eop);
+ break;
+ }
+}
+
+void CompilerGLSL::emit_spv_amd_shader_trinary_minmax_op(uint32_t result_type, uint32_t id, uint32_t eop,
+ const uint32_t *args, uint32_t)
+{
+ require_extension_internal("GL_AMD_shader_trinary_minmax");
+
+ enum AMDShaderTrinaryMinMax
+ {
+ FMin3AMD = 1,
+ UMin3AMD = 2,
+ SMin3AMD = 3,
+ FMax3AMD = 4,
+ UMax3AMD = 5,
+ SMax3AMD = 6,
+ FMid3AMD = 7,
+ UMid3AMD = 8,
+ SMid3AMD = 9
+ };
+
+ auto op = static_cast<AMDShaderTrinaryMinMax>(eop);
+
+ switch (op)
+ {
+ case FMin3AMD:
+ case UMin3AMD:
+ case SMin3AMD:
+ emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "min3");
+ break;
+
+ case FMax3AMD:
+ case UMax3AMD:
+ case SMax3AMD:
+ emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "max3");
+ break;
+
+ case FMid3AMD:
+ case UMid3AMD:
+ case SMid3AMD:
+ emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "mid3");
+ break;
+
+ default:
+ statement("// unimplemented SPV AMD shader trinary minmax op ", eop);
+ break;
+ }
+}
+
+void CompilerGLSL::emit_spv_amd_gcn_shader_op(uint32_t result_type, uint32_t id, uint32_t eop, const uint32_t *args,
+ uint32_t)
+{
+ require_extension_internal("GL_AMD_gcn_shader");
+
+ enum AMDGCNShader
+ {
+ CubeFaceIndexAMD = 1,
+ CubeFaceCoordAMD = 2,
+ TimeAMD = 3
+ };
+
+ auto op = static_cast<AMDGCNShader>(eop);
+
+ switch (op)
+ {
+ case CubeFaceIndexAMD:
+ emit_unary_func_op(result_type, id, args[0], "cubeFaceIndexAMD");
+ break;
+ case CubeFaceCoordAMD:
+ emit_unary_func_op(result_type, id, args[0], "cubeFaceCoordAMD");
+ break;
+ case TimeAMD:
+ {
+ string expr = "timeAMD()";
+ emit_op(result_type, id, expr, true);
+ register_control_dependent_expression(id);
+ break;
+ }
+
+ default:
+ statement("// unimplemented SPV AMD gcn shader op ", eop);
+ break;
+ }
+}
+
+void CompilerGLSL::emit_subgroup_op(const Instruction &i)
+{
+ const uint32_t *ops = stream(i);
+ auto op = static_cast<Op>(i.op);
+
+ if (!options.vulkan_semantics)
+ SPIRV_CROSS_THROW("Can only use subgroup operations in Vulkan semantics.");
+
+ switch (op)
+ {
+ case OpGroupNonUniformElect:
+ require_extension_internal("GL_KHR_shader_subgroup_basic");
+ break;
+
+ case OpGroupNonUniformBroadcast:
+ case OpGroupNonUniformBroadcastFirst:
+ case OpGroupNonUniformBallot:
+ case OpGroupNonUniformInverseBallot:
+ case OpGroupNonUniformBallotBitExtract:
+ case OpGroupNonUniformBallotBitCount:
+ case OpGroupNonUniformBallotFindLSB:
+ case OpGroupNonUniformBallotFindMSB:
+ require_extension_internal("GL_KHR_shader_subgroup_ballot");
+ break;
+
+ case OpGroupNonUniformShuffle:
+ case OpGroupNonUniformShuffleXor:
+ require_extension_internal("GL_KHR_shader_subgroup_shuffle");
+ break;
+
+ case OpGroupNonUniformShuffleUp:
+ case OpGroupNonUniformShuffleDown:
+ require_extension_internal("GL_KHR_shader_subgroup_shuffle_relative");
+ break;
+
+ case OpGroupNonUniformAll:
+ case OpGroupNonUniformAny:
+ case OpGroupNonUniformAllEqual:
+ require_extension_internal("GL_KHR_shader_subgroup_vote");
+ break;
+
+ case OpGroupNonUniformFAdd:
+ case OpGroupNonUniformFMul:
+ case OpGroupNonUniformFMin:
+ case OpGroupNonUniformFMax:
+ case OpGroupNonUniformIAdd:
+ case OpGroupNonUniformIMul:
+ case OpGroupNonUniformSMin:
+ case OpGroupNonUniformSMax:
+ case OpGroupNonUniformUMin:
+ case OpGroupNonUniformUMax:
+ case OpGroupNonUniformBitwiseAnd:
+ case OpGroupNonUniformBitwiseOr:
+ case OpGroupNonUniformBitwiseXor:
+ {
+ auto operation = static_cast<GroupOperation>(ops[3]);
+ if (operation == GroupOperationClusteredReduce)
+ {
+ require_extension_internal("GL_KHR_shader_subgroup_clustered");
+ }
+ else if (operation == GroupOperationExclusiveScan || operation == GroupOperationInclusiveScan ||
+ operation == GroupOperationReduce)
+ {
+ require_extension_internal("GL_KHR_shader_subgroup_arithmetic");
+ }
+ else
+ SPIRV_CROSS_THROW("Invalid group operation.");
+ break;
+ }
+
+ case OpGroupNonUniformQuadSwap:
+ case OpGroupNonUniformQuadBroadcast:
+ require_extension_internal("GL_KHR_shader_subgroup_quad");
+ break;
+
+ default:
+ SPIRV_CROSS_THROW("Invalid opcode for subgroup.");
+ }
+
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+
+ auto scope = static_cast<Scope>(get<SPIRConstant>(ops[2]).scalar());
+ if (scope != ScopeSubgroup)
+ SPIRV_CROSS_THROW("Only subgroup scope is supported.");
+
+ switch (op)
+ {
+ case OpGroupNonUniformElect:
+ emit_op(result_type, id, "subgroupElect()", true);
+ break;
+
+ case OpGroupNonUniformBroadcast:
+ emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupBroadcast");
+ break;
+
+ case OpGroupNonUniformBroadcastFirst:
+ emit_unary_func_op(result_type, id, ops[3], "subgroupBroadcastFirst");
+ break;
+
+ case OpGroupNonUniformBallot:
+ emit_unary_func_op(result_type, id, ops[3], "subgroupBallot");
+ break;
+
+ case OpGroupNonUniformInverseBallot:
+ emit_unary_func_op(result_type, id, ops[3], "subgroupInverseBallot");
+ break;
+
+ case OpGroupNonUniformBallotBitExtract:
+ emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupBallotBitExtract");
+ break;
+
+ case OpGroupNonUniformBallotFindLSB:
+ emit_unary_func_op(result_type, id, ops[3], "subgroupBallotFindLSB");
+ break;
+
+ case OpGroupNonUniformBallotFindMSB:
+ emit_unary_func_op(result_type, id, ops[3], "subgroupBallotFindMSB");
+ break;
+
+ case OpGroupNonUniformBallotBitCount:
+ {
+ auto operation = static_cast<GroupOperation>(ops[3]);
+ if (operation == GroupOperationReduce)
+ emit_unary_func_op(result_type, id, ops[4], "subgroupBallotBitCount");
+ else if (operation == GroupOperationInclusiveScan)
+ emit_unary_func_op(result_type, id, ops[4], "subgroupBallotInclusiveBitCount");
+ else if (operation == GroupOperationExclusiveScan)
+ emit_unary_func_op(result_type, id, ops[4], "subgroupBallotExclusiveBitCount");
+ else
+ SPIRV_CROSS_THROW("Invalid BitCount operation.");
+ break;
+ }
+
+ case OpGroupNonUniformShuffle:
+ emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupShuffle");
+ break;
+
+ case OpGroupNonUniformShuffleXor:
+ emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupShuffleXor");
+ break;
+
+ case OpGroupNonUniformShuffleUp:
+ emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupShuffleUp");
+ break;
+
+ case OpGroupNonUniformShuffleDown:
+ emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupShuffleDown");
+ break;
+
+ case OpGroupNonUniformAll:
+ emit_unary_func_op(result_type, id, ops[3], "subgroupAll");
+ break;
+
+ case OpGroupNonUniformAny:
+ emit_unary_func_op(result_type, id, ops[3], "subgroupAny");
+ break;
+
+ case OpGroupNonUniformAllEqual:
+ emit_unary_func_op(result_type, id, ops[3], "subgroupAllEqual");
+ break;
+
+ // clang-format off
+#define GLSL_GROUP_OP(op, glsl_op) \
+case OpGroupNonUniform##op: \
+ { \
+ auto operation = static_cast<GroupOperation>(ops[3]); \
+ if (operation == GroupOperationReduce) \
+ emit_unary_func_op(result_type, id, ops[4], "subgroup" #glsl_op); \
+ else if (operation == GroupOperationInclusiveScan) \
+ emit_unary_func_op(result_type, id, ops[4], "subgroupInclusive" #glsl_op); \
+ else if (operation == GroupOperationExclusiveScan) \
+ emit_unary_func_op(result_type, id, ops[4], "subgroupExclusive" #glsl_op); \
+ else if (operation == GroupOperationClusteredReduce) \
+ emit_binary_func_op(result_type, id, ops[4], ops[5], "subgroupClustered" #glsl_op); \
+ else \
+ SPIRV_CROSS_THROW("Invalid group operation."); \
+ break; \
+ }
+ GLSL_GROUP_OP(FAdd, Add)
+ GLSL_GROUP_OP(FMul, Mul)
+ GLSL_GROUP_OP(FMin, Min)
+ GLSL_GROUP_OP(FMax, Max)
+ GLSL_GROUP_OP(IAdd, Add)
+ GLSL_GROUP_OP(IMul, Mul)
+ GLSL_GROUP_OP(SMin, Min)
+ GLSL_GROUP_OP(SMax, Max)
+ GLSL_GROUP_OP(UMin, Min)
+ GLSL_GROUP_OP(UMax, Max)
+ GLSL_GROUP_OP(BitwiseAnd, And)
+ GLSL_GROUP_OP(BitwiseOr, Or)
+ GLSL_GROUP_OP(BitwiseXor, Xor)
+#undef GLSL_GROUP_OP
+ // clang-format on
+
+ case OpGroupNonUniformQuadSwap:
+ {
+ uint32_t direction = get<SPIRConstant>(ops[4]).scalar();
+ if (direction == 0)
+ emit_unary_func_op(result_type, id, ops[3], "subgroupQuadSwapHorizontal");
+ else if (direction == 1)
+ emit_unary_func_op(result_type, id, ops[3], "subgroupQuadSwapVertical");
+ else if (direction == 2)
+ emit_unary_func_op(result_type, id, ops[3], "subgroupQuadSwapDiagonal");
+ else
+ SPIRV_CROSS_THROW("Invalid quad swap direction.");
+ break;
+ }
+
+ case OpGroupNonUniformQuadBroadcast:
+ {
+ emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupQuadBroadcast");
+ break;
+ }
+
+ default:
+ SPIRV_CROSS_THROW("Invalid opcode for subgroup.");
+ }
+
+ register_control_dependent_expression(id);
+}
+
+string CompilerGLSL::bitcast_glsl_op(const SPIRType &out_type, const SPIRType &in_type)
+{
+ if (out_type.basetype == in_type.basetype)
+ return "";
+
+ assert(out_type.basetype != SPIRType::Boolean);
+ assert(in_type.basetype != SPIRType::Boolean);
+
+ bool integral_cast = type_is_integral(out_type) && type_is_integral(in_type);
+ bool same_size_cast = out_type.width == in_type.width;
+
+ // Trivial bitcast case, casts between integers.
+ if (integral_cast && same_size_cast)
+ return type_to_glsl(out_type);
+
+ // Catch-all 8-bit arithmetic casts (GL_EXT_shader_explicit_arithmetic_types).
+ if (out_type.width == 8 && in_type.width >= 16 && integral_cast && in_type.vecsize == 1)
+ return "unpack8";
+ else if (in_type.width == 8 && out_type.width == 16 && integral_cast && out_type.vecsize == 1)
+ return "pack16";
+ else if (in_type.width == 8 && out_type.width == 32 && integral_cast && out_type.vecsize == 1)
+ return "pack32";
+
+ // Floating <-> Integer special casts. Just have to enumerate all cases. :(
+ // 16-bit, 32-bit and 64-bit floats.
+ if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::Float)
+ return "floatBitsToUint";
+ else if (out_type.basetype == SPIRType::Int && in_type.basetype == SPIRType::Float)
+ return "floatBitsToInt";
+ else if (out_type.basetype == SPIRType::Float && in_type.basetype == SPIRType::UInt)
+ return "uintBitsToFloat";
+ else if (out_type.basetype == SPIRType::Float && in_type.basetype == SPIRType::Int)
+ return "intBitsToFloat";
+ else if (out_type.basetype == SPIRType::Int64 && in_type.basetype == SPIRType::Double)
+ return "doubleBitsToInt64";
+ else if (out_type.basetype == SPIRType::UInt64 && in_type.basetype == SPIRType::Double)
+ return "doubleBitsToUint64";
+ else if (out_type.basetype == SPIRType::Double && in_type.basetype == SPIRType::Int64)
+ return "int64BitsToDouble";
+ else if (out_type.basetype == SPIRType::Double && in_type.basetype == SPIRType::UInt64)
+ return "uint64BitsToDouble";
+ else if (out_type.basetype == SPIRType::Short && in_type.basetype == SPIRType::Half)
+ return "float16BitsToInt16";
+ else if (out_type.basetype == SPIRType::UShort && in_type.basetype == SPIRType::Half)
+ return "float16BitsToUint16";
+ else if (out_type.basetype == SPIRType::Half && in_type.basetype == SPIRType::Short)
+ return "int16BitsToFloat16";
+ else if (out_type.basetype == SPIRType::Half && in_type.basetype == SPIRType::UShort)
+ return "uint16BitsToFloat16";
+
+ // And finally, some even more special purpose casts.
+ if (out_type.basetype == SPIRType::UInt64 && in_type.basetype == SPIRType::UInt && in_type.vecsize == 2)
+ return "packUint2x32";
+ else if (out_type.basetype == SPIRType::Half && in_type.basetype == SPIRType::UInt && in_type.vecsize == 1)
+ return "unpackFloat2x16";
+ else if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::Half && in_type.vecsize == 2)
+ return "packFloat2x16";
+ else if (out_type.basetype == SPIRType::Int && in_type.basetype == SPIRType::Short && in_type.vecsize == 2)
+ return "packInt2x16";
+ else if (out_type.basetype == SPIRType::Short && in_type.basetype == SPIRType::Int && in_type.vecsize == 1)
+ return "unpackInt2x16";
+ else if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::UShort && in_type.vecsize == 2)
+ return "packUint2x16";
+ else if (out_type.basetype == SPIRType::UShort && in_type.basetype == SPIRType::UInt && in_type.vecsize == 1)
+ return "unpackUint2x16";
+ else if (out_type.basetype == SPIRType::Int64 && in_type.basetype == SPIRType::Short && in_type.vecsize == 4)
+ return "packInt4x16";
+ else if (out_type.basetype == SPIRType::Short && in_type.basetype == SPIRType::Int64 && in_type.vecsize == 1)
+ return "unpackInt4x16";
+ else if (out_type.basetype == SPIRType::UInt64 && in_type.basetype == SPIRType::UShort && in_type.vecsize == 4)
+ return "packUint4x16";
+ else if (out_type.basetype == SPIRType::UShort && in_type.basetype == SPIRType::UInt64 && in_type.vecsize == 1)
+ return "unpackUint4x16";
+
+ return "";
+}
+
+string CompilerGLSL::bitcast_glsl(const SPIRType &result_type, uint32_t argument)
+{
+ auto op = bitcast_glsl_op(result_type, expression_type(argument));
+ if (op.empty())
+ return to_enclosed_expression(argument);
+ else
+ return join(op, "(", to_expression(argument), ")");
+}
+
+std::string CompilerGLSL::bitcast_expression(SPIRType::BaseType target_type, uint32_t arg)
+{
+ auto expr = to_expression(arg);
+ auto &src_type = expression_type(arg);
+ if (src_type.basetype != target_type)
+ {
+ auto target = src_type;
+ target.basetype = target_type;
+ expr = join(bitcast_glsl_op(target, src_type), "(", expr, ")");
+ }
+
+ return expr;
+}
+
+std::string CompilerGLSL::bitcast_expression(const SPIRType &target_type, SPIRType::BaseType expr_type,
+ const std::string &expr)
+{
+ if (target_type.basetype == expr_type)
+ return expr;
+
+ auto src_type = target_type;
+ src_type.basetype = expr_type;
+ return join(bitcast_glsl_op(target_type, src_type), "(", expr, ")");
+}
+
+string CompilerGLSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage)
+{
+ switch (builtin)
+ {
+ case BuiltInPosition:
+ return "gl_Position";
+ case BuiltInPointSize:
+ return "gl_PointSize";
+ case BuiltInClipDistance:
+ return "gl_ClipDistance";
+ case BuiltInCullDistance:
+ return "gl_CullDistance";
+ case BuiltInVertexId:
+ if (options.vulkan_semantics)
+ SPIRV_CROSS_THROW(
+ "Cannot implement gl_VertexID in Vulkan GLSL. This shader was created with GL semantics.");
+ return "gl_VertexID";
+ case BuiltInInstanceId:
+ if (options.vulkan_semantics)
+ SPIRV_CROSS_THROW(
+ "Cannot implement gl_InstanceID in Vulkan GLSL. This shader was created with GL semantics.");
+ return "gl_InstanceID";
+ case BuiltInVertexIndex:
+ if (options.vulkan_semantics)
+ return "gl_VertexIndex";
+ else
+ return "gl_VertexID"; // gl_VertexID already has the base offset applied.
+ case BuiltInInstanceIndex:
+ if (options.vulkan_semantics)
+ return "gl_InstanceIndex";
+ else if (options.vertex.support_nonzero_base_instance)
+ return "(gl_InstanceID + SPIRV_Cross_BaseInstance)"; // ... but not gl_InstanceID.
+ else
+ return "gl_InstanceID";
+ case BuiltInPrimitiveId:
+ if (storage == StorageClassInput && get_entry_point().model == ExecutionModelGeometry)
+ return "gl_PrimitiveIDIn";
+ else
+ return "gl_PrimitiveID";
+ case BuiltInInvocationId:
+ return "gl_InvocationID";
+ case BuiltInLayer:
+ return "gl_Layer";
+ case BuiltInViewportIndex:
+ return "gl_ViewportIndex";
+ case BuiltInTessLevelOuter:
+ return "gl_TessLevelOuter";
+ case BuiltInTessLevelInner:
+ return "gl_TessLevelInner";
+ case BuiltInTessCoord:
+ return "gl_TessCoord";
+ case BuiltInFragCoord:
+ return "gl_FragCoord";
+ case BuiltInPointCoord:
+ return "gl_PointCoord";
+ case BuiltInFrontFacing:
+ return "gl_FrontFacing";
+ case BuiltInFragDepth:
+ return "gl_FragDepth";
+ case BuiltInNumWorkgroups:
+ return "gl_NumWorkGroups";
+ case BuiltInWorkgroupSize:
+ return "gl_WorkGroupSize";
+ case BuiltInWorkgroupId:
+ return "gl_WorkGroupID";
+ case BuiltInLocalInvocationId:
+ return "gl_LocalInvocationID";
+ case BuiltInGlobalInvocationId:
+ return "gl_GlobalInvocationID";
+ case BuiltInLocalInvocationIndex:
+ return "gl_LocalInvocationIndex";
+ case BuiltInHelperInvocation:
+ return "gl_HelperInvocation";
+ case BuiltInBaseVertex:
+ if (options.es)
+ SPIRV_CROSS_THROW("BaseVertex not supported in ES profile.");
+ if (options.version < 460)
+ {
+ require_extension_internal("GL_ARB_shader_draw_parameters");
+ return "gl_BaseVertexARB";
+ }
+ return "gl_BaseVertex";
+ case BuiltInBaseInstance:
+ if (options.es)
+ SPIRV_CROSS_THROW("BaseInstance not supported in ES profile.");
+ if (options.version < 460)
+ {
+ require_extension_internal("GL_ARB_shader_draw_parameters");
+ return "gl_BaseInstanceARB";
+ }
+ return "gl_BaseInstance";
+ case BuiltInDrawIndex:
+ if (options.es)
+ SPIRV_CROSS_THROW("DrawIndex not supported in ES profile.");
+ if (options.version < 460)
+ {
+ require_extension_internal("GL_ARB_shader_draw_parameters");
+ return "gl_DrawIDARB";
+ }
+ return "gl_DrawID";
+
+ case BuiltInSampleId:
+ if (options.es && options.version < 320)
+ require_extension_internal("GL_OES_sample_variables");
+ if (!options.es && options.version < 400)
+ SPIRV_CROSS_THROW("gl_SampleID not supported before GLSL 400.");
+ return "gl_SampleID";
+
+ case BuiltInSampleMask:
+ if (options.es && options.version < 320)
+ require_extension_internal("GL_OES_sample_variables");
+ if (!options.es && options.version < 400)
+ SPIRV_CROSS_THROW("gl_SampleMask/gl_SampleMaskIn not supported before GLSL 400.");
+
+ if (storage == StorageClassInput)
+ return "gl_SampleMaskIn";
+ else
+ return "gl_SampleMask";
+
+ case BuiltInSamplePosition:
+ if (options.es && options.version < 320)
+ require_extension_internal("GL_OES_sample_variables");
+ if (!options.es && options.version < 400)
+ SPIRV_CROSS_THROW("gl_SamplePosition not supported before GLSL 400.");
+ return "gl_SamplePosition";
+
+ case BuiltInViewIndex:
+ if (options.vulkan_semantics)
+ {
+ require_extension_internal("GL_EXT_multiview");
+ return "gl_ViewIndex";
+ }
+ else
+ {
+ require_extension_internal("GL_OVR_multiview2");
+ return "gl_ViewID_OVR";
+ }
+
+ case BuiltInNumSubgroups:
+ if (!options.vulkan_semantics)
+ SPIRV_CROSS_THROW("Need Vulkan semantics for subgroup.");
+ require_extension_internal("GL_KHR_shader_subgroup_basic");
+ return "gl_NumSubgroups";
+
+ case BuiltInSubgroupId:
+ if (!options.vulkan_semantics)
+ SPIRV_CROSS_THROW("Need Vulkan semantics for subgroup.");
+ require_extension_internal("GL_KHR_shader_subgroup_basic");
+ return "gl_SubgroupID";
+
+ case BuiltInSubgroupSize:
+ if (!options.vulkan_semantics)
+ SPIRV_CROSS_THROW("Need Vulkan semantics for subgroup.");
+ require_extension_internal("GL_KHR_shader_subgroup_basic");
+ return "gl_SubgroupSize";
+
+ case BuiltInSubgroupLocalInvocationId:
+ if (!options.vulkan_semantics)
+ SPIRV_CROSS_THROW("Need Vulkan semantics for subgroup.");
+ require_extension_internal("GL_KHR_shader_subgroup_basic");
+ return "gl_SubgroupInvocationID";
+
+ case BuiltInSubgroupEqMask:
+ if (!options.vulkan_semantics)
+ SPIRV_CROSS_THROW("Need Vulkan semantics for subgroup.");
+ require_extension_internal("GL_KHR_shader_subgroup_ballot");
+ return "gl_SubgroupEqMask";
+
+ case BuiltInSubgroupGeMask:
+ if (!options.vulkan_semantics)
+ SPIRV_CROSS_THROW("Need Vulkan semantics for subgroup.");
+ require_extension_internal("GL_KHR_shader_subgroup_ballot");
+ return "gl_SubgroupGeMask";
+
+ case BuiltInSubgroupGtMask:
+ if (!options.vulkan_semantics)
+ SPIRV_CROSS_THROW("Need Vulkan semantics for subgroup.");
+ require_extension_internal("GL_KHR_shader_subgroup_ballot");
+ return "gl_SubgroupGtMask";
+
+ case BuiltInSubgroupLeMask:
+ if (!options.vulkan_semantics)
+ SPIRV_CROSS_THROW("Need Vulkan semantics for subgroup.");
+ require_extension_internal("GL_KHR_shader_subgroup_ballot");
+ return "gl_SubgroupLeMask";
+
+ case BuiltInSubgroupLtMask:
+ if (!options.vulkan_semantics)
+ SPIRV_CROSS_THROW("Need Vulkan semantics for subgroup.");
+ require_extension_internal("GL_KHR_shader_subgroup_ballot");
+ return "gl_SubgroupLtMask";
+
+ case BuiltInLaunchIdNV:
+ return "gl_LaunchIDNV";
+ case BuiltInLaunchSizeNV:
+ return "gl_LaunchSizeNV";
+ case BuiltInWorldRayOriginNV:
+ return "gl_WorldRayOriginNV";
+ case BuiltInWorldRayDirectionNV:
+ return "gl_WorldRayDirectionNV";
+ case BuiltInObjectRayOriginNV:
+ return "gl_ObjectRayOriginNV";
+ case BuiltInObjectRayDirectionNV:
+ return "gl_ObjectRayDirectionNV";
+ case BuiltInRayTminNV:
+ return "gl_RayTminNV";
+ case BuiltInRayTmaxNV:
+ return "gl_RayTmaxNV";
+ case BuiltInInstanceCustomIndexNV:
+ return "gl_InstanceCustomIndexNV";
+ case BuiltInObjectToWorldNV:
+ return "gl_ObjectToWorldNV";
+ case BuiltInWorldToObjectNV:
+ return "gl_WorldToObjectNV";
+ case BuiltInHitTNV:
+ return "gl_HitTNV";
+ case BuiltInHitKindNV:
+ return "gl_HitKindNV";
+ case BuiltInIncomingRayFlagsNV:
+ return "gl_IncomingRayFlagsNV";
+
+ default:
+ return join("gl_BuiltIn_", convert_to_string(builtin));
+ }
+}
+
+const char *CompilerGLSL::index_to_swizzle(uint32_t index)
+{
+ switch (index)
+ {
+ case 0:
+ return "x";
+ case 1:
+ return "y";
+ case 2:
+ return "z";
+ case 3:
+ return "w";
+ default:
+ SPIRV_CROSS_THROW("Swizzle index out of range");
+ }
+}
+
+string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indices, uint32_t count,
+ AccessChainFlags flags, AccessChainMeta *meta)
+{
+ string expr;
+
+ bool index_is_literal = (flags & ACCESS_CHAIN_INDEX_IS_LITERAL_BIT) != 0;
+ bool chain_only = (flags & ACCESS_CHAIN_CHAIN_ONLY_BIT) != 0;
+ bool ptr_chain = (flags & ACCESS_CHAIN_PTR_CHAIN_BIT) != 0;
+ bool register_expression_read = (flags & ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT) == 0;
+
+ if (!chain_only)
+ expr = to_enclosed_expression(base, register_expression_read);
+
+ // Start traversing type hierarchy at the proper non-pointer types,
+ // but keep type_id referencing the original pointer for use below.
+ uint32_t type_id = expression_type_id(base);
+ const auto *type = &get_pointee_type(type_id);
+
+ bool access_chain_is_arrayed = expr.find_first_of('[') != string::npos;
+ bool row_major_matrix_needs_conversion = is_non_native_row_major_matrix(base);
+ bool is_packed = has_extended_decoration(base, SPIRVCrossDecorationPacked);
+ uint32_t packed_type = get_extended_decoration(base, SPIRVCrossDecorationPackedType);
+ bool is_invariant = has_decoration(base, DecorationInvariant);
+ bool pending_array_enclose = false;
+ bool dimension_flatten = false;
+
+ for (uint32_t i = 0; i < count; i++)
+ {
+ uint32_t index = indices[i];
+
+ const auto append_index = [&]() {
+ expr += "[";
+ if (index_is_literal)
+ expr += convert_to_string(index);
+ else
+ expr += to_expression(index, register_expression_read);
+ expr += "]";
+ };
+
+ // Pointer chains
+ if (ptr_chain && i == 0)
+ {
+ // If we are flattening multidimensional arrays, only create opening bracket on first
+ // array index.
+ if (options.flatten_multidimensional_arrays)
+ {
+ dimension_flatten = type->array.size() >= 1;
+ pending_array_enclose = dimension_flatten;
+ if (pending_array_enclose)
+ expr += "[";
+ }
+
+ if (options.flatten_multidimensional_arrays && dimension_flatten)
+ {
+ // If we are flattening multidimensional arrays, do manual stride computation.
+ if (index_is_literal)
+ expr += convert_to_string(index);
+ else
+ expr += to_enclosed_expression(index, register_expression_read);
+
+ for (auto j = uint32_t(type->array.size()); j; j--)
+ {
+ expr += " * ";
+ expr += enclose_expression(to_array_size(*type, j - 1));
+ }
+
+ if (type->array.empty())
+ pending_array_enclose = false;
+ else
+ expr += " + ";
+
+ if (!pending_array_enclose)
+ expr += "]";
+ }
+ else
+ {
+ append_index();
+ }
+
+ if (type->basetype == SPIRType::ControlPointArray)
+ {
+ type_id = type->parent_type;
+ type = &get<SPIRType>(type_id);
+ }
+
+ access_chain_is_arrayed = true;
+ }
+ // Arrays
+ else if (!type->array.empty())
+ {
+ // If we are flattening multidimensional arrays, only create opening bracket on first
+ // array index.
+ if (options.flatten_multidimensional_arrays && !pending_array_enclose)
+ {
+ dimension_flatten = type->array.size() > 1;
+ pending_array_enclose = dimension_flatten;
+ if (pending_array_enclose)
+ expr += "[";
+ }
+
+ assert(type->parent_type);
+
+ auto *var = maybe_get<SPIRVariable>(base);
+ if (backend.force_gl_in_out_block && i == 0 && var && is_builtin_variable(*var) &&
+ !has_decoration(type->self, DecorationBlock))
+ {
+ // This deals with scenarios for tesc/geom where arrays of gl_Position[] are declared.
+ // Normally, these variables live in blocks when compiled from GLSL,
+ // but HLSL seems to just emit straight arrays here.
+ // We must pretend this access goes through gl_in/gl_out arrays
+ // to be able to access certain builtins as arrays.
+ auto builtin = ir.meta[base].decoration.builtin_type;
+ switch (builtin)
+ {
+ // case BuiltInCullDistance: // These are already arrays, need to figure out rules for these in tess/geom.
+ // case BuiltInClipDistance:
+ case BuiltInPosition:
+ case BuiltInPointSize:
+ if (var->storage == StorageClassInput)
+ expr = join("gl_in[", to_expression(index, register_expression_read), "].", expr);
+ else if (var->storage == StorageClassOutput)
+ expr = join("gl_out[", to_expression(index, register_expression_read), "].", expr);
+ else
+ append_index();
+ break;
+
+ default:
+ append_index();
+ break;
+ }
+ }
+ else if (options.flatten_multidimensional_arrays && dimension_flatten)
+ {
+ // If we are flattening multidimensional arrays, do manual stride computation.
+ auto &parent_type = get<SPIRType>(type->parent_type);
+
+ if (index_is_literal)
+ expr += convert_to_string(index);
+ else
+ expr += to_enclosed_expression(index, register_expression_read);
+
+ for (auto j = uint32_t(parent_type.array.size()); j; j--)
+ {
+ expr += " * ";
+ expr += enclose_expression(to_array_size(parent_type, j - 1));
+ }
+
+ if (parent_type.array.empty())
+ pending_array_enclose = false;
+ else
+ expr += " + ";
+
+ if (!pending_array_enclose)
+ expr += "]";
+ }
+ else
+ {
+ append_index();
+ }
+
+ type_id = type->parent_type;
+ type = &get<SPIRType>(type_id);
+
+ access_chain_is_arrayed = true;
+ }
+ // For structs, the index refers to a constant, which indexes into the members.
+ // We also check if this member is a builtin, since we then replace the entire expression with the builtin one.
+ else if (type->basetype == SPIRType::Struct)
+ {
+ if (!index_is_literal)
+ index = get<SPIRConstant>(index).scalar();
+
+ if (index >= type->member_types.size())
+ SPIRV_CROSS_THROW("Member index is out of bounds!");
+
+ BuiltIn builtin;
+ if (is_member_builtin(*type, index, &builtin))
+ {
+ // FIXME: We rely here on OpName on gl_in/gl_out to make this work properly.
+ // To make this properly work by omitting all OpName opcodes,
+ // we need to infer gl_in or gl_out based on the builtin, and stage.
+ if (access_chain_is_arrayed)
+ {
+ expr += ".";
+ expr += builtin_to_glsl(builtin, type->storage);
+ }
+ else
+ expr = builtin_to_glsl(builtin, type->storage);
+ }
+ else
+ {
+ // If the member has a qualified name, use it as the entire chain
+ string qual_mbr_name = get_member_qualified_name(type_id, index);
+ if (!qual_mbr_name.empty())
+ expr = qual_mbr_name;
+ else
+ expr += to_member_reference(base, *type, index, ptr_chain);
+ }
+
+ if (has_member_decoration(type->self, index, DecorationInvariant))
+ is_invariant = true;
+
+ is_packed = member_is_packed_type(*type, index);
+ if (is_packed)
+ packed_type = get_extended_member_decoration(type->self, index, SPIRVCrossDecorationPackedType);
+ else
+ packed_type = 0;
+
+ row_major_matrix_needs_conversion = member_is_non_native_row_major_matrix(*type, index);
+ type = &get<SPIRType>(type->member_types[index]);
+ }
+ // Matrix -> Vector
+ else if (type->columns > 1)
+ {
+ if (row_major_matrix_needs_conversion)
+ {
+ expr = convert_row_major_matrix(expr, *type, is_packed);
+ row_major_matrix_needs_conversion = false;
+ is_packed = false;
+ packed_type = 0;
+ }
+
+ expr += "[";
+ if (index_is_literal)
+ expr += convert_to_string(index);
+ else
+ expr += to_expression(index, register_expression_read);
+ expr += "]";
+
+ type_id = type->parent_type;
+ type = &get<SPIRType>(type_id);
+ }
+ // Vector -> Scalar
+ else if (type->vecsize > 1)
+ {
+ if (index_is_literal && !is_packed)
+ {
+ expr += ".";
+ expr += index_to_swizzle(index);
+ }
+ else if (ir.ids[index].get_type() == TypeConstant && !is_packed)
+ {
+ auto &c = get<SPIRConstant>(index);
+ expr += ".";
+ expr += index_to_swizzle(c.scalar());
+ }
+ else if (index_is_literal)
+ {
+ // For packed vectors, we can only access them as an array, not by swizzle.
+ expr += join("[", index, "]");
+ }
+ else
+ {
+ expr += "[";
+ expr += to_expression(index, register_expression_read);
+ expr += "]";
+ }
+
+ is_packed = false;
+ packed_type = 0;
+ type_id = type->parent_type;
+ type = &get<SPIRType>(type_id);
+ }
+ else if (!backend.allow_truncated_access_chain)
+ SPIRV_CROSS_THROW("Cannot subdivide a scalar value!");
+ }
+
+ if (pending_array_enclose)
+ {
+ SPIRV_CROSS_THROW("Flattening of multidimensional arrays were enabled, "
+ "but the access chain was terminated in the middle of a multidimensional array. "
+ "This is not supported.");
+ }
+
+ if (meta)
+ {
+ meta->need_transpose = row_major_matrix_needs_conversion;
+ meta->storage_is_packed = is_packed;
+ meta->storage_is_invariant = is_invariant;
+ meta->storage_packed_type = packed_type;
+ }
+
+ return expr;
+}
+
+string CompilerGLSL::to_flattened_struct_member(const SPIRVariable &var, uint32_t index)
+{
+ auto &type = get<SPIRType>(var.basetype);
+ return sanitize_underscores(join(to_name(var.self), "_", to_member_name(type, index)));
+}
+
+string CompilerGLSL::access_chain(uint32_t base, const uint32_t *indices, uint32_t count, const SPIRType &target_type,
+ AccessChainMeta *meta, bool ptr_chain)
+{
+ if (flattened_buffer_blocks.count(base))
+ {
+ uint32_t matrix_stride = 0;
+ bool need_transpose = false;
+ flattened_access_chain_offset(expression_type(base), indices, count, 0, 16, &need_transpose, &matrix_stride,
+ ptr_chain);
+
+ if (meta)
+ {
+ meta->need_transpose = target_type.columns > 1 && need_transpose;
+ meta->storage_is_packed = false;
+ }
+
+ return flattened_access_chain(base, indices, count, target_type, 0, matrix_stride, need_transpose);
+ }
+ else if (flattened_structs.count(base) && count > 0)
+ {
+ AccessChainFlags flags = ACCESS_CHAIN_CHAIN_ONLY_BIT | ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT;
+ if (ptr_chain)
+ flags |= ACCESS_CHAIN_PTR_CHAIN_BIT;
+
+ auto chain = access_chain_internal(base, indices, count, flags, nullptr).substr(1);
+ if (meta)
+ {
+ meta->need_transpose = false;
+ meta->storage_is_packed = false;
+ }
+ return sanitize_underscores(join(to_name(base), "_", chain));
+ }
+ else
+ {
+ AccessChainFlags flags = ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT;
+ if (ptr_chain)
+ flags |= ACCESS_CHAIN_PTR_CHAIN_BIT;
+ return access_chain_internal(base, indices, count, flags, meta);
+ }
+}
+
+string CompilerGLSL::load_flattened_struct(SPIRVariable &var)
+{
+ auto expr = type_to_glsl_constructor(get<SPIRType>(var.basetype));
+ expr += '(';
+
+ auto &type = get<SPIRType>(var.basetype);
+ for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
+ {
+ if (i)
+ expr += ", ";
+
+ // Flatten the varyings.
+ // Apply name transformation for flattened I/O blocks.
+ expr += to_flattened_struct_member(var, i);
+ }
+ expr += ')';
+ return expr;
+}
+
+void CompilerGLSL::store_flattened_struct(SPIRVariable &var, uint32_t value)
+{
+ // We're trying to store a structure which has been flattened.
+ // Need to copy members one by one.
+ auto rhs = to_expression(value);
+
+ // Store result locally.
+ // Since we're declaring a variable potentially multiple times here,
+ // store the variable in an isolated scope.
+ begin_scope();
+ statement(variable_decl_function_local(var), " = ", rhs, ";");
+
+ auto &type = get<SPIRType>(var.basetype);
+ for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
+ {
+ // Flatten the varyings.
+ // Apply name transformation for flattened I/O blocks.
+
+ auto lhs = sanitize_underscores(join(to_name(var.self), "_", to_member_name(type, i)));
+ rhs = join(to_name(var.self), ".", to_member_name(type, i));
+ statement(lhs, " = ", rhs, ";");
+ }
+ end_scope();
+}
+
+std::string CompilerGLSL::flattened_access_chain(uint32_t base, const uint32_t *indices, uint32_t count,
+ const SPIRType &target_type, uint32_t offset, uint32_t matrix_stride,
+ bool need_transpose)
+{
+ if (!target_type.array.empty())
+ SPIRV_CROSS_THROW("Access chains that result in an array can not be flattened");
+ else if (target_type.basetype == SPIRType::Struct)
+ return flattened_access_chain_struct(base, indices, count, target_type, offset);
+ else if (target_type.columns > 1)
+ return flattened_access_chain_matrix(base, indices, count, target_type, offset, matrix_stride, need_transpose);
+ else
+ return flattened_access_chain_vector(base, indices, count, target_type, offset, matrix_stride, need_transpose);
+}
+
+std::string CompilerGLSL::flattened_access_chain_struct(uint32_t base, const uint32_t *indices, uint32_t count,
+ const SPIRType &target_type, uint32_t offset)
+{
+ std::string expr;
+
+ expr += type_to_glsl_constructor(target_type);
+ expr += "(";
+
+ for (uint32_t i = 0; i < uint32_t(target_type.member_types.size()); ++i)
+ {
+ if (i != 0)
+ expr += ", ";
+
+ const SPIRType &member_type = get<SPIRType>(target_type.member_types[i]);
+ uint32_t member_offset = type_struct_member_offset(target_type, i);
+
+ // The access chain terminates at the struct, so we need to find matrix strides and row-major information
+ // ahead of time.
+ bool need_transpose = false;
+ uint32_t matrix_stride = 0;
+ if (member_type.columns > 1)
+ {
+ need_transpose = combined_decoration_for_member(target_type, i).get(DecorationRowMajor);
+ matrix_stride = type_struct_member_matrix_stride(target_type, i);
+ }
+
+ auto tmp = flattened_access_chain(base, indices, count, member_type, offset + member_offset, matrix_stride,
+ need_transpose);
+
+ // Cannot forward transpositions, so resolve them here.
+ if (need_transpose)
+ expr += convert_row_major_matrix(tmp, member_type, false);
+ else
+ expr += tmp;
+ }
+
+ expr += ")";
+
+ return expr;
+}
+
+std::string CompilerGLSL::flattened_access_chain_matrix(uint32_t base, const uint32_t *indices, uint32_t count,
+ const SPIRType &target_type, uint32_t offset,
+ uint32_t matrix_stride, bool need_transpose)
+{
+ assert(matrix_stride);
+ SPIRType tmp_type = target_type;
+ if (need_transpose)
+ swap(tmp_type.vecsize, tmp_type.columns);
+
+ std::string expr;
+
+ expr += type_to_glsl_constructor(tmp_type);
+ expr += "(";
+
+ for (uint32_t i = 0; i < tmp_type.columns; i++)
+ {
+ if (i != 0)
+ expr += ", ";
+
+ expr += flattened_access_chain_vector(base, indices, count, tmp_type, offset + i * matrix_stride, matrix_stride,
+ /* need_transpose= */ false);
+ }
+
+ expr += ")";
+
+ return expr;
+}
+
+std::string CompilerGLSL::flattened_access_chain_vector(uint32_t base, const uint32_t *indices, uint32_t count,
+ const SPIRType &target_type, uint32_t offset,
+ uint32_t matrix_stride, bool need_transpose)
+{
+ auto result = flattened_access_chain_offset(expression_type(base), indices, count, offset, 16);
+
+ auto buffer_name = to_name(expression_type(base).self);
+
+ if (need_transpose)
+ {
+ std::string expr;
+
+ if (target_type.vecsize > 1)
+ {
+ expr += type_to_glsl_constructor(target_type);
+ expr += "(";
+ }
+
+ for (uint32_t i = 0; i < target_type.vecsize; ++i)
+ {
+ if (i != 0)
+ expr += ", ";
+
+ uint32_t component_offset = result.second + i * matrix_stride;
+
+ assert(component_offset % (target_type.width / 8) == 0);
+ uint32_t index = component_offset / (target_type.width / 8);
+
+ expr += buffer_name;
+ expr += "[";
+ expr += result.first; // this is a series of N1 * k1 + N2 * k2 + ... that is either empty or ends with a +
+ expr += convert_to_string(index / 4);
+ expr += "]";
+
+ expr += vector_swizzle(1, index % 4);
+ }
+
+ if (target_type.vecsize > 1)
+ {
+ expr += ")";
+ }
+
+ return expr;
+ }
+ else
+ {
+ assert(result.second % (target_type.width / 8) == 0);
+ uint32_t index = result.second / (target_type.width / 8);
+
+ std::string expr;
+
+ expr += buffer_name;
+ expr += "[";
+ expr += result.first; // this is a series of N1 * k1 + N2 * k2 + ... that is either empty or ends with a +
+ expr += convert_to_string(index / 4);
+ expr += "]";
+
+ expr += vector_swizzle(target_type.vecsize, index % 4);
+
+ return expr;
+ }
+}
+
+std::pair<std::string, uint32_t> CompilerGLSL::flattened_access_chain_offset(
+ const SPIRType &basetype, const uint32_t *indices, uint32_t count, uint32_t offset, uint32_t word_stride,
+ bool *need_transpose, uint32_t *out_matrix_stride, bool ptr_chain)
+{
+ // Start traversing type hierarchy at the proper non-pointer types.
+ const auto *type = &get_pointee_type(basetype);
+
+ // This holds the type of the current pointer which we are traversing through.
+ // We always start out from a struct type which is the block.
+ // This is primarily used to reflect the array strides and matrix strides later.
+ // For the first access chain index, type_id won't be needed, so just keep it as 0, it will be set
+ // accordingly as members of structs are accessed.
+ assert(type->basetype == SPIRType::Struct);
+ uint32_t type_id = 0;
+
+ std::string expr;
+
+ // Inherit matrix information in case we are access chaining a vector which might have come from a row major layout.
+ bool row_major_matrix_needs_conversion = need_transpose ? *need_transpose : false;
+ uint32_t matrix_stride = out_matrix_stride ? *out_matrix_stride : 0;
+
+ for (uint32_t i = 0; i < count; i++)
+ {
+ uint32_t index = indices[i];
+
+ // Pointers
+ if (ptr_chain && i == 0)
+ {
+ // Here, the pointer type will be decorated with an array stride.
+ uint32_t array_stride = get_decoration(basetype.self, DecorationArrayStride);
+ if (!array_stride)
+ SPIRV_CROSS_THROW("SPIR-V does not define ArrayStride for buffer block.");
+
+ auto *constant = maybe_get<SPIRConstant>(index);
+ if (constant)
+ {
+ // Constant array access.
+ offset += constant->scalar() * array_stride;
+ }
+ else
+ {
+ // Dynamic array access.
+ if (array_stride % word_stride)
+ {
+ SPIRV_CROSS_THROW(
+ "Array stride for dynamic indexing must be divisible by the size of a 4-component vector. "
+ "Likely culprit here is a float or vec2 array inside a push constant block which is std430. "
+ "This cannot be flattened. Try using std140 layout instead.");
+ }
+
+ expr += to_enclosed_expression(index);
+ expr += " * ";
+ expr += convert_to_string(array_stride / word_stride);
+ expr += " + ";
+ }
+ // Type ID is unchanged.
+ }
+ // Arrays
+ else if (!type->array.empty())
+ {
+ // Here, the type_id will be a type ID for the array type itself.
+ uint32_t array_stride = get_decoration(type_id, DecorationArrayStride);
+ if (!array_stride)
+ SPIRV_CROSS_THROW("SPIR-V does not define ArrayStride for buffer block.");
+
+ auto *constant = maybe_get<SPIRConstant>(index);
+ if (constant)
+ {
+ // Constant array access.
+ offset += constant->scalar() * array_stride;
+ }
+ else
+ {
+ // Dynamic array access.
+ if (array_stride % word_stride)
+ {
+ SPIRV_CROSS_THROW(
+ "Array stride for dynamic indexing must be divisible by the size of a 4-component vector. "
+ "Likely culprit here is a float or vec2 array inside a push constant block which is std430. "
+ "This cannot be flattened. Try using std140 layout instead.");
+ }
+
+ expr += to_enclosed_expression(index, false);
+ expr += " * ";
+ expr += convert_to_string(array_stride / word_stride);
+ expr += " + ";
+ }
+
+ uint32_t parent_type = type->parent_type;
+ type = &get<SPIRType>(parent_type);
+ type_id = parent_type;
+
+ // Type ID now refers to the array type with one less dimension.
+ }
+ // For structs, the index refers to a constant, which indexes into the members.
+ // We also check if this member is a builtin, since we then replace the entire expression with the builtin one.
+ else if (type->basetype == SPIRType::Struct)
+ {
+ index = get<SPIRConstant>(index).scalar();
+
+ if (index >= type->member_types.size())
+ SPIRV_CROSS_THROW("Member index is out of bounds!");
+
+ offset += type_struct_member_offset(*type, index);
+ type_id = type->member_types[index];
+
+ auto &struct_type = *type;
+ type = &get<SPIRType>(type->member_types[index]);
+
+ if (type->columns > 1)
+ {
+ matrix_stride = type_struct_member_matrix_stride(struct_type, index);
+ row_major_matrix_needs_conversion =
+ combined_decoration_for_member(struct_type, index).get(DecorationRowMajor);
+ }
+ else
+ row_major_matrix_needs_conversion = false;
+ }
+ // Matrix -> Vector
+ else if (type->columns > 1)
+ {
+ auto *constant = maybe_get<SPIRConstant>(index);
+ if (constant)
+ {
+ index = get<SPIRConstant>(index).scalar();
+ offset += index * (row_major_matrix_needs_conversion ? (type->width / 8) : matrix_stride);
+ }
+ else
+ {
+ uint32_t indexing_stride = row_major_matrix_needs_conversion ? (type->width / 8) : matrix_stride;
+ // Dynamic array access.
+ if (indexing_stride % word_stride)
+ {
+ SPIRV_CROSS_THROW(
+ "Matrix stride for dynamic indexing must be divisible by the size of a 4-component vector. "
+ "Likely culprit here is a row-major matrix being accessed dynamically. "
+ "This cannot be flattened. Try using std140 layout instead.");
+ }
+
+ expr += to_enclosed_expression(index, false);
+ expr += " * ";
+ expr += convert_to_string(indexing_stride / word_stride);
+ expr += " + ";
+ }
+
+ uint32_t parent_type = type->parent_type;
+ type = &get<SPIRType>(type->parent_type);
+ type_id = parent_type;
+ }
+ // Vector -> Scalar
+ else if (type->vecsize > 1)
+ {
+ auto *constant = maybe_get<SPIRConstant>(index);
+ if (constant)
+ {
+ index = get<SPIRConstant>(index).scalar();
+ offset += index * (row_major_matrix_needs_conversion ? matrix_stride : (type->width / 8));
+ }
+ else
+ {
+ uint32_t indexing_stride = row_major_matrix_needs_conversion ? matrix_stride : (type->width / 8);
+
+ // Dynamic array access.
+ if (indexing_stride % word_stride)
+ {
+ SPIRV_CROSS_THROW(
+ "Stride for dynamic vector indexing must be divisible by the size of a 4-component vector. "
+ "This cannot be flattened in legacy targets.");
+ }
+
+ expr += to_enclosed_expression(index, false);
+ expr += " * ";
+ expr += convert_to_string(indexing_stride / word_stride);
+ expr += " + ";
+ }
+
+ uint32_t parent_type = type->parent_type;
+ type = &get<SPIRType>(type->parent_type);
+ type_id = parent_type;
+ }
+ else
+ SPIRV_CROSS_THROW("Cannot subdivide a scalar value!");
+ }
+
+ if (need_transpose)
+ *need_transpose = row_major_matrix_needs_conversion;
+ if (out_matrix_stride)
+ *out_matrix_stride = matrix_stride;
+
+ return std::make_pair(expr, offset);
+}
+
+bool CompilerGLSL::should_dereference(uint32_t id)
+{
+ const auto &type = expression_type(id);
+ // Non-pointer expressions don't need to be dereferenced.
+ if (!type.pointer)
+ return false;
+
+ // Handles shouldn't be dereferenced either.
+ if (!expression_is_lvalue(id))
+ return false;
+
+ // If id is a variable but not a phi variable, we should not dereference it.
+ if (auto *var = maybe_get<SPIRVariable>(id))
+ return var->phi_variable;
+
+ // If id is an access chain, we should not dereference it.
+ if (auto *expr = maybe_get<SPIRExpression>(id))
+ return !expr->access_chain;
+
+ // Otherwise, we should dereference this pointer expression.
+ return true;
+}
+
+bool CompilerGLSL::should_forward(uint32_t id)
+{
+ // If id is a variable we will try to forward it regardless of force_temporary check below
+ // This is important because otherwise we'll get local sampler copies (highp sampler2D foo = bar) that are invalid in OpenGL GLSL
+ auto *var = maybe_get<SPIRVariable>(id);
+ if (var && var->forwardable)
+ return true;
+
+ // For debugging emit temporary variables for all expressions
+ if (options.force_temporary)
+ return false;
+
+ // Immutable expression can always be forwarded.
+ if (is_immutable(id))
+ return true;
+
+ return false;
+}
+
+void CompilerGLSL::track_expression_read(uint32_t id)
+{
+ switch (ir.ids[id].get_type())
+ {
+ case TypeExpression:
+ {
+ auto &e = get<SPIRExpression>(id);
+ for (auto implied_read : e.implied_read_expressions)
+ track_expression_read(implied_read);
+ break;
+ }
+
+ case TypeAccessChain:
+ {
+ auto &e = get<SPIRAccessChain>(id);
+ for (auto implied_read : e.implied_read_expressions)
+ track_expression_read(implied_read);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ // If we try to read a forwarded temporary more than once we will stamp out possibly complex code twice.
+ // In this case, it's better to just bind the complex expression to the temporary and read that temporary twice.
+ if (expression_is_forwarded(id))
+ {
+ auto &v = expression_usage_counts[id];
+ v++;
+
+ if (v >= 2)
+ {
+ //if (v == 2)
+ // fprintf(stderr, "ID %u was forced to temporary due to more than 1 expression use!\n", id);
+
+ forced_temporaries.insert(id);
+ // Force a recompile after this pass to avoid forwarding this variable.
+ force_recompile = true;
+ }
+ }
+}
+
+bool CompilerGLSL::args_will_forward(uint32_t id, const uint32_t *args, uint32_t num_args, bool pure)
+{
+ if (forced_temporaries.find(id) != end(forced_temporaries))
+ return false;
+
+ for (uint32_t i = 0; i < num_args; i++)
+ if (!should_forward(args[i]))
+ return false;
+
+ // We need to forward globals as well.
+ if (!pure)
+ {
+ for (auto global : global_variables)
+ if (!should_forward(global))
+ return false;
+ for (auto aliased : aliased_variables)
+ if (!should_forward(aliased))
+ return false;
+ }
+
+ return true;
+}
+
+void CompilerGLSL::register_impure_function_call()
+{
+ // Impure functions can modify globals and aliased variables, so invalidate them as well.
+ for (auto global : global_variables)
+ flush_dependees(get<SPIRVariable>(global));
+ for (auto aliased : aliased_variables)
+ flush_dependees(get<SPIRVariable>(aliased));
+}
+
+void CompilerGLSL::register_call_out_argument(uint32_t id)
+{
+ register_write(id);
+
+ auto *var = maybe_get<SPIRVariable>(id);
+ if (var)
+ flush_variable_declaration(var->self);
+}
+
+string CompilerGLSL::variable_decl_function_local(SPIRVariable &var)
+{
+ // These variables are always function local,
+ // so make sure we emit the variable without storage qualifiers.
+ // Some backends will inject custom variables locally in a function
+ // with a storage qualifier which is not function-local.
+ auto old_storage = var.storage;
+ var.storage = StorageClassFunction;
+ auto expr = variable_decl(var);
+ var.storage = old_storage;
+ return expr;
+}
+
+void CompilerGLSL::flush_variable_declaration(uint32_t id)
+{
+ auto *var = maybe_get<SPIRVariable>(id);
+ if (var && var->deferred_declaration)
+ {
+ statement(variable_decl_function_local(*var), ";");
+ if (var->allocate_temporary_copy)
+ {
+ auto &type = get<SPIRType>(var->basetype);
+ auto &flags = ir.meta[id].decoration.decoration_flags;
+ statement(flags_to_precision_qualifiers_glsl(type, flags), variable_decl(type, join("_", id, "_copy")),
+ ";");
+ }
+ var->deferred_declaration = false;
+ }
+}
+
+bool CompilerGLSL::remove_duplicate_swizzle(string &op)
+{
+ auto pos = op.find_last_of('.');
+ if (pos == string::npos || pos == 0)
+ return false;
+
+ string final_swiz = op.substr(pos + 1, string::npos);
+
+ if (backend.swizzle_is_function)
+ {
+ if (final_swiz.size() < 2)
+ return false;
+
+ if (final_swiz.substr(final_swiz.size() - 2, string::npos) == "()")
+ final_swiz.erase(final_swiz.size() - 2, string::npos);
+ else
+ return false;
+ }
+
+ // Check if final swizzle is of form .x, .xy, .xyz, .xyzw or similar.
+ // If so, and previous swizzle is of same length,
+ // we can drop the final swizzle altogether.
+ for (uint32_t i = 0; i < final_swiz.size(); i++)
+ {
+ static const char expected[] = { 'x', 'y', 'z', 'w' };
+ if (i >= 4 || final_swiz[i] != expected[i])
+ return false;
+ }
+
+ auto prevpos = op.find_last_of('.', pos - 1);
+ if (prevpos == string::npos)
+ return false;
+
+ prevpos++;
+
+ // Make sure there are only swizzles here ...
+ for (auto i = prevpos; i < pos; i++)
+ {
+ if (op[i] < 'w' || op[i] > 'z')
+ {
+ // If swizzles are foo.xyz() like in C++ backend for example, check for that.
+ if (backend.swizzle_is_function && i + 2 == pos && op[i] == '(' && op[i + 1] == ')')
+ break;
+ return false;
+ }
+ }
+
+ // If original swizzle is large enough, just carve out the components we need.
+ // E.g. foobar.wyx.xy will turn into foobar.wy.
+ if (pos - prevpos >= final_swiz.size())
+ {
+ op.erase(prevpos + final_swiz.size(), string::npos);
+
+ // Add back the function call ...
+ if (backend.swizzle_is_function)
+ op += "()";
+ }
+ return true;
+}
+
+// Optimizes away vector swizzles where we have something like
+// vec3 foo;
+// foo.xyz <-- swizzle expression does nothing.
+// This is a very common pattern after OpCompositeCombine.
+bool CompilerGLSL::remove_unity_swizzle(uint32_t base, string &op)
+{
+ auto pos = op.find_last_of('.');
+ if (pos == string::npos || pos == 0)
+ return false;
+
+ string final_swiz = op.substr(pos + 1, string::npos);
+
+ if (backend.swizzle_is_function)
+ {
+ if (final_swiz.size() < 2)
+ return false;
+
+ if (final_swiz.substr(final_swiz.size() - 2, string::npos) == "()")
+ final_swiz.erase(final_swiz.size() - 2, string::npos);
+ else
+ return false;
+ }
+
+ // Check if final swizzle is of form .x, .xy, .xyz, .xyzw or similar.
+ // If so, and previous swizzle is of same length,
+ // we can drop the final swizzle altogether.
+ for (uint32_t i = 0; i < final_swiz.size(); i++)
+ {
+ static const char expected[] = { 'x', 'y', 'z', 'w' };
+ if (i >= 4 || final_swiz[i] != expected[i])
+ return false;
+ }
+
+ auto &type = expression_type(base);
+
+ // Sanity checking ...
+ assert(type.columns == 1 && type.array.empty());
+
+ if (type.vecsize == final_swiz.size())
+ op.erase(pos, string::npos);
+ return true;
+}
+
+string CompilerGLSL::build_composite_combiner(uint32_t return_type, const uint32_t *elems, uint32_t length)
+{
+ uint32_t base = 0;
+ string op;
+ string subop;
+
+ // Can only merge swizzles for vectors.
+ auto &type = get<SPIRType>(return_type);
+ bool can_apply_swizzle_opt = type.basetype != SPIRType::Struct && type.array.empty() && type.columns == 1;
+ bool swizzle_optimization = false;
+
+ for (uint32_t i = 0; i < length; i++)
+ {
+ auto *e = maybe_get<SPIRExpression>(elems[i]);
+
+ // If we're merging another scalar which belongs to the same base
+ // object, just merge the swizzles to avoid triggering more than 1 expression read as much as possible!
+ if (can_apply_swizzle_opt && e && e->base_expression && e->base_expression == base)
+ {
+ // Only supposed to be used for vector swizzle -> scalar.
+ assert(!e->expression.empty() && e->expression.front() == '.');
+ subop += e->expression.substr(1, string::npos);
+ swizzle_optimization = true;
+ }
+ else
+ {
+ // We'll likely end up with duplicated swizzles, e.g.
+ // foobar.xyz.xyz from patterns like
+ // OpVectorShuffle
+ // OpCompositeExtract x 3
+ // OpCompositeConstruct 3x + other scalar.
+ // Just modify op in-place.
+ if (swizzle_optimization)
+ {
+ if (backend.swizzle_is_function)
+ subop += "()";
+
+ // Don't attempt to remove unity swizzling if we managed to remove duplicate swizzles.
+ // The base "foo" might be vec4, while foo.xyz is vec3 (OpVectorShuffle) and looks like a vec3 due to the .xyz tacked on.
+ // We only want to remove the swizzles if we're certain that the resulting base will be the same vecsize.
+ // Essentially, we can only remove one set of swizzles, since that's what we have control over ...
+ // Case 1:
+ // foo.yxz.xyz: Duplicate swizzle kicks in, giving foo.yxz, we are done.
+ // foo.yxz was the result of OpVectorShuffle and we don't know the type of foo.
+ // Case 2:
+ // foo.xyz: Duplicate swizzle won't kick in.
+ // If foo is vec3, we can remove xyz, giving just foo.
+ if (!remove_duplicate_swizzle(subop))
+ remove_unity_swizzle(base, subop);
+
+ // Strips away redundant parens if we created them during component extraction.
+ strip_enclosed_expression(subop);
+ swizzle_optimization = false;
+ op += subop;
+ }
+ else
+ op += subop;
+
+ if (i)
+ op += ", ";
+ subop = to_expression(elems[i]);
+ }
+
+ base = e ? e->base_expression : 0;
+ }
+
+ if (swizzle_optimization)
+ {
+ if (backend.swizzle_is_function)
+ subop += "()";
+
+ if (!remove_duplicate_swizzle(subop))
+ remove_unity_swizzle(base, subop);
+ // Strips away redundant parens if we created them during component extraction.
+ strip_enclosed_expression(subop);
+ }
+
+ op += subop;
+ return op;
+}
+
+bool CompilerGLSL::skip_argument(uint32_t id) const
+{
+ if (!combined_image_samplers.empty() || !options.vulkan_semantics)
+ {
+ auto &type = expression_type(id);
+ if (type.basetype == SPIRType::Sampler || (type.basetype == SPIRType::Image && type.image.sampled == 1))
+ return true;
+ }
+ return false;
+}
+
+bool CompilerGLSL::optimize_read_modify_write(const SPIRType &type, const string &lhs, const string &rhs)
+{
+ // Do this with strings because we have a very clear pattern we can check for and it avoids
+ // adding lots of special cases to the code emission.
+ if (rhs.size() < lhs.size() + 3)
+ return false;
+
+ // Do not optimize matrices. They are a bit awkward to reason about in general
+ // (in which order does operation happen?), and it does not work on MSL anyways.
+ if (type.vecsize > 1 && type.columns > 1)
+ return false;
+
+ auto index = rhs.find(lhs);
+ if (index != 0)
+ return false;
+
+ // TODO: Shift operators, but it's not important for now.
+ auto op = rhs.find_first_of("+-/*%|&^", lhs.size() + 1);
+ if (op != lhs.size() + 1)
+ return false;
+
+ // Check that the op is followed by space. This excludes && and ||.
+ if (rhs[op + 1] != ' ')
+ return false;
+
+ char bop = rhs[op];
+ auto expr = rhs.substr(lhs.size() + 3);
+ // Try to find increments and decrements. Makes it look neater as += 1, -= 1 is fairly rare to see in real code.
+ // Find some common patterns which are equivalent.
+ if ((bop == '+' || bop == '-') && (expr == "1" || expr == "uint(1)" || expr == "1u" || expr == "int(1u)"))
+ statement(lhs, bop, bop, ";");
+ else
+ statement(lhs, " ", bop, "= ", expr, ";");
+ return true;
+}
+
+void CompilerGLSL::register_control_dependent_expression(uint32_t expr)
+{
+ if (forwarded_temporaries.find(expr) == end(forwarded_temporaries))
+ return;
+
+ assert(current_emitting_block);
+ current_emitting_block->invalidate_expressions.push_back(expr);
+}
+
+void CompilerGLSL::emit_block_instructions(SPIRBlock &block)
+{
+ current_emitting_block = &block;
+ for (auto &op : block.ops)
+ emit_instruction(op);
+ current_emitting_block = nullptr;
+}
+
+void CompilerGLSL::disallow_forwarding_in_expression_chain(const SPIRExpression &expr)
+{
+ if (forwarded_temporaries.count(expr.self))
+ {
+ forced_temporaries.insert(expr.self);
+ force_recompile = true;
+ }
+
+ for (auto &dependent : expr.expression_dependencies)
+ disallow_forwarding_in_expression_chain(get<SPIRExpression>(dependent));
+}
+
+void CompilerGLSL::handle_store_to_invariant_variable(uint32_t store_id, uint32_t value_id)
+{
+ // Variables or access chains marked invariant are complicated. We will need to make sure the code-gen leading up to
+ // this variable is consistent. The failure case for SPIRV-Cross is when an expression is forced to a temporary
+ // in one translation unit, but not another, e.g. due to multiple use of an expression.
+ // This causes variance despite the output variable being marked invariant, so the solution here is to force all dependent
+ // expressions to be temporaries.
+ // It is uncertain if this is enough to support invariant in all possible cases, but it should be good enough
+ // for all reasonable uses of invariant.
+ if (!has_decoration(store_id, DecorationInvariant))
+ return;
+
+ auto *expr = maybe_get<SPIRExpression>(value_id);
+ if (!expr)
+ return;
+
+ disallow_forwarding_in_expression_chain(*expr);
+}
+
+void CompilerGLSL::emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression)
+{
+ auto rhs = to_pointer_expression(rhs_expression);
+
+ // Statements to OpStore may be empty if it is a struct with zero members. Just forward the store to /dev/null.
+ if (!rhs.empty())
+ {
+ handle_store_to_invariant_variable(lhs_expression, rhs_expression);
+
+ auto lhs = to_dereferenced_expression(lhs_expression);
+
+ // We might need to bitcast in order to store to a builtin.
+ bitcast_to_builtin_store(lhs_expression, rhs, expression_type(rhs_expression));
+
+ // Tries to optimize assignments like "<lhs> = <lhs> op expr".
+ // While this is purely cosmetic, this is important for legacy ESSL where loop
+ // variable increments must be in either i++ or i += const-expr.
+ // Without this, we end up with i = i + 1, which is correct GLSL, but not correct GLES 2.0.
+ if (!optimize_read_modify_write(expression_type(rhs_expression), lhs, rhs))
+ statement(lhs, " = ", rhs, ";");
+ register_write(lhs_expression);
+ }
+}
+
+uint32_t CompilerGLSL::get_integer_width_for_instruction(const Instruction &instr) const
+{
+ if (instr.length < 3)
+ return 32;
+
+ auto *ops = stream(instr);
+
+ switch (instr.op)
+ {
+ case OpIEqual:
+ case OpINotEqual:
+ case OpSLessThan:
+ case OpSLessThanEqual:
+ case OpSGreaterThan:
+ case OpSGreaterThanEqual:
+ return expression_type(ops[2]).width;
+
+ default:
+ {
+ // We can look at result type which is more robust.
+ auto *type = maybe_get<SPIRType>(ops[0]);
+ if (type && type_is_integral(*type))
+ return type->width;
+ else
+ return 32;
+ }
+ }
+}
+
+uint32_t CompilerGLSL::get_integer_width_for_glsl_instruction(GLSLstd450 op, const uint32_t *ops, uint32_t length) const
+{
+ if (length < 1)
+ return 32;
+
+ switch (op)
+ {
+ case GLSLstd450SAbs:
+ case GLSLstd450SSign:
+ case GLSLstd450UMin:
+ case GLSLstd450SMin:
+ case GLSLstd450UMax:
+ case GLSLstd450SMax:
+ case GLSLstd450UClamp:
+ case GLSLstd450SClamp:
+ case GLSLstd450FindSMsb:
+ case GLSLstd450FindUMsb:
+ return expression_type(ops[0]).width;
+
+ default:
+ {
+ // We don't need to care about other opcodes, just return 32.
+ return 32;
+ }
+ }
+}
+
+void CompilerGLSL::emit_instruction(const Instruction &instruction)
+{
+ auto ops = stream(instruction);
+ auto opcode = static_cast<Op>(instruction.op);
+ uint32_t length = instruction.length;
+
+#define GLSL_BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op)
+#define GLSL_BOP_CAST(op, type) \
+ emit_binary_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, opcode_is_sign_invariant(opcode))
+#define GLSL_UOP(op) emit_unary_op(ops[0], ops[1], ops[2], #op)
+#define GLSL_QFOP(op) emit_quaternary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], #op)
+#define GLSL_TFOP(op) emit_trinary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], #op)
+#define GLSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op)
+#define GLSL_BFOP_CAST(op, type) \
+ emit_binary_func_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, opcode_is_sign_invariant(opcode))
+#define GLSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op)
+#define GLSL_UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op)
+
+ // If we need to do implicit bitcasts, make sure we do it with the correct type.
+ uint32_t integer_width = get_integer_width_for_instruction(instruction);
+ auto int_type = to_signed_basetype(integer_width);
+ auto uint_type = to_unsigned_basetype(integer_width);
+
+ switch (opcode)
+ {
+ // Dealing with memory
+ case OpLoad:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ uint32_t ptr = ops[2];
+
+ flush_variable_declaration(ptr);
+
+ // If we're loading from memory that cannot be changed by the shader,
+ // just forward the expression directly to avoid needless temporaries.
+ // If an expression is mutable and forwardable, we speculate that it is immutable.
+ bool forward = should_forward(ptr) && forced_temporaries.find(id) == end(forced_temporaries);
+
+ // If loading a non-native row-major matrix, mark the expression as need_transpose.
+ bool need_transpose = false;
+ bool old_need_transpose = false;
+
+ auto *ptr_expression = maybe_get<SPIRExpression>(ptr);
+ if (ptr_expression && ptr_expression->need_transpose)
+ {
+ old_need_transpose = true;
+ ptr_expression->need_transpose = false;
+ need_transpose = true;
+ }
+ else if (is_non_native_row_major_matrix(ptr))
+ need_transpose = true;
+
+ // If we are forwarding this load,
+ // don't register the read to access chain here, defer that to when we actually use the expression,
+ // using the add_implied_read_expression mechanism.
+ auto expr = to_dereferenced_expression(ptr, !forward);
+
+ // We might need to bitcast in order to load from a builtin.
+ bitcast_from_builtin_load(ptr, expr, get<SPIRType>(result_type));
+
+ // We might be trying to load a gl_Position[N], where we should be
+ // doing float4[](gl_in[i].gl_Position, ...) instead.
+ // Similar workarounds are required for input arrays in tessellation.
+ unroll_array_from_complex_load(id, ptr, expr);
+
+ if (ptr_expression)
+ ptr_expression->need_transpose = old_need_transpose;
+
+ // By default, suppress usage tracking since using same expression multiple times does not imply any extra work.
+ // However, if we try to load a complex, composite object from a flattened buffer,
+ // we should avoid emitting the same code over and over and lower the result to a temporary.
+ auto &type = get<SPIRType>(result_type);
+ bool usage_tracking = ptr_expression && flattened_buffer_blocks.count(ptr_expression->loaded_from) != 0 &&
+ (type.basetype == SPIRType::Struct || (type.columns > 1));
+
+ auto &e = emit_op(result_type, id, expr, forward, !usage_tracking);
+ e.need_transpose = need_transpose;
+ register_read(id, ptr, forward);
+
+ // Pass through whether the result is of a packed type.
+ if (has_extended_decoration(ptr, SPIRVCrossDecorationPacked))
+ {
+ set_extended_decoration(id, SPIRVCrossDecorationPacked);
+ set_extended_decoration(id, SPIRVCrossDecorationPackedType,
+ get_extended_decoration(ptr, SPIRVCrossDecorationPackedType));
+ }
+
+ inherit_expression_dependencies(id, ptr);
+ if (forward)
+ add_implied_read_expression(e, ptr);
+ break;
+ }
+
+ case OpInBoundsAccessChain:
+ case OpAccessChain:
+ case OpPtrAccessChain:
+ {
+ auto *var = maybe_get<SPIRVariable>(ops[2]);
+ if (var)
+ flush_variable_declaration(var->self);
+
+ // If the base is immutable, the access chain pointer must also be.
+ // If an expression is mutable and forwardable, we speculate that it is immutable.
+ AccessChainMeta meta;
+ bool ptr_chain = opcode == OpPtrAccessChain;
+ auto e = access_chain(ops[2], &ops[3], length - 3, get<SPIRType>(ops[0]), &meta, ptr_chain);
+
+ auto &expr = set<SPIRExpression>(ops[1], move(e), ops[0], should_forward(ops[2]));
+
+ auto *backing_variable = maybe_get_backing_variable(ops[2]);
+ expr.loaded_from = backing_variable ? backing_variable->self : ops[2];
+ expr.need_transpose = meta.need_transpose;
+ expr.access_chain = true;
+
+ // Mark the result as being packed. Some platforms handled packed vectors differently than non-packed.
+ if (meta.storage_is_packed)
+ set_extended_decoration(ops[1], SPIRVCrossDecorationPacked);
+ if (meta.storage_packed_type != 0)
+ set_extended_decoration(ops[1], SPIRVCrossDecorationPackedType, meta.storage_packed_type);
+ if (meta.storage_is_invariant)
+ set_decoration(ops[1], DecorationInvariant);
+
+ for (uint32_t i = 2; i < length; i++)
+ {
+ inherit_expression_dependencies(ops[1], ops[i]);
+ add_implied_read_expression(expr, ops[i]);
+ }
+
+ break;
+ }
+
+ case OpStore:
+ {
+ auto *var = maybe_get<SPIRVariable>(ops[0]);
+
+ if (var && var->statically_assigned)
+ var->static_expression = ops[1];
+ else if (var && var->loop_variable && !var->loop_variable_enable)
+ var->static_expression = ops[1];
+ else if (var && var->remapped_variable)
+ {
+ // Skip the write.
+ }
+ else if (var && flattened_structs.count(ops[0]))
+ {
+ store_flattened_struct(*var, ops[1]);
+ register_write(ops[0]);
+ }
+ else
+ {
+ emit_store_statement(ops[0], ops[1]);
+ }
+
+ // Storing a pointer results in a variable pointer, so we must conservatively assume
+ // we can write through it.
+ if (expression_type(ops[1]).pointer)
+ register_write(ops[1]);
+ break;
+ }
+
+ case OpArrayLength:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ auto e = access_chain_internal(ops[2], &ops[3], length - 3, ACCESS_CHAIN_INDEX_IS_LITERAL_BIT, nullptr);
+ set<SPIRExpression>(id, e + ".length()", result_type, true);
+ break;
+ }
+
+ // Function calls
+ case OpFunctionCall:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ uint32_t func = ops[2];
+ const auto *arg = &ops[3];
+ length -= 3;
+
+ auto &callee = get<SPIRFunction>(func);
+ auto &return_type = get<SPIRType>(callee.return_type);
+ bool pure = function_is_pure(callee);
+
+ bool callee_has_out_variables = false;
+ bool emit_return_value_as_argument = false;
+
+ // Invalidate out variables passed to functions since they can be OpStore'd to.
+ for (uint32_t i = 0; i < length; i++)
+ {
+ if (callee.arguments[i].write_count)
+ {
+ register_call_out_argument(arg[i]);
+ callee_has_out_variables = true;
+ }
+
+ flush_variable_declaration(arg[i]);
+ }
+
+ if (!return_type.array.empty() && !backend.can_return_array)
+ {
+ callee_has_out_variables = true;
+ emit_return_value_as_argument = true;
+ }
+
+ if (!pure)
+ register_impure_function_call();
+
+ string funexpr;
+ vector<string> arglist;
+ funexpr += to_name(func) + "(";
+
+ if (emit_return_value_as_argument)
+ {
+ statement(type_to_glsl(return_type), " ", to_name(id), type_to_array_glsl(return_type), ";");
+ arglist.push_back(to_name(id));
+ }
+
+ for (uint32_t i = 0; i < length; i++)
+ {
+ // Do not pass in separate images or samplers if we're remapping
+ // to combined image samplers.
+ if (skip_argument(arg[i]))
+ continue;
+
+ arglist.push_back(to_func_call_arg(arg[i]));
+ }
+
+ for (auto &combined : callee.combined_parameters)
+ {
+ uint32_t image_id = combined.global_image ? combined.image_id : arg[combined.image_id];
+ uint32_t sampler_id = combined.global_sampler ? combined.sampler_id : arg[combined.sampler_id];
+ arglist.push_back(to_combined_image_sampler(image_id, sampler_id));
+ }
+
+ append_global_func_args(callee, length, arglist);
+
+ funexpr += merge(arglist);
+ funexpr += ")";
+
+ // Check for function call constraints.
+ check_function_call_constraints(arg, length);
+
+ if (return_type.basetype != SPIRType::Void)
+ {
+ // If the function actually writes to an out variable,
+ // take the conservative route and do not forward.
+ // The problem is that we might not read the function
+ // result (and emit the function) before an out variable
+ // is read (common case when return value is ignored!
+ // In order to avoid start tracking invalid variables,
+ // just avoid the forwarding problem altogether.
+ bool forward = args_will_forward(id, arg, length, pure) && !callee_has_out_variables && pure &&
+ (forced_temporaries.find(id) == end(forced_temporaries));
+
+ if (emit_return_value_as_argument)
+ {
+ statement(funexpr, ";");
+ set<SPIRExpression>(id, to_name(id), result_type, true);
+ }
+ else
+ emit_op(result_type, id, funexpr, forward);
+
+ // Function calls are implicit loads from all variables in question.
+ // Set dependencies for them.
+ for (uint32_t i = 0; i < length; i++)
+ register_read(id, arg[i], forward);
+
+ // If we're going to forward the temporary result,
+ // put dependencies on every variable that must not change.
+ if (forward)
+ register_global_read_dependencies(callee, id);
+ }
+ else
+ statement(funexpr, ";");
+
+ break;
+ }
+
+ // Composite munging
+ case OpCompositeConstruct:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ const auto *const elems = &ops[2];
+ length -= 2;
+
+ bool forward = true;
+ for (uint32_t i = 0; i < length; i++)
+ forward = forward && should_forward(elems[i]);
+
+ auto &out_type = get<SPIRType>(result_type);
+ auto *in_type = length > 0 ? &expression_type(elems[0]) : nullptr;
+
+ // Only splat if we have vector constructors.
+ // Arrays and structs must be initialized properly in full.
+ bool composite = !out_type.array.empty() || out_type.basetype == SPIRType::Struct;
+
+ bool splat = false;
+ bool swizzle_splat = false;
+
+ if (in_type)
+ {
+ splat = in_type->vecsize == 1 && in_type->columns == 1 && !composite && backend.use_constructor_splatting;
+ swizzle_splat = in_type->vecsize == 1 && in_type->columns == 1 && backend.can_swizzle_scalar;
+
+ if (ir.ids[elems[0]].get_type() == TypeConstant && !type_is_floating_point(*in_type))
+ {
+ // Cannot swizzle literal integers as a special case.
+ swizzle_splat = false;
+ }
+ }
+
+ if (splat || swizzle_splat)
+ {
+ uint32_t input = elems[0];
+ for (uint32_t i = 0; i < length; i++)
+ {
+ if (input != elems[i])
+ {
+ splat = false;
+ swizzle_splat = false;
+ }
+ }
+ }
+
+ if (out_type.basetype == SPIRType::Struct && !backend.can_declare_struct_inline)
+ forward = false;
+ if (!out_type.array.empty() && !backend.can_declare_arrays_inline)
+ forward = false;
+ if (type_is_empty(out_type) && !backend.supports_empty_struct)
+ forward = false;
+
+ string constructor_op;
+ if (!backend.array_is_value_type && out_type.array.size() > 1)
+ {
+ // We cannot construct array of arrays because we cannot treat the inputs
+ // as value types. Need to declare the array-of-arrays, and copy in elements one by one.
+ forced_temporaries.insert(id);
+ auto &flags = ir.meta[id].decoration.decoration_flags;
+ statement(flags_to_precision_qualifiers_glsl(out_type, flags), variable_decl(out_type, to_name(id)), ";");
+ set<SPIRExpression>(id, to_name(id), result_type, true);
+ for (uint32_t i = 0; i < length; i++)
+ emit_array_copy(join(to_expression(id), "[", i, "]"), elems[i]);
+ }
+ else if (backend.use_initializer_list && composite)
+ {
+ // Only use this path if we are building composites.
+ // This path cannot be used for arithmetic.
+ if (backend.use_typed_initializer_list && out_type.basetype == SPIRType::Struct && out_type.array.empty())
+ constructor_op += type_to_glsl_constructor(get<SPIRType>(result_type));
+ constructor_op += "{ ";
+ if (type_is_empty(out_type) && !backend.supports_empty_struct)
+ constructor_op += "0";
+ else if (splat)
+ constructor_op += to_expression(elems[0]);
+ else
+ constructor_op += build_composite_combiner(result_type, elems, length);
+ constructor_op += " }";
+ }
+ else if (swizzle_splat && !composite)
+ {
+ constructor_op = remap_swizzle(get<SPIRType>(result_type), 1, to_expression(elems[0]));
+ }
+ else
+ {
+ constructor_op = type_to_glsl_constructor(get<SPIRType>(result_type)) + "(";
+ if (type_is_empty(out_type) && !backend.supports_empty_struct)
+ constructor_op += "0";
+ else if (splat)
+ constructor_op += to_expression(elems[0]);
+ else
+ constructor_op += build_composite_combiner(result_type, elems, length);
+ constructor_op += ")";
+ }
+
+ if (!constructor_op.empty())
+ {
+ emit_op(result_type, id, constructor_op, forward);
+ for (uint32_t i = 0; i < length; i++)
+ inherit_expression_dependencies(id, elems[i]);
+ }
+ break;
+ }
+
+ case OpVectorInsertDynamic:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ uint32_t vec = ops[2];
+ uint32_t comp = ops[3];
+ uint32_t index = ops[4];
+
+ flush_variable_declaration(vec);
+
+ // Make a copy, then use access chain to store the variable.
+ statement(declare_temporary(result_type, id), to_expression(vec), ";");
+ set<SPIRExpression>(id, to_name(id), result_type, true);
+ auto chain = access_chain_internal(id, &index, 1, 0, nullptr);
+ statement(chain, " = ", to_expression(comp), ";");
+ break;
+ }
+
+ case OpVectorExtractDynamic:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+
+ auto expr = access_chain_internal(ops[2], &ops[3], 1, 0, nullptr);
+ emit_op(result_type, id, expr, should_forward(ops[2]));
+ inherit_expression_dependencies(id, ops[2]);
+ inherit_expression_dependencies(id, ops[3]);
+ break;
+ }
+
+ case OpCompositeExtract:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ length -= 3;
+
+ auto &type = get<SPIRType>(result_type);
+
+ // We can only split the expression here if our expression is forwarded as a temporary.
+ bool allow_base_expression = forced_temporaries.find(id) == end(forced_temporaries);
+
+ // Do not allow base expression for struct members. We risk doing "swizzle" optimizations in this case.
+ auto &composite_type = expression_type(ops[2]);
+ if (composite_type.basetype == SPIRType::Struct || !composite_type.array.empty())
+ allow_base_expression = false;
+
+ // Packed expressions cannot be split up.
+ if (has_extended_decoration(ops[2], SPIRVCrossDecorationPacked))
+ allow_base_expression = false;
+
+ AccessChainMeta meta;
+ SPIRExpression *e = nullptr;
+
+ // Only apply this optimization if result is scalar.
+ if (allow_base_expression && should_forward(ops[2]) && type.vecsize == 1 && type.columns == 1 && length == 1)
+ {
+ // We want to split the access chain from the base.
+ // This is so we can later combine different CompositeExtract results
+ // with CompositeConstruct without emitting code like
+ //
+ // vec3 temp = texture(...).xyz
+ // vec4(temp.x, temp.y, temp.z, 1.0).
+ //
+ // when we actually wanted to emit this
+ // vec4(texture(...).xyz, 1.0).
+ //
+ // Including the base will prevent this and would trigger multiple reads
+ // from expression causing it to be forced to an actual temporary in GLSL.
+ auto expr = access_chain_internal(ops[2], &ops[3], length,
+ ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_CHAIN_ONLY_BIT, &meta);
+ e = &emit_op(result_type, id, expr, true, !expression_is_forwarded(ops[2]));
+ inherit_expression_dependencies(id, ops[2]);
+ e->base_expression = ops[2];
+ }
+ else
+ {
+ auto expr = access_chain_internal(ops[2], &ops[3], length, ACCESS_CHAIN_INDEX_IS_LITERAL_BIT, &meta);
+ e = &emit_op(result_type, id, expr, should_forward(ops[2]), !expression_is_forwarded(ops[2]));
+ inherit_expression_dependencies(id, ops[2]);
+ }
+
+ // Pass through some meta information to the loaded expression.
+ // We can still end up loading a buffer type to a variable, then CompositeExtract from it
+ // instead of loading everything through an access chain.
+ e->need_transpose = meta.need_transpose;
+ if (meta.storage_is_packed)
+ set_extended_decoration(id, SPIRVCrossDecorationPacked);
+ if (meta.storage_packed_type != 0)
+ set_extended_decoration(id, SPIRVCrossDecorationPackedType, meta.storage_packed_type);
+ if (meta.storage_is_invariant)
+ set_decoration(id, DecorationInvariant);
+
+ break;
+ }
+
+ case OpCompositeInsert:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ uint32_t obj = ops[2];
+ uint32_t composite = ops[3];
+ const auto *elems = &ops[4];
+ length -= 4;
+
+ flush_variable_declaration(composite);
+
+ // Make a copy, then use access chain to store the variable.
+ statement(declare_temporary(result_type, id), to_expression(composite), ";");
+ set<SPIRExpression>(id, to_name(id), result_type, true);
+ auto chain = access_chain_internal(id, elems, length, ACCESS_CHAIN_INDEX_IS_LITERAL_BIT, nullptr);
+ statement(chain, " = ", to_expression(obj), ";");
+
+ break;
+ }
+
+ case OpCopyMemory:
+ {
+ uint32_t lhs = ops[0];
+ uint32_t rhs = ops[1];
+ if (lhs != rhs)
+ {
+ flush_variable_declaration(lhs);
+ flush_variable_declaration(rhs);
+ statement(to_expression(lhs), " = ", to_expression(rhs), ";");
+ register_write(lhs);
+ }
+ break;
+ }
+
+ case OpCopyObject:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ uint32_t rhs = ops[2];
+ bool pointer = get<SPIRType>(result_type).pointer;
+
+ if (expression_is_lvalue(rhs) && !pointer)
+ {
+ // Need a copy.
+ // For pointer types, we copy the pointer itself.
+ statement(declare_temporary(result_type, id), to_expression(rhs), ";");
+ set<SPIRExpression>(id, to_name(id), result_type, true);
+ inherit_expression_dependencies(id, rhs);
+ }
+ else
+ {
+ // RHS expression is immutable, so just forward it.
+ // Copying these things really make no sense, but
+ // seems to be allowed anyways.
+ auto &e = set<SPIRExpression>(id, to_expression(rhs), result_type, true);
+ if (pointer)
+ {
+ auto *var = maybe_get_backing_variable(rhs);
+ e.loaded_from = var ? var->self : 0;
+ }
+ }
+ break;
+ }
+
+ case OpVectorShuffle:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ uint32_t vec0 = ops[2];
+ uint32_t vec1 = ops[3];
+ const auto *elems = &ops[4];
+ length -= 4;
+
+ auto &type0 = expression_type(vec0);
+
+ // If we have the undefined swizzle index -1, we need to swizzle in undefined data,
+ // or in our case, T(0).
+ bool shuffle = false;
+ for (uint32_t i = 0; i < length; i++)
+ if (elems[i] >= type0.vecsize || elems[i] == 0xffffffffu)
+ shuffle = true;
+
+ // Cannot use swizzles with packed expressions, force shuffle path.
+ if (!shuffle && has_extended_decoration(vec0, SPIRVCrossDecorationPacked))
+ shuffle = true;
+
+ string expr;
+ bool should_fwd, trivial_forward;
+
+ if (shuffle)
+ {
+ should_fwd = should_forward(vec0) && should_forward(vec1);
+ trivial_forward = !expression_is_forwarded(vec0) && !expression_is_forwarded(vec1);
+
+ // Constructor style and shuffling from two different vectors.
+ vector<string> args;
+ for (uint32_t i = 0; i < length; i++)
+ {
+ if (elems[i] == 0xffffffffu)
+ {
+ // Use a constant 0 here.
+ // We could use the first component or similar, but then we risk propagating
+ // a value we might not need, and bog down codegen.
+ SPIRConstant c;
+ c.constant_type = type0.parent_type;
+ assert(type0.parent_type != 0);
+ args.push_back(constant_expression(c));
+ }
+ else if (elems[i] >= type0.vecsize)
+ args.push_back(to_extract_component_expression(vec1, elems[i] - type0.vecsize));
+ else
+ args.push_back(to_extract_component_expression(vec0, elems[i]));
+ }
+ expr += join(type_to_glsl_constructor(get<SPIRType>(result_type)), "(", merge(args), ")");
+ }
+ else
+ {
+ should_fwd = should_forward(vec0);
+ trivial_forward = !expression_is_forwarded(vec0);
+
+ // We only source from first vector, so can use swizzle.
+ // If the vector is packed, unpack it before applying a swizzle (needed for MSL)
+ expr += to_enclosed_unpacked_expression(vec0);
+ expr += ".";
+ for (uint32_t i = 0; i < length; i++)
+ {
+ assert(elems[i] != 0xffffffffu);
+ expr += index_to_swizzle(elems[i]);
+ }
+
+ if (backend.swizzle_is_function && length > 1)
+ expr += "()";
+ }
+
+ // A shuffle is trivial in that it doesn't actually *do* anything.
+ // We inherit the forwardedness from our arguments to avoid flushing out to temporaries when it's not really needed.
+
+ emit_op(result_type, id, expr, should_fwd, trivial_forward);
+ inherit_expression_dependencies(id, vec0);
+ inherit_expression_dependencies(id, vec1);
+ break;
+ }
+
+ // ALU
+ case OpIsNan:
+ GLSL_UFOP(isnan);
+ break;
+
+ case OpIsInf:
+ GLSL_UFOP(isinf);
+ break;
+
+ case OpSNegate:
+ case OpFNegate:
+ GLSL_UOP(-);
+ break;
+
+ case OpIAdd:
+ {
+ // For simple arith ops, prefer the output type if there's a mismatch to avoid extra bitcasts.
+ auto type = get<SPIRType>(ops[0]).basetype;
+ GLSL_BOP_CAST(+, type);
+ break;
+ }
+
+ case OpFAdd:
+ GLSL_BOP(+);
+ break;
+
+ case OpISub:
+ {
+ auto type = get<SPIRType>(ops[0]).basetype;
+ GLSL_BOP_CAST(-, type);
+ break;
+ }
+
+ case OpFSub:
+ GLSL_BOP(-);
+ break;
+
+ case OpIMul:
+ {
+ auto type = get<SPIRType>(ops[0]).basetype;
+ GLSL_BOP_CAST(*, type);
+ break;
+ }
+
+ case OpVectorTimesMatrix:
+ case OpMatrixTimesVector:
+ {
+ // If the matrix needs transpose, just flip the multiply order.
+ auto *e = maybe_get<SPIRExpression>(ops[opcode == OpMatrixTimesVector ? 2 : 3]);
+ if (e && e->need_transpose)
+ {
+ e->need_transpose = false;
+ emit_binary_op(ops[0], ops[1], ops[3], ops[2], "*");
+ e->need_transpose = true;
+ }
+ else
+ GLSL_BOP(*);
+ break;
+ }
+
+ case OpFMul:
+ case OpMatrixTimesScalar:
+ case OpVectorTimesScalar:
+ case OpMatrixTimesMatrix:
+ GLSL_BOP(*);
+ break;
+
+ case OpOuterProduct:
+ GLSL_BFOP(outerProduct);
+ break;
+
+ case OpDot:
+ GLSL_BFOP(dot);
+ break;
+
+ case OpTranspose:
+ GLSL_UFOP(transpose);
+ break;
+
+ case OpSRem:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t result_id = ops[1];
+ uint32_t op0 = ops[2];
+ uint32_t op1 = ops[3];
+
+ // Needs special handling.
+ bool forward = should_forward(op0) && should_forward(op1);
+ auto expr = join(to_enclosed_expression(op0), " - ", to_enclosed_expression(op1), " * ", "(",
+ to_enclosed_expression(op0), " / ", to_enclosed_expression(op1), ")");
+
+ emit_op(result_type, result_id, expr, forward);
+ inherit_expression_dependencies(result_id, op0);
+ inherit_expression_dependencies(result_id, op1);
+ break;
+ }
+
+ case OpSDiv:
+ GLSL_BOP_CAST(/, int_type);
+ break;
+
+ case OpUDiv:
+ GLSL_BOP_CAST(/, uint_type);
+ break;
+
+ case OpIAddCarry:
+ case OpISubBorrow:
+ {
+ if (options.es && options.version < 310)
+ SPIRV_CROSS_THROW("Extended arithmetic is only available from ESSL 310.");
+ else if (!options.es && options.version < 400)
+ SPIRV_CROSS_THROW("Extended arithmetic is only available from GLSL 400.");
+
+ uint32_t result_type = ops[0];
+ uint32_t result_id = ops[1];
+ uint32_t op0 = ops[2];
+ uint32_t op1 = ops[3];
+ forced_temporaries.insert(result_id);
+ auto &type = get<SPIRType>(result_type);
+ auto &flags = ir.meta[result_id].decoration.decoration_flags;
+ statement(flags_to_precision_qualifiers_glsl(type, flags), variable_decl(type, to_name(result_id)), ";");
+ set<SPIRExpression>(result_id, to_name(result_id), result_type, true);
+
+ const char *op = opcode == OpIAddCarry ? "uaddCarry" : "usubBorrow";
+
+ statement(to_expression(result_id), ".", to_member_name(type, 0), " = ", op, "(", to_expression(op0), ", ",
+ to_expression(op1), ", ", to_expression(result_id), ".", to_member_name(type, 1), ");");
+ break;
+ }
+
+ case OpUMulExtended:
+ case OpSMulExtended:
+ {
+ if (options.es && options.version < 310)
+ SPIRV_CROSS_THROW("Extended arithmetic is only available from ESSL 310.");
+ else if (!options.es && options.version < 400)
+ SPIRV_CROSS_THROW("Extended arithmetic is only available from GLSL 4000.");
+
+ uint32_t result_type = ops[0];
+ uint32_t result_id = ops[1];
+ uint32_t op0 = ops[2];
+ uint32_t op1 = ops[3];
+ forced_temporaries.insert(result_id);
+ auto &type = get<SPIRType>(result_type);
+ auto &flags = ir.meta[result_id].decoration.decoration_flags;
+ statement(flags_to_precision_qualifiers_glsl(type, flags), variable_decl(type, to_name(result_id)), ";");
+ set<SPIRExpression>(result_id, to_name(result_id), result_type, true);
+
+ const char *op = opcode == OpUMulExtended ? "umulExtended" : "imulExtended";
+
+ statement(op, "(", to_expression(op0), ", ", to_expression(op1), ", ", to_expression(result_id), ".",
+ to_member_name(type, 1), ", ", to_expression(result_id), ".", to_member_name(type, 0), ");");
+ break;
+ }
+
+ case OpFDiv:
+ GLSL_BOP(/);
+ break;
+
+ case OpShiftRightLogical:
+ GLSL_BOP_CAST(>>, uint_type);
+ break;
+
+ case OpShiftRightArithmetic:
+ GLSL_BOP_CAST(>>, int_type);
+ break;
+
+ case OpShiftLeftLogical:
+ {
+ auto type = get<SPIRType>(ops[0]).basetype;
+ GLSL_BOP_CAST(<<, type);
+ break;
+ }
+
+ case OpBitwiseOr:
+ {
+ auto type = get<SPIRType>(ops[0]).basetype;
+ GLSL_BOP_CAST(|, type);
+ break;
+ }
+
+ case OpBitwiseXor:
+ {
+ auto type = get<SPIRType>(ops[0]).basetype;
+ GLSL_BOP_CAST(^, type);
+ break;
+ }
+
+ case OpBitwiseAnd:
+ {
+ auto type = get<SPIRType>(ops[0]).basetype;
+ GLSL_BOP_CAST(&, type);
+ break;
+ }
+
+ case OpNot:
+ GLSL_UOP(~);
+ break;
+
+ case OpUMod:
+ GLSL_BOP_CAST(%, uint_type);
+ break;
+
+ case OpSMod:
+ GLSL_BOP_CAST(%, int_type);
+ break;
+
+ case OpFMod:
+ GLSL_BFOP(mod);
+ break;
+
+ case OpFRem:
+ {
+ if (is_legacy())
+ SPIRV_CROSS_THROW("OpFRem requires trunc() and is only supported on non-legacy targets. A workaround is "
+ "needed for legacy.");
+
+ uint32_t result_type = ops[0];
+ uint32_t result_id = ops[1];
+ uint32_t op0 = ops[2];
+ uint32_t op1 = ops[3];
+
+ // Needs special handling.
+ bool forward = should_forward(op0) && should_forward(op1);
+ auto expr = join(to_enclosed_expression(op0), " - ", to_enclosed_expression(op1), " * ", "trunc(",
+ to_enclosed_expression(op0), " / ", to_enclosed_expression(op1), ")");
+
+ emit_op(result_type, result_id, expr, forward);
+ inherit_expression_dependencies(result_id, op0);
+ inherit_expression_dependencies(result_id, op1);
+ break;
+ }
+
+ // Relational
+ case OpAny:
+ GLSL_UFOP(any);
+ break;
+
+ case OpAll:
+ GLSL_UFOP(all);
+ break;
+
+ case OpSelect:
+ emit_mix_op(ops[0], ops[1], ops[4], ops[3], ops[2]);
+ break;
+
+ case OpLogicalOr:
+ {
+ // No vector variant in GLSL for logical OR.
+ auto result_type = ops[0];
+ auto id = ops[1];
+ auto &type = get<SPIRType>(result_type);
+
+ if (type.vecsize > 1)
+ emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "||");
+ else
+ GLSL_BOP(||);
+ break;
+ }
+
+ case OpLogicalAnd:
+ {
+ // No vector variant in GLSL for logical AND.
+ auto result_type = ops[0];
+ auto id = ops[1];
+ auto &type = get<SPIRType>(result_type);
+
+ if (type.vecsize > 1)
+ emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "&&");
+ else
+ GLSL_BOP(&&);
+ break;
+ }
+
+ case OpLogicalNot:
+ {
+ auto &type = get<SPIRType>(ops[0]);
+ if (type.vecsize > 1)
+ GLSL_UFOP(not);
+ else
+ GLSL_UOP(!);
+ break;
+ }
+
+ case OpIEqual:
+ {
+ if (expression_type(ops[2]).vecsize > 1)
+ GLSL_BFOP_CAST(equal, int_type);
+ else
+ GLSL_BOP_CAST(==, int_type);
+ break;
+ }
+
+ case OpLogicalEqual:
+ case OpFOrdEqual:
+ {
+ if (expression_type(ops[2]).vecsize > 1)
+ GLSL_BFOP(equal);
+ else
+ GLSL_BOP(==);
+ break;
+ }
+
+ case OpINotEqual:
+ {
+ if (expression_type(ops[2]).vecsize > 1)
+ GLSL_BFOP_CAST(notEqual, int_type);
+ else
+ GLSL_BOP_CAST(!=, int_type);
+ break;
+ }
+
+ case OpLogicalNotEqual:
+ case OpFOrdNotEqual:
+ {
+ if (expression_type(ops[2]).vecsize > 1)
+ GLSL_BFOP(notEqual);
+ else
+ GLSL_BOP(!=);
+ break;
+ }
+
+ case OpUGreaterThan:
+ case OpSGreaterThan:
+ {
+ auto type = opcode == OpUGreaterThan ? SPIRType::UInt : SPIRType::Int;
+ if (expression_type(ops[2]).vecsize > 1)
+ GLSL_BFOP_CAST(greaterThan, type);
+ else
+ GLSL_BOP_CAST(>, type);
+ break;
+ }
+
+ case OpFOrdGreaterThan:
+ {
+ if (expression_type(ops[2]).vecsize > 1)
+ GLSL_BFOP(greaterThan);
+ else
+ GLSL_BOP(>);
+ break;
+ }
+
+ case OpUGreaterThanEqual:
+ case OpSGreaterThanEqual:
+ {
+ auto type = opcode == OpUGreaterThanEqual ? SPIRType::UInt : SPIRType::Int;
+ if (expression_type(ops[2]).vecsize > 1)
+ GLSL_BFOP_CAST(greaterThanEqual, type);
+ else
+ GLSL_BOP_CAST(>=, type);
+ break;
+ }
+
+ case OpFOrdGreaterThanEqual:
+ {
+ if (expression_type(ops[2]).vecsize > 1)
+ GLSL_BFOP(greaterThanEqual);
+ else
+ GLSL_BOP(>=);
+ break;
+ }
+
+ case OpULessThan:
+ case OpSLessThan:
+ {
+ auto type = opcode == OpULessThan ? SPIRType::UInt : SPIRType::Int;
+ if (expression_type(ops[2]).vecsize > 1)
+ GLSL_BFOP_CAST(lessThan, type);
+ else
+ GLSL_BOP_CAST(<, type);
+ break;
+ }
+
+ case OpFOrdLessThan:
+ {
+ if (expression_type(ops[2]).vecsize > 1)
+ GLSL_BFOP(lessThan);
+ else
+ GLSL_BOP(<);
+ break;
+ }
+
+ case OpULessThanEqual:
+ case OpSLessThanEqual:
+ {
+ auto type = opcode == OpULessThanEqual ? SPIRType::UInt : SPIRType::Int;
+ if (expression_type(ops[2]).vecsize > 1)
+ GLSL_BFOP_CAST(lessThanEqual, type);
+ else
+ GLSL_BOP_CAST(<=, type);
+ break;
+ }
+
+ case OpFOrdLessThanEqual:
+ {
+ if (expression_type(ops[2]).vecsize > 1)
+ GLSL_BFOP(lessThanEqual);
+ else
+ GLSL_BOP(<=);
+ break;
+ }
+
+ // Conversion
+ case OpConvertFToU:
+ case OpConvertFToS:
+ case OpConvertSToF:
+ case OpConvertUToF:
+ case OpUConvert:
+ case OpSConvert:
+ case OpFConvert:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+
+ auto func = type_to_glsl_constructor(get<SPIRType>(result_type));
+ emit_unary_func_op(result_type, id, ops[2], func.c_str());
+ break;
+ }
+
+ case OpBitcast:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ uint32_t arg = ops[2];
+
+ auto op = bitcast_glsl_op(get<SPIRType>(result_type), expression_type(arg));
+ emit_unary_func_op(result_type, id, arg, op.c_str());
+ break;
+ }
+
+ case OpQuantizeToF16:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ uint32_t arg = ops[2];
+
+ string op;
+ auto &type = get<SPIRType>(result_type);
+
+ switch (type.vecsize)
+ {
+ case 1:
+ op = join("unpackHalf2x16(packHalf2x16(vec2(", to_expression(arg), "))).x");
+ break;
+ case 2:
+ op = join("unpackHalf2x16(packHalf2x16(", to_expression(arg), "))");
+ break;
+ case 3:
+ {
+ auto op0 = join("unpackHalf2x16(packHalf2x16(", to_expression(arg), ".xy))");
+ auto op1 = join("unpackHalf2x16(packHalf2x16(", to_expression(arg), ".zz)).x");
+ op = join("vec3(", op0, ", ", op1, ")");
+ break;
+ }
+ case 4:
+ {
+ auto op0 = join("unpackHalf2x16(packHalf2x16(", to_expression(arg), ".xy))");
+ auto op1 = join("unpackHalf2x16(packHalf2x16(", to_expression(arg), ".zw))");
+ op = join("vec4(", op0, ", ", op1, ")");
+ break;
+ }
+ default:
+ SPIRV_CROSS_THROW("Illegal argument to OpQuantizeToF16.");
+ }
+
+ emit_op(result_type, id, op, should_forward(arg));
+ inherit_expression_dependencies(id, arg);
+ break;
+ }
+
+ // Derivatives
+ case OpDPdx:
+ GLSL_UFOP(dFdx);
+ if (is_legacy_es())
+ require_extension_internal("GL_OES_standard_derivatives");
+ register_control_dependent_expression(ops[1]);
+ break;
+
+ case OpDPdy:
+ GLSL_UFOP(dFdy);
+ if (is_legacy_es())
+ require_extension_internal("GL_OES_standard_derivatives");
+ register_control_dependent_expression(ops[1]);
+ break;
+
+ case OpDPdxFine:
+ GLSL_UFOP(dFdxFine);
+ if (options.es)
+ {
+ SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES.");
+ }
+ if (options.version < 450)
+ require_extension_internal("GL_ARB_derivative_control");
+ register_control_dependent_expression(ops[1]);
+ break;
+
+ case OpDPdyFine:
+ GLSL_UFOP(dFdyFine);
+ if (options.es)
+ {
+ SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES.");
+ }
+ if (options.version < 450)
+ require_extension_internal("GL_ARB_derivative_control");
+ register_control_dependent_expression(ops[1]);
+ break;
+
+ case OpDPdxCoarse:
+ if (options.es)
+ {
+ SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES.");
+ }
+ GLSL_UFOP(dFdxCoarse);
+ if (options.version < 450)
+ require_extension_internal("GL_ARB_derivative_control");
+ register_control_dependent_expression(ops[1]);
+ break;
+
+ case OpDPdyCoarse:
+ GLSL_UFOP(dFdyCoarse);
+ if (options.es)
+ {
+ SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES.");
+ }
+ if (options.version < 450)
+ require_extension_internal("GL_ARB_derivative_control");
+ register_control_dependent_expression(ops[1]);
+ break;
+
+ case OpFwidth:
+ GLSL_UFOP(fwidth);
+ if (is_legacy_es())
+ require_extension_internal("GL_OES_standard_derivatives");
+ register_control_dependent_expression(ops[1]);
+ break;
+
+ case OpFwidthCoarse:
+ GLSL_UFOP(fwidthCoarse);
+ if (options.es)
+ {
+ SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES.");
+ }
+ if (options.version < 450)
+ require_extension_internal("GL_ARB_derivative_control");
+ register_control_dependent_expression(ops[1]);
+ break;
+
+ case OpFwidthFine:
+ GLSL_UFOP(fwidthFine);
+ if (options.es)
+ {
+ SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES.");
+ }
+ if (options.version < 450)
+ require_extension_internal("GL_ARB_derivative_control");
+ register_control_dependent_expression(ops[1]);
+ break;
+
+ // Bitfield
+ case OpBitFieldInsert:
+ // TODO: The signedness of inputs is strict in GLSL, but not in SPIR-V, bitcast if necessary.
+ GLSL_QFOP(bitfieldInsert);
+ break;
+
+ case OpBitFieldSExtract:
+ case OpBitFieldUExtract:
+ // TODO: The signedness of inputs is strict in GLSL, but not in SPIR-V, bitcast if necessary.
+ GLSL_TFOP(bitfieldExtract);
+ break;
+
+ case OpBitReverse:
+ GLSL_UFOP(bitfieldReverse);
+ break;
+
+ case OpBitCount:
+ GLSL_UFOP(bitCount);
+ break;
+
+ // Atomics
+ case OpAtomicExchange:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ uint32_t ptr = ops[2];
+ // Ignore semantics for now, probably only relevant to CL.
+ uint32_t val = ops[5];
+ const char *op = check_atomic_image(ptr) ? "imageAtomicExchange" : "atomicExchange";
+ forced_temporaries.insert(id);
+ emit_binary_func_op(result_type, id, ptr, val, op);
+ flush_all_atomic_capable_variables();
+ break;
+ }
+
+ case OpAtomicCompareExchange:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ uint32_t ptr = ops[2];
+ uint32_t val = ops[6];
+ uint32_t comp = ops[7];
+ const char *op = check_atomic_image(ptr) ? "imageAtomicCompSwap" : "atomicCompSwap";
+
+ forced_temporaries.insert(id);
+ emit_trinary_func_op(result_type, id, ptr, comp, val, op);
+ flush_all_atomic_capable_variables();
+ break;
+ }
+
+ case OpAtomicLoad:
+ flush_all_atomic_capable_variables();
+ // FIXME: Image?
+ // OpAtomicLoad seems to only be relevant for atomic counters.
+ GLSL_UFOP(atomicCounter);
+ register_read(ops[1], ops[2], should_forward(ops[2]));
+ break;
+
+ case OpAtomicStore:
+ SPIRV_CROSS_THROW("Unsupported opcode OpAtomicStore.");
+
+ case OpAtomicIIncrement:
+ case OpAtomicIDecrement:
+ {
+ forced_temporaries.insert(ops[1]);
+ auto &type = expression_type(ops[2]);
+ if (type.storage == StorageClassAtomicCounter)
+ {
+ // Legacy GLSL stuff, not sure if this is relevant to support.
+ if (opcode == OpAtomicIIncrement)
+ GLSL_UFOP(atomicCounterIncrement);
+ else
+ GLSL_UFOP(atomicCounterDecrement);
+ }
+ else
+ {
+ bool atomic_image = check_atomic_image(ops[2]);
+ bool unsigned_type = (type.basetype == SPIRType::UInt) ||
+ (atomic_image && get<SPIRType>(type.image.type).basetype == SPIRType::UInt);
+ const char *op = atomic_image ? "imageAtomicAdd" : "atomicAdd";
+
+ const char *increment = nullptr;
+ if (opcode == OpAtomicIIncrement && unsigned_type)
+ increment = "1u";
+ else if (opcode == OpAtomicIIncrement)
+ increment = "1";
+ else if (unsigned_type)
+ increment = "uint(-1)";
+ else
+ increment = "-1";
+
+ emit_op(ops[0], ops[1], join(op, "(", to_expression(ops[2]), ", ", increment, ")"), false);
+ }
+
+ flush_all_atomic_capable_variables();
+ register_read(ops[1], ops[2], should_forward(ops[2]));
+ break;
+ }
+
+ case OpAtomicIAdd:
+ {
+ const char *op = check_atomic_image(ops[2]) ? "imageAtomicAdd" : "atomicAdd";
+ forced_temporaries.insert(ops[1]);
+ emit_binary_func_op(ops[0], ops[1], ops[2], ops[5], op);
+ flush_all_atomic_capable_variables();
+ register_read(ops[1], ops[2], should_forward(ops[2]));
+ break;
+ }
+
+ case OpAtomicISub:
+ {
+ const char *op = check_atomic_image(ops[2]) ? "imageAtomicAdd" : "atomicAdd";
+ forced_temporaries.insert(ops[1]);
+ auto expr = join(op, "(", to_expression(ops[2]), ", -", to_enclosed_expression(ops[5]), ")");
+ emit_op(ops[0], ops[1], expr, should_forward(ops[2]) && should_forward(ops[5]));
+ flush_all_atomic_capable_variables();
+ register_read(ops[1], ops[2], should_forward(ops[2]));
+ break;
+ }
+
+ case OpAtomicSMin:
+ case OpAtomicUMin:
+ {
+ const char *op = check_atomic_image(ops[2]) ? "imageAtomicMin" : "atomicMin";
+ forced_temporaries.insert(ops[1]);
+ emit_binary_func_op(ops[0], ops[1], ops[2], ops[5], op);
+ flush_all_atomic_capable_variables();
+ register_read(ops[1], ops[2], should_forward(ops[2]));
+ break;
+ }
+
+ case OpAtomicSMax:
+ case OpAtomicUMax:
+ {
+ const char *op = check_atomic_image(ops[2]) ? "imageAtomicMax" : "atomicMax";
+ forced_temporaries.insert(ops[1]);
+ emit_binary_func_op(ops[0], ops[1], ops[2], ops[5], op);
+ flush_all_atomic_capable_variables();
+ register_read(ops[1], ops[2], should_forward(ops[2]));
+ break;
+ }
+
+ case OpAtomicAnd:
+ {
+ const char *op = check_atomic_image(ops[2]) ? "imageAtomicAnd" : "atomicAnd";
+ forced_temporaries.insert(ops[1]);
+ emit_binary_func_op(ops[0], ops[1], ops[2], ops[5], op);
+ flush_all_atomic_capable_variables();
+ register_read(ops[1], ops[2], should_forward(ops[2]));
+ break;
+ }
+
+ case OpAtomicOr:
+ {
+ const char *op = check_atomic_image(ops[2]) ? "imageAtomicOr" : "atomicOr";
+ forced_temporaries.insert(ops[1]);
+ emit_binary_func_op(ops[0], ops[1], ops[2], ops[5], op);
+ flush_all_atomic_capable_variables();
+ register_read(ops[1], ops[2], should_forward(ops[2]));
+ break;
+ }
+
+ case OpAtomicXor:
+ {
+ const char *op = check_atomic_image(ops[2]) ? "imageAtomicXor" : "atomicXor";
+ forced_temporaries.insert(ops[1]);
+ emit_binary_func_op(ops[0], ops[1], ops[2], ops[5], op);
+ flush_all_atomic_capable_variables();
+ register_read(ops[1], ops[2], should_forward(ops[2]));
+ break;
+ }
+
+ // Geometry shaders
+ case OpEmitVertex:
+ statement("EmitVertex();");
+ break;
+
+ case OpEndPrimitive:
+ statement("EndPrimitive();");
+ break;
+
+ case OpEmitStreamVertex:
+ statement("EmitStreamVertex();");
+ break;
+
+ case OpEndStreamPrimitive:
+ statement("EndStreamPrimitive();");
+ break;
+
+ // Textures
+ case OpImageSampleExplicitLod:
+ case OpImageSampleProjExplicitLod:
+ case OpImageSampleDrefExplicitLod:
+ case OpImageSampleProjDrefExplicitLod:
+ case OpImageSampleImplicitLod:
+ case OpImageSampleProjImplicitLod:
+ case OpImageSampleDrefImplicitLod:
+ case OpImageSampleProjDrefImplicitLod:
+ case OpImageFetch:
+ case OpImageGather:
+ case OpImageDrefGather:
+ // Gets a bit hairy, so move this to a separate instruction.
+ emit_texture_op(instruction);
+ break;
+
+ case OpImage:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+
+ // Suppress usage tracking.
+ auto &e = emit_op(result_type, id, to_expression(ops[2]), true, true);
+
+ // When using the image, we need to know which variable it is actually loaded from.
+ auto *var = maybe_get_backing_variable(ops[2]);
+ e.loaded_from = var ? var->self : 0;
+ break;
+ }
+
+ case OpImageQueryLod:
+ {
+ if (!options.es && options.version < 400)
+ {
+ require_extension_internal("GL_ARB_texture_query_lod");
+ // For some reason, the ARB spec is all-caps.
+ GLSL_BFOP(textureQueryLOD);
+ }
+ else if (options.es)
+ SPIRV_CROSS_THROW("textureQueryLod not supported in ES profile.");
+ else
+ GLSL_BFOP(textureQueryLod);
+ register_control_dependent_expression(ops[1]);
+ break;
+ }
+
+ case OpImageQueryLevels:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+
+ if (!options.es && options.version < 430)
+ require_extension_internal("GL_ARB_texture_query_levels");
+ if (options.es)
+ SPIRV_CROSS_THROW("textureQueryLevels not supported in ES profile.");
+
+ auto expr = join("textureQueryLevels(", convert_separate_image_to_expression(ops[2]), ")");
+ auto &restype = get<SPIRType>(ops[0]);
+ expr = bitcast_expression(restype, SPIRType::Int, expr);
+ emit_op(result_type, id, expr, true);
+ break;
+ }
+
+ case OpImageQuerySamples:
+ {
+ auto &type = expression_type(ops[2]);
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+
+ string expr;
+ if (type.image.sampled == 2)
+ expr = join("imageSamples(", to_expression(ops[2]), ")");
+ else
+ expr = join("textureSamples(", convert_separate_image_to_expression(ops[2]), ")");
+
+ auto &restype = get<SPIRType>(ops[0]);
+ expr = bitcast_expression(restype, SPIRType::Int, expr);
+ emit_op(result_type, id, expr, true);
+ break;
+ }
+
+ case OpSampledImage:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ emit_sampled_image_op(result_type, id, ops[2], ops[3]);
+ break;
+ }
+
+ case OpImageQuerySizeLod:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+
+ auto expr = join("textureSize(", convert_separate_image_to_expression(ops[2]), ", ",
+ bitcast_expression(SPIRType::Int, ops[3]), ")");
+ auto &restype = get<SPIRType>(ops[0]);
+ expr = bitcast_expression(restype, SPIRType::Int, expr);
+ emit_op(result_type, id, expr, true);
+ break;
+ }
+
+ // Image load/store
+ case OpImageRead:
+ {
+ // We added Nonreadable speculatively to the OpImage variable due to glslangValidator
+ // not adding the proper qualifiers.
+ // If it turns out we need to read the image after all, remove the qualifier and recompile.
+ auto *var = maybe_get_backing_variable(ops[2]);
+ if (var)
+ {
+ auto &flags = ir.meta[var->self].decoration.decoration_flags;
+ if (flags.get(DecorationNonReadable))
+ {
+ flags.clear(DecorationNonReadable);
+ force_recompile = true;
+ }
+ }
+
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+
+ bool pure;
+ string imgexpr;
+ auto &type = expression_type(ops[2]);
+
+ if (var && var->remapped_variable) // Remapped input, just read as-is without any op-code
+ {
+ if (type.image.ms)
+ SPIRV_CROSS_THROW("Trying to remap multisampled image to variable, this is not possible.");
+
+ auto itr =
+ find_if(begin(pls_inputs), end(pls_inputs), [var](const PlsRemap &pls) { return pls.id == var->self; });
+
+ if (itr == end(pls_inputs))
+ {
+ // For non-PLS inputs, we rely on subpass type remapping information to get it right
+ // since ImageRead always returns 4-component vectors and the backing type is opaque.
+ if (!var->remapped_components)
+ SPIRV_CROSS_THROW("subpassInput was remapped, but remap_components is not set correctly.");
+ imgexpr = remap_swizzle(get<SPIRType>(result_type), var->remapped_components, to_expression(ops[2]));
+ }
+ else
+ {
+ // PLS input could have different number of components than what the SPIR expects, swizzle to
+ // the appropriate vector size.
+ uint32_t components = pls_format_to_components(itr->format);
+ imgexpr = remap_swizzle(get<SPIRType>(result_type), components, to_expression(ops[2]));
+ }
+ pure = true;
+ }
+ else if (type.image.dim == DimSubpassData)
+ {
+ if (options.vulkan_semantics)
+ {
+ // With Vulkan semantics, use the proper Vulkan GLSL construct.
+ if (type.image.ms)
+ {
+ uint32_t operands = ops[4];
+ if (operands != ImageOperandsSampleMask || length != 6)
+ SPIRV_CROSS_THROW(
+ "Multisampled image used in OpImageRead, but unexpected operand mask was used.");
+
+ uint32_t samples = ops[5];
+ imgexpr = join("subpassLoad(", to_expression(ops[2]), ", ", to_expression(samples), ")");
+ }
+ else
+ imgexpr = join("subpassLoad(", to_expression(ops[2]), ")");
+ }
+ else
+ {
+ if (type.image.ms)
+ {
+ uint32_t operands = ops[4];
+ if (operands != ImageOperandsSampleMask || length != 6)
+ SPIRV_CROSS_THROW(
+ "Multisampled image used in OpImageRead, but unexpected operand mask was used.");
+
+ uint32_t samples = ops[5];
+ imgexpr = join("texelFetch(", to_expression(ops[2]), ", ivec2(gl_FragCoord.xy), ",
+ to_expression(samples), ")");
+ }
+ else
+ {
+ // Implement subpass loads via texture barrier style sampling.
+ imgexpr = join("texelFetch(", to_expression(ops[2]), ", ivec2(gl_FragCoord.xy), 0)");
+ }
+ }
+ imgexpr = remap_swizzle(get<SPIRType>(result_type), 4, imgexpr);
+ pure = true;
+ }
+ else
+ {
+ // imageLoad only accepts int coords, not uint.
+ auto coord_expr = to_expression(ops[3]);
+ auto target_coord_type = expression_type(ops[3]);
+ target_coord_type.basetype = SPIRType::Int;
+ coord_expr = bitcast_expression(target_coord_type, expression_type(ops[3]).basetype, coord_expr);
+
+ // Plain image load/store.
+ if (type.image.ms)
+ {
+ uint32_t operands = ops[4];
+ if (operands != ImageOperandsSampleMask || length != 6)
+ SPIRV_CROSS_THROW("Multisampled image used in OpImageRead, but unexpected operand mask was used.");
+
+ uint32_t samples = ops[5];
+ imgexpr =
+ join("imageLoad(", to_expression(ops[2]), ", ", coord_expr, ", ", to_expression(samples), ")");
+ }
+ else
+ imgexpr = join("imageLoad(", to_expression(ops[2]), ", ", coord_expr, ")");
+
+ imgexpr = remap_swizzle(get<SPIRType>(result_type), 4, imgexpr);
+ pure = false;
+ }
+
+ if (var && var->forwardable)
+ {
+ bool forward = forced_temporaries.find(id) == end(forced_temporaries);
+ auto &e = emit_op(result_type, id, imgexpr, forward);
+
+ // We only need to track dependencies if we're reading from image load/store.
+ if (!pure)
+ {
+ e.loaded_from = var->self;
+ if (forward)
+ var->dependees.push_back(id);
+ }
+ }
+ else
+ emit_op(result_type, id, imgexpr, false);
+
+ inherit_expression_dependencies(id, ops[2]);
+ if (type.image.ms)
+ inherit_expression_dependencies(id, ops[5]);
+ break;
+ }
+
+ case OpImageTexelPointer:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ auto &e = set<SPIRExpression>(id, join(to_expression(ops[2]), ", ", to_expression(ops[3])), result_type, true);
+
+ // When using the pointer, we need to know which variable it is actually loaded from.
+ auto *var = maybe_get_backing_variable(ops[2]);
+ e.loaded_from = var ? var->self : 0;
+ break;
+ }
+
+ case OpImageWrite:
+ {
+ // We added Nonwritable speculatively to the OpImage variable due to glslangValidator
+ // not adding the proper qualifiers.
+ // If it turns out we need to write to the image after all, remove the qualifier and recompile.
+ auto *var = maybe_get_backing_variable(ops[0]);
+ if (var)
+ {
+ auto &flags = ir.meta[var->self].decoration.decoration_flags;
+ if (flags.get(DecorationNonWritable))
+ {
+ flags.clear(DecorationNonWritable);
+ force_recompile = true;
+ }
+ }
+
+ auto &type = expression_type(ops[0]);
+ auto &value_type = expression_type(ops[2]);
+ auto store_type = value_type;
+ store_type.vecsize = 4;
+
+ // imageStore only accepts int coords, not uint.
+ auto coord_expr = to_expression(ops[1]);
+ auto target_coord_type = expression_type(ops[1]);
+ target_coord_type.basetype = SPIRType::Int;
+ coord_expr = bitcast_expression(target_coord_type, expression_type(ops[1]).basetype, coord_expr);
+
+ if (type.image.ms)
+ {
+ uint32_t operands = ops[3];
+ if (operands != ImageOperandsSampleMask || length != 5)
+ SPIRV_CROSS_THROW("Multisampled image used in OpImageWrite, but unexpected operand mask was used.");
+ uint32_t samples = ops[4];
+ statement("imageStore(", to_expression(ops[0]), ", ", coord_expr, ", ", to_expression(samples), ", ",
+ remap_swizzle(store_type, value_type.vecsize, to_expression(ops[2])), ");");
+ }
+ else
+ statement("imageStore(", to_expression(ops[0]), ", ", coord_expr, ", ",
+ remap_swizzle(store_type, value_type.vecsize, to_expression(ops[2])), ");");
+
+ if (var && variable_storage_is_aliased(*var))
+ flush_all_aliased_variables();
+ break;
+ }
+
+ case OpImageQuerySize:
+ {
+ auto &type = expression_type(ops[2]);
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+
+ if (type.basetype == SPIRType::Image)
+ {
+ string expr;
+ if (type.image.sampled == 2)
+ {
+ // The size of an image is always constant.
+ expr = join("imageSize(", to_expression(ops[2]), ")");
+ }
+ else
+ {
+ // This path is hit for samplerBuffers and multisampled images which do not have LOD.
+ expr = join("textureSize(", convert_separate_image_to_expression(ops[2]), ")");
+ }
+
+ auto &restype = get<SPIRType>(ops[0]);
+ expr = bitcast_expression(restype, SPIRType::Int, expr);
+ emit_op(result_type, id, expr, true);
+ }
+ else
+ SPIRV_CROSS_THROW("Invalid type for OpImageQuerySize.");
+ break;
+ }
+
+ // Compute
+ case OpControlBarrier:
+ case OpMemoryBarrier:
+ {
+ uint32_t execution_scope = 0;
+ uint32_t memory;
+ uint32_t semantics;
+
+ if (opcode == OpMemoryBarrier)
+ {
+ memory = get<SPIRConstant>(ops[0]).scalar();
+ semantics = get<SPIRConstant>(ops[1]).scalar();
+ }
+ else
+ {
+ execution_scope = get<SPIRConstant>(ops[0]).scalar();
+ memory = get<SPIRConstant>(ops[1]).scalar();
+ semantics = get<SPIRConstant>(ops[2]).scalar();
+ }
+
+ if (execution_scope == ScopeSubgroup || memory == ScopeSubgroup)
+ {
+ if (!options.vulkan_semantics)
+ SPIRV_CROSS_THROW("Can only use subgroup operations in Vulkan semantics.");
+ require_extension_internal("GL_KHR_shader_subgroup_basic");
+ }
+
+ if (execution_scope != ScopeSubgroup && get_entry_point().model == ExecutionModelTessellationControl)
+ {
+ // Control shaders only have barriers, and it implies memory barriers.
+ if (opcode == OpControlBarrier)
+ statement("barrier();");
+ break;
+ }
+
+ // We only care about these flags, acquire/release and friends are not relevant to GLSL.
+ semantics = mask_relevant_memory_semantics(semantics);
+
+ if (opcode == OpMemoryBarrier)
+ {
+ // If we are a memory barrier, and the next instruction is a control barrier, check if that memory barrier
+ // does what we need, so we avoid redundant barriers.
+ const Instruction *next = get_next_instruction_in_block(instruction);
+ if (next && next->op == OpControlBarrier)
+ {
+ auto *next_ops = stream(*next);
+ uint32_t next_memory = get<SPIRConstant>(next_ops[1]).scalar();
+ uint32_t next_semantics = get<SPIRConstant>(next_ops[2]).scalar();
+ next_semantics = mask_relevant_memory_semantics(next_semantics);
+
+ bool memory_scope_covered = false;
+ if (next_memory == memory)
+ memory_scope_covered = true;
+ else if (next_semantics == MemorySemanticsWorkgroupMemoryMask)
+ {
+ // If we only care about workgroup memory, either Device or Workgroup scope is fine,
+ // scope does not have to match.
+ if ((next_memory == ScopeDevice || next_memory == ScopeWorkgroup) &&
+ (memory == ScopeDevice || memory == ScopeWorkgroup))
+ {
+ memory_scope_covered = true;
+ }
+ }
+ else if (memory == ScopeWorkgroup && next_memory == ScopeDevice)
+ {
+ // The control barrier has device scope, but the memory barrier just has workgroup scope.
+ memory_scope_covered = true;
+ }
+
+ // If we have the same memory scope, and all memory types are covered, we're good.
+ if (memory_scope_covered && (semantics & next_semantics) == semantics)
+ break;
+ }
+ }
+
+ // We are synchronizing some memory or syncing execution,
+ // so we cannot forward any loads beyond the memory barrier.
+ if (semantics || opcode == OpControlBarrier)
+ {
+ assert(current_emitting_block);
+ flush_control_dependent_expressions(current_emitting_block->self);
+ flush_all_active_variables();
+ }
+
+ if (memory == ScopeWorkgroup) // Only need to consider memory within a group
+ {
+ if (semantics == MemorySemanticsWorkgroupMemoryMask)
+ statement("memoryBarrierShared();");
+ else if (semantics != 0)
+ statement("groupMemoryBarrier();");
+ }
+ else if (memory == ScopeSubgroup)
+ {
+ const uint32_t all_barriers =
+ MemorySemanticsWorkgroupMemoryMask | MemorySemanticsUniformMemoryMask | MemorySemanticsImageMemoryMask;
+
+ if (semantics & (MemorySemanticsCrossWorkgroupMemoryMask | MemorySemanticsSubgroupMemoryMask))
+ {
+ // These are not relevant for GLSL, but assume it means memoryBarrier().
+ // memoryBarrier() does everything, so no need to test anything else.
+ statement("subgroupMemoryBarrier();");
+ }
+ else if ((semantics & all_barriers) == all_barriers)
+ {
+ // Short-hand instead of emitting 3 barriers.
+ statement("subgroupMemoryBarrier();");
+ }
+ else
+ {
+ // Pick out individual barriers.
+ if (semantics & MemorySemanticsWorkgroupMemoryMask)
+ statement("subgroupMemoryBarrierShared();");
+ if (semantics & MemorySemanticsUniformMemoryMask)
+ statement("subgroupMemoryBarrierBuffer();");
+ if (semantics & MemorySemanticsImageMemoryMask)
+ statement("subgroupMemoryBarrierImage();");
+ }
+ }
+ else
+ {
+ const uint32_t all_barriers = MemorySemanticsWorkgroupMemoryMask | MemorySemanticsUniformMemoryMask |
+ MemorySemanticsImageMemoryMask | MemorySemanticsAtomicCounterMemoryMask;
+
+ if (semantics & (MemorySemanticsCrossWorkgroupMemoryMask | MemorySemanticsSubgroupMemoryMask))
+ {
+ // These are not relevant for GLSL, but assume it means memoryBarrier().
+ // memoryBarrier() does everything, so no need to test anything else.
+ statement("memoryBarrier();");
+ }
+ else if ((semantics & all_barriers) == all_barriers)
+ {
+ // Short-hand instead of emitting 4 barriers.
+ statement("memoryBarrier();");
+ }
+ else
+ {
+ // Pick out individual barriers.
+ if (semantics & MemorySemanticsWorkgroupMemoryMask)
+ statement("memoryBarrierShared();");
+ if (semantics & MemorySemanticsUniformMemoryMask)
+ statement("memoryBarrierBuffer();");
+ if (semantics & MemorySemanticsImageMemoryMask)
+ statement("memoryBarrierImage();");
+ if (semantics & MemorySemanticsAtomicCounterMemoryMask)
+ statement("memoryBarrierAtomicCounter();");
+ }
+ }
+
+ if (opcode == OpControlBarrier)
+ {
+ if (execution_scope == ScopeSubgroup)
+ statement("subgroupBarrier();");
+ else
+ statement("barrier();");
+ }
+ break;
+ }
+
+ case OpExtInst:
+ {
+ uint32_t extension_set = ops[2];
+
+ if (get<SPIRExtension>(extension_set).ext == SPIRExtension::GLSL)
+ {
+ emit_glsl_op(ops[0], ops[1], ops[3], &ops[4], length - 4);
+ }
+ else if (get<SPIRExtension>(extension_set).ext == SPIRExtension::SPV_AMD_shader_ballot)
+ {
+ emit_spv_amd_shader_ballot_op(ops[0], ops[1], ops[3], &ops[4], length - 4);
+ }
+ else if (get<SPIRExtension>(extension_set).ext == SPIRExtension::SPV_AMD_shader_explicit_vertex_parameter)
+ {
+ emit_spv_amd_shader_explicit_vertex_parameter_op(ops[0], ops[1], ops[3], &ops[4], length - 4);
+ }
+ else if (get<SPIRExtension>(extension_set).ext == SPIRExtension::SPV_AMD_shader_trinary_minmax)
+ {
+ emit_spv_amd_shader_trinary_minmax_op(ops[0], ops[1], ops[3], &ops[4], length - 4);
+ }
+ else if (get<SPIRExtension>(extension_set).ext == SPIRExtension::SPV_AMD_gcn_shader)
+ {
+ emit_spv_amd_gcn_shader_op(ops[0], ops[1], ops[3], &ops[4], length - 4);
+ }
+ else
+ {
+ statement("// unimplemented ext op ", instruction.op);
+ break;
+ }
+
+ break;
+ }
+
+ // Legacy sub-group stuff ...
+ case OpSubgroupBallotKHR:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ string expr;
+ expr = join("uvec4(unpackUint2x32(ballotARB(" + to_expression(ops[2]) + ")), 0u, 0u)");
+ emit_op(result_type, id, expr, should_forward(ops[2]));
+
+ require_extension_internal("GL_ARB_shader_ballot");
+ inherit_expression_dependencies(id, ops[2]);
+ register_control_dependent_expression(ops[1]);
+ break;
+ }
+
+ case OpSubgroupFirstInvocationKHR:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ emit_unary_func_op(result_type, id, ops[2], "readFirstInvocationARB");
+
+ require_extension_internal("GL_ARB_shader_ballot");
+ register_control_dependent_expression(ops[1]);
+ break;
+ }
+
+ case OpSubgroupReadInvocationKHR:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ emit_binary_func_op(result_type, id, ops[2], ops[3], "readInvocationARB");
+
+ require_extension_internal("GL_ARB_shader_ballot");
+ register_control_dependent_expression(ops[1]);
+ break;
+ }
+
+ case OpSubgroupAllKHR:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ emit_unary_func_op(result_type, id, ops[2], "allInvocationsARB");
+
+ require_extension_internal("GL_ARB_shader_group_vote");
+ register_control_dependent_expression(ops[1]);
+ break;
+ }
+
+ case OpSubgroupAnyKHR:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ emit_unary_func_op(result_type, id, ops[2], "anyInvocationARB");
+
+ require_extension_internal("GL_ARB_shader_group_vote");
+ register_control_dependent_expression(ops[1]);
+ break;
+ }
+
+ case OpSubgroupAllEqualKHR:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ emit_unary_func_op(result_type, id, ops[2], "allInvocationsEqualARB");
+
+ require_extension_internal("GL_ARB_shader_group_vote");
+ register_control_dependent_expression(ops[1]);
+ break;
+ }
+
+ case OpGroupIAddNonUniformAMD:
+ case OpGroupFAddNonUniformAMD:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ emit_unary_func_op(result_type, id, ops[4], "addInvocationsNonUniformAMD");
+
+ require_extension_internal("GL_AMD_shader_ballot");
+ register_control_dependent_expression(ops[1]);
+ break;
+ }
+
+ case OpGroupFMinNonUniformAMD:
+ case OpGroupUMinNonUniformAMD:
+ case OpGroupSMinNonUniformAMD:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ emit_unary_func_op(result_type, id, ops[4], "minInvocationsNonUniformAMD");
+
+ require_extension_internal("GL_AMD_shader_ballot");
+ register_control_dependent_expression(ops[1]);
+ break;
+ }
+
+ case OpGroupFMaxNonUniformAMD:
+ case OpGroupUMaxNonUniformAMD:
+ case OpGroupSMaxNonUniformAMD:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ emit_unary_func_op(result_type, id, ops[4], "maxInvocationsNonUniformAMD");
+
+ require_extension_internal("GL_AMD_shader_ballot");
+ register_control_dependent_expression(ops[1]);
+ break;
+ }
+
+ case OpFragmentMaskFetchAMD:
+ {
+ auto &type = expression_type(ops[2]);
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+
+ if (type.image.dim == spv::DimSubpassData)
+ {
+ emit_unary_func_op(result_type, id, ops[2], "fragmentMaskFetchAMD");
+ }
+ else
+ {
+ emit_binary_func_op(result_type, id, ops[2], ops[3], "fragmentMaskFetchAMD");
+ }
+
+ require_extension_internal("GL_AMD_shader_fragment_mask");
+ break;
+ }
+
+ case OpFragmentFetchAMD:
+ {
+ auto &type = expression_type(ops[2]);
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+
+ if (type.image.dim == spv::DimSubpassData)
+ {
+ emit_binary_func_op(result_type, id, ops[2], ops[4], "fragmentFetchAMD");
+ }
+ else
+ {
+ emit_trinary_func_op(result_type, id, ops[2], ops[3], ops[4], "fragmentFetchAMD");
+ }
+
+ require_extension_internal("GL_AMD_shader_fragment_mask");
+ break;
+ }
+
+ // Vulkan 1.1 sub-group stuff ...
+ case OpGroupNonUniformElect:
+ case OpGroupNonUniformBroadcast:
+ case OpGroupNonUniformBroadcastFirst:
+ case OpGroupNonUniformBallot:
+ case OpGroupNonUniformInverseBallot:
+ case OpGroupNonUniformBallotBitExtract:
+ case OpGroupNonUniformBallotBitCount:
+ case OpGroupNonUniformBallotFindLSB:
+ case OpGroupNonUniformBallotFindMSB:
+ case OpGroupNonUniformShuffle:
+ case OpGroupNonUniformShuffleXor:
+ case OpGroupNonUniformShuffleUp:
+ case OpGroupNonUniformShuffleDown:
+ case OpGroupNonUniformAll:
+ case OpGroupNonUniformAny:
+ case OpGroupNonUniformAllEqual:
+ case OpGroupNonUniformFAdd:
+ case OpGroupNonUniformIAdd:
+ case OpGroupNonUniformFMul:
+ case OpGroupNonUniformIMul:
+ case OpGroupNonUniformFMin:
+ case OpGroupNonUniformFMax:
+ case OpGroupNonUniformSMin:
+ case OpGroupNonUniformSMax:
+ case OpGroupNonUniformUMin:
+ case OpGroupNonUniformUMax:
+ case OpGroupNonUniformBitwiseAnd:
+ case OpGroupNonUniformBitwiseOr:
+ case OpGroupNonUniformBitwiseXor:
+ case OpGroupNonUniformQuadSwap:
+ case OpGroupNonUniformQuadBroadcast:
+ emit_subgroup_op(instruction);
+ break;
+
+ case OpFUnordEqual:
+ GLSL_BFOP(unsupported_FUnordEqual);
+ break;
+
+ case OpFUnordNotEqual:
+ GLSL_BFOP(unsupported_FUnordNotEqual);
+ break;
+
+ case OpFUnordLessThan:
+ GLSL_BFOP(unsupported_FUnordLessThan);
+ break;
+
+ case OpFUnordGreaterThan:
+ GLSL_BFOP(unsupported_FUnordGreaterThan);
+ break;
+
+ case OpFUnordLessThanEqual:
+ GLSL_BFOP(unsupported_FUnordLessThanEqual);
+ break;
+
+ case OpFUnordGreaterThanEqual:
+ GLSL_BFOP(unsupported_FUnordGreaterThanEqual);
+ break;
+
+ case OpReportIntersectionNV:
+ statement("reportIntersectionNV(", to_expression(ops[0]), ", ", to_expression(ops[1]), ");");
+ break;
+ case OpIgnoreIntersectionNV:
+ statement("ignoreIntersectionNV();");
+ break;
+ case OpTerminateRayNV:
+ statement("terminateRayNV();");
+ break;
+ case OpTraceNV:
+ statement("traceNV(", to_expression(ops[0]), ", ", to_expression(ops[1]), ", ", to_expression(ops[2]), ", ",
+ to_expression(ops[3]), ", ", to_expression(ops[4]), ", ", to_expression(ops[5]), ", ",
+ to_expression(ops[6]), ", ", to_expression(ops[7]), ", ", to_expression(ops[8]), ", ",
+ to_expression(ops[9]), ", ", to_expression(ops[10]), ");");
+ break;
+ case OpExecuteCallableNV:
+ statement("executeCallableNV(", to_expression(ops[0]), ", ", to_expression(ops[1]), ");");
+ break;
+
+ default:
+ statement("// unimplemented op ", instruction.op);
+ break;
+ }
+}
+
+// Appends function arguments, mapped from global variables, beyond the specified arg index.
+// This is used when a function call uses fewer arguments than the function defines.
+// This situation may occur if the function signature has been dynamically modified to
+// extract global variables referenced from within the function, and convert them to
+// function arguments. This is necessary for shader languages that do not support global
+// access to shader input content from within a function (eg. Metal). Each additional
+// function args uses the name of the global variable. Function nesting will modify the
+// functions and function calls all the way up the nesting chain.
+void CompilerGLSL::append_global_func_args(const SPIRFunction &func, uint32_t index, vector<string> &arglist)
+{
+ auto &args = func.arguments;
+ uint32_t arg_cnt = uint32_t(args.size());
+ for (uint32_t arg_idx = index; arg_idx < arg_cnt; arg_idx++)
+ {
+ auto &arg = args[arg_idx];
+ assert(arg.alias_global_variable);
+
+ // If the underlying variable needs to be declared
+ // (ie. a local variable with deferred declaration), do so now.
+ uint32_t var_id = get<SPIRVariable>(arg.id).basevariable;
+ if (var_id)
+ flush_variable_declaration(var_id);
+
+ arglist.push_back(to_func_call_arg(arg.id));
+ }
+}
+
+string CompilerGLSL::to_member_name(const SPIRType &type, uint32_t index)
+{
+ auto &memb = ir.meta[type.self].members;
+ if (index < memb.size() && !memb[index].alias.empty())
+ return memb[index].alias;
+ else
+ return join("_m", index);
+}
+
+string CompilerGLSL::to_member_reference(uint32_t, const SPIRType &type, uint32_t index, bool)
+{
+ return join(".", to_member_name(type, index));
+}
+
+void CompilerGLSL::add_member_name(SPIRType &type, uint32_t index)
+{
+ auto &memb = ir.meta[type.self].members;
+ if (index < memb.size() && !memb[index].alias.empty())
+ {
+ auto &name = memb[index].alias;
+ if (name.empty())
+ return;
+
+ // Reserved for temporaries.
+ if (name[0] == '_' && name.size() >= 2 && isdigit(name[1]))
+ {
+ name.clear();
+ return;
+ }
+
+ update_name_cache(type.member_name_cache, name);
+ }
+}
+
+// Checks whether the ID is a row_major matrix that requires conversion before use
+bool CompilerGLSL::is_non_native_row_major_matrix(uint32_t id)
+{
+ // Natively supported row-major matrices do not need to be converted.
+ // Legacy targets do not support row major.
+ if (backend.native_row_major_matrix && !is_legacy())
+ return false;
+
+ // Non-matrix or column-major matrix types do not need to be converted.
+ if (!has_decoration(id, DecorationRowMajor))
+ return false;
+
+ // Only square row-major matrices can be converted at this time.
+ // Converting non-square matrices will require defining custom GLSL function that
+ // swaps matrix elements while retaining the original dimensional form of the matrix.
+ const auto type = expression_type(id);
+ if (type.columns != type.vecsize)
+ SPIRV_CROSS_THROW("Row-major matrices must be square on this platform.");
+
+ return true;
+}
+
+// Checks whether the member is a row_major matrix that requires conversion before use
+bool CompilerGLSL::member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index)
+{
+ // Natively supported row-major matrices do not need to be converted.
+ if (backend.native_row_major_matrix && !is_legacy())
+ return false;
+
+ // Non-matrix or column-major matrix types do not need to be converted.
+ if (!has_member_decoration(type.self, index, DecorationRowMajor))
+ return false;
+
+ // Only square row-major matrices can be converted at this time.
+ // Converting non-square matrices will require defining custom GLSL function that
+ // swaps matrix elements while retaining the original dimensional form of the matrix.
+ const auto mbr_type = get<SPIRType>(type.member_types[index]);
+ if (mbr_type.columns != mbr_type.vecsize)
+ SPIRV_CROSS_THROW("Row-major matrices must be square on this platform.");
+
+ return true;
+}
+
+// Checks whether the member is in packed data type, that might need to be unpacked.
+// GLSL does not define packed data types, but certain subclasses do.
+bool CompilerGLSL::member_is_packed_type(const SPIRType &type, uint32_t index) const
+{
+ return has_extended_member_decoration(type.self, index, SPIRVCrossDecorationPacked);
+}
+
+// Wraps the expression string in a function call that converts the
+// row_major matrix result of the expression to a column_major matrix.
+// Base implementation uses the standard library transpose() function.
+// Subclasses may override to use a different function.
+string CompilerGLSL::convert_row_major_matrix(string exp_str, const SPIRType & /*exp_type*/, bool /*is_packed*/)
+{
+ strip_enclosed_expression(exp_str);
+ return join("transpose(", exp_str, ")");
+}
+
+string CompilerGLSL::variable_decl(const SPIRType &type, const string &name, uint32_t id)
+{
+ string type_name = type_to_glsl(type, id);
+ remap_variable_type_name(type, name, type_name);
+ return join(type_name, " ", name, type_to_array_glsl(type));
+}
+
+// Emit a structure member. Subclasses may override to modify output,
+// or to dynamically add a padding member if needed.
+void CompilerGLSL::emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index,
+ const string &qualifier, uint32_t)
+{
+ auto &membertype = get<SPIRType>(member_type_id);
+
+ Bitset memberflags;
+ auto &memb = ir.meta[type.self].members;
+ if (index < memb.size())
+ memberflags = memb[index].decoration_flags;
+
+ string qualifiers;
+ bool is_block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) ||
+ ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock);
+
+ if (is_block)
+ qualifiers = to_interpolation_qualifiers(memberflags);
+
+ statement(layout_for_member(type, index), qualifiers, qualifier,
+ flags_to_precision_qualifiers_glsl(membertype, memberflags),
+ variable_decl(membertype, to_member_name(type, index)), ";");
+}
+
+const char *CompilerGLSL::flags_to_precision_qualifiers_glsl(const SPIRType &type, const Bitset &flags)
+{
+ // Structs do not have precision qualifiers, neither do doubles (desktop only anyways, so no mediump/highp).
+ if (type.basetype != SPIRType::Float && type.basetype != SPIRType::Int && type.basetype != SPIRType::UInt &&
+ type.basetype != SPIRType::Image && type.basetype != SPIRType::SampledImage &&
+ type.basetype != SPIRType::Sampler)
+ return "";
+
+ if (options.es)
+ {
+ auto &execution = get_entry_point();
+
+ if (flags.get(DecorationRelaxedPrecision))
+ {
+ bool implied_fmediump = type.basetype == SPIRType::Float &&
+ options.fragment.default_float_precision == Options::Mediump &&
+ execution.model == ExecutionModelFragment;
+
+ bool implied_imediump = (type.basetype == SPIRType::Int || type.basetype == SPIRType::UInt) &&
+ options.fragment.default_int_precision == Options::Mediump &&
+ execution.model == ExecutionModelFragment;
+
+ return implied_fmediump || implied_imediump ? "" : "mediump ";
+ }
+ else
+ {
+ bool implied_fhighp =
+ type.basetype == SPIRType::Float && ((options.fragment.default_float_precision == Options::Highp &&
+ execution.model == ExecutionModelFragment) ||
+ (execution.model != ExecutionModelFragment));
+
+ bool implied_ihighp = (type.basetype == SPIRType::Int || type.basetype == SPIRType::UInt) &&
+ ((options.fragment.default_int_precision == Options::Highp &&
+ execution.model == ExecutionModelFragment) ||
+ (execution.model != ExecutionModelFragment));
+
+ return implied_fhighp || implied_ihighp ? "" : "highp ";
+ }
+ }
+ else if (backend.allow_precision_qualifiers)
+ {
+ // Vulkan GLSL supports precision qualifiers, even in desktop profiles, which is convenient.
+ // The default is highp however, so only emit mediump in the rare case that a shader has these.
+ if (flags.get(DecorationRelaxedPrecision))
+ return "mediump ";
+ else
+ return "";
+ }
+ else
+ return "";
+}
+
+const char *CompilerGLSL::to_precision_qualifiers_glsl(uint32_t id)
+{
+ return flags_to_precision_qualifiers_glsl(expression_type(id), ir.meta[id].decoration.decoration_flags);
+}
+
+string CompilerGLSL::to_qualifiers_glsl(uint32_t id)
+{
+ auto &flags = ir.meta[id].decoration.decoration_flags;
+ string res;
+
+ auto *var = maybe_get<SPIRVariable>(id);
+
+ if (var && var->storage == StorageClassWorkgroup && !backend.shared_is_implied)
+ res += "shared ";
+
+ res += to_interpolation_qualifiers(flags);
+ if (var)
+ res += to_storage_qualifiers_glsl(*var);
+
+ auto &type = expression_type(id);
+ if (type.image.dim != DimSubpassData && type.image.sampled == 2)
+ {
+ if (flags.get(DecorationCoherent))
+ res += "coherent ";
+ if (flags.get(DecorationRestrict))
+ res += "restrict ";
+ if (flags.get(DecorationNonWritable))
+ res += "readonly ";
+ if (flags.get(DecorationNonReadable))
+ res += "writeonly ";
+ }
+
+ res += to_precision_qualifiers_glsl(id);
+
+ return res;
+}
+
+string CompilerGLSL::argument_decl(const SPIRFunction::Parameter &arg)
+{
+ // glslangValidator seems to make all arguments pointer no matter what which is rather bizarre ...
+ auto &type = expression_type(arg.id);
+ const char *direction = "";
+
+ if (type.pointer)
+ {
+ if (arg.write_count && arg.read_count)
+ direction = "inout ";
+ else if (arg.write_count)
+ direction = "out ";
+ }
+
+ return join(direction, to_qualifiers_glsl(arg.id), variable_decl(type, to_name(arg.id), arg.id));
+}
+
+string CompilerGLSL::to_initializer_expression(const SPIRVariable &var)
+{
+ return to_expression(var.initializer);
+}
+
+string CompilerGLSL::variable_decl(const SPIRVariable &variable)
+{
+ // Ignore the pointer type since GLSL doesn't have pointers.
+ auto &type = get_variable_data_type(variable);
+
+ if (type.pointer_depth > 1)
+ SPIRV_CROSS_THROW("Cannot declare pointer-to-pointer types.");
+
+ auto res = join(to_qualifiers_glsl(variable.self), variable_decl(type, to_name(variable.self), variable.self));
+
+ if (variable.loop_variable && variable.static_expression)
+ {
+ uint32_t expr = variable.static_expression;
+ if (ir.ids[expr].get_type() != TypeUndef)
+ res += join(" = ", to_expression(variable.static_expression));
+ }
+ else if (variable.initializer)
+ {
+ uint32_t expr = variable.initializer;
+ if (ir.ids[expr].get_type() != TypeUndef)
+ res += join(" = ", to_initializer_expression(variable));
+ }
+ return res;
+}
+
+const char *CompilerGLSL::to_pls_qualifiers_glsl(const SPIRVariable &variable)
+{
+ auto &flags = ir.meta[variable.self].decoration.decoration_flags;
+ if (flags.get(DecorationRelaxedPrecision))
+ return "mediump ";
+ else
+ return "highp ";
+}
+
+string CompilerGLSL::pls_decl(const PlsRemap &var)
+{
+ auto &variable = get<SPIRVariable>(var.id);
+
+ SPIRType type;
+ type.vecsize = pls_format_to_components(var.format);
+ type.basetype = pls_format_to_basetype(var.format);
+
+ return join(to_pls_layout(var.format), to_pls_qualifiers_glsl(variable), type_to_glsl(type), " ",
+ to_name(variable.self));
+}
+
+uint32_t CompilerGLSL::to_array_size_literal(const SPIRType &type) const
+{
+ return to_array_size_literal(type, uint32_t(type.array.size() - 1));
+}
+
+uint32_t CompilerGLSL::to_array_size_literal(const SPIRType &type, uint32_t index) const
+{
+ assert(type.array.size() == type.array_size_literal.size());
+
+ if (type.array_size_literal[index])
+ {
+ return type.array[index];
+ }
+ else
+ {
+ // Use the default spec constant value.
+ // This is the best we can do.
+ uint32_t array_size_id = type.array[index];
+
+ // Explicitly check for this case. The error message you would get (bad cast) makes no sense otherwise.
+ if (ir.ids[array_size_id].get_type() == TypeConstantOp)
+ SPIRV_CROSS_THROW("An array size was found to be an OpSpecConstantOp. This is not supported since "
+ "SPIRV-Cross cannot deduce the actual size here.");
+
+ uint32_t array_size = get<SPIRConstant>(array_size_id).scalar();
+ return array_size;
+ }
+}
+
+string CompilerGLSL::to_array_size(const SPIRType &type, uint32_t index)
+{
+ assert(type.array.size() == type.array_size_literal.size());
+
+ // Tessellation control and evaluation shaders must have either gl_MaxPatchVertices or unsized arrays for input arrays.
+ // Opt for unsized as it's the more "correct" variant to use.
+ if (type.storage == StorageClassInput && (get_entry_point().model == ExecutionModelTessellationControl ||
+ get_entry_point().model == ExecutionModelTessellationEvaluation))
+ return "";
+
+ auto &size = type.array[index];
+ if (!type.array_size_literal[index])
+ return to_expression(size);
+ else if (size)
+ return convert_to_string(size);
+ else if (!backend.flexible_member_array_supported)
+ {
+ // For runtime-sized arrays, we can work around
+ // lack of standard support for this by simply having
+ // a single element array.
+ //
+ // Runtime length arrays must always be the last element
+ // in an interface block.
+ return "1";
+ }
+ else
+ return "";
+}
+
+string CompilerGLSL::type_to_array_glsl(const SPIRType &type)
+{
+ if (type.array.empty())
+ return "";
+
+ if (options.flatten_multidimensional_arrays)
+ {
+ string res;
+ res += "[";
+ for (auto i = uint32_t(type.array.size()); i; i--)
+ {
+ res += enclose_expression(to_array_size(type, i - 1));
+ if (i > 1)
+ res += " * ";
+ }
+ res += "]";
+ return res;
+ }
+ else
+ {
+ if (type.array.size() > 1)
+ {
+ if (!options.es && options.version < 430)
+ require_extension_internal("GL_ARB_arrays_of_arrays");
+ else if (options.es && options.version < 310)
+ SPIRV_CROSS_THROW("Arrays of arrays not supported before ESSL version 310. "
+ "Try using --flatten-multidimensional-arrays or set "
+ "options.flatten_multidimensional_arrays to true.");
+ }
+
+ string res;
+ for (auto i = uint32_t(type.array.size()); i; i--)
+ {
+ res += "[";
+ res += to_array_size(type, i - 1);
+ res += "]";
+ }
+ return res;
+ }
+}
+
+string CompilerGLSL::image_type_glsl(const SPIRType &type, uint32_t id)
+{
+ auto &imagetype = get<SPIRType>(type.image.type);
+ string res;
+
+ switch (imagetype.basetype)
+ {
+ case SPIRType::Int:
+ res = "i";
+ break;
+ case SPIRType::UInt:
+ res = "u";
+ break;
+ default:
+ break;
+ }
+
+ if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData && options.vulkan_semantics)
+ return res + "subpassInput" + (type.image.ms ? "MS" : "");
+
+ // If we're emulating subpassInput with samplers, force sampler2D
+ // so we don't have to specify format.
+ if (type.basetype == SPIRType::Image && type.image.dim != DimSubpassData)
+ {
+ // Sampler buffers are always declared as samplerBuffer even though they might be separate images in the SPIR-V.
+ if (type.image.dim == DimBuffer && type.image.sampled == 1)
+ res += "sampler";
+ else
+ res += type.image.sampled == 2 ? "image" : "texture";
+ }
+ else
+ res += "sampler";
+
+ switch (type.image.dim)
+ {
+ case Dim1D:
+ res += "1D";
+ break;
+ case Dim2D:
+ res += "2D";
+ break;
+ case Dim3D:
+ res += "3D";
+ break;
+ case DimCube:
+ res += "Cube";
+ break;
+ case DimRect:
+ if (options.es)
+ SPIRV_CROSS_THROW("Rectangle textures are not supported on OpenGL ES.");
+
+ if (is_legacy_desktop())
+ require_extension_internal("GL_ARB_texture_rectangle");
+
+ res += "2DRect";
+ break;
+
+ case DimBuffer:
+ if (options.es && options.version < 320)
+ require_extension_internal("GL_OES_texture_buffer");
+ else if (!options.es && options.version < 300)
+ require_extension_internal("GL_EXT_texture_buffer_object");
+ res += "Buffer";
+ break;
+
+ case DimSubpassData:
+ res += "2D";
+ break;
+ default:
+ SPIRV_CROSS_THROW("Only 1D, 2D, 2DRect, 3D, Buffer, InputTarget and Cube textures supported.");
+ }
+
+ if (type.image.ms)
+ res += "MS";
+ if (type.image.arrayed)
+ {
+ if (is_legacy_desktop())
+ require_extension_internal("GL_EXT_texture_array");
+ res += "Array";
+ }
+
+ // "Shadow" state in GLSL only exists for samplers and combined image samplers.
+ if (((type.basetype == SPIRType::SampledImage) || (type.basetype == SPIRType::Sampler)) &&
+ image_is_comparison(type, id))
+ {
+ res += "Shadow";
+ }
+
+ return res;
+}
+
+string CompilerGLSL::type_to_glsl_constructor(const SPIRType &type)
+{
+ if (type.array.size() > 1)
+ {
+ if (options.flatten_multidimensional_arrays)
+ SPIRV_CROSS_THROW("Cannot flatten constructors of multidimensional array constructors, e.g. float[][]().");
+ else if (!options.es && options.version < 430)
+ require_extension_internal("GL_ARB_arrays_of_arrays");
+ else if (options.es && options.version < 310)
+ SPIRV_CROSS_THROW("Arrays of arrays not supported before ESSL version 310.");
+ }
+
+ auto e = type_to_glsl(type);
+ for (uint32_t i = 0; i < type.array.size(); i++)
+ e += "[]";
+ return e;
+}
+
+// The optional id parameter indicates the object whose type we are trying
+// to find the description for. It is optional. Most type descriptions do not
+// depend on a specific object's use of that type.
+string CompilerGLSL::type_to_glsl(const SPIRType &type, uint32_t id)
+{
+ // Ignore the pointer type since GLSL doesn't have pointers.
+
+ switch (type.basetype)
+ {
+ case SPIRType::Struct:
+ // Need OpName lookup here to get a "sensible" name for a struct.
+ if (backend.explicit_struct_type)
+ return join("struct ", to_name(type.self));
+ else
+ return to_name(type.self);
+
+ case SPIRType::Image:
+ case SPIRType::SampledImage:
+ return image_type_glsl(type, id);
+
+ case SPIRType::Sampler:
+ // The depth field is set by calling code based on the variable ID of the sampler, effectively reintroducing
+ // this distinction into the type system.
+ return comparison_ids.count(id) ? "samplerShadow" : "sampler";
+
+ case SPIRType::AccelerationStructureNV:
+ return "accelerationStructureNV";
+
+ case SPIRType::Void:
+ return "void";
+
+ default:
+ break;
+ }
+
+ if (type.basetype == SPIRType::UInt && is_legacy())
+ SPIRV_CROSS_THROW("Unsigned integers are not supported on legacy targets.");
+
+ if (type.vecsize == 1 && type.columns == 1) // Scalar builtin
+ {
+ switch (type.basetype)
+ {
+ case SPIRType::Boolean:
+ return "bool";
+ case SPIRType::SByte:
+ return backend.basic_int8_type;
+ case SPIRType::UByte:
+ return backend.basic_uint8_type;
+ case SPIRType::Short:
+ return backend.basic_int16_type;
+ case SPIRType::UShort:
+ return backend.basic_uint16_type;
+ case SPIRType::Int:
+ return backend.basic_int_type;
+ case SPIRType::UInt:
+ return backend.basic_uint_type;
+ case SPIRType::AtomicCounter:
+ return "atomic_uint";
+ case SPIRType::Half:
+ return "float16_t";
+ case SPIRType::Float:
+ return "float";
+ case SPIRType::Double:
+ return "double";
+ case SPIRType::Int64:
+ return "int64_t";
+ case SPIRType::UInt64:
+ return "uint64_t";
+ default:
+ return "???";
+ }
+ }
+ else if (type.vecsize > 1 && type.columns == 1) // Vector builtin
+ {
+ switch (type.basetype)
+ {
+ case SPIRType::Boolean:
+ return join("bvec", type.vecsize);
+ case SPIRType::SByte:
+ return join("i8vec", type.vecsize);
+ case SPIRType::UByte:
+ return join("u8vec", type.vecsize);
+ case SPIRType::Short:
+ return join("i16vec", type.vecsize);
+ case SPIRType::UShort:
+ return join("u16vec", type.vecsize);
+ case SPIRType::Int:
+ return join("ivec", type.vecsize);
+ case SPIRType::UInt:
+ return join("uvec", type.vecsize);
+ case SPIRType::Half:
+ return join("f16vec", type.vecsize);
+ case SPIRType::Float:
+ return join("vec", type.vecsize);
+ case SPIRType::Double:
+ return join("dvec", type.vecsize);
+ case SPIRType::Int64:
+ return join("i64vec", type.vecsize);
+ case SPIRType::UInt64:
+ return join("u64vec", type.vecsize);
+ default:
+ return "???";
+ }
+ }
+ else if (type.vecsize == type.columns) // Simple Matrix builtin
+ {
+ switch (type.basetype)
+ {
+ case SPIRType::Boolean:
+ return join("bmat", type.vecsize);
+ case SPIRType::Int:
+ return join("imat", type.vecsize);
+ case SPIRType::UInt:
+ return join("umat", type.vecsize);
+ case SPIRType::Half:
+ return join("f16mat", type.vecsize);
+ case SPIRType::Float:
+ return join("mat", type.vecsize);
+ case SPIRType::Double:
+ return join("dmat", type.vecsize);
+ // Matrix types not supported for int64/uint64.
+ default:
+ return "???";
+ }
+ }
+ else
+ {
+ switch (type.basetype)
+ {
+ case SPIRType::Boolean:
+ return join("bmat", type.columns, "x", type.vecsize);
+ case SPIRType::Int:
+ return join("imat", type.columns, "x", type.vecsize);
+ case SPIRType::UInt:
+ return join("umat", type.columns, "x", type.vecsize);
+ case SPIRType::Half:
+ return join("f16mat", type.columns, "x", type.vecsize);
+ case SPIRType::Float:
+ return join("mat", type.columns, "x", type.vecsize);
+ case SPIRType::Double:
+ return join("dmat", type.columns, "x", type.vecsize);
+ // Matrix types not supported for int64/uint64.
+ default:
+ return "???";
+ }
+ }
+}
+
+void CompilerGLSL::add_variable(unordered_set<string> &variables_primary,
+ const unordered_set<string> &variables_secondary, string &name)
+{
+ if (name.empty())
+ return;
+
+ // Reserved for temporaries.
+ if (name[0] == '_' && name.size() >= 2 && isdigit(name[1]))
+ {
+ name.clear();
+ return;
+ }
+
+ // Avoid double underscores.
+ name = sanitize_underscores(name);
+
+ update_name_cache(variables_primary, variables_secondary, name);
+}
+
+void CompilerGLSL::add_local_variable_name(uint32_t id)
+{
+ add_variable(local_variable_names, block_names, ir.meta[id].decoration.alias);
+}
+
+void CompilerGLSL::add_resource_name(uint32_t id)
+{
+ add_variable(resource_names, block_names, ir.meta[id].decoration.alias);
+}
+
+void CompilerGLSL::add_header_line(const std::string &line)
+{
+ header_lines.push_back(line);
+}
+
+bool CompilerGLSL::has_extension(const std::string &ext) const
+{
+ auto itr = find(begin(forced_extensions), end(forced_extensions), ext);
+ return itr != end(forced_extensions);
+}
+
+void CompilerGLSL::require_extension(const std::string &ext)
+{
+ if (!has_extension(ext))
+ forced_extensions.push_back(ext);
+}
+
+void CompilerGLSL::require_extension_internal(const string &ext)
+{
+ if (backend.supports_extensions && !has_extension(ext))
+ {
+ forced_extensions.push_back(ext);
+ force_recompile = true;
+ }
+}
+
+void CompilerGLSL::flatten_buffer_block(uint32_t id)
+{
+ auto &var = get<SPIRVariable>(id);
+ auto &type = get<SPIRType>(var.basetype);
+ auto name = to_name(type.self, false);
+ auto &flags = ir.meta[type.self].decoration.decoration_flags;
+
+ if (!type.array.empty())
+ SPIRV_CROSS_THROW(name + " is an array of UBOs.");
+ if (type.basetype != SPIRType::Struct)
+ SPIRV_CROSS_THROW(name + " is not a struct.");
+ if (!flags.get(DecorationBlock))
+ SPIRV_CROSS_THROW(name + " is not a block.");
+ if (type.member_types.empty())
+ SPIRV_CROSS_THROW(name + " is an empty struct.");
+
+ flattened_buffer_blocks.insert(id);
+}
+
+bool CompilerGLSL::check_atomic_image(uint32_t id)
+{
+ auto &type = expression_type(id);
+ if (type.storage == StorageClassImage)
+ {
+ if (options.es && options.version < 320)
+ require_extension_internal("GL_OES_shader_image_atomic");
+
+ auto *var = maybe_get_backing_variable(id);
+ if (var)
+ {
+ auto &flags = ir.meta[var->self].decoration.decoration_flags;
+ if (flags.get(DecorationNonWritable) || flags.get(DecorationNonReadable))
+ {
+ flags.clear(DecorationNonWritable);
+ flags.clear(DecorationNonReadable);
+ force_recompile = true;
+ }
+ }
+ return true;
+ }
+ else
+ return false;
+}
+
+void CompilerGLSL::add_function_overload(const SPIRFunction &func)
+{
+ Hasher hasher;
+ for (auto &arg : func.arguments)
+ {
+ // Parameters can vary with pointer type or not,
+ // but that will not change the signature in GLSL/HLSL,
+ // so strip the pointer type before hashing.
+ uint32_t type_id = get_pointee_type_id(arg.type);
+ auto &type = get<SPIRType>(type_id);
+
+ if (!combined_image_samplers.empty())
+ {
+ // If we have combined image samplers, we cannot really trust the image and sampler arguments
+ // we pass down to callees, because they may be shuffled around.
+ // Ignore these arguments, to make sure that functions need to differ in some other way
+ // to be considered different overloads.
+ if (type.basetype == SPIRType::SampledImage ||
+ (type.basetype == SPIRType::Image && type.image.sampled == 1) || type.basetype == SPIRType::Sampler)
+ {
+ continue;
+ }
+ }
+
+ hasher.u32(type_id);
+ }
+ uint64_t types_hash = hasher.get();
+
+ auto function_name = to_name(func.self);
+ auto itr = function_overloads.find(function_name);
+ if (itr != end(function_overloads))
+ {
+ // There exists a function with this name already.
+ auto &overloads = itr->second;
+ if (overloads.count(types_hash) != 0)
+ {
+ // Overload conflict, assign a new name.
+ add_resource_name(func.self);
+ function_overloads[to_name(func.self)].insert(types_hash);
+ }
+ else
+ {
+ // Can reuse the name.
+ overloads.insert(types_hash);
+ }
+ }
+ else
+ {
+ // First time we see this function name.
+ add_resource_name(func.self);
+ function_overloads[to_name(func.self)].insert(types_hash);
+ }
+}
+
+void CompilerGLSL::emit_function_prototype(SPIRFunction &func, const Bitset &return_flags)
+{
+ if (func.self != ir.default_entry_point)
+ add_function_overload(func);
+
+ // Avoid shadow declarations.
+ local_variable_names = resource_names;
+
+ string decl;
+
+ auto &type = get<SPIRType>(func.return_type);
+ decl += flags_to_precision_qualifiers_glsl(type, return_flags);
+ decl += type_to_glsl(type);
+ decl += type_to_array_glsl(type);
+ decl += " ";
+
+ if (func.self == ir.default_entry_point)
+ {
+ decl += "main";
+ processing_entry_point = true;
+ }
+ else
+ decl += to_name(func.self);
+
+ decl += "(";
+ vector<string> arglist;
+ for (auto &arg : func.arguments)
+ {
+ // Do not pass in separate images or samplers if we're remapping
+ // to combined image samplers.
+ if (skip_argument(arg.id))
+ continue;
+
+ // Might change the variable name if it already exists in this function.
+ // SPIRV OpName doesn't have any semantic effect, so it's valid for an implementation
+ // to use same name for variables.
+ // Since we want to make the GLSL debuggable and somewhat sane, use fallback names for variables which are duplicates.
+ add_local_variable_name(arg.id);
+
+ arglist.push_back(argument_decl(arg));
+
+ // Hold a pointer to the parameter so we can invalidate the readonly field if needed.
+ auto *var = maybe_get<SPIRVariable>(arg.id);
+ if (var)
+ var->parameter = &arg;
+ }
+
+ for (auto &arg : func.shadow_arguments)
+ {
+ // Might change the variable name if it already exists in this function.
+ // SPIRV OpName doesn't have any semantic effect, so it's valid for an implementation
+ // to use same name for variables.
+ // Since we want to make the GLSL debuggable and somewhat sane, use fallback names for variables which are duplicates.
+ add_local_variable_name(arg.id);
+
+ arglist.push_back(argument_decl(arg));
+
+ // Hold a pointer to the parameter so we can invalidate the readonly field if needed.
+ auto *var = maybe_get<SPIRVariable>(arg.id);
+ if (var)
+ var->parameter = &arg;
+ }
+
+ decl += merge(arglist);
+ decl += ")";
+ statement(decl);
+}
+
+void CompilerGLSL::emit_function(SPIRFunction &func, const Bitset &return_flags)
+{
+ // Avoid potential cycles.
+ if (func.active)
+ return;
+ func.active = true;
+
+ // If we depend on a function, emit that function before we emit our own function.
+ for (auto block : func.blocks)
+ {
+ auto &b = get<SPIRBlock>(block);
+ for (auto &i : b.ops)
+ {
+ auto ops = stream(i);
+ auto op = static_cast<Op>(i.op);
+
+ if (op == OpFunctionCall)
+ {
+ // Recursively emit functions which are called.
+ uint32_t id = ops[2];
+ emit_function(get<SPIRFunction>(id), ir.meta[ops[1]].decoration.decoration_flags);
+ }
+ }
+ }
+
+ emit_function_prototype(func, return_flags);
+ begin_scope();
+
+ if (func.self == ir.default_entry_point)
+ emit_entry_point_declarations();
+
+ current_function = &func;
+ auto &entry_block = get<SPIRBlock>(func.entry_block);
+
+ sort(begin(func.constant_arrays_needed_on_stack), end(func.constant_arrays_needed_on_stack));
+ for (auto &array : func.constant_arrays_needed_on_stack)
+ {
+ auto &c = get<SPIRConstant>(array);
+ auto &type = get<SPIRType>(c.constant_type);
+ statement(variable_decl(type, join("_", array, "_array_copy")), " = ", constant_expression(c), ";");
+ }
+
+ for (auto &v : func.local_variables)
+ {
+ auto &var = get<SPIRVariable>(v);
+ if (var.storage == StorageClassWorkgroup)
+ {
+ // Special variable type which cannot have initializer,
+ // need to be declared as standalone variables.
+ // Comes from MSL which can push global variables as local variables in main function.
+ add_local_variable_name(var.self);
+ statement(variable_decl(var), ";");
+ var.deferred_declaration = false;
+ }
+ else if (var.storage == StorageClassPrivate)
+ {
+ // These variables will not have had their CFG usage analyzed, so move it to the entry block.
+ // Comes from MSL which can push global variables as local variables in main function.
+ // We could just declare them right now, but we would miss out on an important initialization case which is
+ // LUT declaration in MSL.
+ // If we don't declare the variable when it is assigned we're forced to go through a helper function
+ // which copies elements one by one.
+ add_local_variable_name(var.self);
+ auto &dominated = entry_block.dominated_variables;
+ if (find(begin(dominated), end(dominated), var.self) == end(dominated))
+ entry_block.dominated_variables.push_back(var.self);
+ var.deferred_declaration = true;
+ }
+ else if (var.storage == StorageClassFunction && var.remapped_variable && var.static_expression)
+ {
+ // No need to declare this variable, it has a static expression.
+ var.deferred_declaration = false;
+ }
+ else if (expression_is_lvalue(v))
+ {
+ add_local_variable_name(var.self);
+
+ if (var.initializer)
+ statement(variable_decl_function_local(var), ";");
+ else
+ {
+ // Don't declare variable until first use to declutter the GLSL output quite a lot.
+ // If we don't touch the variable before first branch,
+ // declare it then since we need variable declaration to be in top scope.
+ var.deferred_declaration = true;
+ }
+ }
+ else
+ {
+ // HACK: SPIR-V in older glslang output likes to use samplers and images as local variables, but GLSL does not allow this.
+ // For these types (non-lvalue), we enforce forwarding through a shadowed variable.
+ // This means that when we OpStore to these variables, we just write in the expression ID directly.
+ // This breaks any kind of branching, since the variable must be statically assigned.
+ // Branching on samplers and images would be pretty much impossible to fake in GLSL.
+ var.statically_assigned = true;
+ }
+
+ var.loop_variable_enable = false;
+
+ // Loop variables are never declared outside their for-loop, so block any implicit declaration.
+ if (var.loop_variable)
+ var.deferred_declaration = false;
+ }
+
+ for (auto &line : current_function->fixup_hooks_in)
+ line();
+
+ entry_block.loop_dominator = SPIRBlock::NoDominator;
+ emit_block_chain(entry_block);
+
+ end_scope();
+ processing_entry_point = false;
+ statement("");
+}
+
+void CompilerGLSL::emit_fixup()
+{
+ auto &execution = get_entry_point();
+ if (execution.model == ExecutionModelVertex)
+ {
+ if (options.vertex.fixup_clipspace)
+ {
+ const char *suffix = backend.float_literal_suffix ? "f" : "";
+ statement("gl_Position.z = 2.0", suffix, " * gl_Position.z - gl_Position.w;");
+ }
+
+ if (options.vertex.flip_vert_y)
+ statement("gl_Position.y = -gl_Position.y;");
+ }
+}
+
+bool CompilerGLSL::flush_phi_required(uint32_t from, uint32_t to)
+{
+ auto &child = get<SPIRBlock>(to);
+ for (auto &phi : child.phi_variables)
+ if (phi.parent == from)
+ return true;
+ return false;
+}
+
+void CompilerGLSL::flush_phi(uint32_t from, uint32_t to)
+{
+ auto &child = get<SPIRBlock>(to);
+
+ unordered_set<uint32_t> temporary_phi_variables;
+
+ for (auto itr = begin(child.phi_variables); itr != end(child.phi_variables); ++itr)
+ {
+ auto &phi = *itr;
+
+ if (phi.parent == from)
+ {
+ auto &var = get<SPIRVariable>(phi.function_variable);
+
+ // A Phi variable might be a loop variable, so flush to static expression.
+ if (var.loop_variable && !var.loop_variable_enable)
+ var.static_expression = phi.local_variable;
+ else
+ {
+ flush_variable_declaration(phi.function_variable);
+
+ // Check if we are going to write to a Phi variable that another statement will read from
+ // as part of another Phi node in our target block.
+ // For this case, we will need to copy phi.function_variable to a temporary, and use that for future reads.
+ // This is judged to be extremely rare, so deal with it here using a simple, but suboptimal algorithm.
+ bool need_saved_temporary =
+ find_if(itr + 1, end(child.phi_variables), [&](const SPIRBlock::Phi &future_phi) -> bool {
+ return future_phi.local_variable == phi.function_variable && future_phi.parent == from;
+ }) != end(child.phi_variables);
+
+ if (need_saved_temporary)
+ {
+ // Need to make sure we declare the phi variable with a copy at the right scope.
+ // We cannot safely declare a temporary here since we might be inside a continue block.
+ if (!var.allocate_temporary_copy)
+ {
+ var.allocate_temporary_copy = true;
+ force_recompile = true;
+ }
+ statement("_", phi.function_variable, "_copy", " = ", to_name(phi.function_variable), ";");
+ temporary_phi_variables.insert(phi.function_variable);
+ }
+
+ // This might be called in continue block, so make sure we
+ // use this to emit ESSL 1.0 compliant increments/decrements.
+ auto lhs = to_expression(phi.function_variable);
+
+ string rhs;
+ if (temporary_phi_variables.count(phi.local_variable))
+ rhs = join("_", phi.local_variable, "_copy");
+ else
+ rhs = to_pointer_expression(phi.local_variable);
+
+ if (!optimize_read_modify_write(get<SPIRType>(var.basetype), lhs, rhs))
+ statement(lhs, " = ", rhs, ";");
+ }
+
+ register_write(phi.function_variable);
+ }
+ }
+}
+
+void CompilerGLSL::branch_to_continue(uint32_t from, uint32_t to)
+{
+ auto &to_block = get<SPIRBlock>(to);
+ if (from == to)
+ return;
+
+ assert(is_continue(to));
+ if (to_block.complex_continue)
+ {
+ // Just emit the whole block chain as is.
+ auto usage_counts = expression_usage_counts;
+ auto invalid = invalid_expressions;
+
+ emit_block_chain(to_block);
+
+ // Expression usage counts and invalid expressions
+ // are moot after returning from the continue block.
+ // Since we emit the same block multiple times,
+ // we don't want to invalidate ourselves.
+ expression_usage_counts = usage_counts;
+ invalid_expressions = invalid;
+ }
+ else
+ {
+ auto &from_block = get<SPIRBlock>(from);
+ bool outside_control_flow = false;
+ uint32_t loop_dominator = 0;
+
+ // FIXME: Refactor this to not use the old loop_dominator tracking.
+ if (from_block.merge_block)
+ {
+ // If we are a loop header, we don't set the loop dominator,
+ // so just use "self" here.
+ loop_dominator = from;
+ }
+ else if (from_block.loop_dominator != SPIRBlock::NoDominator)
+ {
+ loop_dominator = from_block.loop_dominator;
+ }
+
+ if (loop_dominator != 0)
+ {
+ auto &dominator = get<SPIRBlock>(loop_dominator);
+
+ // For non-complex continue blocks, we implicitly branch to the continue block
+ // by having the continue block be part of the loop header in for (; ; continue-block).
+ outside_control_flow = block_is_outside_flow_control_from_block(dominator, from_block);
+ }
+
+ // Some simplification for for-loops. We always end up with a useless continue;
+ // statement since we branch to a loop block.
+ // Walk the CFG, if we uncoditionally execute the block calling continue assuming we're in the loop block,
+ // we can avoid writing out an explicit continue statement.
+ // Similar optimization to return statements if we know we're outside flow control.
+ if (!outside_control_flow)
+ statement("continue;");
+ }
+}
+
+void CompilerGLSL::branch(uint32_t from, uint32_t to)
+{
+ flush_phi(from, to);
+ flush_control_dependent_expressions(from);
+ flush_all_active_variables();
+
+ // This is only a continue if we branch to our loop dominator.
+ if ((ir.block_meta[to] & ParsedIR::BLOCK_META_LOOP_HEADER_BIT) != 0 && get<SPIRBlock>(from).loop_dominator == to)
+ {
+ // This can happen if we had a complex continue block which was emitted.
+ // Once the continue block tries to branch to the loop header, just emit continue;
+ // and end the chain here.
+ statement("continue;");
+ }
+ else if (is_break(to))
+ {
+ // Very dirty workaround.
+ // Switch constructs are able to break, but they cannot break out of a loop at the same time.
+ // Only sensible solution is to make a ladder variable, which we declare at the top of the switch block,
+ // write to the ladder here, and defer the break.
+ // The loop we're breaking out of must dominate the switch block, or there is no ladder breaking case.
+ if (current_emitting_switch && is_loop_break(to) && current_emitting_switch->loop_dominator != ~0u &&
+ get<SPIRBlock>(current_emitting_switch->loop_dominator).merge_block == to)
+ {
+ if (!current_emitting_switch->need_ladder_break)
+ {
+ force_recompile = true;
+ current_emitting_switch->need_ladder_break = true;
+ }
+
+ statement("_", current_emitting_switch->self, "_ladder_break = true;");
+ }
+ statement("break;");
+ }
+ else if (is_continue(to) || (from == to))
+ {
+ // For from == to case can happen for a do-while loop which branches into itself.
+ // We don't mark these cases as continue blocks, but the only possible way to branch into
+ // ourselves is through means of continue blocks.
+ branch_to_continue(from, to);
+ }
+ else if (!is_conditional(to))
+ emit_block_chain(get<SPIRBlock>(to));
+
+ // It is important that we check for break before continue.
+ // A block might serve two purposes, a break block for the inner scope, and
+ // a continue block in the outer scope.
+ // Inner scope always takes precedence.
+}
+
+void CompilerGLSL::branch(uint32_t from, uint32_t cond, uint32_t true_block, uint32_t false_block)
+{
+ // If we branch directly to a selection merge target, we don't really need a code path.
+ bool true_sub = !is_conditional(true_block);
+ bool false_sub = !is_conditional(false_block);
+
+ if (true_sub)
+ {
+ emit_block_hints(get<SPIRBlock>(from));
+ statement("if (", to_expression(cond), ")");
+ begin_scope();
+ branch(from, true_block);
+ end_scope();
+
+ if (false_sub || is_continue(false_block) || is_break(false_block))
+ {
+ statement("else");
+ begin_scope();
+ branch(from, false_block);
+ end_scope();
+ }
+ else if (flush_phi_required(from, false_block))
+ {
+ statement("else");
+ begin_scope();
+ flush_phi(from, false_block);
+ end_scope();
+ }
+ }
+ else if (false_sub && !true_sub)
+ {
+ // Only need false path, use negative conditional.
+ emit_block_hints(get<SPIRBlock>(from));
+ statement("if (!", to_enclosed_expression(cond), ")");
+ begin_scope();
+ branch(from, false_block);
+ end_scope();
+
+ if (is_continue(true_block) || is_break(true_block))
+ {
+ statement("else");
+ begin_scope();
+ branch(from, true_block);
+ end_scope();
+ }
+ else if (flush_phi_required(from, true_block))
+ {
+ statement("else");
+ begin_scope();
+ flush_phi(from, true_block);
+ end_scope();
+ }
+ }
+}
+
+void CompilerGLSL::propagate_loop_dominators(const SPIRBlock &block)
+{
+ // Propagate down the loop dominator block, so that dominated blocks can back trace.
+ if (block.merge == SPIRBlock::MergeLoop || block.loop_dominator)
+ {
+ uint32_t dominator = block.merge == SPIRBlock::MergeLoop ? block.self : block.loop_dominator;
+
+ auto set_dominator = [this](uint32_t self, uint32_t new_dominator) {
+ auto &dominated_block = this->get<SPIRBlock>(self);
+
+ // If we already have a loop dominator, we're trying to break out to merge targets
+ // which should not update the loop dominator.
+ if (!dominated_block.loop_dominator)
+ dominated_block.loop_dominator = new_dominator;
+ };
+
+ // After merging a loop, we inherit the loop dominator always.
+ if (block.merge_block)
+ set_dominator(block.merge_block, block.loop_dominator);
+
+ if (block.true_block)
+ set_dominator(block.true_block, dominator);
+ if (block.false_block)
+ set_dominator(block.false_block, dominator);
+ if (block.next_block)
+ set_dominator(block.next_block, dominator);
+ if (block.default_block)
+ set_dominator(block.default_block, dominator);
+
+ for (auto &c : block.cases)
+ set_dominator(c.block, dominator);
+
+ // In older glslang output continue_block can be == loop header.
+ if (block.continue_block && block.continue_block != block.self)
+ set_dominator(block.continue_block, dominator);
+ }
+}
+
+// FIXME: This currently cannot handle complex continue blocks
+// as in do-while.
+// This should be seen as a "trivial" continue block.
+string CompilerGLSL::emit_continue_block(uint32_t continue_block, bool follow_true_block, bool follow_false_block)
+{
+ auto *block = &get<SPIRBlock>(continue_block);
+
+ // While emitting the continue block, declare_temporary will check this
+ // if we have to emit temporaries.
+ current_continue_block = block;
+
+ vector<string> statements;
+
+ // Capture all statements into our list.
+ auto *old = redirect_statement;
+ redirect_statement = &statements;
+
+ // Stamp out all blocks one after each other.
+ while ((ir.block_meta[block->self] & ParsedIR::BLOCK_META_LOOP_HEADER_BIT) == 0)
+ {
+ propagate_loop_dominators(*block);
+ // Write out all instructions we have in this block.
+ emit_block_instructions(*block);
+
+ // For plain branchless for/while continue blocks.
+ if (block->next_block)
+ {
+ flush_phi(continue_block, block->next_block);
+ block = &get<SPIRBlock>(block->next_block);
+ }
+ // For do while blocks. The last block will be a select block.
+ else if (block->true_block && follow_true_block)
+ {
+ flush_phi(continue_block, block->true_block);
+ block = &get<SPIRBlock>(block->true_block);
+ }
+ else if (block->false_block && follow_false_block)
+ {
+ flush_phi(continue_block, block->false_block);
+ block = &get<SPIRBlock>(block->false_block);
+ }
+ else
+ {
+ SPIRV_CROSS_THROW("Invalid continue block detected!");
+ }
+ }
+
+ // Restore old pointer.
+ redirect_statement = old;
+
+ // Somewhat ugly, strip off the last ';' since we use ',' instead.
+ // Ideally, we should select this behavior in statement().
+ for (auto &s : statements)
+ {
+ if (!s.empty() && s.back() == ';')
+ s.erase(s.size() - 1, 1);
+ }
+
+ current_continue_block = nullptr;
+ return merge(statements);
+}
+
+void CompilerGLSL::emit_while_loop_initializers(const SPIRBlock &block)
+{
+ // While loops do not take initializers, so declare all of them outside.
+ for (auto &loop_var : block.loop_variables)
+ {
+ auto &var = get<SPIRVariable>(loop_var);
+ statement(variable_decl(var), ";");
+ }
+}
+
+string CompilerGLSL::emit_for_loop_initializers(const SPIRBlock &block)
+{
+ if (block.loop_variables.empty())
+ return "";
+
+ bool same_types = for_loop_initializers_are_same_type(block);
+ // We can only declare for loop initializers if all variables are of same type.
+ // If we cannot do this, declare individual variables before the loop header.
+
+ // We might have a loop variable candidate which was not assigned to for some reason.
+ uint32_t missing_initializers = 0;
+ for (auto &variable : block.loop_variables)
+ {
+ uint32_t expr = get<SPIRVariable>(variable).static_expression;
+
+ // Sometimes loop variables are initialized with OpUndef, but we can just declare
+ // a plain variable without initializer in this case.
+ if (expr == 0 || ir.ids[expr].get_type() == TypeUndef)
+ missing_initializers++;
+ }
+
+ if (block.loop_variables.size() == 1 && missing_initializers == 0)
+ {
+ return variable_decl(get<SPIRVariable>(block.loop_variables.front()));
+ }
+ else if (!same_types || missing_initializers == uint32_t(block.loop_variables.size()))
+ {
+ for (auto &loop_var : block.loop_variables)
+ statement(variable_decl(get<SPIRVariable>(loop_var)), ";");
+ return "";
+ }
+ else
+ {
+ // We have a mix of loop variables, either ones with a clear initializer, or ones without.
+ // Separate the two streams.
+ string expr;
+
+ for (auto &loop_var : block.loop_variables)
+ {
+ uint32_t static_expr = get<SPIRVariable>(loop_var).static_expression;
+ if (static_expr == 0 || ir.ids[static_expr].get_type() == TypeUndef)
+ {
+ statement(variable_decl(get<SPIRVariable>(loop_var)), ";");
+ }
+ else
+ {
+ auto &var = get<SPIRVariable>(loop_var);
+ auto &type = get_variable_data_type(var);
+ if (expr.empty())
+ {
+ // For loop initializers are of the form <type id = value, id = value, id = value, etc ...
+ expr = join(to_qualifiers_glsl(var.self), type_to_glsl(type), " ");
+ }
+ else
+ {
+ expr += ", ";
+ // In MSL, being based on C++, the asterisk marking a pointer
+ // binds to the identifier, not the type.
+ if (type.pointer)
+ expr += "* ";
+ }
+
+ expr += join(to_name(loop_var), " = ", to_pointer_expression(var.static_expression));
+ }
+ }
+ return expr;
+ }
+}
+
+bool CompilerGLSL::for_loop_initializers_are_same_type(const SPIRBlock &block)
+{
+ if (block.loop_variables.size() <= 1)
+ return true;
+
+ uint32_t expected = 0;
+ Bitset expected_flags;
+ for (auto &var : block.loop_variables)
+ {
+ // Don't care about uninitialized variables as they will not be part of the initializers.
+ uint32_t expr = get<SPIRVariable>(var).static_expression;
+ if (expr == 0 || ir.ids[expr].get_type() == TypeUndef)
+ continue;
+
+ if (expected == 0)
+ {
+ expected = get<SPIRVariable>(var).basetype;
+ expected_flags = get_decoration_bitset(var);
+ }
+ else if (expected != get<SPIRVariable>(var).basetype)
+ return false;
+
+ // Precision flags and things like that must also match.
+ if (expected_flags != get_decoration_bitset(var))
+ return false;
+ }
+
+ return true;
+}
+
+bool CompilerGLSL::attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method method)
+{
+ SPIRBlock::ContinueBlockType continue_type = continue_block_type(get<SPIRBlock>(block.continue_block));
+
+ if (method == SPIRBlock::MergeToSelectForLoop || method == SPIRBlock::MergeToSelectContinueForLoop)
+ {
+ uint32_t current_count = statement_count;
+ // If we're trying to create a true for loop,
+ // we need to make sure that all opcodes before branch statement do not actually emit any code.
+ // We can then take the condition expression and create a for (; cond ; ) { body; } structure instead.
+ emit_block_instructions(block);
+
+ bool condition_is_temporary = forced_temporaries.find(block.condition) == end(forced_temporaries);
+
+ // This can work! We only did trivial things which could be forwarded in block body!
+ if (current_count == statement_count && condition_is_temporary)
+ {
+ switch (continue_type)
+ {
+ case SPIRBlock::ForLoop:
+ {
+ // This block may be a dominating block, so make sure we flush undeclared variables before building the for loop header.
+ flush_undeclared_variables(block);
+
+ // Important that we do this in this order because
+ // emitting the continue block can invalidate the condition expression.
+ auto initializer = emit_for_loop_initializers(block);
+ auto condition = to_expression(block.condition);
+
+ // Condition might have to be inverted.
+ if (execution_is_noop(get<SPIRBlock>(block.true_block), get<SPIRBlock>(block.merge_block)))
+ condition = join("!", enclose_expression(condition));
+
+ emit_block_hints(block);
+ if (method != SPIRBlock::MergeToSelectContinueForLoop)
+ {
+ auto continue_block = emit_continue_block(block.continue_block, false, false);
+ statement("for (", initializer, "; ", condition, "; ", continue_block, ")");
+ }
+ else
+ statement("for (", initializer, "; ", condition, "; )");
+ break;
+ }
+
+ case SPIRBlock::WhileLoop:
+ {
+ // This block may be a dominating block, so make sure we flush undeclared variables before building the while loop header.
+ flush_undeclared_variables(block);
+ emit_while_loop_initializers(block);
+ emit_block_hints(block);
+
+ auto condition = to_expression(block.condition);
+ // Condition might have to be inverted.
+ if (execution_is_noop(get<SPIRBlock>(block.true_block), get<SPIRBlock>(block.merge_block)))
+ condition = join("!", enclose_expression(condition));
+
+ statement("while (", condition, ")");
+ break;
+ }
+
+ default:
+ SPIRV_CROSS_THROW("For/while loop detected, but need while/for loop semantics.");
+ }
+
+ begin_scope();
+ return true;
+ }
+ else
+ {
+ block.disable_block_optimization = true;
+ force_recompile = true;
+ begin_scope(); // We'll see an end_scope() later.
+ return false;
+ }
+ }
+ else if (method == SPIRBlock::MergeToDirectForLoop)
+ {
+ auto &child = get<SPIRBlock>(block.next_block);
+
+ // This block may be a dominating block, so make sure we flush undeclared variables before building the for loop header.
+ flush_undeclared_variables(child);
+
+ uint32_t current_count = statement_count;
+
+ // If we're trying to create a true for loop,
+ // we need to make sure that all opcodes before branch statement do not actually emit any code.
+ // We can then take the condition expression and create a for (; cond ; ) { body; } structure instead.
+ emit_block_instructions(child);
+
+ bool condition_is_temporary = forced_temporaries.find(child.condition) == end(forced_temporaries);
+
+ if (current_count == statement_count && condition_is_temporary)
+ {
+ propagate_loop_dominators(child);
+ uint32_t target_block = child.true_block;
+
+ switch (continue_type)
+ {
+ case SPIRBlock::ForLoop:
+ {
+ // Important that we do this in this order because
+ // emitting the continue block can invalidate the condition expression.
+ auto initializer = emit_for_loop_initializers(block);
+ auto condition = to_expression(child.condition);
+
+ // Condition might have to be inverted.
+ if (execution_is_noop(get<SPIRBlock>(child.true_block), get<SPIRBlock>(block.merge_block)))
+ {
+ condition = join("!", enclose_expression(condition));
+ target_block = child.false_block;
+ }
+
+ auto continue_block = emit_continue_block(block.continue_block, false, false);
+ emit_block_hints(block);
+ statement("for (", initializer, "; ", condition, "; ", continue_block, ")");
+ break;
+ }
+
+ case SPIRBlock::WhileLoop:
+ {
+ emit_while_loop_initializers(block);
+ emit_block_hints(block);
+
+ auto condition = to_expression(child.condition);
+ // Condition might have to be inverted.
+ if (execution_is_noop(get<SPIRBlock>(child.true_block), get<SPIRBlock>(block.merge_block)))
+ {
+ condition = join("!", enclose_expression(condition));
+ target_block = child.false_block;
+ }
+
+ statement("while (", condition, ")");
+ break;
+ }
+
+ default:
+ SPIRV_CROSS_THROW("For/while loop detected, but need while/for loop semantics.");
+ }
+
+ begin_scope();
+ branch(child.self, target_block);
+ return true;
+ }
+ else
+ {
+ block.disable_block_optimization = true;
+ force_recompile = true;
+ begin_scope(); // We'll see an end_scope() later.
+ return false;
+ }
+ }
+ else
+ return false;
+}
+
+void CompilerGLSL::flush_undeclared_variables(SPIRBlock &block)
+{
+ // Enforce declaration order for regression testing purposes.
+ sort(begin(block.dominated_variables), end(block.dominated_variables));
+ for (auto &v : block.dominated_variables)
+ flush_variable_declaration(v);
+}
+
+void CompilerGLSL::emit_hoisted_temporaries(vector<pair<uint32_t, uint32_t>> &temporaries)
+{
+ // If we need to force temporaries for certain IDs due to continue blocks, do it before starting loop header.
+ // Need to sort these to ensure that reference output is stable.
+ sort(begin(temporaries), end(temporaries),
+ [](const pair<uint32_t, uint32_t> &a, const pair<uint32_t, uint32_t> &b) { return a.second < b.second; });
+
+ for (auto &tmp : temporaries)
+ {
+ add_local_variable_name(tmp.second);
+ auto &flags = ir.meta[tmp.second].decoration.decoration_flags;
+ auto &type = get<SPIRType>(tmp.first);
+ statement(flags_to_precision_qualifiers_glsl(type, flags), variable_decl(type, to_name(tmp.second)), ";");
+
+ hoisted_temporaries.insert(tmp.second);
+ forced_temporaries.insert(tmp.second);
+
+ // The temporary might be read from before it's assigned, set up the expression now.
+ set<SPIRExpression>(tmp.second, to_name(tmp.second), tmp.first, true);
+ }
+}
+
+void CompilerGLSL::emit_block_chain(SPIRBlock &block)
+{
+ propagate_loop_dominators(block);
+
+ bool select_branch_to_true_block = false;
+ bool select_branch_to_false_block = false;
+ bool skip_direct_branch = false;
+ bool emitted_loop_header_variables = false;
+ bool force_complex_continue_block = false;
+
+ emit_hoisted_temporaries(block.declare_temporary);
+
+ SPIRBlock::ContinueBlockType continue_type = SPIRBlock::ContinueNone;
+ if (block.continue_block)
+ continue_type = continue_block_type(get<SPIRBlock>(block.continue_block));
+
+ // If we have loop variables, stop masking out access to the variable now.
+ for (auto var : block.loop_variables)
+ get<SPIRVariable>(var).loop_variable_enable = true;
+
+ // This is the method often used by spirv-opt to implement loops.
+ // The loop header goes straight into the continue block.
+ // However, don't attempt this on ESSL 1.0, because if a loop variable is used in a continue block,
+ // it *MUST* be used in the continue block. This loop method will not work.
+ if (!is_legacy_es() && block_is_loop_candidate(block, SPIRBlock::MergeToSelectContinueForLoop))
+ {
+ flush_undeclared_variables(block);
+ if (attempt_emit_loop_header(block, SPIRBlock::MergeToSelectContinueForLoop))
+ {
+ if (execution_is_noop(get<SPIRBlock>(block.true_block), get<SPIRBlock>(block.merge_block)))
+ select_branch_to_false_block = true;
+ else
+ select_branch_to_true_block = true;
+
+ emitted_loop_header_variables = true;
+ force_complex_continue_block = true;
+ }
+ }
+ // This is the older loop behavior in glslang which branches to loop body directly from the loop header.
+ else if (block_is_loop_candidate(block, SPIRBlock::MergeToSelectForLoop))
+ {
+ flush_undeclared_variables(block);
+ if (attempt_emit_loop_header(block, SPIRBlock::MergeToSelectForLoop))
+ {
+ // The body of while, is actually just the true (or false) block, so always branch there unconditionally.
+ if (execution_is_noop(get<SPIRBlock>(block.true_block), get<SPIRBlock>(block.merge_block)))
+ select_branch_to_false_block = true;
+ else
+ select_branch_to_true_block = true;
+
+ emitted_loop_header_variables = true;
+ }
+ }
+ // This is the newer loop behavior in glslang which branches from Loop header directly to
+ // a new block, which in turn has a OpBranchSelection without a selection merge.
+ else if (block_is_loop_candidate(block, SPIRBlock::MergeToDirectForLoop))
+ {
+ flush_undeclared_variables(block);
+ if (attempt_emit_loop_header(block, SPIRBlock::MergeToDirectForLoop))
+ {
+ skip_direct_branch = true;
+ emitted_loop_header_variables = true;
+ }
+ }
+ else if (continue_type == SPIRBlock::DoWhileLoop)
+ {
+ flush_undeclared_variables(block);
+ emit_while_loop_initializers(block);
+ emitted_loop_header_variables = true;
+ // We have some temporaries where the loop header is the dominator.
+ // We risk a case where we have code like:
+ // for (;;) { create-temporary; break; } consume-temporary;
+ // so force-declare temporaries here.
+ emit_hoisted_temporaries(block.potential_declare_temporary);
+ statement("do");
+ begin_scope();
+
+ emit_block_instructions(block);
+ }
+ else if (block.merge == SPIRBlock::MergeLoop)
+ {
+ flush_undeclared_variables(block);
+ emit_while_loop_initializers(block);
+ emitted_loop_header_variables = true;
+
+ // We have a generic loop without any distinguishable pattern like for, while or do while.
+ get<SPIRBlock>(block.continue_block).complex_continue = true;
+ continue_type = SPIRBlock::ComplexLoop;
+
+ // We have some temporaries where the loop header is the dominator.
+ // We risk a case where we have code like:
+ // for (;;) { create-temporary; break; } consume-temporary;
+ // so force-declare temporaries here.
+ emit_hoisted_temporaries(block.potential_declare_temporary);
+ statement("for (;;)");
+ begin_scope();
+
+ emit_block_instructions(block);
+ }
+ else
+ {
+ emit_block_instructions(block);
+ }
+
+ // If we didn't successfully emit a loop header and we had loop variable candidates, we have a problem
+ // as writes to said loop variables might have been masked out, we need a recompile.
+ if (!emitted_loop_header_variables && !block.loop_variables.empty())
+ {
+ force_recompile = true;
+ for (auto var : block.loop_variables)
+ get<SPIRVariable>(var).loop_variable = false;
+ block.loop_variables.clear();
+ }
+
+ flush_undeclared_variables(block);
+ bool emit_next_block = true;
+
+ // Handle end of block.
+ switch (block.terminator)
+ {
+ case SPIRBlock::Direct:
+ // True when emitting complex continue block.
+ if (block.loop_dominator == block.next_block)
+ {
+ branch(block.self, block.next_block);
+ emit_next_block = false;
+ }
+ // True if MergeToDirectForLoop succeeded.
+ else if (skip_direct_branch)
+ emit_next_block = false;
+ else if (is_continue(block.next_block) || is_break(block.next_block) || is_conditional(block.next_block))
+ {
+ branch(block.self, block.next_block);
+ emit_next_block = false;
+ }
+ break;
+
+ case SPIRBlock::Select:
+ // True if MergeToSelectForLoop or MergeToSelectContinueForLoop succeeded.
+ if (select_branch_to_true_block)
+ {
+ if (force_complex_continue_block)
+ {
+ assert(block.true_block == block.continue_block);
+
+ // We're going to emit a continue block directly here, so make sure it's marked as complex.
+ auto &complex_continue = get<SPIRBlock>(block.continue_block).complex_continue;
+ bool old_complex = complex_continue;
+ complex_continue = true;
+ branch(block.self, block.true_block);
+ complex_continue = old_complex;
+ }
+ else
+ branch(block.self, block.true_block);
+ }
+ else if (select_branch_to_false_block)
+ {
+ if (force_complex_continue_block)
+ {
+ assert(block.false_block == block.continue_block);
+
+ // We're going to emit a continue block directly here, so make sure it's marked as complex.
+ auto &complex_continue = get<SPIRBlock>(block.continue_block).complex_continue;
+ bool old_complex = complex_continue;
+ complex_continue = true;
+ branch(block.self, block.false_block);
+ complex_continue = old_complex;
+ }
+ else
+ branch(block.self, block.false_block);
+ }
+ else
+ branch(block.self, block.condition, block.true_block, block.false_block);
+ break;
+
+ case SPIRBlock::MultiSelect:
+ {
+ auto &type = expression_type(block.condition);
+ bool unsigned_case = type.basetype == SPIRType::UInt || type.basetype == SPIRType::UShort;
+
+ if (block.merge == SPIRBlock::MergeNone)
+ SPIRV_CROSS_THROW("Switch statement is not structured");
+
+ if (type.basetype == SPIRType::UInt64 || type.basetype == SPIRType::Int64)
+ {
+ // SPIR-V spec suggests this is allowed, but we cannot support it in higher level languages.
+ SPIRV_CROSS_THROW("Cannot use 64-bit switch selectors.");
+ }
+
+ const char *label_suffix = "";
+ if (type.basetype == SPIRType::UInt && backend.uint32_t_literal_suffix)
+ label_suffix = "u";
+ else if (type.basetype == SPIRType::UShort)
+ label_suffix = backend.uint16_t_literal_suffix;
+ else if (type.basetype == SPIRType::Short)
+ label_suffix = backend.int16_t_literal_suffix;
+
+ SPIRBlock *old_emitting_switch = current_emitting_switch;
+ current_emitting_switch = &block;
+
+ if (block.need_ladder_break)
+ statement("bool _", block.self, "_ladder_break = false;");
+
+ emit_block_hints(block);
+ statement("switch (", to_expression(block.condition), ")");
+ begin_scope();
+
+ // Multiple case labels can branch to same block, so find all unique blocks.
+ bool emitted_default = false;
+ unordered_set<uint32_t> emitted_blocks;
+
+ for (auto &c : block.cases)
+ {
+ if (emitted_blocks.count(c.block) != 0)
+ continue;
+
+ // Emit all case labels which branch to our target.
+ // FIXME: O(n^2), revisit if we hit shaders with 100++ case labels ...
+ for (auto &other_case : block.cases)
+ {
+ if (other_case.block == c.block)
+ {
+ // The case label value must be sign-extended properly in SPIR-V, so we can assume 32-bit values here.
+ auto case_value = unsigned_case ? convert_to_string(uint32_t(other_case.value)) :
+ convert_to_string(int32_t(other_case.value));
+ statement("case ", case_value, label_suffix, ":");
+ }
+ }
+
+ // Maybe we share with default block?
+ if (block.default_block == c.block)
+ {
+ statement("default:");
+ emitted_default = true;
+ }
+
+ // Complete the target.
+ emitted_blocks.insert(c.block);
+
+ begin_scope();
+ branch(block.self, c.block);
+ end_scope();
+ }
+
+ if (!emitted_default)
+ {
+ if (block.default_block != block.next_block)
+ {
+ statement("default:");
+ begin_scope();
+ if (is_break(block.default_block))
+ SPIRV_CROSS_THROW("Cannot break; out of a switch statement and out of a loop at the same time ...");
+ branch(block.self, block.default_block);
+ end_scope();
+ }
+ else if (flush_phi_required(block.self, block.next_block))
+ {
+ statement("default:");
+ begin_scope();
+ flush_phi(block.self, block.next_block);
+ statement("break;");
+ end_scope();
+ }
+ }
+
+ end_scope();
+
+ if (block.need_ladder_break)
+ {
+ statement("if (_", block.self, "_ladder_break)");
+ begin_scope();
+ statement("break;");
+ end_scope();
+ }
+
+ current_emitting_switch = old_emitting_switch;
+ break;
+ }
+
+ case SPIRBlock::Return:
+ for (auto &line : current_function->fixup_hooks_out)
+ line();
+
+ if (processing_entry_point)
+ emit_fixup();
+
+ if (block.return_value)
+ {
+ auto &type = expression_type(block.return_value);
+ if (!type.array.empty() && !backend.can_return_array)
+ {
+ // If we cannot return arrays, we will have a special out argument we can write to instead.
+ // The backend is responsible for setting this up, and redirection the return values as appropriate.
+ if (ir.ids[block.return_value].get_type() != TypeUndef)
+ emit_array_copy("SPIRV_Cross_return_value", block.return_value);
+
+ if (!block_is_outside_flow_control_from_block(get<SPIRBlock>(current_function->entry_block), block) ||
+ block.loop_dominator != SPIRBlock::NoDominator)
+ {
+ statement("return;");
+ }
+ }
+ else
+ {
+ // OpReturnValue can return Undef, so don't emit anything for this case.
+ if (ir.ids[block.return_value].get_type() != TypeUndef)
+ statement("return ", to_expression(block.return_value), ";");
+ }
+ }
+ // If this block is the very final block and not called from control flow,
+ // we do not need an explicit return which looks out of place. Just end the function here.
+ // In the very weird case of for(;;) { return; } executing return is unconditional,
+ // but we actually need a return here ...
+ else if (!block_is_outside_flow_control_from_block(get<SPIRBlock>(current_function->entry_block), block) ||
+ block.loop_dominator != SPIRBlock::NoDominator)
+ {
+ statement("return;");
+ }
+ break;
+
+ case SPIRBlock::Kill:
+ statement(backend.discard_literal, ";");
+ break;
+
+ case SPIRBlock::Unreachable:
+ emit_next_block = false;
+ break;
+
+ default:
+ SPIRV_CROSS_THROW("Unimplemented block terminator.");
+ }
+
+ if (block.next_block && emit_next_block)
+ {
+ // If we hit this case, we're dealing with an unconditional branch, which means we will output
+ // that block after this. If we had selection merge, we already flushed phi variables.
+ if (block.merge != SPIRBlock::MergeSelection)
+ flush_phi(block.self, block.next_block);
+
+ // For merge selects we might have ignored the fact that a merge target
+ // could have been a break; or continue;
+ // We will need to deal with it here.
+ if (is_loop_break(block.next_block))
+ {
+ // Cannot check for just break, because switch statements will also use break.
+ assert(block.merge == SPIRBlock::MergeSelection);
+ statement("break;");
+ }
+ else if (is_continue(block.next_block))
+ {
+ assert(block.merge == SPIRBlock::MergeSelection);
+ branch_to_continue(block.self, block.next_block);
+ }
+ else
+ emit_block_chain(get<SPIRBlock>(block.next_block));
+ }
+
+ if (block.merge == SPIRBlock::MergeLoop)
+ {
+ if (continue_type == SPIRBlock::DoWhileLoop)
+ {
+ // Make sure that we run the continue block to get the expressions set, but this
+ // should become an empty string.
+ // We have no fallbacks if we cannot forward everything to temporaries ...
+ const auto &continue_block = get<SPIRBlock>(block.continue_block);
+ bool positive_test = execution_is_noop(get<SPIRBlock>(continue_block.true_block),
+ get<SPIRBlock>(continue_block.loop_dominator));
+
+ auto statements = emit_continue_block(block.continue_block, positive_test, !positive_test);
+ if (!statements.empty())
+ {
+ // The DoWhile block has side effects, force ComplexLoop pattern next pass.
+ get<SPIRBlock>(block.continue_block).complex_continue = true;
+ force_recompile = true;
+ }
+
+ // Might have to invert the do-while test here.
+ auto condition = to_expression(continue_block.condition);
+ if (!positive_test)
+ condition = join("!", enclose_expression(condition));
+
+ end_scope_decl(join("while (", condition, ")"));
+ }
+ else
+ end_scope();
+
+ // We cannot break out of two loops at once, so don't check for break; here.
+ // Using block.self as the "from" block isn't quite right, but it has the same scope
+ // and dominance structure, so it's fine.
+ if (is_continue(block.merge_block))
+ branch_to_continue(block.self, block.merge_block);
+ else
+ emit_block_chain(get<SPIRBlock>(block.merge_block));
+ }
+
+ // Forget about control dependent expressions now.
+ block.invalidate_expressions.clear();
+}
+
+void CompilerGLSL::begin_scope()
+{
+ statement("{");
+ indent++;
+}
+
+void CompilerGLSL::end_scope()
+{
+ if (!indent)
+ SPIRV_CROSS_THROW("Popping empty indent stack.");
+ indent--;
+ statement("}");
+}
+
+void CompilerGLSL::end_scope_decl()
+{
+ if (!indent)
+ SPIRV_CROSS_THROW("Popping empty indent stack.");
+ indent--;
+ statement("};");
+}
+
+void CompilerGLSL::end_scope_decl(const string &decl)
+{
+ if (!indent)
+ SPIRV_CROSS_THROW("Popping empty indent stack.");
+ indent--;
+ statement("} ", decl, ";");
+}
+
+void CompilerGLSL::check_function_call_constraints(const uint32_t *args, uint32_t length)
+{
+ // If our variable is remapped, and we rely on type-remapping information as
+ // well, then we cannot pass the variable as a function parameter.
+ // Fixing this is non-trivial without stamping out variants of the same function,
+ // so for now warn about this and suggest workarounds instead.
+ for (uint32_t i = 0; i < length; i++)
+ {
+ auto *var = maybe_get<SPIRVariable>(args[i]);
+ if (!var || !var->remapped_variable)
+ continue;
+
+ auto &type = get<SPIRType>(var->basetype);
+ if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData)
+ {
+ SPIRV_CROSS_THROW("Tried passing a remapped subpassInput variable to a function. "
+ "This will not work correctly because type-remapping information is lost. "
+ "To workaround, please consider not passing the subpass input as a function parameter, "
+ "or use in/out variables instead which do not need type remapping information.");
+ }
+ }
+}
+
+const Instruction *CompilerGLSL::get_next_instruction_in_block(const Instruction &instr)
+{
+ // FIXME: This is kind of hacky. There should be a cleaner way.
+ auto offset = uint32_t(&instr - current_emitting_block->ops.data());
+ if ((offset + 1) < current_emitting_block->ops.size())
+ return &current_emitting_block->ops[offset + 1];
+ else
+ return nullptr;
+}
+
+uint32_t CompilerGLSL::mask_relevant_memory_semantics(uint32_t semantics)
+{
+ return semantics & (MemorySemanticsAtomicCounterMemoryMask | MemorySemanticsImageMemoryMask |
+ MemorySemanticsWorkgroupMemoryMask | MemorySemanticsUniformMemoryMask |
+ MemorySemanticsCrossWorkgroupMemoryMask | MemorySemanticsSubgroupMemoryMask);
+}
+
+void CompilerGLSL::emit_array_copy(const string &lhs, uint32_t rhs_id)
+{
+ statement(lhs, " = ", to_expression(rhs_id), ";");
+}
+
+void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t source_id, std::string &expr)
+{
+ if (!backend.force_gl_in_out_block)
+ return;
+ // This path is only relevant for GL backends.
+
+ auto *var = maybe_get<SPIRVariable>(source_id);
+ if (!var)
+ return;
+
+ if (var->storage != StorageClassInput)
+ return;
+
+ auto &type = get_variable_data_type(*var);
+ if (type.array.empty())
+ return;
+
+ auto builtin = BuiltIn(get_decoration(var->self, DecorationBuiltIn));
+ bool is_builtin = is_builtin_variable(*var) && (builtin == BuiltInPointSize || builtin == BuiltInPosition);
+ bool is_tess = is_tessellation_shader();
+
+ // Tessellation input arrays are special in that they are unsized, so we cannot directly copy from it.
+ // We must unroll the array load.
+ // For builtins, we couldn't catch this case normally,
+ // because this is resolved in the OpAccessChain in most cases.
+ // If we load the entire array, we have no choice but to unroll here.
+ if (is_builtin || is_tess)
+ {
+ auto new_expr = join("_", target_id, "_unrolled");
+ statement(variable_decl(type, new_expr, target_id), ";");
+ string array_expr;
+ if (type.array_size_literal.front())
+ {
+ array_expr = convert_to_string(type.array.front());
+ if (type.array.front() == 0)
+ SPIRV_CROSS_THROW("Cannot unroll an array copy from unsized array.");
+ }
+ else
+ array_expr = to_expression(type.array.front());
+
+ // The array size might be a specialization constant, so use a for-loop instead.
+ statement("for (int i = 0; i < int(", array_expr, "); i++)");
+ begin_scope();
+ if (is_builtin)
+ statement(new_expr, "[i] = gl_in[i].", expr, ";");
+ else
+ statement(new_expr, "[i] = ", expr, "[i];");
+ end_scope();
+
+ expr = move(new_expr);
+ }
+}
+
+void CompilerGLSL::bitcast_from_builtin_load(uint32_t source_id, std::string &expr,
+ const spirv_cross::SPIRType &expr_type)
+{
+ auto *var = maybe_get_backing_variable(source_id);
+ if (var)
+ source_id = var->self;
+
+ // Only interested in standalone builtin variables.
+ if (!has_decoration(source_id, DecorationBuiltIn))
+ return;
+
+ auto builtin = static_cast<BuiltIn>(get_decoration(source_id, DecorationBuiltIn));
+ auto expected_type = expr_type.basetype;
+
+ // TODO: Fill in for more builtins.
+ switch (builtin)
+ {
+ case BuiltInLayer:
+ case BuiltInPrimitiveId:
+ case BuiltInViewportIndex:
+ case BuiltInInstanceId:
+ case BuiltInInstanceIndex:
+ case BuiltInVertexId:
+ case BuiltInVertexIndex:
+ case BuiltInSampleId:
+ case BuiltInBaseVertex:
+ case BuiltInBaseInstance:
+ case BuiltInDrawIndex:
+ expected_type = SPIRType::Int;
+ break;
+
+ case BuiltInGlobalInvocationId:
+ case BuiltInLocalInvocationId:
+ case BuiltInWorkgroupId:
+ case BuiltInLocalInvocationIndex:
+ case BuiltInWorkgroupSize:
+ case BuiltInNumWorkgroups:
+ expected_type = SPIRType::UInt;
+ break;
+
+ default:
+ break;
+ }
+
+ if (expected_type != expr_type.basetype)
+ expr = bitcast_expression(expr_type, expected_type, expr);
+}
+
+void CompilerGLSL::bitcast_to_builtin_store(uint32_t target_id, std::string &expr,
+ const spirv_cross::SPIRType &expr_type)
+{
+ // Only interested in standalone builtin variables.
+ if (!has_decoration(target_id, DecorationBuiltIn))
+ return;
+
+ auto builtin = static_cast<BuiltIn>(get_decoration(target_id, DecorationBuiltIn));
+ auto expected_type = expr_type.basetype;
+
+ // TODO: Fill in for more builtins.
+ switch (builtin)
+ {
+ case BuiltInLayer:
+ case BuiltInPrimitiveId:
+ case BuiltInViewportIndex:
+ expected_type = SPIRType::Int;
+ break;
+
+ default:
+ break;
+ }
+
+ if (expected_type != expr_type.basetype)
+ {
+ auto type = expr_type;
+ type.basetype = expected_type;
+ expr = bitcast_expression(type, expr_type.basetype, expr);
+ }
+}
+
+void CompilerGLSL::emit_block_hints(const SPIRBlock &)
+{
+}
+
+void CompilerGLSL::preserve_alias_on_reset(uint32_t id)
+{
+ preserved_aliases[id] = get_name(id);
+}
+
+void CompilerGLSL::reset_name_caches()
+{
+ for (auto &preserved : preserved_aliases)
+ set_name(preserved.first, preserved.second);
+
+ preserved_aliases.clear();
+ resource_names.clear();
+ block_input_names.clear();
+ block_output_names.clear();
+ block_ubo_names.clear();
+ block_ssbo_names.clear();
+ block_names.clear();
+ function_overloads.clear();
+}
diff --git a/src/3rdparty/SPIRV-Cross/spirv_glsl.hpp b/src/3rdparty/SPIRV-Cross/spirv_glsl.hpp
new file mode 100644
index 0000000..33e3547
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_glsl.hpp
@@ -0,0 +1,655 @@
+/*
+ * Copyright 2015-2019 Arm Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SPIRV_CROSS_GLSL_HPP
+#define SPIRV_CROSS_GLSL_HPP
+
+#include "spirv_cross.hpp"
+#include "GLSL.std.450.h"
+#include <sstream>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+
+namespace spirv_cross
+{
+enum PlsFormat
+{
+ PlsNone = 0,
+
+ PlsR11FG11FB10F,
+ PlsR32F,
+ PlsRG16F,
+ PlsRGB10A2,
+ PlsRGBA8,
+ PlsRG16,
+
+ PlsRGBA8I,
+ PlsRG16I,
+
+ PlsRGB10A2UI,
+ PlsRGBA8UI,
+ PlsRG16UI,
+ PlsR32UI
+};
+
+struct PlsRemap
+{
+ uint32_t id;
+ PlsFormat format;
+};
+
+enum AccessChainFlagBits
+{
+ ACCESS_CHAIN_INDEX_IS_LITERAL_BIT = 1 << 0,
+ ACCESS_CHAIN_CHAIN_ONLY_BIT = 1 << 1,
+ ACCESS_CHAIN_PTR_CHAIN_BIT = 1 << 2,
+ ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT = 1 << 3
+};
+typedef uint32_t AccessChainFlags;
+
+class CompilerGLSL : public Compiler
+{
+public:
+ struct Options
+ {
+ // The shading language version. Corresponds to #version $VALUE.
+ uint32_t version = 450;
+
+ // Emit the OpenGL ES shading language instead of desktop OpenGL.
+ bool es = false;
+
+ // Debug option to always emit temporary variables for all expressions.
+ bool force_temporary = false;
+
+ // If true, Vulkan GLSL features are used instead of GL-compatible features.
+ // Mostly useful for debugging SPIR-V files.
+ bool vulkan_semantics = false;
+
+ // If true, gl_PerVertex is explicitly redeclared in vertex, geometry and tessellation shaders.
+ // The members of gl_PerVertex is determined by which built-ins are declared by the shader.
+ // This option is ignored in ES versions, as redeclaration in ES is not required, and it depends on a different extension
+ // (EXT_shader_io_blocks) which makes things a bit more fuzzy.
+ bool separate_shader_objects = false;
+
+ // Flattens multidimensional arrays, e.g. float foo[a][b][c] into single-dimensional arrays,
+ // e.g. float foo[a * b * c].
+ // This function does not change the actual SPIRType of any object.
+ // Only the generated code, including declarations of interface variables are changed to be single array dimension.
+ bool flatten_multidimensional_arrays = false;
+
+ // For older desktop GLSL targets than version 420, the
+ // GL_ARB_shading_language_420pack extensions is used to be able to support
+ // layout(binding) on UBOs and samplers.
+ // If disabled on older targets, binding decorations will be stripped.
+ bool enable_420pack_extension = true;
+
+ // In non-Vulkan GLSL, emit push constant blocks as UBOs rather than plain uniforms.
+ bool emit_push_constant_as_uniform_buffer = false;
+
+ enum Precision
+ {
+ DontCare,
+ Lowp,
+ Mediump,
+ Highp
+ };
+
+ struct
+ {
+ // GLSL: In vertex shaders, rewrite [0, w] depth (Vulkan/D3D style) to [-w, w] depth (GL style).
+ // MSL: In vertex shaders, rewrite [-w, w] depth (GL style) to [0, w] depth.
+ // HLSL: In vertex shaders, rewrite [-w, w] depth (GL style) to [0, w] depth.
+ bool fixup_clipspace = false;
+
+ // Inverts gl_Position.y or equivalent.
+ bool flip_vert_y = false;
+
+ // GLSL only, for HLSL version of this option, see CompilerHLSL.
+ // If true, the backend will assume that InstanceIndex will need to apply
+ // a base instance offset. Set to false if you know you will never use base instance
+ // functionality as it might remove some internal uniforms.
+ bool support_nonzero_base_instance = true;
+ } vertex;
+
+ struct
+ {
+ // Add precision mediump float in ES targets when emitting GLES source.
+ // Add precision highp int in ES targets when emitting GLES source.
+ Precision default_float_precision = Mediump;
+ Precision default_int_precision = Highp;
+ } fragment;
+ };
+
+ void remap_pixel_local_storage(std::vector<PlsRemap> inputs, std::vector<PlsRemap> outputs)
+ {
+ pls_inputs = std::move(inputs);
+ pls_outputs = std::move(outputs);
+ remap_pls_variables();
+ }
+
+ explicit CompilerGLSL(std::vector<uint32_t> spirv_)
+ : Compiler(move(spirv_))
+ {
+ init();
+ }
+
+ CompilerGLSL(const uint32_t *ir_, size_t word_count)
+ : Compiler(ir_, word_count)
+ {
+ init();
+ }
+
+ explicit CompilerGLSL(const ParsedIR &ir_)
+ : Compiler(ir_)
+ {
+ init();
+ }
+
+ explicit CompilerGLSL(ParsedIR &&ir_)
+ : Compiler(std::move(ir_))
+ {
+ init();
+ }
+
+ const Options &get_common_options() const
+ {
+ return options;
+ }
+
+ void set_common_options(const Options &opts)
+ {
+ options = opts;
+ }
+
+ std::string compile() override;
+
+ // Returns the current string held in the conversion buffer. Useful for
+ // capturing what has been converted so far when compile() throws an error.
+ std::string get_partial_source();
+
+ // Adds a line to be added right after #version in GLSL backend.
+ // This is useful for enabling custom extensions which are outside the scope of SPIRV-Cross.
+ // This can be combined with variable remapping.
+ // A new-line will be added.
+ //
+ // While add_header_line() is a more generic way of adding arbitrary text to the header
+ // of a GLSL file, require_extension() should be used when adding extensions since it will
+ // avoid creating collisions with SPIRV-Cross generated extensions.
+ //
+ // Code added via add_header_line() is typically backend-specific.
+ void add_header_line(const std::string &str);
+
+ // Adds an extension which is required to run this shader, e.g.
+ // require_extension("GL_KHR_my_extension");
+ void require_extension(const std::string &ext);
+
+ // Legacy GLSL compatibility method.
+ // Takes a uniform or push constant variable and flattens it into a (i|u)vec4 array[N]; array instead.
+ // For this to work, all types in the block must be the same basic type, e.g. mixing vec2 and vec4 is fine, but
+ // mixing int and float is not.
+ // The name of the uniform array will be the same as the interface block name.
+ void flatten_buffer_block(uint32_t id);
+
+protected:
+ void reset();
+ void emit_function(SPIRFunction &func, const Bitset &return_flags);
+
+ bool has_extension(const std::string &ext) const;
+ void require_extension_internal(const std::string &ext);
+
+ // Virtualize methods which need to be overridden by subclass targets like C++ and such.
+ virtual void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags);
+
+ SPIRBlock *current_emitting_block = nullptr;
+ SPIRBlock *current_emitting_switch = nullptr;
+
+ virtual void emit_instruction(const Instruction &instr);
+ void emit_block_instructions(SPIRBlock &block);
+ virtual void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args,
+ uint32_t count);
+ virtual void emit_spv_amd_shader_ballot_op(uint32_t result_type, uint32_t result_id, uint32_t op,
+ const uint32_t *args, uint32_t count);
+ virtual void emit_spv_amd_shader_explicit_vertex_parameter_op(uint32_t result_type, uint32_t result_id, uint32_t op,
+ const uint32_t *args, uint32_t count);
+ virtual void emit_spv_amd_shader_trinary_minmax_op(uint32_t result_type, uint32_t result_id, uint32_t op,
+ const uint32_t *args, uint32_t count);
+ virtual void emit_spv_amd_gcn_shader_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args,
+ uint32_t count);
+ virtual void emit_header();
+ void build_workgroup_size(std::vector<std::string> &arguments, const SpecializationConstant &x,
+ const SpecializationConstant &y, const SpecializationConstant &z);
+
+ virtual void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id);
+ virtual void emit_texture_op(const Instruction &i);
+ virtual void emit_subgroup_op(const Instruction &i);
+ virtual std::string type_to_glsl(const SPIRType &type, uint32_t id = 0);
+ virtual std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage);
+ virtual void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index,
+ const std::string &qualifier = "", uint32_t base_offset = 0);
+ virtual std::string image_type_glsl(const SPIRType &type, uint32_t id = 0);
+ std::string constant_expression(const SPIRConstant &c);
+ std::string constant_op_expression(const SPIRConstantOp &cop);
+ virtual std::string constant_expression_vector(const SPIRConstant &c, uint32_t vector);
+ virtual void emit_fixup();
+ virtual std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id = 0);
+ virtual std::string to_func_call_arg(uint32_t id);
+ virtual std::string to_function_name(uint32_t img, const SPIRType &imgtype, bool is_fetch, bool is_gather,
+ bool is_proj, bool has_array_offsets, bool has_offset, bool has_grad,
+ bool has_dref, uint32_t lod);
+ virtual std::string to_function_args(uint32_t img, const SPIRType &imgtype, bool is_fetch, bool is_gather,
+ bool is_proj, uint32_t coord, uint32_t coord_components, uint32_t dref,
+ uint32_t grad_x, uint32_t grad_y, uint32_t lod, uint32_t coffset,
+ uint32_t offset, uint32_t bias, uint32_t comp, uint32_t sample,
+ bool *p_forward);
+ virtual void emit_buffer_block(const SPIRVariable &type);
+ virtual void emit_push_constant_block(const SPIRVariable &var);
+ virtual void emit_uniform(const SPIRVariable &var);
+ virtual std::string unpack_expression_type(std::string expr_str, const SPIRType &type, uint32_t packed_type_id);
+
+ std::unique_ptr<std::ostringstream> buffer;
+
+ template <typename T>
+ inline void statement_inner(T &&t)
+ {
+ (*buffer) << std::forward<T>(t);
+ statement_count++;
+ }
+
+ template <typename T, typename... Ts>
+ inline void statement_inner(T &&t, Ts &&... ts)
+ {
+ (*buffer) << std::forward<T>(t);
+ statement_count++;
+ statement_inner(std::forward<Ts>(ts)...);
+ }
+
+ template <typename... Ts>
+ inline void statement(Ts &&... ts)
+ {
+ if (force_recompile)
+ {
+ // Do not bother emitting code while force_recompile is active.
+ // We will compile again.
+ statement_count++;
+ return;
+ }
+
+ if (redirect_statement)
+ redirect_statement->push_back(join(std::forward<Ts>(ts)...));
+ else
+ {
+ for (uint32_t i = 0; i < indent; i++)
+ (*buffer) << " ";
+ statement_inner(std::forward<Ts>(ts)...);
+ (*buffer) << '\n';
+ }
+ }
+
+ template <typename... Ts>
+ inline void statement_no_indent(Ts &&... ts)
+ {
+ auto old_indent = indent;
+ indent = 0;
+ statement(std::forward<Ts>(ts)...);
+ indent = old_indent;
+ }
+
+ // Used for implementing continue blocks where
+ // we want to obtain a list of statements we can merge
+ // on a single line separated by comma.
+ std::vector<std::string> *redirect_statement = nullptr;
+ const SPIRBlock *current_continue_block = nullptr;
+
+ void begin_scope();
+ void end_scope();
+ void end_scope_decl();
+ void end_scope_decl(const std::string &decl);
+
+ Options options;
+
+ std::string type_to_array_glsl(const SPIRType &type);
+ std::string to_array_size(const SPIRType &type, uint32_t index);
+ uint32_t to_array_size_literal(const SPIRType &type, uint32_t index) const;
+ uint32_t to_array_size_literal(const SPIRType &type) const;
+ std::string variable_decl(const SPIRVariable &variable);
+ std::string variable_decl_function_local(SPIRVariable &variable);
+
+ void add_local_variable_name(uint32_t id);
+ void add_resource_name(uint32_t id);
+ void add_member_name(SPIRType &type, uint32_t name);
+ void add_function_overload(const SPIRFunction &func);
+
+ virtual bool is_non_native_row_major_matrix(uint32_t id);
+ virtual bool member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index);
+ bool member_is_packed_type(const SPIRType &type, uint32_t index) const;
+ virtual std::string convert_row_major_matrix(std::string exp_str, const SPIRType &exp_type, bool is_packed);
+
+ std::unordered_set<std::string> local_variable_names;
+ std::unordered_set<std::string> resource_names;
+ std::unordered_set<std::string> block_input_names;
+ std::unordered_set<std::string> block_output_names;
+ std::unordered_set<std::string> block_ubo_names;
+ std::unordered_set<std::string> block_ssbo_names;
+ std::unordered_set<std::string> block_names; // A union of all block_*_names.
+ std::unordered_map<std::string, std::unordered_set<uint64_t>> function_overloads;
+ std::unordered_map<uint32_t, std::string> preserved_aliases;
+ void preserve_alias_on_reset(uint32_t id);
+ void reset_name_caches();
+
+ bool processing_entry_point = false;
+
+ // Can be overriden by subclass backends for trivial things which
+ // shouldn't need polymorphism.
+ struct BackendVariations
+ {
+ std::string discard_literal = "discard";
+ std::string null_pointer_literal = "";
+ bool float_literal_suffix = false;
+ bool double_literal_suffix = true;
+ bool uint32_t_literal_suffix = true;
+ bool long_long_literal_suffix = false;
+ const char *basic_int_type = "int";
+ const char *basic_uint_type = "uint";
+ const char *basic_int8_type = "int8_t";
+ const char *basic_uint8_type = "uint8_t";
+ const char *basic_int16_type = "int16_t";
+ const char *basic_uint16_type = "uint16_t";
+ const char *int16_t_literal_suffix = "s";
+ const char *uint16_t_literal_suffix = "us";
+ bool swizzle_is_function = false;
+ bool shared_is_implied = false;
+ bool flexible_member_array_supported = true;
+ bool explicit_struct_type = false;
+ bool use_initializer_list = false;
+ bool use_typed_initializer_list = false;
+ bool can_declare_struct_inline = true;
+ bool can_declare_arrays_inline = true;
+ bool native_row_major_matrix = true;
+ bool use_constructor_splatting = true;
+ bool boolean_mix_support = true;
+ bool allow_precision_qualifiers = false;
+ bool can_swizzle_scalar = false;
+ bool force_gl_in_out_block = false;
+ bool can_return_array = true;
+ bool allow_truncated_access_chain = false;
+ bool supports_extensions = false;
+ bool supports_empty_struct = false;
+ bool array_is_value_type = true;
+ bool comparison_image_samples_scalar = false;
+ } backend;
+
+ void emit_struct(SPIRType &type);
+ void emit_resources();
+ void emit_buffer_block_native(const SPIRVariable &var);
+ void emit_buffer_block_legacy(const SPIRVariable &var);
+ void emit_buffer_block_flattened(const SPIRVariable &type);
+ void emit_declared_builtin_block(spv::StorageClass storage, spv::ExecutionModel model);
+ void emit_push_constant_block_vulkan(const SPIRVariable &var);
+ void emit_push_constant_block_glsl(const SPIRVariable &var);
+ void emit_interface_block(const SPIRVariable &type);
+ void emit_flattened_io_block(const SPIRVariable &var, const char *qual);
+ void emit_block_chain(SPIRBlock &block);
+ void emit_hoisted_temporaries(std::vector<std::pair<uint32_t, uint32_t>> &temporaries);
+ std::string constant_value_macro_name(uint32_t id);
+ void emit_constant(const SPIRConstant &constant);
+ void emit_specialization_constant_op(const SPIRConstantOp &constant);
+ std::string emit_continue_block(uint32_t continue_block, bool follow_true_block, bool follow_false_block);
+ bool attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method method);
+ void propagate_loop_dominators(const SPIRBlock &block);
+
+ void branch(uint32_t from, uint32_t to);
+ void branch_to_continue(uint32_t from, uint32_t to);
+ void branch(uint32_t from, uint32_t cond, uint32_t true_block, uint32_t false_block);
+ void flush_phi(uint32_t from, uint32_t to);
+ bool flush_phi_required(uint32_t from, uint32_t to);
+ void flush_variable_declaration(uint32_t id);
+ void flush_undeclared_variables(SPIRBlock &block);
+
+ bool should_dereference(uint32_t id);
+ bool should_forward(uint32_t id);
+ void emit_mix_op(uint32_t result_type, uint32_t id, uint32_t left, uint32_t right, uint32_t lerp);
+ void emit_nminmax_op(uint32_t result_type, uint32_t id, uint32_t op0, uint32_t op1, GLSLstd450 op);
+ bool to_trivial_mix_op(const SPIRType &type, std::string &op, uint32_t left, uint32_t right, uint32_t lerp);
+ void emit_quaternary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2,
+ uint32_t op3, const char *op);
+ void emit_trinary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2,
+ const char *op);
+ void emit_binary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op);
+
+ void emit_unary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op,
+ SPIRType::BaseType input_type, SPIRType::BaseType expected_result_type);
+ void emit_binary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op,
+ SPIRType::BaseType input_type, bool skip_cast_if_equal_type);
+ void emit_trinary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, const char *op,
+ SPIRType::BaseType input_type);
+
+ void emit_unary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op);
+ void emit_unrolled_unary_op(uint32_t result_type, uint32_t result_id, uint32_t operand, const char *op);
+ void emit_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op);
+ void emit_unrolled_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op);
+ void emit_binary_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op,
+ SPIRType::BaseType input_type, bool skip_cast_if_equal_type);
+
+ SPIRType binary_op_bitcast_helper(std::string &cast_op0, std::string &cast_op1, SPIRType::BaseType &input_type,
+ uint32_t op0, uint32_t op1, bool skip_cast_if_equal_type);
+
+ std::string to_ternary_expression(const SPIRType &result_type, uint32_t select, uint32_t true_value,
+ uint32_t false_value);
+
+ void emit_unary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op);
+ bool expression_is_forwarded(uint32_t id);
+ SPIRExpression &emit_op(uint32_t result_type, uint32_t result_id, const std::string &rhs, bool forward_rhs,
+ bool suppress_usage_tracking = false);
+
+ std::string access_chain_internal(uint32_t base, const uint32_t *indices, uint32_t count, AccessChainFlags flags,
+ AccessChainMeta *meta);
+
+ std::string access_chain(uint32_t base, const uint32_t *indices, uint32_t count, const SPIRType &target_type,
+ AccessChainMeta *meta = nullptr, bool ptr_chain = false);
+
+ std::string flattened_access_chain(uint32_t base, const uint32_t *indices, uint32_t count,
+ const SPIRType &target_type, uint32_t offset, uint32_t matrix_stride,
+ bool need_transpose);
+ std::string flattened_access_chain_struct(uint32_t base, const uint32_t *indices, uint32_t count,
+ const SPIRType &target_type, uint32_t offset);
+ std::string flattened_access_chain_matrix(uint32_t base, const uint32_t *indices, uint32_t count,
+ const SPIRType &target_type, uint32_t offset, uint32_t matrix_stride,
+ bool need_transpose);
+ std::string flattened_access_chain_vector(uint32_t base, const uint32_t *indices, uint32_t count,
+ const SPIRType &target_type, uint32_t offset, uint32_t matrix_stride,
+ bool need_transpose);
+ std::pair<std::string, uint32_t> flattened_access_chain_offset(const SPIRType &basetype, const uint32_t *indices,
+ uint32_t count, uint32_t offset,
+ uint32_t word_stride, bool *need_transpose = nullptr,
+ uint32_t *matrix_stride = nullptr,
+ bool ptr_chain = false);
+
+ const char *index_to_swizzle(uint32_t index);
+ std::string remap_swizzle(const SPIRType &result_type, uint32_t input_components, const std::string &expr);
+ std::string declare_temporary(uint32_t type, uint32_t id);
+ void append_global_func_args(const SPIRFunction &func, uint32_t index, std::vector<std::string> &arglist);
+ std::string to_expression(uint32_t id, bool register_expression_read = true);
+ std::string to_enclosed_expression(uint32_t id, bool register_expression_read = true);
+ std::string to_unpacked_expression(uint32_t id, bool register_expression_read = true);
+ std::string to_enclosed_unpacked_expression(uint32_t id, bool register_expression_read = true);
+ std::string to_dereferenced_expression(uint32_t id, bool register_expression_read = true);
+ std::string to_pointer_expression(uint32_t id, bool register_expression_read = true);
+ std::string to_enclosed_pointer_expression(uint32_t id, bool register_expression_read = true);
+ std::string to_extract_component_expression(uint32_t id, uint32_t index);
+ std::string enclose_expression(const std::string &expr);
+ std::string dereference_expression(const std::string &expr);
+ std::string address_of_expression(const std::string &expr);
+ void strip_enclosed_expression(std::string &expr);
+ std::string to_member_name(const SPIRType &type, uint32_t index);
+ virtual std::string to_member_reference(uint32_t base, const SPIRType &type, uint32_t index, bool ptr_chain);
+ std::string type_to_glsl_constructor(const SPIRType &type);
+ std::string argument_decl(const SPIRFunction::Parameter &arg);
+ virtual std::string to_qualifiers_glsl(uint32_t id);
+ const char *to_precision_qualifiers_glsl(uint32_t id);
+ virtual const char *to_storage_qualifiers_glsl(const SPIRVariable &var);
+ const char *flags_to_precision_qualifiers_glsl(const SPIRType &type, const Bitset &flags);
+ const char *format_to_glsl(spv::ImageFormat format);
+ virtual std::string layout_for_member(const SPIRType &type, uint32_t index);
+ virtual std::string to_interpolation_qualifiers(const Bitset &flags);
+ std::string layout_for_variable(const SPIRVariable &variable);
+ std::string to_combined_image_sampler(uint32_t image_id, uint32_t samp_id);
+ virtual bool skip_argument(uint32_t id) const;
+ virtual void emit_array_copy(const std::string &lhs, uint32_t rhs_id);
+ virtual void emit_block_hints(const SPIRBlock &block);
+ virtual std::string to_initializer_expression(const SPIRVariable &var);
+
+ bool buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing, uint32_t start_offset = 0,
+ uint32_t end_offset = ~(0u));
+ uint32_t type_to_packed_base_size(const SPIRType &type, BufferPackingStandard packing);
+ uint32_t type_to_packed_alignment(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing);
+ uint32_t type_to_packed_array_stride(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing);
+ uint32_t type_to_packed_size(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing);
+
+ std::string bitcast_glsl(const SPIRType &result_type, uint32_t arg);
+ virtual std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type);
+
+ std::string bitcast_expression(SPIRType::BaseType target_type, uint32_t arg);
+ std::string bitcast_expression(const SPIRType &target_type, SPIRType::BaseType expr_type, const std::string &expr);
+
+ std::string build_composite_combiner(uint32_t result_type, const uint32_t *elems, uint32_t length);
+ bool remove_duplicate_swizzle(std::string &op);
+ bool remove_unity_swizzle(uint32_t base, std::string &op);
+
+ // Can modify flags to remote readonly/writeonly if image type
+ // and force recompile.
+ bool check_atomic_image(uint32_t id);
+
+ virtual void replace_illegal_names();
+ virtual void emit_entry_point_declarations();
+
+ void replace_fragment_output(SPIRVariable &var);
+ void replace_fragment_outputs();
+ bool check_explicit_lod_allowed(uint32_t lod);
+ std::string legacy_tex_op(const std::string &op, const SPIRType &imgtype, uint32_t lod, uint32_t id);
+
+ uint32_t indent = 0;
+
+ std::unordered_set<uint32_t> emitted_functions;
+
+ std::unordered_set<uint32_t> flattened_buffer_blocks;
+ std::unordered_set<uint32_t> flattened_structs;
+
+ std::string load_flattened_struct(SPIRVariable &var);
+ std::string to_flattened_struct_member(const SPIRVariable &var, uint32_t index);
+ void store_flattened_struct(SPIRVariable &var, uint32_t value);
+
+ // Usage tracking. If a temporary is used more than once, use the temporary instead to
+ // avoid AST explosion when SPIRV is generated with pure SSA and doesn't write stuff to variables.
+ std::unordered_map<uint32_t, uint32_t> expression_usage_counts;
+ void track_expression_read(uint32_t id);
+
+ std::vector<std::string> forced_extensions;
+ std::vector<std::string> header_lines;
+
+ // Used when expressions emit extra opcodes with their own unique IDs,
+ // and we need to reuse the IDs across recompilation loops.
+ // Currently used by NMin/Max/Clamp implementations.
+ std::unordered_map<uint32_t, uint32_t> extra_sub_expressions;
+
+ uint32_t statement_count;
+
+ inline bool is_legacy() const
+ {
+ return (options.es && options.version < 300) || (!options.es && options.version < 130);
+ }
+
+ inline bool is_legacy_es() const
+ {
+ return options.es && options.version < 300;
+ }
+
+ inline bool is_legacy_desktop() const
+ {
+ return !options.es && options.version < 130;
+ }
+
+ bool args_will_forward(uint32_t id, const uint32_t *args, uint32_t num_args, bool pure);
+ void register_call_out_argument(uint32_t id);
+ void register_impure_function_call();
+ void register_control_dependent_expression(uint32_t expr);
+
+ // GL_EXT_shader_pixel_local_storage support.
+ std::vector<PlsRemap> pls_inputs;
+ std::vector<PlsRemap> pls_outputs;
+ std::string pls_decl(const PlsRemap &variable);
+ const char *to_pls_qualifiers_glsl(const SPIRVariable &variable);
+ void emit_pls();
+ void remap_pls_variables();
+
+ // A variant which takes two sets of name. The secondary is only used to verify there are no collisions,
+ // but the set is not updated when we have found a new name.
+ // Used primarily when adding block interface names.
+ void add_variable(std::unordered_set<std::string> &variables_primary,
+ const std::unordered_set<std::string> &variables_secondary, std::string &name);
+
+ void check_function_call_constraints(const uint32_t *args, uint32_t length);
+ void handle_invalid_expression(uint32_t id);
+ void find_static_extensions();
+
+ std::string emit_for_loop_initializers(const SPIRBlock &block);
+ void emit_while_loop_initializers(const SPIRBlock &block);
+ bool for_loop_initializers_are_same_type(const SPIRBlock &block);
+ bool optimize_read_modify_write(const SPIRType &type, const std::string &lhs, const std::string &rhs);
+ void fixup_image_load_store_access();
+
+ bool type_is_empty(const SPIRType &type);
+
+ virtual void declare_undefined_values();
+
+ static std::string sanitize_underscores(const std::string &str);
+
+ bool can_use_io_location(spv::StorageClass storage, bool block);
+ const Instruction *get_next_instruction_in_block(const Instruction &instr);
+ static uint32_t mask_relevant_memory_semantics(uint32_t semantics);
+
+ std::string convert_half_to_string(const SPIRConstant &value, uint32_t col, uint32_t row);
+ std::string convert_float_to_string(const SPIRConstant &value, uint32_t col, uint32_t row);
+ std::string convert_double_to_string(const SPIRConstant &value, uint32_t col, uint32_t row);
+
+ std::string convert_separate_image_to_expression(uint32_t id);
+
+ // Builtins in GLSL are always specific signedness, but the SPIR-V can declare them
+ // as either unsigned or signed.
+ // Sometimes we will need to automatically perform bitcasts on load and store to make this work.
+ virtual void bitcast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type);
+ virtual void bitcast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type);
+ void unroll_array_from_complex_load(uint32_t target_id, uint32_t source_id, std::string &expr);
+
+ void handle_store_to_invariant_variable(uint32_t store_id, uint32_t value_id);
+ void disallow_forwarding_in_expression_chain(const SPIRExpression &expr);
+
+ bool expression_is_constant_null(uint32_t id) const;
+ virtual void emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression);
+
+ uint32_t get_integer_width_for_instruction(const Instruction &instr) const;
+ uint32_t get_integer_width_for_glsl_instruction(GLSLstd450 op, const uint32_t *arguments, uint32_t length) const;
+
+ bool variable_is_lut(const SPIRVariable &var) const;
+
+ char current_locale_radix_character = '.';
+
+private:
+ void init();
+};
+} // namespace spirv_cross
+
+#endif
diff --git a/src/3rdparty/SPIRV-Cross/spirv_hlsl.cpp b/src/3rdparty/SPIRV-Cross/spirv_hlsl.cpp
new file mode 100644
index 0000000..3f6b627
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_hlsl.cpp
@@ -0,0 +1,4705 @@
+/*
+ * Copyright 2016-2019 Robert Konrad
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "spirv_hlsl.hpp"
+#include "GLSL.std.450.h"
+#include <algorithm>
+#include <assert.h>
+
+using namespace spv;
+using namespace spirv_cross;
+using namespace std;
+
+static unsigned image_format_to_components(ImageFormat fmt)
+{
+ switch (fmt)
+ {
+ case ImageFormatR8:
+ case ImageFormatR16:
+ case ImageFormatR8Snorm:
+ case ImageFormatR16Snorm:
+ case ImageFormatR16f:
+ case ImageFormatR32f:
+ case ImageFormatR8i:
+ case ImageFormatR16i:
+ case ImageFormatR32i:
+ case ImageFormatR8ui:
+ case ImageFormatR16ui:
+ case ImageFormatR32ui:
+ return 1;
+
+ case ImageFormatRg8:
+ case ImageFormatRg16:
+ case ImageFormatRg8Snorm:
+ case ImageFormatRg16Snorm:
+ case ImageFormatRg16f:
+ case ImageFormatRg32f:
+ case ImageFormatRg8i:
+ case ImageFormatRg16i:
+ case ImageFormatRg32i:
+ case ImageFormatRg8ui:
+ case ImageFormatRg16ui:
+ case ImageFormatRg32ui:
+ return 2;
+
+ case ImageFormatR11fG11fB10f:
+ return 3;
+
+ case ImageFormatRgba8:
+ case ImageFormatRgba16:
+ case ImageFormatRgb10A2:
+ case ImageFormatRgba8Snorm:
+ case ImageFormatRgba16Snorm:
+ case ImageFormatRgba16f:
+ case ImageFormatRgba32f:
+ case ImageFormatRgba8i:
+ case ImageFormatRgba16i:
+ case ImageFormatRgba32i:
+ case ImageFormatRgba8ui:
+ case ImageFormatRgba16ui:
+ case ImageFormatRgba32ui:
+ case ImageFormatRgb10a2ui:
+ return 4;
+
+ case ImageFormatUnknown:
+ return 4; // Assume 4.
+
+ default:
+ SPIRV_CROSS_THROW("Unrecognized typed image format.");
+ }
+}
+
+static string image_format_to_type(ImageFormat fmt, SPIRType::BaseType basetype)
+{
+ switch (fmt)
+ {
+ case ImageFormatR8:
+ case ImageFormatR16:
+ if (basetype != SPIRType::Float)
+ SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
+ return "unorm float";
+ case ImageFormatRg8:
+ case ImageFormatRg16:
+ if (basetype != SPIRType::Float)
+ SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
+ return "unorm float2";
+ case ImageFormatRgba8:
+ case ImageFormatRgba16:
+ if (basetype != SPIRType::Float)
+ SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
+ return "unorm float4";
+ case ImageFormatRgb10A2:
+ if (basetype != SPIRType::Float)
+ SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
+ return "unorm float4";
+
+ case ImageFormatR8Snorm:
+ case ImageFormatR16Snorm:
+ if (basetype != SPIRType::Float)
+ SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
+ return "snorm float";
+ case ImageFormatRg8Snorm:
+ case ImageFormatRg16Snorm:
+ if (basetype != SPIRType::Float)
+ SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
+ return "snorm float2";
+ case ImageFormatRgba8Snorm:
+ case ImageFormatRgba16Snorm:
+ if (basetype != SPIRType::Float)
+ SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
+ return "snorm float4";
+
+ case ImageFormatR16f:
+ case ImageFormatR32f:
+ if (basetype != SPIRType::Float)
+ SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
+ return "float";
+ case ImageFormatRg16f:
+ case ImageFormatRg32f:
+ if (basetype != SPIRType::Float)
+ SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
+ return "float2";
+ case ImageFormatRgba16f:
+ case ImageFormatRgba32f:
+ if (basetype != SPIRType::Float)
+ SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
+ return "float4";
+
+ case ImageFormatR11fG11fB10f:
+ if (basetype != SPIRType::Float)
+ SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
+ return "float3";
+
+ case ImageFormatR8i:
+ case ImageFormatR16i:
+ case ImageFormatR32i:
+ if (basetype != SPIRType::Int)
+ SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
+ return "int";
+ case ImageFormatRg8i:
+ case ImageFormatRg16i:
+ case ImageFormatRg32i:
+ if (basetype != SPIRType::Int)
+ SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
+ return "int2";
+ case ImageFormatRgba8i:
+ case ImageFormatRgba16i:
+ case ImageFormatRgba32i:
+ if (basetype != SPIRType::Int)
+ SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
+ return "int4";
+
+ case ImageFormatR8ui:
+ case ImageFormatR16ui:
+ case ImageFormatR32ui:
+ if (basetype != SPIRType::UInt)
+ SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
+ return "uint";
+ case ImageFormatRg8ui:
+ case ImageFormatRg16ui:
+ case ImageFormatRg32ui:
+ if (basetype != SPIRType::UInt)
+ SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
+ return "uint2";
+ case ImageFormatRgba8ui:
+ case ImageFormatRgba16ui:
+ case ImageFormatRgba32ui:
+ if (basetype != SPIRType::UInt)
+ SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
+ return "uint4";
+ case ImageFormatRgb10a2ui:
+ if (basetype != SPIRType::UInt)
+ SPIRV_CROSS_THROW("Mismatch in image type and base type of image.");
+ return "uint4";
+
+ case ImageFormatUnknown:
+ switch (basetype)
+ {
+ case SPIRType::Float:
+ return "float4";
+ case SPIRType::Int:
+ return "int4";
+ case SPIRType::UInt:
+ return "uint4";
+ default:
+ SPIRV_CROSS_THROW("Unsupported base type for image.");
+ }
+
+ default:
+ SPIRV_CROSS_THROW("Unrecognized typed image format.");
+ }
+}
+
+string CompilerHLSL::image_type_hlsl_modern(const SPIRType &type, uint32_t)
+{
+ auto &imagetype = get<SPIRType>(type.image.type);
+ const char *dim = nullptr;
+ bool typed_load = false;
+ uint32_t components = 4;
+
+ switch (type.image.dim)
+ {
+ case Dim1D:
+ typed_load = type.image.sampled == 2;
+ dim = "1D";
+ break;
+ case Dim2D:
+ typed_load = type.image.sampled == 2;
+ dim = "2D";
+ break;
+ case Dim3D:
+ typed_load = type.image.sampled == 2;
+ dim = "3D";
+ break;
+ case DimCube:
+ if (type.image.sampled == 2)
+ SPIRV_CROSS_THROW("RWTextureCube does not exist in HLSL.");
+ dim = "Cube";
+ break;
+ case DimRect:
+ SPIRV_CROSS_THROW("Rectangle texture support is not yet implemented for HLSL."); // TODO
+ case DimBuffer:
+ if (type.image.sampled == 1)
+ return join("Buffer<", type_to_glsl(imagetype), components, ">");
+ else if (type.image.sampled == 2)
+ return join("RWBuffer<", image_format_to_type(type.image.format, imagetype.basetype), ">");
+ else
+ SPIRV_CROSS_THROW("Sampler buffers must be either sampled or unsampled. Cannot deduce in runtime.");
+ case DimSubpassData:
+ dim = "2D";
+ typed_load = false;
+ break;
+ default:
+ SPIRV_CROSS_THROW("Invalid dimension.");
+ }
+ const char *arrayed = type.image.arrayed ? "Array" : "";
+ const char *ms = type.image.ms ? "MS" : "";
+ const char *rw = typed_load ? "RW" : "";
+ return join(rw, "Texture", dim, ms, arrayed, "<",
+ typed_load ? image_format_to_type(type.image.format, imagetype.basetype) :
+ join(type_to_glsl(imagetype), components),
+ ">");
+}
+
+string CompilerHLSL::image_type_hlsl_legacy(const SPIRType &type, uint32_t id)
+{
+ auto &imagetype = get<SPIRType>(type.image.type);
+ string res;
+
+ switch (imagetype.basetype)
+ {
+ case SPIRType::Int:
+ res = "i";
+ break;
+ case SPIRType::UInt:
+ res = "u";
+ break;
+ default:
+ break;
+ }
+
+ if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData)
+ return res + "subpassInput" + (type.image.ms ? "MS" : "");
+
+ // If we're emulating subpassInput with samplers, force sampler2D
+ // so we don't have to specify format.
+ if (type.basetype == SPIRType::Image && type.image.dim != DimSubpassData)
+ {
+ // Sampler buffers are always declared as samplerBuffer even though they might be separate images in the SPIR-V.
+ if (type.image.dim == DimBuffer && type.image.sampled == 1)
+ res += "sampler";
+ else
+ res += type.image.sampled == 2 ? "image" : "texture";
+ }
+ else
+ res += "sampler";
+
+ switch (type.image.dim)
+ {
+ case Dim1D:
+ res += "1D";
+ break;
+ case Dim2D:
+ res += "2D";
+ break;
+ case Dim3D:
+ res += "3D";
+ break;
+ case DimCube:
+ res += "CUBE";
+ break;
+
+ case DimBuffer:
+ res += "Buffer";
+ break;
+
+ case DimSubpassData:
+ res += "2D";
+ break;
+ default:
+ SPIRV_CROSS_THROW("Only 1D, 2D, 3D, Buffer, InputTarget and Cube textures supported.");
+ }
+
+ if (type.image.ms)
+ res += "MS";
+ if (type.image.arrayed)
+ res += "Array";
+ if (image_is_comparison(type, id))
+ res += "Shadow";
+
+ return res;
+}
+
+string CompilerHLSL::image_type_hlsl(const SPIRType &type, uint32_t id)
+{
+ if (hlsl_options.shader_model <= 30)
+ return image_type_hlsl_legacy(type, id);
+ else
+ return image_type_hlsl_modern(type, id);
+}
+
+// The optional id parameter indicates the object whose type we are trying
+// to find the description for. It is optional. Most type descriptions do not
+// depend on a specific object's use of that type.
+string CompilerHLSL::type_to_glsl(const SPIRType &type, uint32_t id)
+{
+ // Ignore the pointer type since GLSL doesn't have pointers.
+
+ switch (type.basetype)
+ {
+ case SPIRType::Struct:
+ // Need OpName lookup here to get a "sensible" name for a struct.
+ if (backend.explicit_struct_type)
+ return join("struct ", to_name(type.self));
+ else
+ return to_name(type.self);
+
+ case SPIRType::Image:
+ case SPIRType::SampledImage:
+ return image_type_hlsl(type, id);
+
+ case SPIRType::Sampler:
+ return comparison_ids.count(id) ? "SamplerComparisonState" : "SamplerState";
+
+ case SPIRType::Void:
+ return "void";
+
+ default:
+ break;
+ }
+
+ if (type.vecsize == 1 && type.columns == 1) // Scalar builtin
+ {
+ switch (type.basetype)
+ {
+ case SPIRType::Boolean:
+ return "bool";
+ case SPIRType::Int:
+ return backend.basic_int_type;
+ case SPIRType::UInt:
+ return backend.basic_uint_type;
+ case SPIRType::AtomicCounter:
+ return "atomic_uint";
+ case SPIRType::Half:
+ return "min16float";
+ case SPIRType::Float:
+ return "float";
+ case SPIRType::Double:
+ return "double";
+ case SPIRType::Int64:
+ return "int64_t";
+ case SPIRType::UInt64:
+ return "uint64_t";
+ default:
+ return "???";
+ }
+ }
+ else if (type.vecsize > 1 && type.columns == 1) // Vector builtin
+ {
+ switch (type.basetype)
+ {
+ case SPIRType::Boolean:
+ return join("bool", type.vecsize);
+ case SPIRType::Int:
+ return join("int", type.vecsize);
+ case SPIRType::UInt:
+ return join("uint", type.vecsize);
+ case SPIRType::Half:
+ return join("min16float", type.vecsize);
+ case SPIRType::Float:
+ return join("float", type.vecsize);
+ case SPIRType::Double:
+ return join("double", type.vecsize);
+ case SPIRType::Int64:
+ return join("i64vec", type.vecsize);
+ case SPIRType::UInt64:
+ return join("u64vec", type.vecsize);
+ default:
+ return "???";
+ }
+ }
+ else
+ {
+ switch (type.basetype)
+ {
+ case SPIRType::Boolean:
+ return join("bool", type.columns, "x", type.vecsize);
+ case SPIRType::Int:
+ return join("int", type.columns, "x", type.vecsize);
+ case SPIRType::UInt:
+ return join("uint", type.columns, "x", type.vecsize);
+ case SPIRType::Half:
+ return join("min16float", type.columns, "x", type.vecsize);
+ case SPIRType::Float:
+ return join("float", type.columns, "x", type.vecsize);
+ case SPIRType::Double:
+ return join("double", type.columns, "x", type.vecsize);
+ // Matrix types not supported for int64/uint64.
+ default:
+ return "???";
+ }
+ }
+}
+
+void CompilerHLSL::emit_header()
+{
+ for (auto &header : header_lines)
+ statement(header);
+
+ if (header_lines.size() > 0)
+ {
+ statement("");
+ }
+}
+
+void CompilerHLSL::emit_interface_block_globally(const SPIRVariable &var)
+{
+ add_resource_name(var.self);
+
+ // The global copies of I/O variables should not contain interpolation qualifiers.
+ // These are emitted inside the interface structs.
+ auto &flags = ir.meta[var.self].decoration.decoration_flags;
+ auto old_flags = flags;
+ flags.reset();
+ statement("static ", variable_decl(var), ";");
+ flags = old_flags;
+}
+
+const char *CompilerHLSL::to_storage_qualifiers_glsl(const SPIRVariable &var)
+{
+ // Input and output variables are handled specially in HLSL backend.
+ // The variables are declared as global, private variables, and do not need any qualifiers.
+ if (var.storage == StorageClassUniformConstant || var.storage == StorageClassUniform ||
+ var.storage == StorageClassPushConstant)
+ {
+ return "uniform ";
+ }
+
+ return "";
+}
+
+void CompilerHLSL::emit_builtin_outputs_in_struct()
+{
+ auto &execution = get_entry_point();
+
+ bool legacy = hlsl_options.shader_model <= 30;
+ active_output_builtins.for_each_bit([&](uint32_t i) {
+ const char *type = nullptr;
+ const char *semantic = nullptr;
+ auto builtin = static_cast<BuiltIn>(i);
+ switch (builtin)
+ {
+ case BuiltInPosition:
+ type = "float4";
+ semantic = legacy ? "POSITION" : "SV_Position";
+ break;
+
+ case BuiltInFragDepth:
+ type = "float";
+ if (legacy)
+ {
+ semantic = "DEPTH";
+ }
+ else
+ {
+ if (hlsl_options.shader_model >= 50 && execution.flags.get(ExecutionModeDepthGreater))
+ semantic = "SV_DepthGreaterEqual";
+ else if (hlsl_options.shader_model >= 50 && execution.flags.get(ExecutionModeDepthLess))
+ semantic = "SV_DepthLessEqual";
+ else
+ semantic = "SV_Depth";
+ }
+ break;
+
+ case BuiltInClipDistance:
+ // HLSL is a bit weird here, use SV_ClipDistance0, SV_ClipDistance1 and so on with vectors.
+ for (uint32_t clip = 0; clip < clip_distance_count; clip += 4)
+ {
+ uint32_t to_declare = clip_distance_count - clip;
+ if (to_declare > 4)
+ to_declare = 4;
+
+ uint32_t semantic_index = clip / 4;
+
+ static const char *types[] = { "float", "float2", "float3", "float4" };
+ statement(types[to_declare - 1], " ", builtin_to_glsl(builtin, StorageClassOutput), semantic_index,
+ " : SV_ClipDistance", semantic_index, ";");
+ }
+ break;
+
+ case BuiltInCullDistance:
+ // HLSL is a bit weird here, use SV_CullDistance0, SV_CullDistance1 and so on with vectors.
+ for (uint32_t cull = 0; cull < cull_distance_count; cull += 4)
+ {
+ uint32_t to_declare = cull_distance_count - cull;
+ if (to_declare > 4)
+ to_declare = 4;
+
+ uint32_t semantic_index = cull / 4;
+
+ static const char *types[] = { "float", "float2", "float3", "float4" };
+ statement(types[to_declare - 1], " ", builtin_to_glsl(builtin, StorageClassOutput), semantic_index,
+ " : SV_CullDistance", semantic_index, ";");
+ }
+ break;
+
+ case BuiltInPointSize:
+ // If point_size_compat is enabled, just ignore PointSize.
+ // PointSize does not exist in HLSL, but some code bases might want to be able to use these shaders,
+ // even if it means working around the missing feature.
+ if (hlsl_options.point_size_compat)
+ break;
+ else
+ SPIRV_CROSS_THROW("Unsupported builtin in HLSL.");
+
+ default:
+ SPIRV_CROSS_THROW("Unsupported builtin in HLSL.");
+ break;
+ }
+
+ if (type && semantic)
+ statement(type, " ", builtin_to_glsl(builtin, StorageClassOutput), " : ", semantic, ";");
+ });
+}
+
+void CompilerHLSL::emit_builtin_inputs_in_struct()
+{
+ bool legacy = hlsl_options.shader_model <= 30;
+ active_input_builtins.for_each_bit([&](uint32_t i) {
+ const char *type = nullptr;
+ const char *semantic = nullptr;
+ auto builtin = static_cast<BuiltIn>(i);
+ switch (builtin)
+ {
+ case BuiltInFragCoord:
+ type = "float4";
+ semantic = legacy ? "VPOS" : "SV_Position";
+ break;
+
+ case BuiltInVertexId:
+ case BuiltInVertexIndex:
+ if (legacy)
+ SPIRV_CROSS_THROW("Vertex index not supported in SM 3.0 or lower.");
+ type = "uint";
+ semantic = "SV_VertexID";
+ break;
+
+ case BuiltInInstanceId:
+ case BuiltInInstanceIndex:
+ if (legacy)
+ SPIRV_CROSS_THROW("Instance index not supported in SM 3.0 or lower.");
+ type = "uint";
+ semantic = "SV_InstanceID";
+ break;
+
+ case BuiltInSampleId:
+ if (legacy)
+ SPIRV_CROSS_THROW("Sample ID not supported in SM 3.0 or lower.");
+ type = "uint";
+ semantic = "SV_SampleIndex";
+ break;
+
+ case BuiltInGlobalInvocationId:
+ type = "uint3";
+ semantic = "SV_DispatchThreadID";
+ break;
+
+ case BuiltInLocalInvocationId:
+ type = "uint3";
+ semantic = "SV_GroupThreadID";
+ break;
+
+ case BuiltInLocalInvocationIndex:
+ type = "uint";
+ semantic = "SV_GroupIndex";
+ break;
+
+ case BuiltInWorkgroupId:
+ type = "uint3";
+ semantic = "SV_GroupID";
+ break;
+
+ case BuiltInFrontFacing:
+ type = "bool";
+ semantic = "SV_IsFrontFace";
+ break;
+
+ case BuiltInNumWorkgroups:
+ case BuiltInSubgroupSize:
+ case BuiltInSubgroupLocalInvocationId:
+ case BuiltInSubgroupEqMask:
+ case BuiltInSubgroupLtMask:
+ case BuiltInSubgroupLeMask:
+ case BuiltInSubgroupGtMask:
+ case BuiltInSubgroupGeMask:
+ // Handled specially.
+ break;
+
+ case BuiltInClipDistance:
+ // HLSL is a bit weird here, use SV_ClipDistance0, SV_ClipDistance1 and so on with vectors.
+ for (uint32_t clip = 0; clip < clip_distance_count; clip += 4)
+ {
+ uint32_t to_declare = clip_distance_count - clip;
+ if (to_declare > 4)
+ to_declare = 4;
+
+ uint32_t semantic_index = clip / 4;
+
+ static const char *types[] = { "float", "float2", "float3", "float4" };
+ statement(types[to_declare - 1], " ", builtin_to_glsl(builtin, StorageClassInput), semantic_index,
+ " : SV_ClipDistance", semantic_index, ";");
+ }
+ break;
+
+ case BuiltInCullDistance:
+ // HLSL is a bit weird here, use SV_CullDistance0, SV_CullDistance1 and so on with vectors.
+ for (uint32_t cull = 0; cull < cull_distance_count; cull += 4)
+ {
+ uint32_t to_declare = cull_distance_count - cull;
+ if (to_declare > 4)
+ to_declare = 4;
+
+ uint32_t semantic_index = cull / 4;
+
+ static const char *types[] = { "float", "float2", "float3", "float4" };
+ statement(types[to_declare - 1], " ", builtin_to_glsl(builtin, StorageClassInput), semantic_index,
+ " : SV_CullDistance", semantic_index, ";");
+ }
+ break;
+
+ case BuiltInPointCoord:
+ // PointCoord is not supported, but provide a way to just ignore that, similar to PointSize.
+ if (hlsl_options.point_coord_compat)
+ break;
+ else
+ SPIRV_CROSS_THROW("Unsupported builtin in HLSL.");
+
+ default:
+ SPIRV_CROSS_THROW("Unsupported builtin in HLSL.");
+ break;
+ }
+
+ if (type && semantic)
+ statement(type, " ", builtin_to_glsl(builtin, StorageClassInput), " : ", semantic, ";");
+ });
+}
+
+uint32_t CompilerHLSL::type_to_consumed_locations(const SPIRType &type) const
+{
+ // TODO: Need to verify correctness.
+ uint32_t elements = 0;
+
+ if (type.basetype == SPIRType::Struct)
+ {
+ for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
+ elements += type_to_consumed_locations(get<SPIRType>(type.member_types[i]));
+ }
+ else
+ {
+ uint32_t array_multiplier = 1;
+ for (uint32_t i = 0; i < uint32_t(type.array.size()); i++)
+ {
+ if (type.array_size_literal[i])
+ array_multiplier *= type.array[i];
+ else
+ array_multiplier *= get<SPIRConstant>(type.array[i]).scalar();
+ }
+ elements += array_multiplier * type.columns;
+ }
+ return elements;
+}
+
+string CompilerHLSL::to_interpolation_qualifiers(const Bitset &flags)
+{
+ string res;
+ //if (flags & (1ull << DecorationSmooth))
+ // res += "linear ";
+ if (flags.get(DecorationFlat))
+ res += "nointerpolation ";
+ if (flags.get(DecorationNoPerspective))
+ res += "noperspective ";
+ if (flags.get(DecorationCentroid))
+ res += "centroid ";
+ if (flags.get(DecorationPatch))
+ res += "patch "; // Seems to be different in actual HLSL.
+ if (flags.get(DecorationSample))
+ res += "sample ";
+ if (flags.get(DecorationInvariant))
+ res += "invariant "; // Not supported?
+
+ return res;
+}
+
+std::string CompilerHLSL::to_semantic(uint32_t vertex_location)
+{
+ for (auto &attribute : remap_vertex_attributes)
+ if (attribute.location == vertex_location)
+ return attribute.semantic;
+
+ return join("TEXCOORD", vertex_location);
+}
+
+void CompilerHLSL::emit_io_block(const SPIRVariable &var)
+{
+ auto &type = get<SPIRType>(var.basetype);
+ add_resource_name(type.self);
+
+ statement("struct ", to_name(type.self));
+ begin_scope();
+ type.member_name_cache.clear();
+
+ uint32_t base_location = get_decoration(var.self, DecorationLocation);
+
+ for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
+ {
+ string semantic;
+ if (has_member_decoration(type.self, i, DecorationLocation))
+ {
+ uint32_t location = get_member_decoration(type.self, i, DecorationLocation);
+ semantic = join(" : ", to_semantic(location));
+ }
+ else
+ {
+ // If the block itself has a location, but not its members, use the implicit location.
+ // There could be a conflict if the block members partially specialize the locations.
+ // It is unclear how SPIR-V deals with this. Assume this does not happen for now.
+ uint32_t location = base_location + i;
+ semantic = join(" : ", to_semantic(location));
+ }
+
+ add_member_name(type, i);
+
+ auto &membertype = get<SPIRType>(type.member_types[i]);
+ statement(to_interpolation_qualifiers(get_member_decoration_bitset(type.self, i)),
+ variable_decl(membertype, to_member_name(type, i)), semantic, ";");
+ }
+
+ end_scope_decl();
+ statement("");
+
+ statement("static ", variable_decl(var), ";");
+ statement("");
+}
+
+void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, unordered_set<uint32_t> &active_locations)
+{
+ auto &execution = get_entry_point();
+ auto type = get<SPIRType>(var.basetype);
+
+ string binding;
+ bool use_location_number = true;
+ bool legacy = hlsl_options.shader_model <= 30;
+ if (execution.model == ExecutionModelFragment && var.storage == StorageClassOutput)
+ {
+ // Dual-source blending is achieved in HLSL by emitting to SV_Target0 and 1.
+ uint32_t index = get_decoration(var.self, DecorationIndex);
+ uint32_t location = get_decoration(var.self, DecorationLocation);
+
+ if (index != 0 && location != 0)
+ SPIRV_CROSS_THROW("Dual-source blending is only supported on MRT #0 in HLSL.");
+
+ binding = join(legacy ? "COLOR" : "SV_Target", location + index);
+ use_location_number = false;
+ if (legacy) // COLOR must be a four-component vector on legacy shader model targets (HLSL ERR_COLOR_4COMP)
+ type.vecsize = 4;
+ }
+
+ const auto get_vacant_location = [&]() -> uint32_t {
+ for (uint32_t i = 0; i < 64; i++)
+ if (!active_locations.count(i))
+ return i;
+ SPIRV_CROSS_THROW("All locations from 0 to 63 are exhausted.");
+ };
+
+ bool need_matrix_unroll = var.storage == StorageClassInput && execution.model == ExecutionModelVertex;
+
+ auto &m = ir.meta[var.self].decoration;
+ auto name = to_name(var.self);
+ if (use_location_number)
+ {
+ uint32_t location_number;
+
+ // If an explicit location exists, use it with TEXCOORD[N] semantic.
+ // Otherwise, pick a vacant location.
+ if (m.decoration_flags.get(DecorationLocation))
+ location_number = m.location;
+ else
+ location_number = get_vacant_location();
+
+ // Allow semantic remap if specified.
+ auto semantic = to_semantic(location_number);
+
+ if (need_matrix_unroll && type.columns > 1)
+ {
+ if (!type.array.empty())
+ SPIRV_CROSS_THROW("Arrays of matrices used as input/output. This is not supported.");
+
+ // Unroll matrices.
+ for (uint32_t i = 0; i < type.columns; i++)
+ {
+ SPIRType newtype = type;
+ newtype.columns = 1;
+ statement(to_interpolation_qualifiers(get_decoration_bitset(var.self)),
+ variable_decl(newtype, join(name, "_", i)), " : ", semantic, "_", i, ";");
+ active_locations.insert(location_number++);
+ }
+ }
+ else
+ {
+ statement(to_interpolation_qualifiers(get_decoration_bitset(var.self)), variable_decl(type, name), " : ",
+ semantic, ";");
+
+ // Structs and arrays should consume more locations.
+ uint32_t consumed_locations = type_to_consumed_locations(type);
+ for (uint32_t i = 0; i < consumed_locations; i++)
+ active_locations.insert(location_number + i);
+ }
+ }
+ else
+ statement(variable_decl(type, name), " : ", binding, ";");
+}
+
+std::string CompilerHLSL::builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage)
+{
+ switch (builtin)
+ {
+ case BuiltInVertexId:
+ return "gl_VertexID";
+ case BuiltInInstanceId:
+ return "gl_InstanceID";
+ case BuiltInNumWorkgroups:
+ {
+ if (!num_workgroups_builtin)
+ SPIRV_CROSS_THROW("NumWorkgroups builtin is used, but remap_num_workgroups_builtin() was not called. "
+ "Cannot emit code for this builtin.");
+
+ auto &var = get<SPIRVariable>(num_workgroups_builtin);
+ auto &type = get<SPIRType>(var.basetype);
+ return sanitize_underscores(join(to_name(num_workgroups_builtin), "_", get_member_name(type.self, 0)));
+ }
+ case BuiltInPointCoord:
+ // Crude hack, but there is no real alternative. This path is only enabled if point_coord_compat is set.
+ return "float2(0.5f, 0.5f)";
+ case BuiltInSubgroupLocalInvocationId:
+ return "WaveGetLaneIndex()";
+ case BuiltInSubgroupSize:
+ return "WaveGetLaneCount()";
+
+ default:
+ return CompilerGLSL::builtin_to_glsl(builtin, storage);
+ }
+}
+
+void CompilerHLSL::emit_builtin_variables()
+{
+ Bitset builtins = active_input_builtins;
+ builtins.merge_or(active_output_builtins);
+
+ bool need_base_vertex_info = false;
+
+ // Emit global variables for the interface variables which are statically used by the shader.
+ builtins.for_each_bit([&](uint32_t i) {
+ const char *type = nullptr;
+ auto builtin = static_cast<BuiltIn>(i);
+ uint32_t array_size = 0;
+
+ switch (builtin)
+ {
+ case BuiltInFragCoord:
+ case BuiltInPosition:
+ type = "float4";
+ break;
+
+ case BuiltInFragDepth:
+ type = "float";
+ break;
+
+ case BuiltInVertexId:
+ case BuiltInVertexIndex:
+ case BuiltInInstanceIndex:
+ type = "int";
+ if (hlsl_options.support_nonzero_base_vertex_base_instance)
+ need_base_vertex_info = true;
+ break;
+
+ case BuiltInInstanceId:
+ case BuiltInSampleId:
+ type = "int";
+ break;
+
+ case BuiltInPointSize:
+ if (hlsl_options.point_size_compat)
+ {
+ // Just emit the global variable, it will be ignored.
+ type = "float";
+ break;
+ }
+ else
+ SPIRV_CROSS_THROW(join("Unsupported builtin in HLSL: ", unsigned(builtin)));
+
+ case BuiltInGlobalInvocationId:
+ case BuiltInLocalInvocationId:
+ case BuiltInWorkgroupId:
+ type = "uint3";
+ break;
+
+ case BuiltInLocalInvocationIndex:
+ type = "uint";
+ break;
+
+ case BuiltInFrontFacing:
+ type = "bool";
+ break;
+
+ case BuiltInNumWorkgroups:
+ case BuiltInPointCoord:
+ // Handled specially.
+ break;
+
+ case BuiltInSubgroupLocalInvocationId:
+ case BuiltInSubgroupSize:
+ if (hlsl_options.shader_model < 60)
+ SPIRV_CROSS_THROW("Need SM 6.0 for Wave ops.");
+ break;
+
+ case BuiltInSubgroupEqMask:
+ case BuiltInSubgroupLtMask:
+ case BuiltInSubgroupLeMask:
+ case BuiltInSubgroupGtMask:
+ case BuiltInSubgroupGeMask:
+ if (hlsl_options.shader_model < 60)
+ SPIRV_CROSS_THROW("Need SM 6.0 for Wave ops.");
+ type = "uint4";
+ break;
+
+ case BuiltInClipDistance:
+ array_size = clip_distance_count;
+ type = "float";
+ break;
+
+ case BuiltInCullDistance:
+ array_size = cull_distance_count;
+ type = "float";
+ break;
+
+ default:
+ SPIRV_CROSS_THROW(join("Unsupported builtin in HLSL: ", unsigned(builtin)));
+ }
+
+ StorageClass storage = active_input_builtins.get(i) ? StorageClassInput : StorageClassOutput;
+ // FIXME: SampleMask can be both in and out with sample builtin,
+ // need to distinguish that when we add support for that.
+
+ if (type)
+ {
+ if (array_size)
+ statement("static ", type, " ", builtin_to_glsl(builtin, storage), "[", array_size, "];");
+ else
+ statement("static ", type, " ", builtin_to_glsl(builtin, storage), ";");
+ }
+ });
+
+ if (need_base_vertex_info)
+ {
+ statement("cbuffer SPIRV_Cross_VertexInfo");
+ begin_scope();
+ statement("int SPIRV_Cross_BaseVertex;");
+ statement("int SPIRV_Cross_BaseInstance;");
+ end_scope_decl();
+ statement("");
+ }
+}
+
+void CompilerHLSL::emit_composite_constants()
+{
+ // HLSL cannot declare structs or arrays inline, so we must move them out to
+ // global constants directly.
+ bool emitted = false;
+
+ ir.for_each_typed_id<SPIRConstant>([&](uint32_t, SPIRConstant &c) {
+ if (c.specialization)
+ return;
+
+ auto &type = this->get<SPIRType>(c.constant_type);
+ if (type.basetype == SPIRType::Struct || !type.array.empty())
+ {
+ auto name = to_name(c.self);
+ statement("static const ", variable_decl(type, name), " = ", constant_expression(c), ";");
+ emitted = true;
+ }
+ });
+
+ if (emitted)
+ statement("");
+}
+
+void CompilerHLSL::emit_specialization_constants_and_structs()
+{
+ bool emitted = false;
+ SpecializationConstant wg_x, wg_y, wg_z;
+ uint32_t workgroup_size_id = get_work_group_size_specialization_constants(wg_x, wg_y, wg_z);
+
+ for (auto &id_ : ir.ids_for_constant_or_type)
+ {
+ auto &id = ir.ids[id_];
+
+ if (id.get_type() == TypeConstant)
+ {
+ auto &c = id.get<SPIRConstant>();
+
+ if (c.self == workgroup_size_id)
+ {
+ statement("static const uint3 gl_WorkGroupSize = ",
+ constant_expression(get<SPIRConstant>(workgroup_size_id)), ";");
+ emitted = true;
+ }
+ else if (c.specialization)
+ {
+ auto &type = get<SPIRType>(c.constant_type);
+ auto name = to_name(c.self);
+
+ // HLSL does not support specialization constants, so fallback to macros.
+ c.specialization_constant_macro_name =
+ constant_value_macro_name(get_decoration(c.self, DecorationSpecId));
+
+ statement("#ifndef ", c.specialization_constant_macro_name);
+ statement("#define ", c.specialization_constant_macro_name, " ", constant_expression(c));
+ statement("#endif");
+ statement("static const ", variable_decl(type, name), " = ", c.specialization_constant_macro_name, ";");
+ emitted = true;
+ }
+ }
+ else if (id.get_type() == TypeConstantOp)
+ {
+ auto &c = id.get<SPIRConstantOp>();
+ auto &type = get<SPIRType>(c.basetype);
+ auto name = to_name(c.self);
+ statement("static const ", variable_decl(type, name), " = ", constant_op_expression(c), ";");
+ emitted = true;
+ }
+ else if (id.get_type() == TypeType)
+ {
+ auto &type = id.get<SPIRType>();
+ if (type.basetype == SPIRType::Struct && type.array.empty() && !type.pointer &&
+ (!ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) &&
+ !ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock)))
+ {
+ if (emitted)
+ statement("");
+ emitted = false;
+
+ emit_struct(type);
+ }
+ }
+ }
+
+ if (emitted)
+ statement("");
+}
+
+void CompilerHLSL::replace_illegal_names()
+{
+ static const unordered_set<string> keywords = {
+ // Additional HLSL specific keywords.
+ "line", "linear", "matrix", "point", "row_major", "sampler",
+ };
+
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ if (!is_hidden_variable(var))
+ {
+ auto &m = ir.meta[var.self].decoration;
+ if (keywords.find(m.alias) != end(keywords))
+ m.alias = join("_", m.alias);
+ }
+ });
+
+ CompilerGLSL::replace_illegal_names();
+}
+
+void CompilerHLSL::emit_resources()
+{
+ auto &execution = get_entry_point();
+
+ replace_illegal_names();
+
+ emit_specialization_constants_and_structs();
+ emit_composite_constants();
+
+ bool emitted = false;
+
+ // Output UBOs and SSBOs
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ auto &type = this->get<SPIRType>(var.basetype);
+
+ bool is_block_storage = type.storage == StorageClassStorageBuffer || type.storage == StorageClassUniform;
+ bool has_block_flags = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) ||
+ ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock);
+
+ if (var.storage != StorageClassFunction && type.pointer && is_block_storage && !is_hidden_variable(var) &&
+ has_block_flags)
+ {
+ emit_buffer_block(var);
+ emitted = true;
+ }
+ });
+
+ // Output push constant blocks
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ auto &type = this->get<SPIRType>(var.basetype);
+ if (var.storage != StorageClassFunction && type.pointer && type.storage == StorageClassPushConstant &&
+ !is_hidden_variable(var))
+ {
+ emit_push_constant_block(var);
+ emitted = true;
+ }
+ });
+
+ if (execution.model == ExecutionModelVertex && hlsl_options.shader_model <= 30)
+ {
+ statement("uniform float4 gl_HalfPixel;");
+ emitted = true;
+ }
+
+ bool skip_separate_image_sampler = !combined_image_samplers.empty() || hlsl_options.shader_model <= 30;
+
+ // Output Uniform Constants (values, samplers, images, etc).
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ auto &type = this->get<SPIRType>(var.basetype);
+
+ // If we're remapping separate samplers and images, only emit the combined samplers.
+ if (skip_separate_image_sampler)
+ {
+ // Sampler buffers are always used without a sampler, and they will also work in regular D3D.
+ bool sampler_buffer = type.basetype == SPIRType::Image && type.image.dim == DimBuffer;
+ bool separate_image = type.basetype == SPIRType::Image && type.image.sampled == 1;
+ bool separate_sampler = type.basetype == SPIRType::Sampler;
+ if (!sampler_buffer && (separate_image || separate_sampler))
+ return;
+ }
+
+ if (var.storage != StorageClassFunction && !is_builtin_variable(var) && !var.remapped_variable &&
+ type.pointer && (type.storage == StorageClassUniformConstant || type.storage == StorageClassAtomicCounter))
+ {
+ emit_uniform(var);
+ emitted = true;
+ }
+ });
+
+ if (emitted)
+ statement("");
+ emitted = false;
+
+ // Emit builtin input and output variables here.
+ emit_builtin_variables();
+
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ auto &type = this->get<SPIRType>(var.basetype);
+ bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock);
+
+ // Do not emit I/O blocks here.
+ // I/O blocks can be arrayed, so we must deal with them separately to support geometry shaders
+ // and tessellation down the line.
+ if (!block && var.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
+ (var.storage == StorageClassInput || var.storage == StorageClassOutput) && !is_builtin_variable(var) &&
+ interface_variable_exists_in_entry_point(var.self))
+ {
+ // Only emit non-builtins which are not blocks here. Builtin variables are handled separately.
+ emit_interface_block_globally(var);
+ emitted = true;
+ }
+ });
+
+ if (emitted)
+ statement("");
+ emitted = false;
+
+ require_input = false;
+ require_output = false;
+ unordered_set<uint32_t> active_inputs;
+ unordered_set<uint32_t> active_outputs;
+ vector<SPIRVariable *> input_variables;
+ vector<SPIRVariable *> output_variables;
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ auto &type = this->get<SPIRType>(var.basetype);
+ bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock);
+
+ if (var.storage != StorageClassInput && var.storage != StorageClassOutput)
+ return;
+
+ // Do not emit I/O blocks here.
+ // I/O blocks can be arrayed, so we must deal with them separately to support geometry shaders
+ // and tessellation down the line.
+ if (!block && !var.remapped_variable && type.pointer && !is_builtin_variable(var) &&
+ interface_variable_exists_in_entry_point(var.self))
+ {
+ if (var.storage == StorageClassInput)
+ input_variables.push_back(&var);
+ else
+ output_variables.push_back(&var);
+ }
+
+ // Reserve input and output locations for block variables as necessary.
+ if (block && !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self))
+ {
+ auto &active = var.storage == StorageClassInput ? active_inputs : active_outputs;
+ for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
+ {
+ if (has_member_decoration(type.self, i, DecorationLocation))
+ {
+ uint32_t location = get_member_decoration(type.self, i, DecorationLocation);
+ active.insert(location);
+ }
+ }
+
+ // Emit the block struct and a global variable here.
+ emit_io_block(var);
+ }
+ });
+
+ const auto variable_compare = [&](const SPIRVariable *a, const SPIRVariable *b) -> bool {
+ // Sort input and output variables based on, from more robust to less robust:
+ // - Location
+ // - Variable has a location
+ // - Name comparison
+ // - Variable has a name
+ // - Fallback: ID
+ bool has_location_a = has_decoration(a->self, DecorationLocation);
+ bool has_location_b = has_decoration(b->self, DecorationLocation);
+
+ if (has_location_a && has_location_b)
+ {
+ return get_decoration(a->self, DecorationLocation) < get_decoration(b->self, DecorationLocation);
+ }
+ else if (has_location_a && !has_location_b)
+ return true;
+ else if (!has_location_a && has_location_b)
+ return false;
+
+ const auto &name1 = to_name(a->self);
+ const auto &name2 = to_name(b->self);
+
+ if (name1.empty() && name2.empty())
+ return a->self < b->self;
+ else if (name1.empty())
+ return true;
+ else if (name2.empty())
+ return false;
+
+ return name1.compare(name2) < 0;
+ };
+
+ auto input_builtins = active_input_builtins;
+ input_builtins.clear(BuiltInNumWorkgroups);
+ input_builtins.clear(BuiltInPointCoord);
+ input_builtins.clear(BuiltInSubgroupSize);
+ input_builtins.clear(BuiltInSubgroupLocalInvocationId);
+ input_builtins.clear(BuiltInSubgroupEqMask);
+ input_builtins.clear(BuiltInSubgroupLtMask);
+ input_builtins.clear(BuiltInSubgroupLeMask);
+ input_builtins.clear(BuiltInSubgroupGtMask);
+ input_builtins.clear(BuiltInSubgroupGeMask);
+
+ if (!input_variables.empty() || !input_builtins.empty())
+ {
+ require_input = true;
+ statement("struct SPIRV_Cross_Input");
+
+ begin_scope();
+ sort(input_variables.begin(), input_variables.end(), variable_compare);
+ for (auto var : input_variables)
+ emit_interface_block_in_struct(*var, active_inputs);
+ emit_builtin_inputs_in_struct();
+ end_scope_decl();
+ statement("");
+ }
+
+ if (!output_variables.empty() || !active_output_builtins.empty())
+ {
+ require_output = true;
+ statement("struct SPIRV_Cross_Output");
+
+ begin_scope();
+ // FIXME: Use locations properly if they exist.
+ sort(output_variables.begin(), output_variables.end(), variable_compare);
+ for (auto var : output_variables)
+ emit_interface_block_in_struct(*var, active_outputs);
+ emit_builtin_outputs_in_struct();
+ end_scope_decl();
+ statement("");
+ }
+
+ // Global variables.
+ for (auto global : global_variables)
+ {
+ auto &var = get<SPIRVariable>(global);
+ if (var.storage != StorageClassOutput)
+ {
+ if (!variable_is_lut(var))
+ {
+ add_resource_name(var.self);
+
+ const char *storage = nullptr;
+ switch (var.storage)
+ {
+ case StorageClassWorkgroup:
+ storage = "groupshared";
+ break;
+
+ default:
+ storage = "static";
+ break;
+ }
+ statement(storage, " ", variable_decl(var), ";");
+ emitted = true;
+ }
+ }
+ }
+
+ if (emitted)
+ statement("");
+
+ declare_undefined_values();
+
+ if (requires_op_fmod)
+ {
+ static const char *types[] = {
+ "float",
+ "float2",
+ "float3",
+ "float4",
+ };
+
+ for (auto &type : types)
+ {
+ statement(type, " mod(", type, " x, ", type, " y)");
+ begin_scope();
+ statement("return x - y * floor(x / y);");
+ end_scope();
+ statement("");
+ }
+ }
+
+ if (required_textureSizeVariants != 0)
+ {
+ static const char *types[QueryTypeCount] = { "float4", "int4", "uint4" };
+ static const char *dims[QueryDimCount] = { "Texture1D", "Texture1DArray", "Texture2D", "Texture2DArray",
+ "Texture3D", "Buffer", "TextureCube", "TextureCubeArray",
+ "Texture2DMS", "Texture2DMSArray" };
+
+ static const bool has_lod[QueryDimCount] = { true, true, true, true, true, false, true, true, false, false };
+
+ static const char *ret_types[QueryDimCount] = {
+ "uint", "uint2", "uint2", "uint3", "uint3", "uint", "uint2", "uint3", "uint2", "uint3",
+ };
+
+ static const uint32_t return_arguments[QueryDimCount] = {
+ 1, 2, 2, 3, 3, 1, 2, 3, 2, 3,
+ };
+
+ for (uint32_t index = 0; index < QueryDimCount; index++)
+ {
+ for (uint32_t type_index = 0; type_index < QueryTypeCount; type_index++)
+ {
+ uint32_t bit = 16 * type_index + index;
+ uint64_t mask = 1ull << bit;
+
+ if ((required_textureSizeVariants & mask) == 0)
+ continue;
+
+ statement(ret_types[index], " SPIRV_Cross_textureSize(", dims[index], "<", types[type_index],
+ "> Tex, uint Level, out uint Param)");
+ begin_scope();
+ statement(ret_types[index], " ret;");
+ switch (return_arguments[index])
+ {
+ case 1:
+ if (has_lod[index])
+ statement("Tex.GetDimensions(Level, ret.x, Param);");
+ else
+ {
+ statement("Tex.GetDimensions(ret.x);");
+ statement("Param = 0u;");
+ }
+ break;
+ case 2:
+ if (has_lod[index])
+ statement("Tex.GetDimensions(Level, ret.x, ret.y, Param);");
+ else
+ statement("Tex.GetDimensions(ret.x, ret.y, Param);");
+ break;
+ case 3:
+ if (has_lod[index])
+ statement("Tex.GetDimensions(Level, ret.x, ret.y, ret.z, Param);");
+ else
+ statement("Tex.GetDimensions(ret.x, ret.y, ret.z, Param);");
+ break;
+ }
+
+ statement("return ret;");
+ end_scope();
+ statement("");
+ }
+ }
+ }
+
+ if (requires_fp16_packing)
+ {
+ // HLSL does not pack into a single word sadly :(
+ statement("uint SPIRV_Cross_packHalf2x16(float2 value)");
+ begin_scope();
+ statement("uint2 Packed = f32tof16(value);");
+ statement("return Packed.x | (Packed.y << 16);");
+ end_scope();
+ statement("");
+
+ statement("float2 SPIRV_Cross_unpackHalf2x16(uint value)");
+ begin_scope();
+ statement("return f16tof32(uint2(value & 0xffff, value >> 16));");
+ end_scope();
+ statement("");
+ }
+
+ if (requires_explicit_fp16_packing)
+ {
+ // HLSL does not pack into a single word sadly :(
+ statement("uint SPIRV_Cross_packFloat2x16(min16float2 value)");
+ begin_scope();
+ statement("uint2 Packed = f32tof16(value);");
+ statement("return Packed.x | (Packed.y << 16);");
+ end_scope();
+ statement("");
+
+ statement("min16float2 SPIRV_Cross_unpackFloat2x16(uint value)");
+ begin_scope();
+ statement("return min16float2(f16tof32(uint2(value & 0xffff, value >> 16)));");
+ end_scope();
+ statement("");
+ }
+
+ // HLSL does not seem to have builtins for these operation, so roll them by hand ...
+ if (requires_unorm8_packing)
+ {
+ statement("uint SPIRV_Cross_packUnorm4x8(float4 value)");
+ begin_scope();
+ statement("uint4 Packed = uint4(round(saturate(value) * 255.0));");
+ statement("return Packed.x | (Packed.y << 8) | (Packed.z << 16) | (Packed.w << 24);");
+ end_scope();
+ statement("");
+
+ statement("float4 SPIRV_Cross_unpackUnorm4x8(uint value)");
+ begin_scope();
+ statement("uint4 Packed = uint4(value & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff, value >> 24);");
+ statement("return float4(Packed) / 255.0;");
+ end_scope();
+ statement("");
+ }
+
+ if (requires_snorm8_packing)
+ {
+ statement("uint SPIRV_Cross_packSnorm4x8(float4 value)");
+ begin_scope();
+ statement("int4 Packed = int4(round(clamp(value, -1.0, 1.0) * 127.0)) & 0xff;");
+ statement("return uint(Packed.x | (Packed.y << 8) | (Packed.z << 16) | (Packed.w << 24));");
+ end_scope();
+ statement("");
+
+ statement("float4 SPIRV_Cross_unpackSnorm4x8(uint value)");
+ begin_scope();
+ statement("int SignedValue = int(value);");
+ statement("int4 Packed = int4(SignedValue << 24, SignedValue << 16, SignedValue << 8, SignedValue) >> 24;");
+ statement("return clamp(float4(Packed) / 127.0, -1.0, 1.0);");
+ end_scope();
+ statement("");
+ }
+
+ if (requires_unorm16_packing)
+ {
+ statement("uint SPIRV_Cross_packUnorm2x16(float2 value)");
+ begin_scope();
+ statement("uint2 Packed = uint2(round(saturate(value) * 65535.0));");
+ statement("return Packed.x | (Packed.y << 16);");
+ end_scope();
+ statement("");
+
+ statement("float2 SPIRV_Cross_unpackUnorm2x16(uint value)");
+ begin_scope();
+ statement("uint2 Packed = uint2(value & 0xffff, value >> 16);");
+ statement("return float2(Packed) / 65535.0;");
+ end_scope();
+ statement("");
+ }
+
+ if (requires_snorm16_packing)
+ {
+ statement("uint SPIRV_Cross_packSnorm2x16(float2 value)");
+ begin_scope();
+ statement("int2 Packed = int2(round(clamp(value, -1.0, 1.0) * 32767.0)) & 0xffff;");
+ statement("return uint(Packed.x | (Packed.y << 16));");
+ end_scope();
+ statement("");
+
+ statement("float2 SPIRV_Cross_unpackSnorm2x16(uint value)");
+ begin_scope();
+ statement("int SignedValue = int(value);");
+ statement("int2 Packed = int2(SignedValue << 16, SignedValue) >> 16;");
+ statement("return clamp(float2(Packed) / 32767.0, -1.0, 1.0);");
+ end_scope();
+ statement("");
+ }
+
+ if (requires_bitfield_insert)
+ {
+ static const char *types[] = { "uint", "uint2", "uint3", "uint4" };
+ for (auto &type : types)
+ {
+ statement(type, " SPIRV_Cross_bitfieldInsert(", type, " Base, ", type, " Insert, uint Offset, uint Count)");
+ begin_scope();
+ statement("uint Mask = Count == 32 ? 0xffffffff : (((1u << Count) - 1) << (Offset & 31));");
+ statement("return (Base & ~Mask) | ((Insert << Offset) & Mask);");
+ end_scope();
+ statement("");
+ }
+ }
+
+ if (requires_bitfield_extract)
+ {
+ static const char *unsigned_types[] = { "uint", "uint2", "uint3", "uint4" };
+ for (auto &type : unsigned_types)
+ {
+ statement(type, " SPIRV_Cross_bitfieldUExtract(", type, " Base, uint Offset, uint Count)");
+ begin_scope();
+ statement("uint Mask = Count == 32 ? 0xffffffff : ((1 << Count) - 1);");
+ statement("return (Base >> Offset) & Mask;");
+ end_scope();
+ statement("");
+ }
+
+ // In this overload, we will have to do sign-extension, which we will emulate by shifting up and down.
+ static const char *signed_types[] = { "int", "int2", "int3", "int4" };
+ for (auto &type : signed_types)
+ {
+ statement(type, " SPIRV_Cross_bitfieldSExtract(", type, " Base, int Offset, int Count)");
+ begin_scope();
+ statement("int Mask = Count == 32 ? -1 : ((1 << Count) - 1);");
+ statement(type, " Masked = (Base >> Offset) & Mask;");
+ statement("int ExtendShift = (32 - Count) & 31;");
+ statement("return (Masked << ExtendShift) >> ExtendShift;");
+ end_scope();
+ statement("");
+ }
+ }
+
+ if (requires_inverse_2x2)
+ {
+ statement("// Returns the inverse of a matrix, by using the algorithm of calculating the classical");
+ statement("// adjoint and dividing by the determinant. The contents of the matrix are changed.");
+ statement("float2x2 SPIRV_Cross_Inverse(float2x2 m)");
+ begin_scope();
+ statement("float2x2 adj; // The adjoint matrix (inverse after dividing by determinant)");
+ statement_no_indent("");
+ statement("// Create the transpose of the cofactors, as the classical adjoint of the matrix.");
+ statement("adj[0][0] = m[1][1];");
+ statement("adj[0][1] = -m[0][1];");
+ statement_no_indent("");
+ statement("adj[1][0] = -m[1][0];");
+ statement("adj[1][1] = m[0][0];");
+ statement_no_indent("");
+ statement("// Calculate the determinant as a combination of the cofactors of the first row.");
+ statement("float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]);");
+ statement_no_indent("");
+ statement("// Divide the classical adjoint matrix by the determinant.");
+ statement("// If determinant is zero, matrix is not invertable, so leave it unchanged.");
+ statement("return (det != 0.0f) ? (adj * (1.0f / det)) : m;");
+ end_scope();
+ statement("");
+ }
+
+ if (requires_inverse_3x3)
+ {
+ statement("// Returns the determinant of a 2x2 matrix.");
+ statement("float SPIRV_Cross_Det2x2(float a1, float a2, float b1, float b2)");
+ begin_scope();
+ statement("return a1 * b2 - b1 * a2;");
+ end_scope();
+ statement_no_indent("");
+ statement("// Returns the inverse of a matrix, by using the algorithm of calculating the classical");
+ statement("// adjoint and dividing by the determinant. The contents of the matrix are changed.");
+ statement("float3x3 SPIRV_Cross_Inverse(float3x3 m)");
+ begin_scope();
+ statement("float3x3 adj; // The adjoint matrix (inverse after dividing by determinant)");
+ statement_no_indent("");
+ statement("// Create the transpose of the cofactors, as the classical adjoint of the matrix.");
+ statement("adj[0][0] = SPIRV_Cross_Det2x2(m[1][1], m[1][2], m[2][1], m[2][2]);");
+ statement("adj[0][1] = -SPIRV_Cross_Det2x2(m[0][1], m[0][2], m[2][1], m[2][2]);");
+ statement("adj[0][2] = SPIRV_Cross_Det2x2(m[0][1], m[0][2], m[1][1], m[1][2]);");
+ statement_no_indent("");
+ statement("adj[1][0] = -SPIRV_Cross_Det2x2(m[1][0], m[1][2], m[2][0], m[2][2]);");
+ statement("adj[1][1] = SPIRV_Cross_Det2x2(m[0][0], m[0][2], m[2][0], m[2][2]);");
+ statement("adj[1][2] = -SPIRV_Cross_Det2x2(m[0][0], m[0][2], m[1][0], m[1][2]);");
+ statement_no_indent("");
+ statement("adj[2][0] = SPIRV_Cross_Det2x2(m[1][0], m[1][1], m[2][0], m[2][1]);");
+ statement("adj[2][1] = -SPIRV_Cross_Det2x2(m[0][0], m[0][1], m[2][0], m[2][1]);");
+ statement("adj[2][2] = SPIRV_Cross_Det2x2(m[0][0], m[0][1], m[1][0], m[1][1]);");
+ statement_no_indent("");
+ statement("// Calculate the determinant as a combination of the cofactors of the first row.");
+ statement("float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]);");
+ statement_no_indent("");
+ statement("// Divide the classical adjoint matrix by the determinant.");
+ statement("// If determinant is zero, matrix is not invertable, so leave it unchanged.");
+ statement("return (det != 0.0f) ? (adj * (1.0f / det)) : m;");
+ end_scope();
+ statement("");
+ }
+
+ if (requires_inverse_4x4)
+ {
+ if (!requires_inverse_3x3)
+ {
+ statement("// Returns the determinant of a 2x2 matrix.");
+ statement("float SPIRV_Cross_Det2x2(float a1, float a2, float b1, float b2)");
+ begin_scope();
+ statement("return a1 * b2 - b1 * a2;");
+ end_scope();
+ statement("");
+ }
+
+ statement("// Returns the determinant of a 3x3 matrix.");
+ statement("float SPIRV_Cross_Det3x3(float a1, float a2, float a3, float b1, float b2, float b3, float c1, "
+ "float c2, float c3)");
+ begin_scope();
+ statement("return a1 * SPIRV_Cross_Det2x2(b2, b3, c2, c3) - b1 * SPIRV_Cross_Det2x2(a2, a3, c2, c3) + c1 * "
+ "SPIRV_Cross_Det2x2(a2, a3, "
+ "b2, b3);");
+ end_scope();
+ statement_no_indent("");
+ statement("// Returns the inverse of a matrix, by using the algorithm of calculating the classical");
+ statement("// adjoint and dividing by the determinant. The contents of the matrix are changed.");
+ statement("float4x4 SPIRV_Cross_Inverse(float4x4 m)");
+ begin_scope();
+ statement("float4x4 adj; // The adjoint matrix (inverse after dividing by determinant)");
+ statement_no_indent("");
+ statement("// Create the transpose of the cofactors, as the classical adjoint of the matrix.");
+ statement(
+ "adj[0][0] = SPIRV_Cross_Det3x3(m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], "
+ "m[3][3]);");
+ statement(
+ "adj[0][1] = -SPIRV_Cross_Det3x3(m[0][1], m[0][2], m[0][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], "
+ "m[3][3]);");
+ statement(
+ "adj[0][2] = SPIRV_Cross_Det3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[3][1], m[3][2], "
+ "m[3][3]);");
+ statement(
+ "adj[0][3] = -SPIRV_Cross_Det3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], "
+ "m[2][3]);");
+ statement_no_indent("");
+ statement(
+ "adj[1][0] = -SPIRV_Cross_Det3x3(m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], "
+ "m[3][3]);");
+ statement(
+ "adj[1][1] = SPIRV_Cross_Det3x3(m[0][0], m[0][2], m[0][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], "
+ "m[3][3]);");
+ statement(
+ "adj[1][2] = -SPIRV_Cross_Det3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[3][0], m[3][2], "
+ "m[3][3]);");
+ statement(
+ "adj[1][3] = SPIRV_Cross_Det3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], "
+ "m[2][3]);");
+ statement_no_indent("");
+ statement(
+ "adj[2][0] = SPIRV_Cross_Det3x3(m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], "
+ "m[3][3]);");
+ statement(
+ "adj[2][1] = -SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], "
+ "m[3][3]);");
+ statement(
+ "adj[2][2] = SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[3][0], m[3][1], "
+ "m[3][3]);");
+ statement(
+ "adj[2][3] = -SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], "
+ "m[2][3]);");
+ statement_no_indent("");
+ statement(
+ "adj[3][0] = -SPIRV_Cross_Det3x3(m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], "
+ "m[3][2]);");
+ statement(
+ "adj[3][1] = SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], "
+ "m[3][2]);");
+ statement(
+ "adj[3][2] = -SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[3][0], m[3][1], "
+ "m[3][2]);");
+ statement(
+ "adj[3][3] = SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], "
+ "m[2][2]);");
+ statement_no_indent("");
+ statement("// Calculate the determinant as a combination of the cofactors of the first row.");
+ statement("float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]) + (adj[0][3] "
+ "* m[3][0]);");
+ statement_no_indent("");
+ statement("// Divide the classical adjoint matrix by the determinant.");
+ statement("// If determinant is zero, matrix is not invertable, so leave it unchanged.");
+ statement("return (det != 0.0f) ? (adj * (1.0f / det)) : m;");
+ end_scope();
+ statement("");
+ }
+}
+
+string CompilerHLSL::layout_for_member(const SPIRType &type, uint32_t index)
+{
+ auto &flags = get_member_decoration_bitset(type.self, index);
+
+ // HLSL can emit row_major or column_major decoration in any struct.
+ // Do not try to merge combined decorations for children like in GLSL.
+
+ // Flip the convention. HLSL is a bit odd in that the memory layout is column major ... but the language API is "row-major".
+ // The way to deal with this is to multiply everything in inverse order, and reverse the memory layout.
+ if (flags.get(DecorationColMajor))
+ return "row_major ";
+ else if (flags.get(DecorationRowMajor))
+ return "column_major ";
+
+ return "";
+}
+
+void CompilerHLSL::emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index,
+ const string &qualifier, uint32_t base_offset)
+{
+ auto &membertype = get<SPIRType>(member_type_id);
+
+ Bitset memberflags;
+ auto &memb = ir.meta[type.self].members;
+ if (index < memb.size())
+ memberflags = memb[index].decoration_flags;
+
+ string qualifiers;
+ bool is_block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) ||
+ ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock);
+
+ if (is_block)
+ qualifiers = to_interpolation_qualifiers(memberflags);
+
+ string packing_offset;
+ bool is_push_constant = type.storage == StorageClassPushConstant;
+
+ if ((has_extended_decoration(type.self, SPIRVCrossDecorationPacked) || is_push_constant) &&
+ has_member_decoration(type.self, index, DecorationOffset))
+ {
+ uint32_t offset = memb[index].offset - base_offset;
+ if (offset & 3)
+ SPIRV_CROSS_THROW("Cannot pack on tighter bounds than 4 bytes in HLSL.");
+
+ static const char *packing_swizzle[] = { "", ".y", ".z", ".w" };
+ packing_offset = join(" : packoffset(c", offset / 16, packing_swizzle[(offset & 15) >> 2], ")");
+ }
+
+ statement(layout_for_member(type, index), qualifiers, qualifier,
+ variable_decl(membertype, to_member_name(type, index)), packing_offset, ";");
+}
+
+void CompilerHLSL::emit_buffer_block(const SPIRVariable &var)
+{
+ auto &type = get<SPIRType>(var.basetype);
+
+ bool is_uav = var.storage == StorageClassStorageBuffer || has_decoration(type.self, DecorationBufferBlock);
+
+ if (is_uav)
+ {
+ Bitset flags = ir.get_buffer_block_flags(var);
+ bool is_readonly = flags.get(DecorationNonWritable);
+ bool is_coherent = flags.get(DecorationCoherent);
+ add_resource_name(var.self);
+ statement(is_coherent ? "globallycoherent " : "", is_readonly ? "ByteAddressBuffer " : "RWByteAddressBuffer ",
+ to_name(var.self), type_to_array_glsl(type), to_resource_binding(var), ";");
+ }
+ else
+ {
+ if (type.array.empty())
+ {
+ if (buffer_is_packing_standard(type, BufferPackingHLSLCbufferPackOffset))
+ set_extended_decoration(type.self, SPIRVCrossDecorationPacked);
+ else
+ SPIRV_CROSS_THROW("cbuffer cannot be expressed with either HLSL packing layout or packoffset.");
+
+ // Flatten the top-level struct so we can use packoffset,
+ // this restriction is similar to GLSL where layout(offset) is not possible on sub-structs.
+ flattened_structs.insert(var.self);
+
+ // Prefer the block name if possible.
+ auto buffer_name = to_name(type.self, false);
+ if (ir.meta[type.self].decoration.alias.empty() ||
+ resource_names.find(buffer_name) != end(resource_names) ||
+ block_names.find(buffer_name) != end(block_names))
+ {
+ buffer_name = get_block_fallback_name(var.self);
+ }
+
+ add_variable(block_names, resource_names, buffer_name);
+
+ // If for some reason buffer_name is an illegal name, make a final fallback to a workaround name.
+ // This cannot conflict with anything else, so we're safe now.
+ if (buffer_name.empty())
+ buffer_name = join("_", get<SPIRType>(var.basetype).self, "_", var.self);
+
+ block_names.insert(buffer_name);
+
+ // Save for post-reflection later.
+ declared_block_names[var.self] = buffer_name;
+
+ type.member_name_cache.clear();
+ // var.self can be used as a backup name for the block name,
+ // so we need to make sure we don't disturb the name here on a recompile.
+ // It will need to be reset if we have to recompile.
+ preserve_alias_on_reset(var.self);
+ add_resource_name(var.self);
+ statement("cbuffer ", buffer_name, to_resource_binding(var));
+ begin_scope();
+
+ uint32_t i = 0;
+ for (auto &member : type.member_types)
+ {
+ add_member_name(type, i);
+ auto backup_name = get_member_name(type.self, i);
+ auto member_name = to_member_name(type, i);
+ set_member_name(type.self, i, sanitize_underscores(join(to_name(var.self), "_", member_name)));
+ emit_struct_member(type, member, i, "");
+ set_member_name(type.self, i, backup_name);
+ i++;
+ }
+
+ end_scope_decl();
+ statement("");
+ }
+ else
+ {
+ if (hlsl_options.shader_model < 51)
+ SPIRV_CROSS_THROW(
+ "Need ConstantBuffer<T> to use arrays of UBOs, but this is only supported in SM 5.1.");
+
+ // ConstantBuffer<T> does not support packoffset, so it is unuseable unless everything aligns as we expect.
+ if (!buffer_is_packing_standard(type, BufferPackingHLSLCbuffer))
+ SPIRV_CROSS_THROW("HLSL ConstantBuffer<T> cannot be expressed with normal HLSL packing rules.");
+
+ add_resource_name(type.self);
+ add_resource_name(var.self);
+
+ emit_struct(get<SPIRType>(type.self));
+ statement("ConstantBuffer<", to_name(type.self), "> ", to_name(var.self), type_to_array_glsl(type),
+ to_resource_binding(var), ";");
+ }
+ }
+}
+
+void CompilerHLSL::emit_push_constant_block(const SPIRVariable &var)
+{
+ if (root_constants_layout.empty())
+ {
+ emit_buffer_block(var);
+ }
+ else
+ {
+ for (const auto &layout : root_constants_layout)
+ {
+ auto &type = get<SPIRType>(var.basetype);
+
+ if (buffer_is_packing_standard(type, BufferPackingHLSLCbufferPackOffset, layout.start, layout.end))
+ set_extended_decoration(type.self, SPIRVCrossDecorationPacked);
+ else
+ SPIRV_CROSS_THROW(
+ "root constant cbuffer cannot be expressed with either HLSL packing layout or packoffset.");
+
+ flattened_structs.insert(var.self);
+ type.member_name_cache.clear();
+ add_resource_name(var.self);
+ auto &memb = ir.meta[type.self].members;
+
+ statement("cbuffer SPIRV_CROSS_RootConstant_", to_name(var.self),
+ to_resource_register('b', layout.binding, layout.space));
+ begin_scope();
+
+ // Index of the next field in the generated root constant constant buffer
+ auto constant_index = 0u;
+
+ // Iterate over all member of the push constant and check which of the fields
+ // fit into the given root constant layout.
+ for (auto i = 0u; i < memb.size(); i++)
+ {
+ const auto offset = memb[i].offset;
+ if (layout.start <= offset && offset < layout.end)
+ {
+ const auto &member = type.member_types[i];
+
+ add_member_name(type, constant_index);
+ auto backup_name = get_member_name(type.self, i);
+ auto member_name = to_member_name(type, i);
+ set_member_name(type.self, constant_index,
+ sanitize_underscores(join(to_name(var.self), "_", member_name)));
+ emit_struct_member(type, member, i, "", layout.start);
+ set_member_name(type.self, constant_index, backup_name);
+
+ constant_index++;
+ }
+ }
+
+ end_scope_decl();
+ }
+ }
+}
+
+string CompilerHLSL::to_sampler_expression(uint32_t id)
+{
+ auto expr = join("_", to_expression(id));
+ auto index = expr.find_first_of('[');
+ if (index == string::npos)
+ {
+ return expr + "_sampler";
+ }
+ else
+ {
+ // We have an expression like _ident[array], so we cannot tack on _sampler, insert it inside the string instead.
+ return expr.insert(index, "_sampler");
+ }
+}
+
+void CompilerHLSL::emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id)
+{
+ if (hlsl_options.shader_model >= 40 && combined_image_samplers.empty())
+ {
+ set<SPIRCombinedImageSampler>(result_id, result_type, image_id, samp_id);
+ }
+ else
+ {
+ // Make sure to suppress usage tracking. It is illegal to create temporaries of opaque types.
+ emit_op(result_type, result_id, to_combined_image_sampler(image_id, samp_id), true, true);
+ }
+}
+
+string CompilerHLSL::to_func_call_arg(uint32_t id)
+{
+ string arg_str = CompilerGLSL::to_func_call_arg(id);
+
+ if (hlsl_options.shader_model <= 30)
+ return arg_str;
+
+ // Manufacture automatic sampler arg if the arg is a SampledImage texture and we're in modern HLSL.
+ auto &type = expression_type(id);
+
+ // We don't have to consider combined image samplers here via OpSampledImage because
+ // those variables cannot be passed as arguments to functions.
+ // Only global SampledImage variables may be used as arguments.
+ if (type.basetype == SPIRType::SampledImage && type.image.dim != DimBuffer)
+ arg_str += ", " + to_sampler_expression(id);
+
+ return arg_str;
+}
+
+void CompilerHLSL::emit_function_prototype(SPIRFunction &func, const Bitset &return_flags)
+{
+ if (func.self != ir.default_entry_point)
+ add_function_overload(func);
+
+ auto &execution = get_entry_point();
+ // Avoid shadow declarations.
+ local_variable_names = resource_names;
+
+ string decl;
+
+ auto &type = get<SPIRType>(func.return_type);
+ if (type.array.empty())
+ {
+ decl += flags_to_precision_qualifiers_glsl(type, return_flags);
+ decl += type_to_glsl(type);
+ decl += " ";
+ }
+ else
+ {
+ // We cannot return arrays in HLSL, so "return" through an out variable.
+ decl = "void ";
+ }
+
+ if (func.self == ir.default_entry_point)
+ {
+ if (execution.model == ExecutionModelVertex)
+ decl += "vert_main";
+ else if (execution.model == ExecutionModelFragment)
+ decl += "frag_main";
+ else if (execution.model == ExecutionModelGLCompute)
+ decl += "comp_main";
+ else
+ SPIRV_CROSS_THROW("Unsupported execution model.");
+ processing_entry_point = true;
+ }
+ else
+ decl += to_name(func.self);
+
+ decl += "(";
+ vector<string> arglist;
+
+ if (!type.array.empty())
+ {
+ // Fake array returns by writing to an out array instead.
+ string out_argument;
+ out_argument += "out ";
+ out_argument += type_to_glsl(type);
+ out_argument += " ";
+ out_argument += "SPIRV_Cross_return_value";
+ out_argument += type_to_array_glsl(type);
+ arglist.push_back(move(out_argument));
+ }
+
+ for (auto &arg : func.arguments)
+ {
+ // Do not pass in separate images or samplers if we're remapping
+ // to combined image samplers.
+ if (skip_argument(arg.id))
+ continue;
+
+ // Might change the variable name if it already exists in this function.
+ // SPIRV OpName doesn't have any semantic effect, so it's valid for an implementation
+ // to use same name for variables.
+ // Since we want to make the GLSL debuggable and somewhat sane, use fallback names for variables which are duplicates.
+ add_local_variable_name(arg.id);
+
+ arglist.push_back(argument_decl(arg));
+
+ // Flatten a combined sampler to two separate arguments in modern HLSL.
+ auto &arg_type = get<SPIRType>(arg.type);
+ if (hlsl_options.shader_model > 30 && arg_type.basetype == SPIRType::SampledImage &&
+ arg_type.image.dim != DimBuffer)
+ {
+ // Manufacture automatic sampler arg for SampledImage texture
+ arglist.push_back(join(image_is_comparison(arg_type, arg.id) ? "SamplerComparisonState " : "SamplerState ",
+ to_sampler_expression(arg.id), type_to_array_glsl(arg_type)));
+ }
+
+ // Hold a pointer to the parameter so we can invalidate the readonly field if needed.
+ auto *var = maybe_get<SPIRVariable>(arg.id);
+ if (var)
+ var->parameter = &arg;
+ }
+
+ for (auto &arg : func.shadow_arguments)
+ {
+ // Might change the variable name if it already exists in this function.
+ // SPIRV OpName doesn't have any semantic effect, so it's valid for an implementation
+ // to use same name for variables.
+ // Since we want to make the GLSL debuggable and somewhat sane, use fallback names for variables which are duplicates.
+ add_local_variable_name(arg.id);
+
+ arglist.push_back(argument_decl(arg));
+
+ // Hold a pointer to the parameter so we can invalidate the readonly field if needed.
+ auto *var = maybe_get<SPIRVariable>(arg.id);
+ if (var)
+ var->parameter = &arg;
+ }
+
+ decl += merge(arglist);
+ decl += ")";
+ statement(decl);
+}
+
+void CompilerHLSL::emit_hlsl_entry_point()
+{
+ vector<string> arguments;
+
+ if (require_input)
+ arguments.push_back("SPIRV_Cross_Input stage_input");
+
+ // Add I/O blocks as separate arguments with appropriate storage qualifier.
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ auto &type = this->get<SPIRType>(var.basetype);
+ bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock);
+
+ if (var.storage != StorageClassInput && var.storage != StorageClassOutput)
+ return;
+
+ if (block && !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self))
+ {
+ if (var.storage == StorageClassInput)
+ {
+ arguments.push_back(join("in ", variable_decl(type, join("stage_input", to_name(var.self)))));
+ }
+ else if (var.storage == StorageClassOutput)
+ {
+ arguments.push_back(join("out ", variable_decl(type, join("stage_output", to_name(var.self)))));
+ }
+ }
+ });
+
+ auto &execution = get_entry_point();
+
+ switch (execution.model)
+ {
+ case ExecutionModelGLCompute:
+ {
+ SpecializationConstant wg_x, wg_y, wg_z;
+ get_work_group_size_specialization_constants(wg_x, wg_y, wg_z);
+
+ uint32_t x = execution.workgroup_size.x;
+ uint32_t y = execution.workgroup_size.y;
+ uint32_t z = execution.workgroup_size.z;
+
+ auto x_expr = wg_x.id ? get<SPIRConstant>(wg_x.id).specialization_constant_macro_name : to_string(x);
+ auto y_expr = wg_y.id ? get<SPIRConstant>(wg_y.id).specialization_constant_macro_name : to_string(y);
+ auto z_expr = wg_z.id ? get<SPIRConstant>(wg_z.id).specialization_constant_macro_name : to_string(z);
+
+ statement("[numthreads(", x_expr, ", ", y_expr, ", ", z_expr, ")]");
+ break;
+ }
+ case ExecutionModelFragment:
+ if (execution.flags.get(ExecutionModeEarlyFragmentTests))
+ statement("[earlydepthstencil]");
+ break;
+ default:
+ break;
+ }
+
+ statement(require_output ? "SPIRV_Cross_Output " : "void ", "main(", merge(arguments), ")");
+ begin_scope();
+ bool legacy = hlsl_options.shader_model <= 30;
+
+ // Copy builtins from entry point arguments to globals.
+ active_input_builtins.for_each_bit([&](uint32_t i) {
+ auto builtin = builtin_to_glsl(static_cast<BuiltIn>(i), StorageClassInput);
+ switch (static_cast<BuiltIn>(i))
+ {
+ case BuiltInFragCoord:
+ // VPOS in D3D9 is sampled at integer locations, apply half-pixel offset to be consistent.
+ // TODO: Do we need an option here? Any reason why a D3D9 shader would be used
+ // on a D3D10+ system with a different rasterization config?
+ if (legacy)
+ statement(builtin, " = stage_input.", builtin, " + float4(0.5f, 0.5f, 0.0f, 0.0f);");
+ else
+ statement(builtin, " = stage_input.", builtin, ";");
+ break;
+
+ case BuiltInVertexId:
+ case BuiltInVertexIndex:
+ case BuiltInInstanceIndex:
+ // D3D semantics are uint, but shader wants int.
+ if (hlsl_options.support_nonzero_base_vertex_base_instance)
+ {
+ if (static_cast<BuiltIn>(i) == BuiltInInstanceIndex)
+ statement(builtin, " = int(stage_input.", builtin, ") + SPIRV_Cross_BaseInstance;");
+ else
+ statement(builtin, " = int(stage_input.", builtin, ") + SPIRV_Cross_BaseVertex;");
+ }
+ else
+ statement(builtin, " = int(stage_input.", builtin, ");");
+ break;
+
+ case BuiltInInstanceId:
+ // D3D semantics are uint, but shader wants int.
+ statement(builtin, " = int(stage_input.", builtin, ");");
+ break;
+
+ case BuiltInNumWorkgroups:
+ case BuiltInPointCoord:
+ case BuiltInSubgroupSize:
+ case BuiltInSubgroupLocalInvocationId:
+ break;
+
+ case BuiltInSubgroupEqMask:
+ // Emulate these ...
+ // No 64-bit in HLSL, so have to do it in 32-bit and unroll.
+ statement("gl_SubgroupEqMask = 1u << (WaveGetLaneIndex() - uint4(0, 32, 64, 96));");
+ statement("if (WaveGetLaneIndex() >= 32) gl_SubgroupEqMask.x = 0;");
+ statement("if (WaveGetLaneIndex() >= 64 || WaveGetLaneIndex() < 32) gl_SubgroupEqMask.y = 0;");
+ statement("if (WaveGetLaneIndex() >= 96 || WaveGetLaneIndex() < 64) gl_SubgroupEqMask.z = 0;");
+ statement("if (WaveGetLaneIndex() < 96) gl_SubgroupEqMask.w = 0;");
+ break;
+
+ case BuiltInSubgroupGeMask:
+ // Emulate these ...
+ // No 64-bit in HLSL, so have to do it in 32-bit and unroll.
+ statement("gl_SubgroupGeMask = ~((1u << (WaveGetLaneIndex() - uint4(0, 32, 64, 96))) - 1u);");
+ statement("if (WaveGetLaneIndex() >= 32) gl_SubgroupGeMask.x = 0u;");
+ statement("if (WaveGetLaneIndex() >= 64) gl_SubgroupGeMask.y = 0u;");
+ statement("if (WaveGetLaneIndex() >= 96) gl_SubgroupGeMask.z = 0u;");
+ statement("if (WaveGetLaneIndex() < 32) gl_SubgroupGeMask.y = ~0u;");
+ statement("if (WaveGetLaneIndex() < 64) gl_SubgroupGeMask.z = ~0u;");
+ statement("if (WaveGetLaneIndex() < 96) gl_SubgroupGeMask.w = ~0u;");
+ break;
+
+ case BuiltInSubgroupGtMask:
+ // Emulate these ...
+ // No 64-bit in HLSL, so have to do it in 32-bit and unroll.
+ statement("uint gt_lane_index = WaveGetLaneIndex() + 1;");
+ statement("gl_SubgroupGtMask = ~((1u << (gt_lane_index - uint4(0, 32, 64, 96))) - 1u);");
+ statement("if (gt_lane_index >= 32) gl_SubgroupGtMask.x = 0u;");
+ statement("if (gt_lane_index >= 64) gl_SubgroupGtMask.y = 0u;");
+ statement("if (gt_lane_index >= 96) gl_SubgroupGtMask.z = 0u;");
+ statement("if (gt_lane_index >= 128) gl_SubgroupGtMask.w = 0u;");
+ statement("if (gt_lane_index < 32) gl_SubgroupGtMask.y = ~0u;");
+ statement("if (gt_lane_index < 64) gl_SubgroupGtMask.z = ~0u;");
+ statement("if (gt_lane_index < 96) gl_SubgroupGtMask.w = ~0u;");
+ break;
+
+ case BuiltInSubgroupLeMask:
+ // Emulate these ...
+ // No 64-bit in HLSL, so have to do it in 32-bit and unroll.
+ statement("uint le_lane_index = WaveGetLaneIndex() + 1;");
+ statement("gl_SubgroupLeMask = (1u << (le_lane_index - uint4(0, 32, 64, 96))) - 1u;");
+ statement("if (le_lane_index >= 32) gl_SubgroupLeMask.x = ~0u;");
+ statement("if (le_lane_index >= 64) gl_SubgroupLeMask.y = ~0u;");
+ statement("if (le_lane_index >= 96) gl_SubgroupLeMask.z = ~0u;");
+ statement("if (le_lane_index >= 128) gl_SubgroupLeMask.w = ~0u;");
+ statement("if (le_lane_index < 32) gl_SubgroupLeMask.y = 0u;");
+ statement("if (le_lane_index < 64) gl_SubgroupLeMask.z = 0u;");
+ statement("if (le_lane_index < 96) gl_SubgroupLeMask.w = 0u;");
+ break;
+
+ case BuiltInSubgroupLtMask:
+ // Emulate these ...
+ // No 64-bit in HLSL, so have to do it in 32-bit and unroll.
+ statement("gl_SubgroupLtMask = (1u << (WaveGetLaneIndex() - uint4(0, 32, 64, 96))) - 1u;");
+ statement("if (WaveGetLaneIndex() >= 32) gl_SubgroupLtMask.x = ~0u;");
+ statement("if (WaveGetLaneIndex() >= 64) gl_SubgroupLtMask.y = ~0u;");
+ statement("if (WaveGetLaneIndex() >= 96) gl_SubgroupLtMask.z = ~0u;");
+ statement("if (WaveGetLaneIndex() < 32) gl_SubgroupLtMask.y = 0u;");
+ statement("if (WaveGetLaneIndex() < 64) gl_SubgroupLtMask.z = 0u;");
+ statement("if (WaveGetLaneIndex() < 96) gl_SubgroupLtMask.w = 0u;");
+ break;
+
+ case BuiltInClipDistance:
+ for (uint32_t clip = 0; clip < clip_distance_count; clip++)
+ statement("gl_ClipDistance[", clip, "] = stage_input.gl_ClipDistance", clip / 4, ".", "xyzw"[clip & 3],
+ ";");
+ break;
+
+ case BuiltInCullDistance:
+ for (uint32_t cull = 0; cull < cull_distance_count; cull++)
+ statement("gl_CullDistance[", cull, "] = stage_input.gl_CullDistance", cull / 4, ".", "xyzw"[cull & 3],
+ ";");
+ break;
+
+ default:
+ statement(builtin, " = stage_input.", builtin, ";");
+ break;
+ }
+ });
+
+ // Copy from stage input struct to globals.
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ auto &type = this->get<SPIRType>(var.basetype);
+ bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock);
+
+ if (var.storage != StorageClassInput)
+ return;
+
+ bool need_matrix_unroll = var.storage == StorageClassInput && execution.model == ExecutionModelVertex;
+
+ if (!block && !var.remapped_variable && type.pointer && !is_builtin_variable(var) &&
+ interface_variable_exists_in_entry_point(var.self))
+ {
+ auto name = to_name(var.self);
+ auto &mtype = this->get<SPIRType>(var.basetype);
+ if (need_matrix_unroll && mtype.columns > 1)
+ {
+ // Unroll matrices.
+ for (uint32_t col = 0; col < mtype.columns; col++)
+ statement(name, "[", col, "] = stage_input.", name, "_", col, ";");
+ }
+ else
+ {
+ statement(name, " = stage_input.", name, ";");
+ }
+ }
+
+ // I/O blocks don't use the common stage input/output struct, but separate outputs.
+ if (block && !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self))
+ {
+ auto name = to_name(var.self);
+ statement(name, " = stage_input", name, ";");
+ }
+ });
+
+ // Run the shader.
+ if (execution.model == ExecutionModelVertex)
+ statement("vert_main();");
+ else if (execution.model == ExecutionModelFragment)
+ statement("frag_main();");
+ else if (execution.model == ExecutionModelGLCompute)
+ statement("comp_main();");
+ else
+ SPIRV_CROSS_THROW("Unsupported shader stage.");
+
+ // Copy block outputs.
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ auto &type = this->get<SPIRType>(var.basetype);
+ bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock);
+
+ if (var.storage != StorageClassOutput)
+ return;
+
+ // I/O blocks don't use the common stage input/output struct, but separate outputs.
+ if (block && !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self))
+ {
+ auto name = to_name(var.self);
+ statement("stage_output", name, " = ", name, ";");
+ }
+ });
+
+ // Copy stage outputs.
+ if (require_output)
+ {
+ statement("SPIRV_Cross_Output stage_output;");
+
+ // Copy builtins from globals to return struct.
+ active_output_builtins.for_each_bit([&](uint32_t i) {
+ // PointSize doesn't exist in HLSL.
+ if (i == BuiltInPointSize)
+ return;
+
+ switch (static_cast<BuiltIn>(i))
+ {
+ case BuiltInClipDistance:
+ for (uint32_t clip = 0; clip < clip_distance_count; clip++)
+ statement("stage_output.gl_ClipDistance", clip / 4, ".", "xyzw"[clip & 3], " = gl_ClipDistance[",
+ clip, "];");
+ break;
+
+ case BuiltInCullDistance:
+ for (uint32_t cull = 0; cull < cull_distance_count; cull++)
+ statement("stage_output.gl_CullDistance", cull / 4, ".", "xyzw"[cull & 3], " = gl_CullDistance[",
+ cull, "];");
+ break;
+
+ default:
+ {
+ auto builtin_expr = builtin_to_glsl(static_cast<BuiltIn>(i), StorageClassOutput);
+ statement("stage_output.", builtin_expr, " = ", builtin_expr, ";");
+ break;
+ }
+ }
+ });
+
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ auto &type = this->get<SPIRType>(var.basetype);
+ bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock);
+
+ if (var.storage != StorageClassOutput)
+ return;
+
+ if (!block && var.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
+ !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self))
+ {
+ auto name = to_name(var.self);
+
+ if (legacy && execution.model == ExecutionModelFragment)
+ {
+ string output_filler;
+ for (uint32_t size = type.vecsize; size < 4; ++size)
+ output_filler += ", 0.0";
+
+ statement("stage_output.", name, " = float4(", name, output_filler, ");");
+ }
+ else
+ {
+ statement("stage_output.", name, " = ", name, ";");
+ }
+ }
+ });
+
+ statement("return stage_output;");
+ }
+
+ end_scope();
+}
+
+void CompilerHLSL::emit_fixup()
+{
+ if (get_entry_point().model == ExecutionModelVertex)
+ {
+ // Do various mangling on the gl_Position.
+ if (hlsl_options.shader_model <= 30)
+ {
+ statement("gl_Position.x = gl_Position.x - gl_HalfPixel.x * "
+ "gl_Position.w;");
+ statement("gl_Position.y = gl_Position.y + gl_HalfPixel.y * "
+ "gl_Position.w;");
+ }
+
+ if (options.vertex.flip_vert_y)
+ statement("gl_Position.y = -gl_Position.y;");
+ if (options.vertex.fixup_clipspace)
+ statement("gl_Position.z = (gl_Position.z + gl_Position.w) * 0.5;");
+ }
+}
+
+void CompilerHLSL::emit_texture_op(const Instruction &i)
+{
+ auto *ops = stream(i);
+ auto op = static_cast<Op>(i.op);
+ uint32_t length = i.length;
+
+ vector<uint32_t> inherited_expressions;
+
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ uint32_t img = ops[2];
+ uint32_t coord = ops[3];
+ uint32_t dref = 0;
+ uint32_t comp = 0;
+ bool gather = false;
+ bool proj = false;
+ const uint32_t *opt = nullptr;
+ auto *combined_image = maybe_get<SPIRCombinedImageSampler>(img);
+ auto img_expr = to_expression(combined_image ? combined_image->image : img);
+
+ inherited_expressions.push_back(coord);
+
+ switch (op)
+ {
+ case OpImageSampleDrefImplicitLod:
+ case OpImageSampleDrefExplicitLod:
+ dref = ops[4];
+ opt = &ops[5];
+ length -= 5;
+ break;
+
+ case OpImageSampleProjDrefImplicitLod:
+ case OpImageSampleProjDrefExplicitLod:
+ dref = ops[4];
+ proj = true;
+ opt = &ops[5];
+ length -= 5;
+ break;
+
+ case OpImageDrefGather:
+ dref = ops[4];
+ opt = &ops[5];
+ gather = true;
+ length -= 5;
+ break;
+
+ case OpImageGather:
+ comp = ops[4];
+ opt = &ops[5];
+ gather = true;
+ length -= 5;
+ break;
+
+ case OpImageSampleProjImplicitLod:
+ case OpImageSampleProjExplicitLod:
+ opt = &ops[4];
+ length -= 4;
+ proj = true;
+ break;
+
+ case OpImageQueryLod:
+ opt = &ops[4];
+ length -= 4;
+ break;
+
+ default:
+ opt = &ops[4];
+ length -= 4;
+ break;
+ }
+
+ auto &imgtype = expression_type(img);
+ uint32_t coord_components = 0;
+ switch (imgtype.image.dim)
+ {
+ case spv::Dim1D:
+ coord_components = 1;
+ break;
+ case spv::Dim2D:
+ coord_components = 2;
+ break;
+ case spv::Dim3D:
+ coord_components = 3;
+ break;
+ case spv::DimCube:
+ coord_components = 3;
+ break;
+ case spv::DimBuffer:
+ coord_components = 1;
+ break;
+ default:
+ coord_components = 2;
+ break;
+ }
+
+ if (dref)
+ inherited_expressions.push_back(dref);
+
+ if (imgtype.image.arrayed)
+ coord_components++;
+
+ uint32_t bias = 0;
+ uint32_t lod = 0;
+ uint32_t grad_x = 0;
+ uint32_t grad_y = 0;
+ uint32_t coffset = 0;
+ uint32_t offset = 0;
+ uint32_t coffsets = 0;
+ uint32_t sample = 0;
+ uint32_t flags = 0;
+
+ if (length)
+ {
+ flags = opt[0];
+ opt++;
+ length--;
+ }
+
+ auto test = [&](uint32_t &v, uint32_t flag) {
+ if (length && (flags & flag))
+ {
+ v = *opt++;
+ inherited_expressions.push_back(v);
+ length--;
+ }
+ };
+
+ test(bias, ImageOperandsBiasMask);
+ test(lod, ImageOperandsLodMask);
+ test(grad_x, ImageOperandsGradMask);
+ test(grad_y, ImageOperandsGradMask);
+ test(coffset, ImageOperandsConstOffsetMask);
+ test(offset, ImageOperandsOffsetMask);
+ test(coffsets, ImageOperandsConstOffsetsMask);
+ test(sample, ImageOperandsSampleMask);
+
+ string expr;
+ string texop;
+
+ if (op == OpImageFetch)
+ {
+ if (hlsl_options.shader_model < 40)
+ {
+ SPIRV_CROSS_THROW("texelFetch is not supported in HLSL shader model 2/3.");
+ }
+ texop += img_expr;
+ texop += ".Load";
+ }
+ else if (op == OpImageQueryLod)
+ {
+ texop += img_expr;
+ texop += ".CalculateLevelOfDetail";
+ }
+ else
+ {
+ auto &imgformat = get<SPIRType>(imgtype.image.type);
+ if (imgformat.basetype != SPIRType::Float)
+ {
+ SPIRV_CROSS_THROW("Sampling non-float textures is not supported in HLSL.");
+ }
+
+ if (hlsl_options.shader_model >= 40)
+ {
+ texop += img_expr;
+
+ if (image_is_comparison(imgtype, img))
+ {
+ if (gather)
+ {
+ SPIRV_CROSS_THROW("GatherCmp does not exist in HLSL.");
+ }
+ else if (lod || grad_x || grad_y)
+ {
+ // Assume we want a fixed level, and the only thing we can get in HLSL is SampleCmpLevelZero.
+ texop += ".SampleCmpLevelZero";
+ }
+ else
+ texop += ".SampleCmp";
+ }
+ else if (gather)
+ {
+ uint32_t comp_num = get<SPIRConstant>(comp).scalar();
+ if (hlsl_options.shader_model >= 50)
+ {
+ switch (comp_num)
+ {
+ case 0:
+ texop += ".GatherRed";
+ break;
+ case 1:
+ texop += ".GatherGreen";
+ break;
+ case 2:
+ texop += ".GatherBlue";
+ break;
+ case 3:
+ texop += ".GatherAlpha";
+ break;
+ default:
+ SPIRV_CROSS_THROW("Invalid component.");
+ }
+ }
+ else
+ {
+ if (comp_num == 0)
+ texop += ".Gather";
+ else
+ SPIRV_CROSS_THROW("HLSL shader model 4 can only gather from the red component.");
+ }
+ }
+ else if (bias)
+ texop += ".SampleBias";
+ else if (grad_x || grad_y)
+ texop += ".SampleGrad";
+ else if (lod)
+ texop += ".SampleLevel";
+ else
+ texop += ".Sample";
+ }
+ else
+ {
+ switch (imgtype.image.dim)
+ {
+ case Dim1D:
+ texop += "tex1D";
+ break;
+ case Dim2D:
+ texop += "tex2D";
+ break;
+ case Dim3D:
+ texop += "tex3D";
+ break;
+ case DimCube:
+ texop += "texCUBE";
+ break;
+ case DimRect:
+ case DimBuffer:
+ case DimSubpassData:
+ SPIRV_CROSS_THROW("Buffer texture support is not yet implemented for HLSL"); // TODO
+ default:
+ SPIRV_CROSS_THROW("Invalid dimension.");
+ }
+
+ if (gather)
+ SPIRV_CROSS_THROW("textureGather is not supported in HLSL shader model 2/3.");
+ if (offset || coffset)
+ SPIRV_CROSS_THROW("textureOffset is not supported in HLSL shader model 2/3.");
+ if (proj)
+ texop += "proj";
+ if (grad_x || grad_y)
+ texop += "grad";
+ if (lod)
+ texop += "lod";
+ if (bias)
+ texop += "bias";
+ }
+ }
+
+ expr += texop;
+ expr += "(";
+ if (hlsl_options.shader_model < 40)
+ {
+ if (combined_image)
+ SPIRV_CROSS_THROW("Separate images/samplers are not supported in HLSL shader model 2/3.");
+ expr += to_expression(img);
+ }
+ else if (op != OpImageFetch)
+ {
+ string sampler_expr;
+ if (combined_image)
+ sampler_expr = to_expression(combined_image->sampler);
+ else
+ sampler_expr = to_sampler_expression(img);
+ expr += sampler_expr;
+ }
+
+ auto swizzle = [](uint32_t comps, uint32_t in_comps) -> const char * {
+ if (comps == in_comps)
+ return "";
+
+ switch (comps)
+ {
+ case 1:
+ return ".x";
+ case 2:
+ return ".xy";
+ case 3:
+ return ".xyz";
+ default:
+ return "";
+ }
+ };
+
+ bool forward = should_forward(coord);
+
+ // The IR can give us more components than we need, so chop them off as needed.
+ string coord_expr;
+ if (coord_components != expression_type(coord).vecsize)
+ coord_expr = to_enclosed_expression(coord) + swizzle(coord_components, expression_type(coord).vecsize);
+ else
+ coord_expr = to_expression(coord);
+
+ if (proj && hlsl_options.shader_model >= 40) // Legacy HLSL has "proj" operations which do this for us.
+ coord_expr = coord_expr + " / " + to_extract_component_expression(coord, coord_components);
+
+ if (hlsl_options.shader_model < 40 && lod)
+ {
+ auto &coordtype = expression_type(coord);
+ string coord_filler;
+ for (uint32_t size = coordtype.vecsize; size < 3; ++size)
+ {
+ coord_filler += ", 0.0";
+ }
+ coord_expr = "float4(" + coord_expr + coord_filler + ", " + to_expression(lod) + ")";
+ }
+
+ if (hlsl_options.shader_model < 40 && bias)
+ {
+ auto &coordtype = expression_type(coord);
+ string coord_filler;
+ for (uint32_t size = coordtype.vecsize; size < 3; ++size)
+ {
+ coord_filler += ", 0.0";
+ }
+ coord_expr = "float4(" + coord_expr + coord_filler + ", " + to_expression(bias) + ")";
+ }
+
+ if (op == OpImageFetch)
+ {
+ auto &coordtype = expression_type(coord);
+ if (imgtype.image.dim != DimBuffer && !imgtype.image.ms)
+ coord_expr =
+ join("int", coordtype.vecsize + 1, "(", coord_expr, ", ", lod ? to_expression(lod) : string("0"), ")");
+ }
+ else
+ expr += ", ";
+ expr += coord_expr;
+
+ if (dref)
+ {
+ if (hlsl_options.shader_model < 40)
+ SPIRV_CROSS_THROW("Legacy HLSL does not support comparison sampling.");
+
+ forward = forward && should_forward(dref);
+ expr += ", ";
+
+ if (proj)
+ expr += to_enclosed_expression(dref) + " / " + to_extract_component_expression(coord, coord_components);
+ else
+ expr += to_expression(dref);
+ }
+
+ if (!dref && (grad_x || grad_y))
+ {
+ forward = forward && should_forward(grad_x);
+ forward = forward && should_forward(grad_y);
+ expr += ", ";
+ expr += to_expression(grad_x);
+ expr += ", ";
+ expr += to_expression(grad_y);
+ }
+
+ if (!dref && lod && hlsl_options.shader_model >= 40 && op != OpImageFetch)
+ {
+ forward = forward && should_forward(lod);
+ expr += ", ";
+ expr += to_expression(lod);
+ }
+
+ if (!dref && bias && hlsl_options.shader_model >= 40)
+ {
+ forward = forward && should_forward(bias);
+ expr += ", ";
+ expr += to_expression(bias);
+ }
+
+ if (coffset)
+ {
+ forward = forward && should_forward(coffset);
+ expr += ", ";
+ expr += to_expression(coffset);
+ }
+ else if (offset)
+ {
+ forward = forward && should_forward(offset);
+ expr += ", ";
+ expr += to_expression(offset);
+ }
+
+ if (sample)
+ {
+ expr += ", ";
+ expr += to_expression(sample);
+ }
+
+ expr += ")";
+
+ if (op == OpImageQueryLod)
+ {
+ // This is rather awkward.
+ // textureQueryLod returns two values, the "accessed level",
+ // as well as the actual LOD lambda.
+ // As far as I can tell, there is no way to get the .x component
+ // according to GLSL spec, and it depends on the sampler itself.
+ // Just assume X == Y, so we will need to splat the result to a float2.
+ statement("float _", id, "_tmp = ", expr, ";");
+ emit_op(result_type, id, join("float2(_", id, "_tmp, _", id, "_tmp)"), true, true);
+ }
+ else
+ {
+ emit_op(result_type, id, expr, forward, false);
+ }
+
+ for (auto &inherit : inherited_expressions)
+ inherit_expression_dependencies(id, inherit);
+
+ switch (op)
+ {
+ case OpImageSampleDrefImplicitLod:
+ case OpImageSampleImplicitLod:
+ case OpImageSampleProjImplicitLod:
+ case OpImageSampleProjDrefImplicitLod:
+ case OpImageQueryLod:
+ register_control_dependent_expression(id);
+ break;
+
+ default:
+ break;
+ }
+}
+
+string CompilerHLSL::to_resource_binding(const SPIRVariable &var)
+{
+ // TODO: Basic implementation, might need special consideration for RW/RO structured buffers,
+ // RW/RO images, and so on.
+
+ if (!has_decoration(var.self, DecorationBinding))
+ return "";
+
+ const auto &type = get<SPIRType>(var.basetype);
+ char space = '\0';
+
+ switch (type.basetype)
+ {
+ case SPIRType::SampledImage:
+ space = 't'; // SRV
+ break;
+
+ case SPIRType::Image:
+ if (type.image.sampled == 2 && type.image.dim != DimSubpassData)
+ space = 'u'; // UAV
+ else
+ space = 't'; // SRV
+ break;
+
+ case SPIRType::Sampler:
+ space = 's';
+ break;
+
+ case SPIRType::Struct:
+ {
+ auto storage = type.storage;
+ if (storage == StorageClassUniform)
+ {
+ if (has_decoration(type.self, DecorationBufferBlock))
+ {
+ Bitset flags = ir.get_buffer_block_flags(var);
+ bool is_readonly = flags.get(DecorationNonWritable);
+ space = is_readonly ? 't' : 'u'; // UAV
+ }
+ else if (has_decoration(type.self, DecorationBlock))
+ space = 'b'; // Constant buffers
+ }
+ else if (storage == StorageClassPushConstant)
+ space = 'b'; // Constant buffers
+ else if (storage == StorageClassStorageBuffer)
+ {
+ // UAV or SRV depending on readonly flag.
+ Bitset flags = ir.get_buffer_block_flags(var);
+ bool is_readonly = flags.get(DecorationNonWritable);
+ space = is_readonly ? 't' : 'u';
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (!space)
+ return "";
+
+ return to_resource_register(space, get_decoration(var.self, DecorationBinding),
+ get_decoration(var.self, DecorationDescriptorSet));
+}
+
+string CompilerHLSL::to_resource_binding_sampler(const SPIRVariable &var)
+{
+ // For combined image samplers.
+ if (!has_decoration(var.self, DecorationBinding))
+ return "";
+
+ return to_resource_register('s', get_decoration(var.self, DecorationBinding),
+ get_decoration(var.self, DecorationDescriptorSet));
+}
+
+string CompilerHLSL::to_resource_register(char space, uint32_t binding, uint32_t space_set)
+{
+ if (hlsl_options.shader_model >= 51)
+ return join(" : register(", space, binding, ", space", space_set, ")");
+ else
+ return join(" : register(", space, binding, ")");
+}
+
+void CompilerHLSL::emit_modern_uniform(const SPIRVariable &var)
+{
+ auto &type = get<SPIRType>(var.basetype);
+ switch (type.basetype)
+ {
+ case SPIRType::SampledImage:
+ case SPIRType::Image:
+ {
+ bool is_coherent = false;
+ if (type.basetype == SPIRType::Image && type.image.sampled == 2)
+ is_coherent = has_decoration(var.self, DecorationCoherent);
+
+ statement(is_coherent ? "globallycoherent " : "", image_type_hlsl_modern(type, var.self), " ",
+ to_name(var.self), type_to_array_glsl(type), to_resource_binding(var), ";");
+
+ if (type.basetype == SPIRType::SampledImage && type.image.dim != DimBuffer)
+ {
+ // For combined image samplers, also emit a combined image sampler.
+ if (image_is_comparison(type, var.self))
+ statement("SamplerComparisonState ", to_sampler_expression(var.self), type_to_array_glsl(type),
+ to_resource_binding_sampler(var), ";");
+ else
+ statement("SamplerState ", to_sampler_expression(var.self), type_to_array_glsl(type),
+ to_resource_binding_sampler(var), ";");
+ }
+ break;
+ }
+
+ case SPIRType::Sampler:
+ if (comparison_ids.count(var.self))
+ statement("SamplerComparisonState ", to_name(var.self), type_to_array_glsl(type), to_resource_binding(var),
+ ";");
+ else
+ statement("SamplerState ", to_name(var.self), type_to_array_glsl(type), to_resource_binding(var), ";");
+ break;
+
+ default:
+ statement(variable_decl(var), to_resource_binding(var), ";");
+ break;
+ }
+}
+
+void CompilerHLSL::emit_legacy_uniform(const SPIRVariable &var)
+{
+ auto &type = get<SPIRType>(var.basetype);
+ switch (type.basetype)
+ {
+ case SPIRType::Sampler:
+ case SPIRType::Image:
+ SPIRV_CROSS_THROW("Separate image and samplers not supported in legacy HLSL.");
+
+ default:
+ statement(variable_decl(var), ";");
+ break;
+ }
+}
+
+void CompilerHLSL::emit_uniform(const SPIRVariable &var)
+{
+ add_resource_name(var.self);
+ if (hlsl_options.shader_model >= 40)
+ emit_modern_uniform(var);
+ else
+ emit_legacy_uniform(var);
+}
+
+string CompilerHLSL::bitcast_glsl_op(const SPIRType &out_type, const SPIRType &in_type)
+{
+ if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::Int)
+ return type_to_glsl(out_type);
+ else if (out_type.basetype == SPIRType::UInt64 && in_type.basetype == SPIRType::Int64)
+ return type_to_glsl(out_type);
+ else if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::Float)
+ return "asuint";
+ else if (out_type.basetype == SPIRType::Int && in_type.basetype == SPIRType::UInt)
+ return type_to_glsl(out_type);
+ else if (out_type.basetype == SPIRType::Int64 && in_type.basetype == SPIRType::UInt64)
+ return type_to_glsl(out_type);
+ else if (out_type.basetype == SPIRType::Int && in_type.basetype == SPIRType::Float)
+ return "asint";
+ else if (out_type.basetype == SPIRType::Float && in_type.basetype == SPIRType::UInt)
+ return "asfloat";
+ else if (out_type.basetype == SPIRType::Float && in_type.basetype == SPIRType::Int)
+ return "asfloat";
+ else if (out_type.basetype == SPIRType::Int64 && in_type.basetype == SPIRType::Double)
+ SPIRV_CROSS_THROW("Double to Int64 is not supported in HLSL.");
+ else if (out_type.basetype == SPIRType::UInt64 && in_type.basetype == SPIRType::Double)
+ SPIRV_CROSS_THROW("Double to UInt64 is not supported in HLSL.");
+ else if (out_type.basetype == SPIRType::Double && in_type.basetype == SPIRType::Int64)
+ return "asdouble";
+ else if (out_type.basetype == SPIRType::Double && in_type.basetype == SPIRType::UInt64)
+ return "asdouble";
+ else if (out_type.basetype == SPIRType::Half && in_type.basetype == SPIRType::UInt && in_type.vecsize == 1)
+ {
+ if (!requires_explicit_fp16_packing)
+ {
+ requires_explicit_fp16_packing = true;
+ force_recompile = true;
+ }
+ return "SPIRV_Cross_unpackFloat2x16";
+ }
+ else if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::Half && in_type.vecsize == 2)
+ {
+ if (!requires_explicit_fp16_packing)
+ {
+ requires_explicit_fp16_packing = true;
+ force_recompile = true;
+ }
+ return "SPIRV_Cross_packFloat2x16";
+ }
+ else
+ return "";
+}
+
+void CompilerHLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop, const uint32_t *args, uint32_t count)
+{
+ auto op = static_cast<GLSLstd450>(eop);
+
+ // If we need to do implicit bitcasts, make sure we do it with the correct type.
+ uint32_t integer_width = get_integer_width_for_glsl_instruction(op, args, count);
+ auto int_type = to_signed_basetype(integer_width);
+ auto uint_type = to_unsigned_basetype(integer_width);
+
+ switch (op)
+ {
+ case GLSLstd450InverseSqrt:
+ emit_unary_func_op(result_type, id, args[0], "rsqrt");
+ break;
+
+ case GLSLstd450Fract:
+ emit_unary_func_op(result_type, id, args[0], "frac");
+ break;
+
+ case GLSLstd450RoundEven:
+ SPIRV_CROSS_THROW("roundEven is not supported on HLSL.");
+
+ case GLSLstd450Acosh:
+ case GLSLstd450Asinh:
+ case GLSLstd450Atanh:
+ SPIRV_CROSS_THROW("Inverse hyperbolics are not supported on HLSL.");
+
+ case GLSLstd450FMix:
+ case GLSLstd450IMix:
+ emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "lerp");
+ break;
+
+ case GLSLstd450Atan2:
+ emit_binary_func_op(result_type, id, args[0], args[1], "atan2");
+ break;
+
+ case GLSLstd450Fma:
+ emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "mad");
+ break;
+
+ case GLSLstd450InterpolateAtCentroid:
+ emit_unary_func_op(result_type, id, args[0], "EvaluateAttributeAtCentroid");
+ break;
+ case GLSLstd450InterpolateAtSample:
+ emit_binary_func_op(result_type, id, args[0], args[1], "EvaluateAttributeAtSample");
+ break;
+ case GLSLstd450InterpolateAtOffset:
+ emit_binary_func_op(result_type, id, args[0], args[1], "EvaluateAttributeSnapped");
+ break;
+
+ case GLSLstd450PackHalf2x16:
+ if (!requires_fp16_packing)
+ {
+ requires_fp16_packing = true;
+ force_recompile = true;
+ }
+ emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_packHalf2x16");
+ break;
+
+ case GLSLstd450UnpackHalf2x16:
+ if (!requires_fp16_packing)
+ {
+ requires_fp16_packing = true;
+ force_recompile = true;
+ }
+ emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_unpackHalf2x16");
+ break;
+
+ case GLSLstd450PackSnorm4x8:
+ if (!requires_snorm8_packing)
+ {
+ requires_snorm8_packing = true;
+ force_recompile = true;
+ }
+ emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_packSnorm4x8");
+ break;
+
+ case GLSLstd450UnpackSnorm4x8:
+ if (!requires_snorm8_packing)
+ {
+ requires_snorm8_packing = true;
+ force_recompile = true;
+ }
+ emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_unpackSnorm4x8");
+ break;
+
+ case GLSLstd450PackUnorm4x8:
+ if (!requires_unorm8_packing)
+ {
+ requires_unorm8_packing = true;
+ force_recompile = true;
+ }
+ emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_packUnorm4x8");
+ break;
+
+ case GLSLstd450UnpackUnorm4x8:
+ if (!requires_unorm8_packing)
+ {
+ requires_unorm8_packing = true;
+ force_recompile = true;
+ }
+ emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_unpackUnorm4x8");
+ break;
+
+ case GLSLstd450PackSnorm2x16:
+ if (!requires_snorm16_packing)
+ {
+ requires_snorm16_packing = true;
+ force_recompile = true;
+ }
+ emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_packSnorm2x16");
+ break;
+
+ case GLSLstd450UnpackSnorm2x16:
+ if (!requires_snorm16_packing)
+ {
+ requires_snorm16_packing = true;
+ force_recompile = true;
+ }
+ emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_unpackSnorm2x16");
+ break;
+
+ case GLSLstd450PackUnorm2x16:
+ if (!requires_unorm16_packing)
+ {
+ requires_unorm16_packing = true;
+ force_recompile = true;
+ }
+ emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_packUnorm2x16");
+ break;
+
+ case GLSLstd450UnpackUnorm2x16:
+ if (!requires_unorm16_packing)
+ {
+ requires_unorm16_packing = true;
+ force_recompile = true;
+ }
+ emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_unpackUnorm2x16");
+ break;
+
+ case GLSLstd450PackDouble2x32:
+ case GLSLstd450UnpackDouble2x32:
+ SPIRV_CROSS_THROW("packDouble2x32/unpackDouble2x32 not supported in HLSL.");
+
+ case GLSLstd450FindILsb:
+ emit_unary_func_op(result_type, id, args[0], "firstbitlow");
+ break;
+
+ case GLSLstd450FindSMsb:
+ emit_unary_func_op_cast(result_type, id, args[0], "firstbithigh", int_type, int_type);
+ break;
+
+ case GLSLstd450FindUMsb:
+ emit_unary_func_op_cast(result_type, id, args[0], "firstbithigh", uint_type, uint_type);
+ break;
+
+ case GLSLstd450MatrixInverse:
+ {
+ auto &type = get<SPIRType>(result_type);
+ if (type.vecsize == 2 && type.columns == 2)
+ {
+ if (!requires_inverse_2x2)
+ {
+ requires_inverse_2x2 = true;
+ force_recompile = true;
+ }
+ }
+ else if (type.vecsize == 3 && type.columns == 3)
+ {
+ if (!requires_inverse_3x3)
+ {
+ requires_inverse_3x3 = true;
+ force_recompile = true;
+ }
+ }
+ else if (type.vecsize == 4 && type.columns == 4)
+ {
+ if (!requires_inverse_4x4)
+ {
+ requires_inverse_4x4 = true;
+ force_recompile = true;
+ }
+ }
+ emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_Inverse");
+ break;
+ }
+
+ default:
+ CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count);
+ break;
+ }
+}
+
+string CompilerHLSL::read_access_chain(const SPIRAccessChain &chain)
+{
+ auto &type = get<SPIRType>(chain.basetype);
+
+ SPIRType target_type;
+ target_type.basetype = SPIRType::UInt;
+ target_type.vecsize = type.vecsize;
+ target_type.columns = type.columns;
+
+ if (type.basetype == SPIRType::Struct)
+ SPIRV_CROSS_THROW("Reading structs from ByteAddressBuffer not yet supported.");
+
+ if (type.width != 32)
+ SPIRV_CROSS_THROW("Reading types other than 32-bit from ByteAddressBuffer not yet supported.");
+
+ if (!type.array.empty())
+ SPIRV_CROSS_THROW("Reading arrays from ByteAddressBuffer not yet supported.");
+
+ string load_expr;
+
+ // Load a vector or scalar.
+ if (type.columns == 1 && !chain.row_major_matrix)
+ {
+ const char *load_op = nullptr;
+ switch (type.vecsize)
+ {
+ case 1:
+ load_op = "Load";
+ break;
+ case 2:
+ load_op = "Load2";
+ break;
+ case 3:
+ load_op = "Load3";
+ break;
+ case 4:
+ load_op = "Load4";
+ break;
+ default:
+ SPIRV_CROSS_THROW("Unknown vector size.");
+ }
+
+ load_expr = join(chain.base, ".", load_op, "(", chain.dynamic_index, chain.static_index, ")");
+ }
+ else if (type.columns == 1)
+ {
+ // Strided load since we are loading a column from a row-major matrix.
+ if (type.vecsize > 1)
+ {
+ load_expr = type_to_glsl(target_type);
+ load_expr += "(";
+ }
+
+ for (uint32_t r = 0; r < type.vecsize; r++)
+ {
+ load_expr +=
+ join(chain.base, ".Load(", chain.dynamic_index, chain.static_index + r * chain.matrix_stride, ")");
+ if (r + 1 < type.vecsize)
+ load_expr += ", ";
+ }
+
+ if (type.vecsize > 1)
+ load_expr += ")";
+ }
+ else if (!chain.row_major_matrix)
+ {
+ // Load a matrix, column-major, the easy case.
+ const char *load_op = nullptr;
+ switch (type.vecsize)
+ {
+ case 1:
+ load_op = "Load";
+ break;
+ case 2:
+ load_op = "Load2";
+ break;
+ case 3:
+ load_op = "Load3";
+ break;
+ case 4:
+ load_op = "Load4";
+ break;
+ default:
+ SPIRV_CROSS_THROW("Unknown vector size.");
+ }
+
+ // Note, this loading style in HLSL is *actually* row-major, but we always treat matrices as transposed in this backend,
+ // so row-major is technically column-major ...
+ load_expr = type_to_glsl(target_type);
+ load_expr += "(";
+ for (uint32_t c = 0; c < type.columns; c++)
+ {
+ load_expr += join(chain.base, ".", load_op, "(", chain.dynamic_index,
+ chain.static_index + c * chain.matrix_stride, ")");
+ if (c + 1 < type.columns)
+ load_expr += ", ";
+ }
+ load_expr += ")";
+ }
+ else
+ {
+ // Pick out elements one by one ... Hopefully compilers are smart enough to recognize this pattern
+ // considering HLSL is "row-major decl", but "column-major" memory layout (basically implicit transpose model, ugh) ...
+
+ load_expr = type_to_glsl(target_type);
+ load_expr += "(";
+ for (uint32_t c = 0; c < type.columns; c++)
+ {
+ for (uint32_t r = 0; r < type.vecsize; r++)
+ {
+ load_expr += join(chain.base, ".Load(", chain.dynamic_index,
+ chain.static_index + c * (type.width / 8) + r * chain.matrix_stride, ")");
+
+ if ((r + 1 < type.vecsize) || (c + 1 < type.columns))
+ load_expr += ", ";
+ }
+ }
+ load_expr += ")";
+ }
+
+ auto bitcast_op = bitcast_glsl_op(type, target_type);
+ if (!bitcast_op.empty())
+ load_expr = join(bitcast_op, "(", load_expr, ")");
+
+ return load_expr;
+}
+
+void CompilerHLSL::emit_load(const Instruction &instruction)
+{
+ auto ops = stream(instruction);
+
+ auto *chain = maybe_get<SPIRAccessChain>(ops[2]);
+ if (chain)
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ uint32_t ptr = ops[2];
+
+ auto load_expr = read_access_chain(*chain);
+
+ bool forward = should_forward(ptr) && forced_temporaries.find(id) == end(forced_temporaries);
+
+ // If we are forwarding this load,
+ // don't register the read to access chain here, defer that to when we actually use the expression,
+ // using the add_implied_read_expression mechanism.
+ if (!forward)
+ track_expression_read(chain->self);
+
+ // Do not forward complex load sequences like matrices, structs and arrays.
+ auto &type = get<SPIRType>(result_type);
+ if (type.columns > 1 || !type.array.empty() || type.basetype == SPIRType::Struct)
+ forward = false;
+
+ auto &e = emit_op(result_type, id, load_expr, forward, true);
+ e.need_transpose = false;
+ register_read(id, ptr, forward);
+ inherit_expression_dependencies(id, ptr);
+ if (forward)
+ add_implied_read_expression(e, chain->self);
+ }
+ else
+ CompilerGLSL::emit_instruction(instruction);
+}
+
+void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t value)
+{
+ auto &type = get<SPIRType>(chain.basetype);
+
+ // Make sure we trigger a read of the constituents in the access chain.
+ track_expression_read(chain.self);
+
+ SPIRType target_type;
+ target_type.basetype = SPIRType::UInt;
+ target_type.vecsize = type.vecsize;
+ target_type.columns = type.columns;
+
+ if (type.basetype == SPIRType::Struct)
+ SPIRV_CROSS_THROW("Writing structs to RWByteAddressBuffer not yet supported.");
+ if (type.width != 32)
+ SPIRV_CROSS_THROW("Writing types other than 32-bit to RWByteAddressBuffer not yet supported.");
+ if (!type.array.empty())
+ SPIRV_CROSS_THROW("Reading arrays from ByteAddressBuffer not yet supported.");
+
+ if (type.columns == 1 && !chain.row_major_matrix)
+ {
+ const char *store_op = nullptr;
+ switch (type.vecsize)
+ {
+ case 1:
+ store_op = "Store";
+ break;
+ case 2:
+ store_op = "Store2";
+ break;
+ case 3:
+ store_op = "Store3";
+ break;
+ case 4:
+ store_op = "Store4";
+ break;
+ default:
+ SPIRV_CROSS_THROW("Unknown vector size.");
+ }
+
+ auto store_expr = to_expression(value);
+ auto bitcast_op = bitcast_glsl_op(target_type, type);
+ if (!bitcast_op.empty())
+ store_expr = join(bitcast_op, "(", store_expr, ")");
+ statement(chain.base, ".", store_op, "(", chain.dynamic_index, chain.static_index, ", ", store_expr, ");");
+ }
+ else if (type.columns == 1)
+ {
+ // Strided store.
+ for (uint32_t r = 0; r < type.vecsize; r++)
+ {
+ auto store_expr = to_enclosed_expression(value);
+ if (type.vecsize > 1)
+ {
+ store_expr += ".";
+ store_expr += index_to_swizzle(r);
+ }
+ remove_duplicate_swizzle(store_expr);
+
+ auto bitcast_op = bitcast_glsl_op(target_type, type);
+ if (!bitcast_op.empty())
+ store_expr = join(bitcast_op, "(", store_expr, ")");
+ statement(chain.base, ".Store(", chain.dynamic_index, chain.static_index + chain.matrix_stride * r, ", ",
+ store_expr, ");");
+ }
+ }
+ else if (!chain.row_major_matrix)
+ {
+ const char *store_op = nullptr;
+ switch (type.vecsize)
+ {
+ case 1:
+ store_op = "Store";
+ break;
+ case 2:
+ store_op = "Store2";
+ break;
+ case 3:
+ store_op = "Store3";
+ break;
+ case 4:
+ store_op = "Store4";
+ break;
+ default:
+ SPIRV_CROSS_THROW("Unknown vector size.");
+ }
+
+ for (uint32_t c = 0; c < type.columns; c++)
+ {
+ auto store_expr = join(to_enclosed_expression(value), "[", c, "]");
+ auto bitcast_op = bitcast_glsl_op(target_type, type);
+ if (!bitcast_op.empty())
+ store_expr = join(bitcast_op, "(", store_expr, ")");
+ statement(chain.base, ".", store_op, "(", chain.dynamic_index, chain.static_index + c * chain.matrix_stride,
+ ", ", store_expr, ");");
+ }
+ }
+ else
+ {
+ for (uint32_t r = 0; r < type.vecsize; r++)
+ {
+ for (uint32_t c = 0; c < type.columns; c++)
+ {
+ auto store_expr = join(to_enclosed_expression(value), "[", c, "].", index_to_swizzle(r));
+ remove_duplicate_swizzle(store_expr);
+ auto bitcast_op = bitcast_glsl_op(target_type, type);
+ if (!bitcast_op.empty())
+ store_expr = join(bitcast_op, "(", store_expr, ")");
+ statement(chain.base, ".Store(", chain.dynamic_index,
+ chain.static_index + c * (type.width / 8) + r * chain.matrix_stride, ", ", store_expr, ");");
+ }
+ }
+ }
+
+ register_write(chain.self);
+}
+
+void CompilerHLSL::emit_store(const Instruction &instruction)
+{
+ auto ops = stream(instruction);
+ auto *chain = maybe_get<SPIRAccessChain>(ops[0]);
+ if (chain)
+ write_access_chain(*chain, ops[1]);
+ else
+ CompilerGLSL::emit_instruction(instruction);
+}
+
+void CompilerHLSL::emit_access_chain(const Instruction &instruction)
+{
+ auto ops = stream(instruction);
+ uint32_t length = instruction.length;
+
+ bool need_byte_access_chain = false;
+ auto &type = expression_type(ops[2]);
+ const auto *chain = maybe_get<SPIRAccessChain>(ops[2]);
+
+ if (chain)
+ {
+ // Keep tacking on an existing access chain.
+ need_byte_access_chain = true;
+ }
+ else if (type.storage == StorageClassStorageBuffer || has_decoration(type.self, DecorationBufferBlock))
+ {
+ // If we are starting to poke into an SSBO, we are dealing with ByteAddressBuffers, and we need
+ // to emit SPIRAccessChain rather than a plain SPIRExpression.
+ uint32_t chain_arguments = length - 3;
+ if (chain_arguments > type.array.size())
+ need_byte_access_chain = true;
+ }
+
+ if (need_byte_access_chain)
+ {
+ uint32_t to_plain_buffer_length = static_cast<uint32_t>(type.array.size());
+ auto *backing_variable = maybe_get_backing_variable(ops[2]);
+
+ string base;
+ if (to_plain_buffer_length != 0)
+ base = access_chain(ops[2], &ops[3], to_plain_buffer_length, get<SPIRType>(ops[0]));
+ else if (chain)
+ base = chain->base;
+ else
+ base = to_expression(ops[2]);
+
+ // Start traversing type hierarchy at the proper non-pointer types.
+ auto *basetype = &get_pointee_type(type);
+
+ // Traverse the type hierarchy down to the actual buffer types.
+ for (uint32_t i = 0; i < to_plain_buffer_length; i++)
+ {
+ assert(basetype->parent_type);
+ basetype = &get<SPIRType>(basetype->parent_type);
+ }
+
+ uint32_t matrix_stride = 0;
+ bool row_major_matrix = false;
+
+ // Inherit matrix information.
+ if (chain)
+ {
+ matrix_stride = chain->matrix_stride;
+ row_major_matrix = chain->row_major_matrix;
+ }
+
+ auto offsets =
+ flattened_access_chain_offset(*basetype, &ops[3 + to_plain_buffer_length],
+ length - 3 - to_plain_buffer_length, 0, 1, &row_major_matrix, &matrix_stride);
+
+ auto &e = set<SPIRAccessChain>(ops[1], ops[0], type.storage, base, offsets.first, offsets.second);
+ e.row_major_matrix = row_major_matrix;
+ e.matrix_stride = matrix_stride;
+ e.immutable = should_forward(ops[2]);
+ e.loaded_from = backing_variable ? backing_variable->self : 0;
+
+ if (chain)
+ {
+ e.dynamic_index += chain->dynamic_index;
+ e.static_index += chain->static_index;
+ }
+
+ for (uint32_t i = 2; i < length; i++)
+ {
+ inherit_expression_dependencies(ops[1], ops[i]);
+ add_implied_read_expression(e, ops[i]);
+ }
+ }
+ else
+ {
+ CompilerGLSL::emit_instruction(instruction);
+ }
+}
+
+void CompilerHLSL::emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op)
+{
+ const char *atomic_op = nullptr;
+
+ string value_expr;
+ if (op != OpAtomicIDecrement && op != OpAtomicIIncrement)
+ value_expr = to_expression(ops[op == OpAtomicCompareExchange ? 6 : 5]);
+
+ switch (op)
+ {
+ case OpAtomicIIncrement:
+ atomic_op = "InterlockedAdd";
+ value_expr = "1";
+ break;
+
+ case OpAtomicIDecrement:
+ atomic_op = "InterlockedAdd";
+ value_expr = "-1";
+ break;
+
+ case OpAtomicISub:
+ atomic_op = "InterlockedAdd";
+ value_expr = join("-", enclose_expression(value_expr));
+ break;
+
+ case OpAtomicSMin:
+ case OpAtomicUMin:
+ atomic_op = "InterlockedMin";
+ break;
+
+ case OpAtomicSMax:
+ case OpAtomicUMax:
+ atomic_op = "InterlockedMax";
+ break;
+
+ case OpAtomicAnd:
+ atomic_op = "InterlockedAnd";
+ break;
+
+ case OpAtomicOr:
+ atomic_op = "InterlockedOr";
+ break;
+
+ case OpAtomicXor:
+ atomic_op = "InterlockedXor";
+ break;
+
+ case OpAtomicIAdd:
+ atomic_op = "InterlockedAdd";
+ break;
+
+ case OpAtomicExchange:
+ atomic_op = "InterlockedExchange";
+ break;
+
+ case OpAtomicCompareExchange:
+ if (length < 8)
+ SPIRV_CROSS_THROW("Not enough data for opcode.");
+ atomic_op = "InterlockedCompareExchange";
+ value_expr = join(to_expression(ops[7]), ", ", value_expr);
+ break;
+
+ default:
+ SPIRV_CROSS_THROW("Unknown atomic opcode.");
+ }
+
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ forced_temporaries.insert(ops[1]);
+
+ auto &type = get<SPIRType>(result_type);
+ statement(variable_decl(type, to_name(id)), ";");
+
+ auto &data_type = expression_type(ops[2]);
+ auto *chain = maybe_get<SPIRAccessChain>(ops[2]);
+ SPIRType::BaseType expr_type;
+ if (data_type.storage == StorageClassImage || !chain)
+ {
+ statement(atomic_op, "(", to_expression(ops[2]), ", ", value_expr, ", ", to_name(id), ");");
+ expr_type = data_type.basetype;
+ }
+ else
+ {
+ // RWByteAddress buffer is always uint in its underlying type.
+ expr_type = SPIRType::UInt;
+ statement(chain->base, ".", atomic_op, "(", chain->dynamic_index, chain->static_index, ", ", value_expr, ", ",
+ to_name(id), ");");
+ }
+
+ auto expr = bitcast_expression(type, expr_type, to_name(id));
+ set<SPIRExpression>(id, expr, result_type, true);
+ flush_all_atomic_capable_variables();
+ register_read(ops[1], ops[2], should_forward(ops[2]));
+}
+
+void CompilerHLSL::emit_subgroup_op(const Instruction &i)
+{
+ if (hlsl_options.shader_model < 60)
+ SPIRV_CROSS_THROW("Wave ops requires SM 6.0 or higher.");
+
+ const uint32_t *ops = stream(i);
+ auto op = static_cast<Op>(i.op);
+
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+
+ auto scope = static_cast<Scope>(get<SPIRConstant>(ops[2]).scalar());
+ if (scope != ScopeSubgroup)
+ SPIRV_CROSS_THROW("Only subgroup scope is supported.");
+
+ const auto make_inclusive_Sum = [&](const string &expr) -> string {
+ return join(expr, " + ", to_expression(ops[4]));
+ };
+
+ const auto make_inclusive_Product = [&](const string &expr) -> string {
+ return join(expr, " * ", to_expression(ops[4]));
+ };
+
+#define make_inclusive_BitAnd(expr) ""
+#define make_inclusive_BitOr(expr) ""
+#define make_inclusive_BitXor(expr) ""
+#define make_inclusive_Min(expr) ""
+#define make_inclusive_Max(expr) ""
+
+ switch (op)
+ {
+ case OpGroupNonUniformElect:
+ emit_op(result_type, id, "WaveIsFirstLane()", true);
+ break;
+
+ case OpGroupNonUniformBroadcast:
+ emit_binary_func_op(result_type, id, ops[3], ops[4], "WaveReadLaneAt");
+ break;
+
+ case OpGroupNonUniformBroadcastFirst:
+ emit_unary_func_op(result_type, id, ops[3], "WaveReadLaneFirst");
+ break;
+
+ case OpGroupNonUniformBallot:
+ emit_unary_func_op(result_type, id, ops[3], "WaveActiveBallot");
+ break;
+
+ case OpGroupNonUniformInverseBallot:
+ SPIRV_CROSS_THROW("Cannot trivially implement InverseBallot in HLSL.");
+ break;
+
+ case OpGroupNonUniformBallotBitExtract:
+ SPIRV_CROSS_THROW("Cannot trivially implement BallotBitExtract in HLSL.");
+ break;
+
+ case OpGroupNonUniformBallotFindLSB:
+ SPIRV_CROSS_THROW("Cannot trivially implement BallotFindLSB in HLSL.");
+ break;
+
+ case OpGroupNonUniformBallotFindMSB:
+ SPIRV_CROSS_THROW("Cannot trivially implement BallotFindMSB in HLSL.");
+ break;
+
+ case OpGroupNonUniformBallotBitCount:
+ {
+ auto operation = static_cast<GroupOperation>(ops[3]);
+ if (operation == GroupOperationReduce)
+ {
+ bool forward = should_forward(ops[4]);
+ auto left = join("countbits(", to_enclosed_expression(ops[4]), ".x) + countbits(",
+ to_enclosed_expression(ops[4]), ".y)");
+ auto right = join("countbits(", to_enclosed_expression(ops[4]), ".z) + countbits(",
+ to_enclosed_expression(ops[4]), ".w)");
+ emit_op(result_type, id, join(left, " + ", right), forward);
+ inherit_expression_dependencies(id, ops[4]);
+ }
+ else if (operation == GroupOperationInclusiveScan)
+ SPIRV_CROSS_THROW("Cannot trivially implement BallotBitCount Inclusive Scan in HLSL.");
+ else if (operation == GroupOperationExclusiveScan)
+ SPIRV_CROSS_THROW("Cannot trivially implement BallotBitCount Exclusive Scan in HLSL.");
+ else
+ SPIRV_CROSS_THROW("Invalid BitCount operation.");
+ break;
+ }
+
+ case OpGroupNonUniformShuffle:
+ SPIRV_CROSS_THROW("Cannot trivially implement Shuffle in HLSL.");
+ case OpGroupNonUniformShuffleXor:
+ SPIRV_CROSS_THROW("Cannot trivially implement ShuffleXor in HLSL.");
+ case OpGroupNonUniformShuffleUp:
+ SPIRV_CROSS_THROW("Cannot trivially implement ShuffleUp in HLSL.");
+ case OpGroupNonUniformShuffleDown:
+ SPIRV_CROSS_THROW("Cannot trivially implement ShuffleDown in HLSL.");
+
+ case OpGroupNonUniformAll:
+ emit_unary_func_op(result_type, id, ops[3], "WaveActiveAllTrue");
+ break;
+
+ case OpGroupNonUniformAny:
+ emit_unary_func_op(result_type, id, ops[3], "WaveActiveAnyTrue");
+ break;
+
+ case OpGroupNonUniformAllEqual:
+ {
+ auto &type = get<SPIRType>(result_type);
+ emit_unary_func_op(result_type, id, ops[3],
+ type.basetype == SPIRType::Boolean ? "WaveActiveAllEqualBool" : "WaveActiveAllEqual");
+ break;
+ }
+
+ // clang-format off
+#define HLSL_GROUP_OP(op, hlsl_op, supports_scan) \
+case OpGroupNonUniform##op: \
+ { \
+ auto operation = static_cast<GroupOperation>(ops[3]); \
+ if (operation == GroupOperationReduce) \
+ emit_unary_func_op(result_type, id, ops[4], "WaveActive" #hlsl_op); \
+ else if (operation == GroupOperationInclusiveScan && supports_scan) \
+ { \
+ bool forward = should_forward(ops[4]); \
+ emit_op(result_type, id, make_inclusive_##hlsl_op (join("WavePrefix" #hlsl_op, "(", to_expression(ops[4]), ")")), forward); \
+ inherit_expression_dependencies(id, ops[4]); \
+ } \
+ else if (operation == GroupOperationExclusiveScan && supports_scan) \
+ emit_unary_func_op(result_type, id, ops[4], "WavePrefix" #hlsl_op); \
+ else if (operation == GroupOperationClusteredReduce) \
+ SPIRV_CROSS_THROW("Cannot trivially implement ClusteredReduce in HLSL."); \
+ else \
+ SPIRV_CROSS_THROW("Invalid group operation."); \
+ break; \
+ }
+ HLSL_GROUP_OP(FAdd, Sum, true)
+ HLSL_GROUP_OP(FMul, Product, true)
+ HLSL_GROUP_OP(FMin, Min, false)
+ HLSL_GROUP_OP(FMax, Max, false)
+ HLSL_GROUP_OP(IAdd, Sum, true)
+ HLSL_GROUP_OP(IMul, Product, true)
+ HLSL_GROUP_OP(SMin, Min, false)
+ HLSL_GROUP_OP(SMax, Max, false)
+ HLSL_GROUP_OP(UMin, Min, false)
+ HLSL_GROUP_OP(UMax, Max, false)
+ HLSL_GROUP_OP(BitwiseAnd, BitAnd, false)
+ HLSL_GROUP_OP(BitwiseOr, BitOr, false)
+ HLSL_GROUP_OP(BitwiseXor, BitXor, false)
+#undef HLSL_GROUP_OP
+ // clang-format on
+
+ case OpGroupNonUniformQuadSwap:
+ {
+ uint32_t direction = get<SPIRConstant>(ops[4]).scalar();
+ if (direction == 0)
+ emit_unary_func_op(result_type, id, ops[3], "QuadReadAcrossX");
+ else if (direction == 1)
+ emit_unary_func_op(result_type, id, ops[3], "QuadReadAcrossY");
+ else if (direction == 2)
+ emit_unary_func_op(result_type, id, ops[3], "QuadReadAcrossDiagonal");
+ else
+ SPIRV_CROSS_THROW("Invalid quad swap direction.");
+ break;
+ }
+
+ case OpGroupNonUniformQuadBroadcast:
+ {
+ emit_binary_func_op(result_type, id, ops[3], ops[4], "QuadReadLaneAt");
+ break;
+ }
+
+ default:
+ SPIRV_CROSS_THROW("Invalid opcode for subgroup.");
+ }
+
+ register_control_dependent_expression(id);
+}
+
+void CompilerHLSL::emit_instruction(const Instruction &instruction)
+{
+ auto ops = stream(instruction);
+ auto opcode = static_cast<Op>(instruction.op);
+
+#define HLSL_BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op)
+#define HLSL_BOP_CAST(op, type) \
+ emit_binary_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, opcode_is_sign_invariant(opcode))
+#define HLSL_UOP(op) emit_unary_op(ops[0], ops[1], ops[2], #op)
+#define HLSL_QFOP(op) emit_quaternary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], #op)
+#define HLSL_TFOP(op) emit_trinary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], #op)
+#define HLSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op)
+#define HLSL_BFOP_CAST(op, type) \
+ emit_binary_func_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, opcode_is_sign_invariant(opcode))
+#define HLSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op)
+#define HLSL_UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op)
+
+ // If we need to do implicit bitcasts, make sure we do it with the correct type.
+ uint32_t integer_width = get_integer_width_for_instruction(instruction);
+ auto int_type = to_signed_basetype(integer_width);
+
+ switch (opcode)
+ {
+ case OpAccessChain:
+ case OpInBoundsAccessChain:
+ {
+ emit_access_chain(instruction);
+ break;
+ }
+
+ case OpStore:
+ {
+ emit_store(instruction);
+ break;
+ }
+
+ case OpLoad:
+ {
+ emit_load(instruction);
+ break;
+ }
+
+ case OpMatrixTimesVector:
+ {
+ emit_binary_func_op(ops[0], ops[1], ops[3], ops[2], "mul");
+ break;
+ }
+
+ case OpVectorTimesMatrix:
+ {
+ emit_binary_func_op(ops[0], ops[1], ops[3], ops[2], "mul");
+ break;
+ }
+
+ case OpMatrixTimesMatrix:
+ {
+ emit_binary_func_op(ops[0], ops[1], ops[3], ops[2], "mul");
+ break;
+ }
+
+ case OpFMod:
+ {
+ if (!requires_op_fmod)
+ {
+ requires_op_fmod = true;
+ force_recompile = true;
+ }
+ CompilerGLSL::emit_instruction(instruction);
+ break;
+ }
+
+ case OpFRem:
+ emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], "fmod");
+ break;
+
+ case OpImage:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ auto *combined = maybe_get<SPIRCombinedImageSampler>(ops[2]);
+
+ if (combined)
+ {
+ auto &e = emit_op(result_type, id, to_expression(combined->image), true, true);
+ auto *var = maybe_get_backing_variable(combined->image);
+ if (var)
+ e.loaded_from = var->self;
+ }
+ else
+ {
+ auto &e = emit_op(result_type, id, to_expression(ops[2]), true, true);
+ auto *var = maybe_get_backing_variable(ops[2]);
+ if (var)
+ e.loaded_from = var->self;
+ }
+ break;
+ }
+
+ case OpDPdx:
+ HLSL_UFOP(ddx);
+ register_control_dependent_expression(ops[1]);
+ break;
+
+ case OpDPdy:
+ HLSL_UFOP(ddy);
+ register_control_dependent_expression(ops[1]);
+ break;
+
+ case OpDPdxFine:
+ HLSL_UFOP(ddx_fine);
+ register_control_dependent_expression(ops[1]);
+ break;
+
+ case OpDPdyFine:
+ HLSL_UFOP(ddy_fine);
+ register_control_dependent_expression(ops[1]);
+ break;
+
+ case OpDPdxCoarse:
+ HLSL_UFOP(ddx_coarse);
+ register_control_dependent_expression(ops[1]);
+ break;
+
+ case OpDPdyCoarse:
+ HLSL_UFOP(ddy_coarse);
+ register_control_dependent_expression(ops[1]);
+ break;
+
+ case OpFwidth:
+ case OpFwidthCoarse:
+ case OpFwidthFine:
+ HLSL_UFOP(fwidth);
+ register_control_dependent_expression(ops[1]);
+ break;
+
+ case OpLogicalNot:
+ {
+ auto result_type = ops[0];
+ auto id = ops[1];
+ auto &type = get<SPIRType>(result_type);
+
+ if (type.vecsize > 1)
+ emit_unrolled_unary_op(result_type, id, ops[2], "!");
+ else
+ HLSL_UOP(!);
+ break;
+ }
+
+ case OpIEqual:
+ {
+ auto result_type = ops[0];
+ auto id = ops[1];
+
+ if (expression_type(ops[2]).vecsize > 1)
+ emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "==");
+ else
+ HLSL_BOP_CAST(==, int_type);
+ break;
+ }
+
+ case OpLogicalEqual:
+ case OpFOrdEqual:
+ {
+ auto result_type = ops[0];
+ auto id = ops[1];
+
+ if (expression_type(ops[2]).vecsize > 1)
+ emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "==");
+ else
+ HLSL_BOP(==);
+ break;
+ }
+
+ case OpINotEqual:
+ {
+ auto result_type = ops[0];
+ auto id = ops[1];
+
+ if (expression_type(ops[2]).vecsize > 1)
+ emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "!=");
+ else
+ HLSL_BOP_CAST(!=, int_type);
+ break;
+ }
+
+ case OpLogicalNotEqual:
+ case OpFOrdNotEqual:
+ {
+ auto result_type = ops[0];
+ auto id = ops[1];
+
+ if (expression_type(ops[2]).vecsize > 1)
+ emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "!=");
+ else
+ HLSL_BOP(!=);
+ break;
+ }
+
+ case OpUGreaterThan:
+ case OpSGreaterThan:
+ {
+ auto result_type = ops[0];
+ auto id = ops[1];
+ auto type = opcode == OpUGreaterThan ? SPIRType::UInt : SPIRType::Int;
+
+ if (expression_type(ops[2]).vecsize > 1)
+ emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">");
+ else
+ HLSL_BOP_CAST(>, type);
+ break;
+ }
+
+ case OpFOrdGreaterThan:
+ {
+ auto result_type = ops[0];
+ auto id = ops[1];
+
+ if (expression_type(ops[2]).vecsize > 1)
+ emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">");
+ else
+ HLSL_BOP(>);
+ break;
+ }
+
+ case OpUGreaterThanEqual:
+ case OpSGreaterThanEqual:
+ {
+ auto result_type = ops[0];
+ auto id = ops[1];
+
+ auto type = opcode == OpUGreaterThanEqual ? SPIRType::UInt : SPIRType::Int;
+ if (expression_type(ops[2]).vecsize > 1)
+ emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">=");
+ else
+ HLSL_BOP_CAST(>=, type);
+ break;
+ }
+
+ case OpFOrdGreaterThanEqual:
+ {
+ auto result_type = ops[0];
+ auto id = ops[1];
+
+ if (expression_type(ops[2]).vecsize > 1)
+ emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">=");
+ else
+ HLSL_BOP(>=);
+ break;
+ }
+
+ case OpULessThan:
+ case OpSLessThan:
+ {
+ auto result_type = ops[0];
+ auto id = ops[1];
+
+ auto type = opcode == OpULessThan ? SPIRType::UInt : SPIRType::Int;
+ if (expression_type(ops[2]).vecsize > 1)
+ emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<");
+ else
+ HLSL_BOP_CAST(<, type);
+ break;
+ }
+
+ case OpFOrdLessThan:
+ {
+ auto result_type = ops[0];
+ auto id = ops[1];
+
+ if (expression_type(ops[2]).vecsize > 1)
+ emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<");
+ else
+ HLSL_BOP(<);
+ break;
+ }
+
+ case OpULessThanEqual:
+ case OpSLessThanEqual:
+ {
+ auto result_type = ops[0];
+ auto id = ops[1];
+
+ auto type = opcode == OpULessThanEqual ? SPIRType::UInt : SPIRType::Int;
+ if (expression_type(ops[2]).vecsize > 1)
+ emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<=");
+ else
+ HLSL_BOP_CAST(<=, type);
+ break;
+ }
+
+ case OpFOrdLessThanEqual:
+ {
+ auto result_type = ops[0];
+ auto id = ops[1];
+
+ if (expression_type(ops[2]).vecsize > 1)
+ emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<=");
+ else
+ HLSL_BOP(<=);
+ break;
+ }
+
+ case OpImageQueryLod:
+ emit_texture_op(instruction);
+ break;
+
+ case OpImageQuerySizeLod:
+ {
+ auto result_type = ops[0];
+ auto id = ops[1];
+
+ require_texture_query_variant(expression_type(ops[2]));
+
+ auto dummy_samples_levels = join(get_fallback_name(id), "_dummy_parameter");
+ statement("uint ", dummy_samples_levels, ";");
+
+ auto expr = join("SPIRV_Cross_textureSize(", to_expression(ops[2]), ", ",
+ bitcast_expression(SPIRType::UInt, ops[3]), ", ", dummy_samples_levels, ")");
+
+ auto &restype = get<SPIRType>(ops[0]);
+ expr = bitcast_expression(restype, SPIRType::UInt, expr);
+ emit_op(result_type, id, expr, true);
+ break;
+ }
+
+ case OpImageQuerySize:
+ {
+ auto result_type = ops[0];
+ auto id = ops[1];
+
+ require_texture_query_variant(expression_type(ops[2]));
+
+ auto dummy_samples_levels = join(get_fallback_name(id), "_dummy_parameter");
+ statement("uint ", dummy_samples_levels, ";");
+
+ auto expr = join("SPIRV_Cross_textureSize(", to_expression(ops[2]), ", 0u, ", dummy_samples_levels, ")");
+ auto &restype = get<SPIRType>(ops[0]);
+ expr = bitcast_expression(restype, SPIRType::UInt, expr);
+ emit_op(result_type, id, expr, true);
+ break;
+ }
+
+ case OpImageQuerySamples:
+ case OpImageQueryLevels:
+ {
+ auto result_type = ops[0];
+ auto id = ops[1];
+
+ require_texture_query_variant(expression_type(ops[2]));
+
+ // Keep it simple and do not emit special variants to make this look nicer ...
+ // This stuff is barely, if ever, used.
+ forced_temporaries.insert(id);
+ auto &type = get<SPIRType>(result_type);
+ statement(variable_decl(type, to_name(id)), ";");
+ statement("SPIRV_Cross_textureSize(", to_expression(ops[2]), ", 0u, ", to_name(id), ");");
+
+ auto &restype = get<SPIRType>(ops[0]);
+ auto expr = bitcast_expression(restype, SPIRType::UInt, to_name(id));
+ set<SPIRExpression>(id, expr, result_type, true);
+ break;
+ }
+
+ case OpImageRead:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ auto *var = maybe_get_backing_variable(ops[2]);
+ auto &type = expression_type(ops[2]);
+ bool subpass_data = type.image.dim == DimSubpassData;
+ bool pure = false;
+
+ string imgexpr;
+
+ if (subpass_data)
+ {
+ if (hlsl_options.shader_model < 40)
+ SPIRV_CROSS_THROW("Subpass loads are not supported in HLSL shader model 2/3.");
+
+ // Similar to GLSL, implement subpass loads using texelFetch.
+ if (type.image.ms)
+ {
+ uint32_t operands = ops[4];
+ if (operands != ImageOperandsSampleMask || instruction.length != 6)
+ SPIRV_CROSS_THROW("Multisampled image used in OpImageRead, but unexpected operand mask was used.");
+ uint32_t sample = ops[5];
+ imgexpr = join(to_expression(ops[2]), ".Load(int2(gl_FragCoord.xy), ", to_expression(sample), ")");
+ }
+ else
+ imgexpr = join(to_expression(ops[2]), ".Load(int3(int2(gl_FragCoord.xy), 0))");
+
+ pure = true;
+ }
+ else
+ {
+ imgexpr = join(to_expression(ops[2]), "[", to_expression(ops[3]), "]");
+ // The underlying image type in HLSL depends on the image format, unlike GLSL, where all images are "vec4",
+ // except that the underlying type changes how the data is interpreted.
+ if (var && !subpass_data)
+ imgexpr = remap_swizzle(get<SPIRType>(result_type),
+ image_format_to_components(get<SPIRType>(var->basetype).image.format), imgexpr);
+ }
+
+ if (var && var->forwardable)
+ {
+ bool forward = forced_temporaries.find(id) == end(forced_temporaries);
+ auto &e = emit_op(result_type, id, imgexpr, forward);
+
+ if (!pure)
+ {
+ e.loaded_from = var->self;
+ if (forward)
+ var->dependees.push_back(id);
+ }
+ }
+ else
+ emit_op(result_type, id, imgexpr, false);
+
+ inherit_expression_dependencies(id, ops[2]);
+ if (type.image.ms)
+ inherit_expression_dependencies(id, ops[5]);
+ break;
+ }
+
+ case OpImageWrite:
+ {
+ auto *var = maybe_get_backing_variable(ops[0]);
+
+ // The underlying image type in HLSL depends on the image format, unlike GLSL, where all images are "vec4",
+ // except that the underlying type changes how the data is interpreted.
+ auto value_expr = to_expression(ops[2]);
+ if (var)
+ {
+ auto &type = get<SPIRType>(var->basetype);
+ auto narrowed_type = get<SPIRType>(type.image.type);
+ narrowed_type.vecsize = image_format_to_components(type.image.format);
+ value_expr = remap_swizzle(narrowed_type, expression_type(ops[2]).vecsize, value_expr);
+ }
+
+ statement(to_expression(ops[0]), "[", to_expression(ops[1]), "] = ", value_expr, ";");
+ if (var && variable_storage_is_aliased(*var))
+ flush_all_aliased_variables();
+ break;
+ }
+
+ case OpImageTexelPointer:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ auto &e =
+ set<SPIRExpression>(id, join(to_expression(ops[2]), "[", to_expression(ops[3]), "]"), result_type, true);
+
+ // When using the pointer, we need to know which variable it is actually loaded from.
+ auto *var = maybe_get_backing_variable(ops[2]);
+ e.loaded_from = var ? var->self : 0;
+ break;
+ }
+
+ case OpAtomicCompareExchange:
+ case OpAtomicExchange:
+ case OpAtomicISub:
+ case OpAtomicSMin:
+ case OpAtomicUMin:
+ case OpAtomicSMax:
+ case OpAtomicUMax:
+ case OpAtomicAnd:
+ case OpAtomicOr:
+ case OpAtomicXor:
+ case OpAtomicIAdd:
+ case OpAtomicIIncrement:
+ case OpAtomicIDecrement:
+ {
+ emit_atomic(ops, instruction.length, opcode);
+ break;
+ }
+
+ case OpControlBarrier:
+ case OpMemoryBarrier:
+ {
+ uint32_t memory;
+ uint32_t semantics;
+
+ if (opcode == OpMemoryBarrier)
+ {
+ memory = get<SPIRConstant>(ops[0]).scalar();
+ semantics = get<SPIRConstant>(ops[1]).scalar();
+ }
+ else
+ {
+ memory = get<SPIRConstant>(ops[1]).scalar();
+ semantics = get<SPIRConstant>(ops[2]).scalar();
+ }
+
+ if (memory == ScopeSubgroup)
+ {
+ // No Wave-barriers in HLSL.
+ break;
+ }
+
+ // We only care about these flags, acquire/release and friends are not relevant to GLSL.
+ semantics = mask_relevant_memory_semantics(semantics);
+
+ if (opcode == OpMemoryBarrier)
+ {
+ // If we are a memory barrier, and the next instruction is a control barrier, check if that memory barrier
+ // does what we need, so we avoid redundant barriers.
+ const Instruction *next = get_next_instruction_in_block(instruction);
+ if (next && next->op == OpControlBarrier)
+ {
+ auto *next_ops = stream(*next);
+ uint32_t next_memory = get<SPIRConstant>(next_ops[1]).scalar();
+ uint32_t next_semantics = get<SPIRConstant>(next_ops[2]).scalar();
+ next_semantics = mask_relevant_memory_semantics(next_semantics);
+
+ // There is no "just execution barrier" in HLSL.
+ // If there are no memory semantics for next instruction, we will imply group shared memory is synced.
+ if (next_semantics == 0)
+ next_semantics = MemorySemanticsWorkgroupMemoryMask;
+
+ bool memory_scope_covered = false;
+ if (next_memory == memory)
+ memory_scope_covered = true;
+ else if (next_semantics == MemorySemanticsWorkgroupMemoryMask)
+ {
+ // If we only care about workgroup memory, either Device or Workgroup scope is fine,
+ // scope does not have to match.
+ if ((next_memory == ScopeDevice || next_memory == ScopeWorkgroup) &&
+ (memory == ScopeDevice || memory == ScopeWorkgroup))
+ {
+ memory_scope_covered = true;
+ }
+ }
+ else if (memory == ScopeWorkgroup && next_memory == ScopeDevice)
+ {
+ // The control barrier has device scope, but the memory barrier just has workgroup scope.
+ memory_scope_covered = true;
+ }
+
+ // If we have the same memory scope, and all memory types are covered, we're good.
+ if (memory_scope_covered && (semantics & next_semantics) == semantics)
+ break;
+ }
+ }
+
+ // We are synchronizing some memory or syncing execution,
+ // so we cannot forward any loads beyond the memory barrier.
+ if (semantics || opcode == OpControlBarrier)
+ {
+ assert(current_emitting_block);
+ flush_control_dependent_expressions(current_emitting_block->self);
+ flush_all_active_variables();
+ }
+
+ if (opcode == OpControlBarrier)
+ {
+ // We cannot emit just execution barrier, for no memory semantics pick the cheapest option.
+ if (semantics == MemorySemanticsWorkgroupMemoryMask || semantics == 0)
+ statement("GroupMemoryBarrierWithGroupSync();");
+ else if (semantics != 0 && (semantics & MemorySemanticsWorkgroupMemoryMask) == 0)
+ statement("DeviceMemoryBarrierWithGroupSync();");
+ else
+ statement("AllMemoryBarrierWithGroupSync();");
+ }
+ else
+ {
+ if (semantics == MemorySemanticsWorkgroupMemoryMask)
+ statement("GroupMemoryBarrier();");
+ else if (semantics != 0 && (semantics & MemorySemanticsWorkgroupMemoryMask) == 0)
+ statement("DeviceMemoryBarrier();");
+ else
+ statement("AllMemoryBarrier();");
+ }
+ break;
+ }
+
+ case OpBitFieldInsert:
+ {
+ if (!requires_bitfield_insert)
+ {
+ requires_bitfield_insert = true;
+ force_recompile = true;
+ }
+
+ auto expr = join("SPIRV_Cross_bitfieldInsert(", to_expression(ops[2]), ", ", to_expression(ops[3]), ", ",
+ to_expression(ops[4]), ", ", to_expression(ops[5]), ")");
+
+ bool forward =
+ should_forward(ops[2]) && should_forward(ops[3]) && should_forward(ops[4]) && should_forward(ops[5]);
+
+ auto &restype = get<SPIRType>(ops[0]);
+ expr = bitcast_expression(restype, SPIRType::UInt, expr);
+ emit_op(ops[0], ops[1], expr, forward);
+ break;
+ }
+
+ case OpBitFieldSExtract:
+ case OpBitFieldUExtract:
+ {
+ if (!requires_bitfield_extract)
+ {
+ requires_bitfield_extract = true;
+ force_recompile = true;
+ }
+
+ if (opcode == OpBitFieldSExtract)
+ HLSL_TFOP(SPIRV_Cross_bitfieldSExtract);
+ else
+ HLSL_TFOP(SPIRV_Cross_bitfieldUExtract);
+ break;
+ }
+
+ case OpBitCount:
+ HLSL_UFOP(countbits);
+ break;
+
+ case OpBitReverse:
+ HLSL_UFOP(reversebits);
+ break;
+
+ default:
+ CompilerGLSL::emit_instruction(instruction);
+ break;
+ }
+}
+
+void CompilerHLSL::require_texture_query_variant(const SPIRType &type)
+{
+ uint32_t bit = 0;
+ switch (type.image.dim)
+ {
+ case Dim1D:
+ bit = type.image.arrayed ? Query1DArray : Query1D;
+ break;
+
+ case Dim2D:
+ if (type.image.ms)
+ bit = type.image.arrayed ? Query2DMSArray : Query2DMS;
+ else
+ bit = type.image.arrayed ? Query2DArray : Query2D;
+ break;
+
+ case Dim3D:
+ bit = Query3D;
+ break;
+
+ case DimCube:
+ bit = type.image.arrayed ? QueryCubeArray : QueryCube;
+ break;
+
+ case DimBuffer:
+ bit = QueryBuffer;
+ break;
+
+ default:
+ SPIRV_CROSS_THROW("Unsupported query type.");
+ }
+
+ switch (get<SPIRType>(type.image.type).basetype)
+ {
+ case SPIRType::Float:
+ bit += QueryTypeFloat;
+ break;
+
+ case SPIRType::Int:
+ bit += QueryTypeInt;
+ break;
+
+ case SPIRType::UInt:
+ bit += QueryTypeUInt;
+ break;
+
+ default:
+ SPIRV_CROSS_THROW("Unsupported query type.");
+ }
+
+ uint64_t mask = 1ull << bit;
+ if ((required_textureSizeVariants & mask) == 0)
+ {
+ force_recompile = true;
+ required_textureSizeVariants |= mask;
+ }
+}
+
+void CompilerHLSL::set_root_constant_layouts(vector<RootConstants> layout)
+{
+ root_constants_layout = move(layout);
+}
+
+void CompilerHLSL::add_vertex_attribute_remap(const HLSLVertexAttributeRemap &vertex_attributes)
+{
+ remap_vertex_attributes.push_back(vertex_attributes);
+}
+
+uint32_t CompilerHLSL::remap_num_workgroups_builtin()
+{
+ update_active_builtins();
+
+ if (!active_input_builtins.get(BuiltInNumWorkgroups))
+ return 0;
+
+ // Create a new, fake UBO.
+ uint32_t offset = ir.increase_bound_by(4);
+
+ uint32_t uint_type_id = offset;
+ uint32_t block_type_id = offset + 1;
+ uint32_t block_pointer_type_id = offset + 2;
+ uint32_t variable_id = offset + 3;
+
+ SPIRType uint_type;
+ uint_type.basetype = SPIRType::UInt;
+ uint_type.width = 32;
+ uint_type.vecsize = 3;
+ uint_type.columns = 1;
+ set<SPIRType>(uint_type_id, uint_type);
+
+ SPIRType block_type;
+ block_type.basetype = SPIRType::Struct;
+ block_type.member_types.push_back(uint_type_id);
+ set<SPIRType>(block_type_id, block_type);
+ set_decoration(block_type_id, DecorationBlock);
+ set_member_name(block_type_id, 0, "count");
+ set_member_decoration(block_type_id, 0, DecorationOffset, 0);
+
+ SPIRType block_pointer_type = block_type;
+ block_pointer_type.pointer = true;
+ block_pointer_type.storage = StorageClassUniform;
+ block_pointer_type.parent_type = block_type_id;
+ auto &ptr_type = set<SPIRType>(block_pointer_type_id, block_pointer_type);
+
+ // Preserve self.
+ ptr_type.self = block_type_id;
+
+ set<SPIRVariable>(variable_id, block_pointer_type_id, StorageClassUniform);
+ ir.meta[variable_id].decoration.alias = "SPIRV_Cross_NumWorkgroups";
+
+ num_workgroups_builtin = variable_id;
+ return variable_id;
+}
+
+string CompilerHLSL::compile()
+{
+ // Do not deal with ES-isms like precision, older extensions and such.
+ options.es = false;
+ options.version = 450;
+ options.vulkan_semantics = true;
+ backend.float_literal_suffix = true;
+ backend.double_literal_suffix = false;
+ backend.long_long_literal_suffix = true;
+ backend.uint32_t_literal_suffix = true;
+ backend.int16_t_literal_suffix = "";
+ backend.uint16_t_literal_suffix = "u";
+ backend.basic_int_type = "int";
+ backend.basic_uint_type = "uint";
+ backend.swizzle_is_function = false;
+ backend.shared_is_implied = true;
+ backend.flexible_member_array_supported = false;
+ backend.explicit_struct_type = false;
+ backend.use_initializer_list = true;
+ backend.use_constructor_splatting = false;
+ backend.boolean_mix_support = false;
+ backend.can_swizzle_scalar = true;
+ backend.can_declare_struct_inline = false;
+ backend.can_declare_arrays_inline = false;
+ backend.can_return_array = false;
+
+ build_function_control_flow_graphs_and_analyze();
+ update_active_builtins();
+ analyze_image_and_sampler_usage();
+
+ // Subpass input needs SV_Position.
+ if (need_subpass_input)
+ active_input_builtins.set(BuiltInFragCoord);
+
+ uint32_t pass_count = 0;
+ do
+ {
+ if (pass_count >= 3)
+ SPIRV_CROSS_THROW("Over 3 compilation loops detected. Must be a bug!");
+
+ reset();
+
+ // Move constructor for this type is broken on GCC 4.9 ...
+ buffer = unique_ptr<ostringstream>(new ostringstream());
+
+ emit_header();
+ emit_resources();
+
+ emit_function(get<SPIRFunction>(ir.default_entry_point), Bitset());
+ emit_hlsl_entry_point();
+
+ pass_count++;
+ } while (force_recompile);
+
+ // Entry point in HLSL is always main() for the time being.
+ get_entry_point().name = "main";
+
+ return buffer->str();
+}
+
+void CompilerHLSL::emit_block_hints(const SPIRBlock &block)
+{
+ switch (block.hint)
+ {
+ case SPIRBlock::HintFlatten:
+ statement("[flatten]");
+ break;
+ case SPIRBlock::HintDontFlatten:
+ statement("[branch]");
+ break;
+ case SPIRBlock::HintUnroll:
+ statement("[unroll]");
+ break;
+ case SPIRBlock::HintDontUnroll:
+ statement("[loop]");
+ break;
+ default:
+ break;
+ }
+}
diff --git a/src/3rdparty/SPIRV-Cross/spirv_hlsl.hpp b/src/3rdparty/SPIRV-Cross/spirv_hlsl.hpp
new file mode 100644
index 0000000..12b8ae1
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_hlsl.hpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2016-2019 Robert Konrad
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SPIRV_HLSL_HPP
+#define SPIRV_HLSL_HPP
+
+#include "spirv_glsl.hpp"
+#include <utility>
+#include <vector>
+
+namespace spirv_cross
+{
+// Interface which remaps vertex inputs to a fixed semantic name to make linking easier.
+struct HLSLVertexAttributeRemap
+{
+ uint32_t location;
+ std::string semantic;
+};
+// Specifying a root constant (d3d12) or push constant range (vulkan).
+//
+// `start` and `end` denotes the range of the root constant in bytes.
+// Both values need to be multiple of 4.
+struct RootConstants
+{
+ uint32_t start;
+ uint32_t end;
+
+ uint32_t binding;
+ uint32_t space;
+};
+
+class CompilerHLSL : public CompilerGLSL
+{
+public:
+ struct Options
+ {
+ uint32_t shader_model = 30; // TODO: map ps_4_0_level_9_0,... somehow
+
+ // Allows the PointSize builtin, and ignores it, as PointSize is not supported in HLSL.
+ bool point_size_compat = false;
+
+ // Allows the PointCoord builtin, returns float2(0.5, 0.5), as PointCoord is not supported in HLSL.
+ bool point_coord_compat = false;
+
+ // If true, the backend will assume that VertexIndex and InstanceIndex will need to apply
+ // a base offset, and you will need to fill in a cbuffer with offsets.
+ // Set to false if you know you will never use base instance or base vertex
+ // functionality as it might remove an internal cbuffer.
+ bool support_nonzero_base_vertex_base_instance = false;
+ };
+
+ explicit CompilerHLSL(std::vector<uint32_t> spirv_)
+ : CompilerGLSL(move(spirv_))
+ {
+ }
+
+ CompilerHLSL(const uint32_t *ir_, size_t size)
+ : CompilerGLSL(ir_, size)
+ {
+ }
+
+ explicit CompilerHLSL(const ParsedIR &ir_)
+ : CompilerGLSL(ir_)
+ {
+ }
+
+ explicit CompilerHLSL(ParsedIR &&ir_)
+ : CompilerGLSL(std::move(ir_))
+ {
+ }
+
+ const Options &get_hlsl_options() const
+ {
+ return hlsl_options;
+ }
+
+ void set_hlsl_options(const Options &opts)
+ {
+ hlsl_options = opts;
+ }
+
+ // Optionally specify a custom root constant layout.
+ //
+ // Push constants ranges will be split up according to the
+ // layout specified.
+ void set_root_constant_layouts(std::vector<RootConstants> layout);
+
+ // Compiles and remaps vertex attributes at specific locations to a fixed semantic.
+ // The default is TEXCOORD# where # denotes location.
+ // Matrices are unrolled to vectors with notation ${SEMANTIC}_#, where # denotes row.
+ // $SEMANTIC is either TEXCOORD# or a semantic name specified here.
+ void add_vertex_attribute_remap(const HLSLVertexAttributeRemap &vertex_attributes);
+ std::string compile() override;
+
+ // This is a special HLSL workaround for the NumWorkGroups builtin.
+ // This does not exist in HLSL, so the calling application must create a dummy cbuffer in
+ // which the application will store this builtin.
+ // The cbuffer layout will be:
+ // cbuffer SPIRV_Cross_NumWorkgroups : register(b#, space#) { uint3 SPIRV_Cross_NumWorkgroups_count; };
+ // This must be called before compile().
+ // The function returns 0 if NumWorkGroups builtin is not statically used in the shader from the current entry point.
+ // If non-zero, this returns the variable ID of a cbuffer which corresponds to
+ // the cbuffer declared above. By default, no binding or descriptor set decoration is set,
+ // so the calling application should declare explicit bindings on this ID before calling compile().
+ uint32_t remap_num_workgroups_builtin();
+
+private:
+ std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override;
+ std::string image_type_hlsl(const SPIRType &type, uint32_t id);
+ std::string image_type_hlsl_modern(const SPIRType &type, uint32_t id);
+ std::string image_type_hlsl_legacy(const SPIRType &type, uint32_t id);
+ void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override;
+ void emit_hlsl_entry_point();
+ void emit_header() override;
+ void emit_resources();
+ void emit_interface_block_globally(const SPIRVariable &type);
+ void emit_interface_block_in_struct(const SPIRVariable &type, std::unordered_set<uint32_t> &active_locations);
+ void emit_builtin_inputs_in_struct();
+ void emit_builtin_outputs_in_struct();
+ void emit_texture_op(const Instruction &i) override;
+ void emit_instruction(const Instruction &instruction) override;
+ void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args,
+ uint32_t count) override;
+ void emit_buffer_block(const SPIRVariable &type) override;
+ void emit_push_constant_block(const SPIRVariable &var) override;
+ void emit_uniform(const SPIRVariable &var) override;
+ void emit_modern_uniform(const SPIRVariable &var);
+ void emit_legacy_uniform(const SPIRVariable &var);
+ void emit_specialization_constants_and_structs();
+ void emit_composite_constants();
+ void emit_fixup() override;
+ std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) override;
+ std::string layout_for_member(const SPIRType &type, uint32_t index) override;
+ std::string to_interpolation_qualifiers(const Bitset &flags) override;
+ std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type) override;
+ std::string to_func_call_arg(uint32_t id) override;
+ std::string to_sampler_expression(uint32_t id);
+ std::string to_resource_binding(const SPIRVariable &var);
+ std::string to_resource_binding_sampler(const SPIRVariable &var);
+ std::string to_resource_register(char space, uint32_t binding, uint32_t set);
+ void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override;
+ void emit_access_chain(const Instruction &instruction);
+ void emit_load(const Instruction &instruction);
+ std::string read_access_chain(const SPIRAccessChain &chain);
+ void write_access_chain(const SPIRAccessChain &chain, uint32_t value);
+ void emit_store(const Instruction &instruction);
+ void emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op);
+ void emit_subgroup_op(const Instruction &i) override;
+ void emit_block_hints(const SPIRBlock &block) override;
+
+ void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, const std::string &qualifier,
+ uint32_t base_offset = 0) override;
+
+ const char *to_storage_qualifiers_glsl(const SPIRVariable &var) override;
+ void replace_illegal_names() override;
+
+ Options hlsl_options;
+ bool requires_op_fmod = false;
+ bool requires_fp16_packing = false;
+ bool requires_explicit_fp16_packing = false;
+ bool requires_unorm8_packing = false;
+ bool requires_snorm8_packing = false;
+ bool requires_unorm16_packing = false;
+ bool requires_snorm16_packing = false;
+ bool requires_bitfield_insert = false;
+ bool requires_bitfield_extract = false;
+ bool requires_inverse_2x2 = false;
+ bool requires_inverse_3x3 = false;
+ bool requires_inverse_4x4 = false;
+ uint64_t required_textureSizeVariants = 0;
+ void require_texture_query_variant(const SPIRType &type);
+
+ enum TextureQueryVariantDim
+ {
+ Query1D = 0,
+ Query1DArray,
+ Query2D,
+ Query2DArray,
+ Query3D,
+ QueryBuffer,
+ QueryCube,
+ QueryCubeArray,
+ Query2DMS,
+ Query2DMSArray,
+ QueryDimCount
+ };
+
+ enum TextureQueryVariantType
+ {
+ QueryTypeFloat = 0,
+ QueryTypeInt = 16,
+ QueryTypeUInt = 32,
+ QueryTypeCount = 3
+ };
+
+ void emit_builtin_variables();
+ bool require_output = false;
+ bool require_input = false;
+ std::vector<HLSLVertexAttributeRemap> remap_vertex_attributes;
+
+ uint32_t type_to_consumed_locations(const SPIRType &type) const;
+
+ void emit_io_block(const SPIRVariable &var);
+ std::string to_semantic(uint32_t vertex_location);
+
+ uint32_t num_workgroups_builtin = 0;
+
+ // Custom root constant layout, which should be emitted
+ // when translating push constant ranges.
+ std::vector<RootConstants> root_constants_layout;
+};
+} // namespace spirv_cross
+
+#endif
diff --git a/src/3rdparty/SPIRV-Cross/spirv_msl.cpp b/src/3rdparty/SPIRV-Cross/spirv_msl.cpp
new file mode 100644
index 0000000..41a3aaa
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_msl.cpp
@@ -0,0 +1,7715 @@
+/*
+ * Copyright 2016-2019 The Brenwill Workshop Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "spirv_msl.hpp"
+#include "GLSL.std.450.h"
+
+#include <algorithm>
+#include <assert.h>
+#include <numeric>
+
+using namespace spv;
+using namespace spirv_cross;
+using namespace std;
+
+static const uint32_t k_unknown_location = ~0u;
+static const uint32_t k_unknown_component = ~0u;
+
+static const uint32_t k_aux_mbr_idx_swizzle_const = 0u;
+
+CompilerMSL::CompilerMSL(vector<uint32_t> spirv_)
+ : CompilerGLSL(move(spirv_))
+{
+}
+
+CompilerMSL::CompilerMSL(const uint32_t *ir_, size_t word_count)
+ : CompilerGLSL(ir_, word_count)
+{
+}
+
+CompilerMSL::CompilerMSL(const ParsedIR &ir_)
+ : CompilerGLSL(ir_)
+{
+}
+
+CompilerMSL::CompilerMSL(ParsedIR &&ir_)
+ : CompilerGLSL(std::move(ir_))
+{
+}
+
+void CompilerMSL::add_msl_vertex_attribute(const MSLVertexAttr &va)
+{
+ vtx_attrs_by_location[va.location] = va;
+ if (va.builtin != BuiltInMax && !vtx_attrs_by_builtin.count(va.builtin))
+ vtx_attrs_by_builtin[va.builtin] = va;
+}
+
+void CompilerMSL::add_msl_resource_binding(const MSLResourceBinding &binding)
+{
+ resource_bindings.push_back({ binding, false });
+}
+
+void CompilerMSL::add_discrete_descriptor_set(uint32_t desc_set)
+{
+ if (desc_set < kMaxArgumentBuffers)
+ argument_buffer_discrete_mask |= 1u << desc_set;
+}
+
+bool CompilerMSL::is_msl_vertex_attribute_used(uint32_t location)
+{
+ return vtx_attrs_in_use.count(location) != 0;
+}
+
+bool CompilerMSL::is_msl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding)
+{
+ auto itr = find_if(begin(resource_bindings), end(resource_bindings),
+ [&](const std::pair<MSLResourceBinding, bool> &resource) -> bool {
+ return model == resource.first.stage && desc_set == resource.first.desc_set &&
+ binding == resource.first.binding;
+ });
+ return itr != end(resource_bindings) && itr->second;
+}
+
+void CompilerMSL::set_fragment_output_components(uint32_t location, uint32_t components)
+{
+ fragment_output_components[location] = components;
+}
+
+void CompilerMSL::build_implicit_builtins()
+{
+ bool need_sample_pos = active_input_builtins.get(BuiltInSamplePosition);
+ bool need_vertex_params = capture_output_to_buffer && get_execution_model() == ExecutionModelVertex;
+ bool need_tesc_params = get_execution_model() == ExecutionModelTessellationControl;
+ if (need_subpass_input || need_sample_pos || need_vertex_params || need_tesc_params)
+ {
+ bool has_frag_coord = false;
+ bool has_sample_id = false;
+ bool has_vertex_idx = false;
+ bool has_base_vertex = false;
+ bool has_instance_idx = false;
+ bool has_base_instance = false;
+ bool has_invocation_id = false;
+ bool has_primitive_id = false;
+
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ if (var.storage != StorageClassInput || !ir.meta[var.self].decoration.builtin)
+ return;
+
+ if (need_subpass_input && ir.meta[var.self].decoration.builtin_type == BuiltInFragCoord)
+ {
+ builtin_frag_coord_id = var.self;
+ has_frag_coord = true;
+ }
+
+ if (need_sample_pos && ir.meta[var.self].decoration.builtin_type == BuiltInSampleId)
+ {
+ builtin_sample_id_id = var.self;
+ has_sample_id = true;
+ }
+
+ if (need_vertex_params)
+ {
+ switch (ir.meta[var.self].decoration.builtin_type)
+ {
+ case BuiltInVertexIndex:
+ builtin_vertex_idx_id = var.self;
+ has_vertex_idx = true;
+ break;
+ case BuiltInBaseVertex:
+ builtin_base_vertex_id = var.self;
+ has_base_vertex = true;
+ break;
+ case BuiltInInstanceIndex:
+ builtin_instance_idx_id = var.self;
+ has_instance_idx = true;
+ break;
+ case BuiltInBaseInstance:
+ builtin_base_instance_id = var.self;
+ has_base_instance = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (need_tesc_params)
+ {
+ switch (ir.meta[var.self].decoration.builtin_type)
+ {
+ case BuiltInInvocationId:
+ builtin_invocation_id_id = var.self;
+ has_invocation_id = true;
+ break;
+ case BuiltInPrimitiveId:
+ builtin_primitive_id_id = var.self;
+ has_primitive_id = true;
+ break;
+ default:
+ break;
+ }
+ }
+ });
+
+ if (!has_frag_coord && need_subpass_input)
+ {
+ uint32_t offset = ir.increase_bound_by(3);
+ uint32_t type_id = offset;
+ uint32_t type_ptr_id = offset + 1;
+ uint32_t var_id = offset + 2;
+
+ // Create gl_FragCoord.
+ SPIRType vec4_type;
+ vec4_type.basetype = SPIRType::Float;
+ vec4_type.width = 32;
+ vec4_type.vecsize = 4;
+ set<SPIRType>(type_id, vec4_type);
+
+ SPIRType vec4_type_ptr;
+ vec4_type_ptr = vec4_type;
+ vec4_type_ptr.pointer = true;
+ vec4_type_ptr.parent_type = type_id;
+ vec4_type_ptr.storage = StorageClassInput;
+ auto &ptr_type = set<SPIRType>(type_ptr_id, vec4_type_ptr);
+ ptr_type.self = type_id;
+
+ set<SPIRVariable>(var_id, type_ptr_id, StorageClassInput);
+ set_decoration(var_id, DecorationBuiltIn, BuiltInFragCoord);
+ builtin_frag_coord_id = var_id;
+ }
+
+ if (!has_sample_id && need_sample_pos)
+ {
+ uint32_t offset = ir.increase_bound_by(3);
+ uint32_t type_id = offset;
+ uint32_t type_ptr_id = offset + 1;
+ uint32_t var_id = offset + 2;
+
+ // Create gl_SampleID.
+ SPIRType uint_type;
+ uint_type.basetype = SPIRType::UInt;
+ uint_type.width = 32;
+ set<SPIRType>(type_id, uint_type);
+
+ SPIRType uint_type_ptr;
+ uint_type_ptr = uint_type;
+ uint_type_ptr.pointer = true;
+ uint_type_ptr.parent_type = type_id;
+ uint_type_ptr.storage = StorageClassInput;
+ auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
+ ptr_type.self = type_id;
+
+ set<SPIRVariable>(var_id, type_ptr_id, StorageClassInput);
+ set_decoration(var_id, DecorationBuiltIn, BuiltInSampleId);
+ builtin_sample_id_id = var_id;
+ }
+
+ if (need_vertex_params && (!has_vertex_idx || !has_base_vertex || !has_instance_idx || !has_base_instance))
+ {
+ uint32_t offset = ir.increase_bound_by(2);
+ uint32_t type_id = offset;
+ uint32_t type_ptr_id = offset + 1;
+
+ SPIRType uint_type;
+ uint_type.basetype = SPIRType::UInt;
+ uint_type.width = 32;
+ set<SPIRType>(type_id, uint_type);
+
+ SPIRType uint_type_ptr;
+ uint_type_ptr = uint_type;
+ uint_type_ptr.pointer = true;
+ uint_type_ptr.parent_type = type_id;
+ uint_type_ptr.storage = StorageClassInput;
+ auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
+ ptr_type.self = type_id;
+
+ if (!has_vertex_idx)
+ {
+ uint32_t var_id = ir.increase_bound_by(1);
+
+ // Create gl_VertexIndex.
+ set<SPIRVariable>(var_id, type_ptr_id, StorageClassInput);
+ set_decoration(var_id, DecorationBuiltIn, BuiltInVertexIndex);
+ builtin_vertex_idx_id = var_id;
+ }
+ if (!has_base_vertex)
+ {
+ uint32_t var_id = ir.increase_bound_by(1);
+
+ // Create gl_BaseVertex.
+ set<SPIRVariable>(var_id, type_ptr_id, StorageClassInput);
+ set_decoration(var_id, DecorationBuiltIn, BuiltInBaseVertex);
+ builtin_base_vertex_id = var_id;
+ }
+ if (!has_instance_idx)
+ {
+ uint32_t var_id = ir.increase_bound_by(1);
+
+ // Create gl_InstanceIndex.
+ set<SPIRVariable>(var_id, type_ptr_id, StorageClassInput);
+ set_decoration(var_id, DecorationBuiltIn, BuiltInInstanceIndex);
+ builtin_instance_idx_id = var_id;
+ }
+ if (!has_base_instance)
+ {
+ uint32_t var_id = ir.increase_bound_by(1);
+
+ // Create gl_BaseInstance.
+ set<SPIRVariable>(var_id, type_ptr_id, StorageClassInput);
+ set_decoration(var_id, DecorationBuiltIn, BuiltInBaseInstance);
+ builtin_base_instance_id = var_id;
+ }
+ }
+
+ if (need_tesc_params && (!has_invocation_id || !has_primitive_id))
+ {
+ uint32_t offset = ir.increase_bound_by(2);
+ uint32_t type_id = offset;
+ uint32_t type_ptr_id = offset + 1;
+
+ SPIRType uint_type;
+ uint_type.basetype = SPIRType::UInt;
+ uint_type.width = 32;
+ set<SPIRType>(type_id, uint_type);
+
+ SPIRType uint_type_ptr;
+ uint_type_ptr = uint_type;
+ uint_type_ptr.pointer = true;
+ uint_type_ptr.parent_type = type_id;
+ uint_type_ptr.storage = StorageClassInput;
+ auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
+ ptr_type.self = type_id;
+
+ if (!has_invocation_id)
+ {
+ uint32_t var_id = ir.increase_bound_by(1);
+
+ // Create gl_InvocationID.
+ set<SPIRVariable>(var_id, type_ptr_id, StorageClassInput);
+ set_decoration(var_id, DecorationBuiltIn, BuiltInInvocationId);
+ builtin_invocation_id_id = var_id;
+ }
+ if (!has_primitive_id)
+ {
+ uint32_t var_id = ir.increase_bound_by(1);
+
+ // Create gl_PrimitiveID.
+ set<SPIRVariable>(var_id, type_ptr_id, StorageClassInput);
+ set_decoration(var_id, DecorationBuiltIn, BuiltInPrimitiveId);
+ builtin_primitive_id_id = var_id;
+ }
+ }
+ }
+
+ if (needs_aux_buffer_def)
+ {
+ uint32_t offset = ir.increase_bound_by(5);
+ uint32_t type_id = offset;
+ uint32_t type_arr_id = offset + 1;
+ uint32_t struct_id = offset + 2;
+ uint32_t struct_ptr_id = offset + 3;
+ uint32_t var_id = offset + 4;
+
+ // Create a buffer to hold extra data, including the swizzle constants.
+ SPIRType uint_type;
+ uint_type.basetype = SPIRType::UInt;
+ uint_type.width = 32;
+ set<SPIRType>(type_id, uint_type);
+
+ SPIRType uint_type_arr = uint_type;
+ uint_type_arr.array.push_back(0);
+ uint_type_arr.array_size_literal.push_back(true);
+ uint_type_arr.parent_type = type_id;
+ set<SPIRType>(type_arr_id, uint_type_arr);
+ set_decoration(type_arr_id, DecorationArrayStride, 4);
+
+ SPIRType struct_type;
+ struct_type.basetype = SPIRType::Struct;
+ struct_type.member_types.push_back(type_arr_id);
+ auto &type = set<SPIRType>(struct_id, struct_type);
+ type.self = struct_id;
+ set_decoration(struct_id, DecorationBlock);
+ set_name(struct_id, "spvAux");
+ set_member_name(struct_id, k_aux_mbr_idx_swizzle_const, "swizzleConst");
+ set_member_decoration(struct_id, k_aux_mbr_idx_swizzle_const, DecorationOffset, 0);
+
+ SPIRType struct_type_ptr = struct_type;
+ struct_type_ptr.pointer = true;
+ struct_type_ptr.parent_type = struct_id;
+ struct_type_ptr.storage = StorageClassUniform;
+ auto &ptr_type = set<SPIRType>(struct_ptr_id, struct_type_ptr);
+ ptr_type.self = struct_id;
+
+ set<SPIRVariable>(var_id, struct_ptr_id, StorageClassUniform);
+ set_name(var_id, "spvAuxBuffer");
+ // This should never match anything.
+ set_decoration(var_id, DecorationDescriptorSet, 0xFFFFFFFE);
+ set_decoration(var_id, DecorationBinding, msl_options.aux_buffer_index);
+ aux_buffer_id = var_id;
+ }
+}
+
+static string create_sampler_address(const char *prefix, MSLSamplerAddress addr)
+{
+ switch (addr)
+ {
+ case MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE:
+ return join(prefix, "address::clamp_to_edge");
+ case MSL_SAMPLER_ADDRESS_CLAMP_TO_ZERO:
+ return join(prefix, "address::clamp_to_zero");
+ case MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER:
+ return join(prefix, "address::clamp_to_border");
+ case MSL_SAMPLER_ADDRESS_REPEAT:
+ return join(prefix, "address::repeat");
+ case MSL_SAMPLER_ADDRESS_MIRRORED_REPEAT:
+ return join(prefix, "address::mirrored_repeat");
+ default:
+ SPIRV_CROSS_THROW("Invalid sampler addressing mode.");
+ }
+}
+
+SPIRType &CompilerMSL::get_stage_in_struct_type()
+{
+ auto &si_var = get<SPIRVariable>(stage_in_var_id);
+ return get_variable_data_type(si_var);
+}
+
+SPIRType &CompilerMSL::get_stage_out_struct_type()
+{
+ auto &so_var = get<SPIRVariable>(stage_out_var_id);
+ return get_variable_data_type(so_var);
+}
+
+SPIRType &CompilerMSL::get_patch_stage_in_struct_type()
+{
+ auto &si_var = get<SPIRVariable>(patch_stage_in_var_id);
+ return get_variable_data_type(si_var);
+}
+
+SPIRType &CompilerMSL::get_patch_stage_out_struct_type()
+{
+ auto &so_var = get<SPIRVariable>(patch_stage_out_var_id);
+ return get_variable_data_type(so_var);
+}
+
+std::string CompilerMSL::get_tess_factor_struct_name()
+{
+ if (get_entry_point().flags.get(ExecutionModeTriangles))
+ return "MTLTriangleTessellationFactorsHalf";
+ return "MTLQuadTessellationFactorsHalf";
+}
+
+void CompilerMSL::emit_entry_point_declarations()
+{
+ // FIXME: Get test coverage here ...
+
+ // Emit constexpr samplers here.
+ for (auto &samp : constexpr_samplers)
+ {
+ auto &var = get<SPIRVariable>(samp.first);
+ auto &type = get<SPIRType>(var.basetype);
+ if (type.basetype == SPIRType::Sampler)
+ add_resource_name(samp.first);
+
+ vector<string> args;
+ auto &s = samp.second;
+
+ if (s.coord != MSL_SAMPLER_COORD_NORMALIZED)
+ args.push_back("coord::pixel");
+
+ if (s.min_filter == s.mag_filter)
+ {
+ if (s.min_filter != MSL_SAMPLER_FILTER_NEAREST)
+ args.push_back("filter::linear");
+ }
+ else
+ {
+ if (s.min_filter != MSL_SAMPLER_FILTER_NEAREST)
+ args.push_back("min_filter::linear");
+ if (s.mag_filter != MSL_SAMPLER_FILTER_NEAREST)
+ args.push_back("mag_filter::linear");
+ }
+
+ switch (s.mip_filter)
+ {
+ case MSL_SAMPLER_MIP_FILTER_NONE:
+ // Default
+ break;
+ case MSL_SAMPLER_MIP_FILTER_NEAREST:
+ args.push_back("mip_filter::nearest");
+ break;
+ case MSL_SAMPLER_MIP_FILTER_LINEAR:
+ args.push_back("mip_filter::linear");
+ break;
+ default:
+ SPIRV_CROSS_THROW("Invalid mip filter.");
+ }
+
+ if (s.s_address == s.t_address && s.s_address == s.r_address)
+ {
+ if (s.s_address != MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE)
+ args.push_back(create_sampler_address("", s.s_address));
+ }
+ else
+ {
+ if (s.s_address != MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE)
+ args.push_back(create_sampler_address("s_", s.s_address));
+ if (s.t_address != MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE)
+ args.push_back(create_sampler_address("t_", s.t_address));
+ if (s.r_address != MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE)
+ args.push_back(create_sampler_address("r_", s.r_address));
+ }
+
+ if (s.compare_enable)
+ {
+ switch (s.compare_func)
+ {
+ case MSL_SAMPLER_COMPARE_FUNC_ALWAYS:
+ args.push_back("compare_func::always");
+ break;
+ case MSL_SAMPLER_COMPARE_FUNC_NEVER:
+ args.push_back("compare_func::never");
+ break;
+ case MSL_SAMPLER_COMPARE_FUNC_EQUAL:
+ args.push_back("compare_func::equal");
+ break;
+ case MSL_SAMPLER_COMPARE_FUNC_NOT_EQUAL:
+ args.push_back("compare_func::not_equal");
+ break;
+ case MSL_SAMPLER_COMPARE_FUNC_LESS:
+ args.push_back("compare_func::less");
+ break;
+ case MSL_SAMPLER_COMPARE_FUNC_LESS_EQUAL:
+ args.push_back("compare_func::less_equal");
+ break;
+ case MSL_SAMPLER_COMPARE_FUNC_GREATER:
+ args.push_back("compare_func::greater");
+ break;
+ case MSL_SAMPLER_COMPARE_FUNC_GREATER_EQUAL:
+ args.push_back("compare_func::greater_equal");
+ break;
+ default:
+ SPIRV_CROSS_THROW("Invalid sampler compare function.");
+ }
+ }
+
+ if (s.s_address == MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER || s.t_address == MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER ||
+ s.r_address == MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER)
+ {
+ switch (s.border_color)
+ {
+ case MSL_SAMPLER_BORDER_COLOR_OPAQUE_BLACK:
+ args.push_back("border_color::opaque_black");
+ break;
+ case MSL_SAMPLER_BORDER_COLOR_OPAQUE_WHITE:
+ args.push_back("border_color::opaque_white");
+ break;
+ case MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK:
+ args.push_back("border_color::transparent_black");
+ break;
+ default:
+ SPIRV_CROSS_THROW("Invalid sampler border color.");
+ }
+ }
+
+ if (s.anisotropy_enable)
+ args.push_back(join("max_anisotropy(", s.max_anisotropy, ")"));
+ if (s.lod_clamp_enable)
+ {
+ args.push_back(join("lod_clamp(", convert_to_string(s.lod_clamp_min, current_locale_radix_character), ", ",
+ convert_to_string(s.lod_clamp_max, current_locale_radix_character), ")"));
+ }
+
+ statement("constexpr sampler ",
+ type.basetype == SPIRType::SampledImage ? to_sampler_expression(samp.first) : to_name(samp.first),
+ "(", merge(args), ");");
+ }
+
+ // Emit buffer arrays here.
+ for (uint32_t array_id : buffer_arrays)
+ {
+ const auto &var = get<SPIRVariable>(array_id);
+ const auto &type = get_variable_data_type(var);
+ string name = to_name(array_id);
+ statement(get_argument_address_space(var) + " " + type_to_glsl(type) + "* " + name + "[] =");
+ begin_scope();
+ for (uint32_t i = 0; i < type.array[0]; ++i)
+ statement(name + "_" + convert_to_string(i) + ",");
+ end_scope_decl();
+ statement_no_indent("");
+ }
+ // For some reason, without this, we end up emitting the arrays twice.
+ buffer_arrays.clear();
+}
+
+string CompilerMSL::compile()
+{
+ // Do not deal with GLES-isms like precision, older extensions and such.
+ options.vulkan_semantics = true;
+ options.es = false;
+ options.version = 450;
+ backend.null_pointer_literal = "nullptr";
+ backend.float_literal_suffix = false;
+ backend.uint32_t_literal_suffix = true;
+ backend.int16_t_literal_suffix = "";
+ backend.uint16_t_literal_suffix = "u";
+ backend.basic_int_type = "int";
+ backend.basic_uint_type = "uint";
+ backend.basic_int8_type = "char";
+ backend.basic_uint8_type = "uchar";
+ backend.basic_int16_type = "short";
+ backend.basic_uint16_type = "ushort";
+ backend.discard_literal = "discard_fragment()";
+ backend.swizzle_is_function = false;
+ backend.shared_is_implied = false;
+ backend.use_initializer_list = true;
+ backend.use_typed_initializer_list = true;
+ backend.native_row_major_matrix = false;
+ backend.flexible_member_array_supported = false;
+ backend.can_declare_arrays_inline = false;
+ backend.can_return_array = false;
+ backend.boolean_mix_support = false;
+ backend.allow_truncated_access_chain = true;
+ backend.array_is_value_type = false;
+ backend.comparison_image_samples_scalar = true;
+
+ capture_output_to_buffer = msl_options.capture_output_to_buffer;
+ is_rasterization_disabled = msl_options.disable_rasterization || capture_output_to_buffer;
+
+ replace_illegal_names();
+
+ struct_member_padding.clear();
+
+ build_function_control_flow_graphs_and_analyze();
+ update_active_builtins();
+ analyze_image_and_sampler_usage();
+ analyze_sampled_image_usage();
+ build_implicit_builtins();
+
+ fixup_image_load_store_access();
+
+ set_enabled_interface_variables(get_active_interface_variables());
+ if (aux_buffer_id)
+ active_interface_variables.insert(aux_buffer_id);
+
+ // Preprocess OpCodes to extract the need to output additional header content
+ preprocess_op_codes();
+
+ // Create structs to hold input, output and uniform variables.
+ // Do output first to ensure out. is declared at top of entry function.
+ qual_pos_var_name = "";
+ stage_out_var_id = add_interface_block(StorageClassOutput);
+ patch_stage_out_var_id = add_interface_block(StorageClassOutput, true);
+ stage_in_var_id = add_interface_block(StorageClassInput);
+ if (get_execution_model() == ExecutionModelTessellationEvaluation)
+ patch_stage_in_var_id = add_interface_block(StorageClassInput, true);
+
+ if (get_execution_model() == ExecutionModelTessellationControl)
+ stage_out_ptr_var_id = add_interface_block_pointer(stage_out_var_id, StorageClassOutput);
+ if (is_tessellation_shader())
+ stage_in_ptr_var_id = add_interface_block_pointer(stage_in_var_id, StorageClassInput);
+
+ // Metal vertex functions that define no output must disable rasterization and return void.
+ if (!stage_out_var_id)
+ is_rasterization_disabled = true;
+
+ // Convert the use of global variables to recursively-passed function parameters
+ localize_global_variables();
+ extract_global_variables_from_functions();
+
+ // Mark any non-stage-in structs to be tightly packed.
+ mark_packable_structs();
+
+ // Add fixup hooks required by shader inputs and outputs. This needs to happen before
+ // the loop, so the hooks aren't added multiple times.
+ fix_up_shader_inputs_outputs();
+
+ // If we are using argument buffers, we create argument buffer structures for them here.
+ // These buffers will be used in the entry point, not the individual resources.
+ if (msl_options.argument_buffers)
+ {
+ if (!msl_options.supports_msl_version(2, 0))
+ SPIRV_CROSS_THROW("Argument buffers can only be used with MSL 2.0 and up.");
+ analyze_argument_buffers();
+ }
+
+ uint32_t pass_count = 0;
+ do
+ {
+ if (pass_count >= 3)
+ SPIRV_CROSS_THROW("Over 3 compilation loops detected. Must be a bug!");
+
+ reset();
+
+ // Start bindings at zero.
+ next_metal_resource_index_buffer = 0;
+ next_metal_resource_index_texture = 0;
+ next_metal_resource_index_sampler = 0;
+
+ // Move constructor for this type is broken on GCC 4.9 ...
+ buffer = unique_ptr<ostringstream>(new ostringstream());
+
+ emit_header();
+ emit_specialization_constants_and_structs();
+ emit_resources();
+ emit_custom_functions();
+ emit_function(get<SPIRFunction>(ir.default_entry_point), Bitset());
+
+ pass_count++;
+ } while (force_recompile);
+
+ return buffer->str();
+}
+
+// Register the need to output any custom functions.
+void CompilerMSL::preprocess_op_codes()
+{
+ OpCodePreprocessor preproc(*this);
+ traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), preproc);
+
+ if (preproc.suppress_missing_prototypes)
+ add_pragma_line("#pragma clang diagnostic ignored \"-Wmissing-prototypes\"");
+
+ if (preproc.uses_atomics)
+ {
+ add_header_line("#include <metal_atomic>");
+ add_pragma_line("#pragma clang diagnostic ignored \"-Wunused-variable\"");
+ }
+
+ // Metal vertex functions that write to resources must disable rasterization and return void.
+ if (preproc.uses_resource_write)
+ is_rasterization_disabled = true;
+
+ // Tessellation control shaders are run as compute functions in Metal, and so
+ // must capture their output to a buffer.
+ if (get_execution_model() == ExecutionModelTessellationControl)
+ {
+ is_rasterization_disabled = true;
+ capture_output_to_buffer = true;
+ }
+}
+
+// Move the Private and Workgroup global variables to the entry function.
+// Non-constant variables cannot have global scope in Metal.
+void CompilerMSL::localize_global_variables()
+{
+ auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
+ auto iter = global_variables.begin();
+ while (iter != global_variables.end())
+ {
+ uint32_t v_id = *iter;
+ auto &var = get<SPIRVariable>(v_id);
+ if (var.storage == StorageClassPrivate || var.storage == StorageClassWorkgroup)
+ {
+ if (!variable_is_lut(var))
+ entry_func.add_local_variable(v_id);
+ iter = global_variables.erase(iter);
+ }
+ else
+ iter++;
+ }
+}
+
+// For any global variable accessed directly by a function,
+// extract that variable and add it as an argument to that function.
+void CompilerMSL::extract_global_variables_from_functions()
+{
+ // Uniforms
+ unordered_set<uint32_t> global_var_ids;
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ if (var.storage == StorageClassInput || var.storage == StorageClassOutput ||
+ var.storage == StorageClassUniform || var.storage == StorageClassUniformConstant ||
+ var.storage == StorageClassPushConstant || var.storage == StorageClassStorageBuffer)
+ {
+ global_var_ids.insert(var.self);
+ }
+ });
+
+ // Local vars that are declared in the main function and accessed directly by a function
+ auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
+ for (auto &var : entry_func.local_variables)
+ if (get<SPIRVariable>(var).storage != StorageClassFunction)
+ global_var_ids.insert(var);
+
+ std::set<uint32_t> added_arg_ids;
+ unordered_set<uint32_t> processed_func_ids;
+ extract_global_variables_from_function(ir.default_entry_point, added_arg_ids, global_var_ids, processed_func_ids);
+}
+
+// MSL does not support the use of global variables for shader input content.
+// For any global variable accessed directly by the specified function, extract that variable,
+// add it as an argument to that function, and the arg to the added_arg_ids collection.
+void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std::set<uint32_t> &added_arg_ids,
+ unordered_set<uint32_t> &global_var_ids,
+ unordered_set<uint32_t> &processed_func_ids)
+{
+ // Avoid processing a function more than once
+ if (processed_func_ids.find(func_id) != processed_func_ids.end())
+ {
+ // Return function global variables
+ added_arg_ids = function_global_vars[func_id];
+ return;
+ }
+
+ processed_func_ids.insert(func_id);
+
+ auto &func = get<SPIRFunction>(func_id);
+
+ // Recursively establish global args added to functions on which we depend.
+ for (auto block : func.blocks)
+ {
+ auto &b = get<SPIRBlock>(block);
+ for (auto &i : b.ops)
+ {
+ auto ops = stream(i);
+ auto op = static_cast<Op>(i.op);
+
+ switch (op)
+ {
+ case OpLoad:
+ case OpInBoundsAccessChain:
+ case OpAccessChain:
+ case OpPtrAccessChain:
+ {
+ uint32_t base_id = ops[2];
+ if (global_var_ids.find(base_id) != global_var_ids.end())
+ added_arg_ids.insert(base_id);
+
+ auto &type = get<SPIRType>(ops[0]);
+ if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData)
+ {
+ // Implicitly reads gl_FragCoord.
+ assert(builtin_frag_coord_id != 0);
+ added_arg_ids.insert(builtin_frag_coord_id);
+ }
+
+ break;
+ }
+
+ case OpFunctionCall:
+ {
+ // First see if any of the function call args are globals
+ for (uint32_t arg_idx = 3; arg_idx < i.length; arg_idx++)
+ {
+ uint32_t arg_id = ops[arg_idx];
+ if (global_var_ids.find(arg_id) != global_var_ids.end())
+ added_arg_ids.insert(arg_id);
+ }
+
+ // Then recurse into the function itself to extract globals used internally in the function
+ uint32_t inner_func_id = ops[2];
+ std::set<uint32_t> inner_func_args;
+ extract_global_variables_from_function(inner_func_id, inner_func_args, global_var_ids,
+ processed_func_ids);
+ added_arg_ids.insert(inner_func_args.begin(), inner_func_args.end());
+ break;
+ }
+
+ case OpStore:
+ {
+ uint32_t base_id = ops[0];
+ if (global_var_ids.find(base_id) != global_var_ids.end())
+ added_arg_ids.insert(base_id);
+ break;
+ }
+
+ case OpSelect:
+ {
+ uint32_t base_id = ops[3];
+ if (global_var_ids.find(base_id) != global_var_ids.end())
+ added_arg_ids.insert(base_id);
+ base_id = ops[4];
+ if (global_var_ids.find(base_id) != global_var_ids.end())
+ added_arg_ids.insert(base_id);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ // TODO: Add all other operations which can affect memory.
+ // We should consider a more unified system here to reduce boiler-plate.
+ // This kind of analysis is done in several places ...
+ }
+ }
+
+ function_global_vars[func_id] = added_arg_ids;
+
+ // Add the global variables as arguments to the function
+ if (func_id != ir.default_entry_point)
+ {
+ bool added_in = false;
+ bool added_out = false;
+ for (uint32_t arg_id : added_arg_ids)
+ {
+ auto &var = get<SPIRVariable>(arg_id);
+ uint32_t type_id = var.basetype;
+ auto *p_type = &get<SPIRType>(type_id);
+ BuiltIn bi_type = BuiltIn(get_decoration(arg_id, DecorationBuiltIn));
+
+ if (((is_tessellation_shader() && var.storage == StorageClassInput) ||
+ (get_execution_model() == ExecutionModelTessellationControl && var.storage == StorageClassOutput)) &&
+ !(has_decoration(arg_id, DecorationPatch) || is_patch_block(*p_type)) &&
+ (!is_builtin_variable(var) || bi_type == BuiltInPosition || bi_type == BuiltInPointSize ||
+ bi_type == BuiltInClipDistance || bi_type == BuiltInCullDistance ||
+ p_type->basetype == SPIRType::Struct))
+ {
+ // Tessellation control shaders see inputs and per-vertex outputs as arrays.
+ // Similarly, tessellation evaluation shaders see per-vertex inputs as arrays.
+ // We collected them into a structure; we must pass the array of this
+ // structure to the function.
+ std::string name;
+ if (var.storage == StorageClassInput)
+ {
+ if (added_in)
+ continue;
+ name = input_wg_var_name;
+ arg_id = stage_in_ptr_var_id;
+ added_in = true;
+ }
+ else if (var.storage == StorageClassOutput)
+ {
+ if (added_out)
+ continue;
+ name = "gl_out";
+ arg_id = stage_out_ptr_var_id;
+ added_out = true;
+ }
+ type_id = get<SPIRVariable>(arg_id).basetype;
+ p_type = &get<SPIRType>(type_id);
+ uint32_t next_id = ir.increase_bound_by(1);
+ func.add_parameter(type_id, next_id, true);
+ set<SPIRVariable>(next_id, type_id, StorageClassFunction, 0, arg_id);
+
+ set_name(next_id, name);
+ }
+ else if (is_builtin_variable(var) && p_type->basetype == SPIRType::Struct)
+ {
+ // Get the pointee type
+ type_id = get_pointee_type_id(type_id);
+ p_type = &get<SPIRType>(type_id);
+
+ uint32_t mbr_idx = 0;
+ for (auto &mbr_type_id : p_type->member_types)
+ {
+ BuiltIn builtin = BuiltInMax;
+ bool is_builtin = is_member_builtin(*p_type, mbr_idx, &builtin);
+ if (is_builtin && has_active_builtin(builtin, var.storage))
+ {
+ // Add a arg variable with the same type and decorations as the member
+ uint32_t next_ids = ir.increase_bound_by(2);
+ uint32_t ptr_type_id = next_ids + 0;
+ uint32_t var_id = next_ids + 1;
+
+ // Make sure we have an actual pointer type,
+ // so that we will get the appropriate address space when declaring these builtins.
+ auto &ptr = set<SPIRType>(ptr_type_id, get<SPIRType>(mbr_type_id));
+ ptr.self = mbr_type_id;
+ ptr.storage = var.storage;
+ ptr.pointer = true;
+ ptr.parent_type = mbr_type_id;
+
+ func.add_parameter(mbr_type_id, var_id, true);
+ set<SPIRVariable>(var_id, ptr_type_id, StorageClassFunction);
+ ir.meta[var_id].decoration = ir.meta[type_id].members[mbr_idx];
+ }
+ mbr_idx++;
+ }
+ }
+ else
+ {
+ uint32_t next_id = ir.increase_bound_by(1);
+ func.add_parameter(type_id, next_id, true);
+ set<SPIRVariable>(next_id, type_id, StorageClassFunction, 0, arg_id);
+
+ // Ensure the existing variable has a valid name and the new variable has all the same meta info
+ set_name(arg_id, ensure_valid_name(to_name(arg_id), "v"));
+ ir.meta[next_id] = ir.meta[arg_id];
+ }
+ }
+ }
+}
+
+// For all variables that are some form of non-input-output interface block, mark that all the structs
+// that are recursively contained within the type referenced by that variable should be packed tightly.
+void CompilerMSL::mark_packable_structs()
+{
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ if (var.storage != StorageClassFunction && !is_hidden_variable(var))
+ {
+ auto &type = this->get<SPIRType>(var.basetype);
+ if (type.pointer &&
+ (type.storage == StorageClassUniform || type.storage == StorageClassUniformConstant ||
+ type.storage == StorageClassPushConstant || type.storage == StorageClassStorageBuffer) &&
+ (has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock)))
+ mark_as_packable(type);
+ }
+ });
+}
+
+// If the specified type is a struct, it and any nested structs
+// are marked as packable with the SPIRVCrossDecorationPacked decoration,
+void CompilerMSL::mark_as_packable(SPIRType &type)
+{
+ // If this is not the base type (eg. it's a pointer or array), tunnel down
+ if (type.parent_type)
+ {
+ mark_as_packable(get<SPIRType>(type.parent_type));
+ return;
+ }
+
+ if (type.basetype == SPIRType::Struct)
+ {
+ set_extended_decoration(type.self, SPIRVCrossDecorationPacked);
+
+ // Recurse
+ size_t mbr_cnt = type.member_types.size();
+ for (uint32_t mbr_idx = 0; mbr_idx < mbr_cnt; mbr_idx++)
+ {
+ uint32_t mbr_type_id = type.member_types[mbr_idx];
+ auto &mbr_type = get<SPIRType>(mbr_type_id);
+ mark_as_packable(mbr_type);
+ if (mbr_type.type_alias)
+ {
+ auto &mbr_type_alias = get<SPIRType>(mbr_type.type_alias);
+ mark_as_packable(mbr_type_alias);
+ }
+ }
+ }
+}
+
+// If a vertex attribute exists at the location, it is marked as being used by this shader
+void CompilerMSL::mark_location_as_used_by_shader(uint32_t location, StorageClass storage)
+{
+ if ((get_execution_model() == ExecutionModelVertex || is_tessellation_shader()) && (storage == StorageClassInput))
+ vtx_attrs_in_use.insert(location);
+}
+
+uint32_t CompilerMSL::get_target_components_for_fragment_location(uint32_t location) const
+{
+ auto itr = fragment_output_components.find(location);
+ if (itr == end(fragment_output_components))
+ return 4;
+ else
+ return itr->second;
+}
+
+uint32_t CompilerMSL::build_extended_vector_type(uint32_t type_id, uint32_t components)
+{
+ uint32_t new_type_id = ir.increase_bound_by(1);
+ auto &type = set<SPIRType>(new_type_id, get<SPIRType>(type_id));
+ type.vecsize = components;
+ type.self = new_type_id;
+ type.parent_type = type_id;
+ type.pointer = false;
+
+ return new_type_id;
+}
+
+void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, const string &ib_var_ref,
+ SPIRType &ib_type, SPIRVariable &var, bool strip_array)
+{
+ bool is_builtin = is_builtin_variable(var);
+ BuiltIn builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn));
+ bool is_flat = has_decoration(var.self, DecorationFlat);
+ bool is_noperspective = has_decoration(var.self, DecorationNoPerspective);
+ bool is_centroid = has_decoration(var.self, DecorationCentroid);
+ bool is_sample = has_decoration(var.self, DecorationSample);
+
+ // Add a reference to the variable type to the interface struct.
+ uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size());
+ uint32_t type_id = ensure_correct_builtin_type(var.basetype, builtin);
+ var.basetype = type_id;
+
+ type_id = get_pointee_type_id(var.basetype);
+ if (strip_array && is_array(get<SPIRType>(type_id)))
+ type_id = get<SPIRType>(type_id).parent_type;
+ auto &type = get<SPIRType>(type_id);
+ uint32_t target_components = 0;
+ uint32_t type_components = type.vecsize;
+ bool padded_output = false;
+
+ // Check if we need to pad fragment output to match a certain number of components.
+ if (get_decoration_bitset(var.self).get(DecorationLocation) && msl_options.pad_fragment_output_components &&
+ get_entry_point().model == ExecutionModelFragment && storage == StorageClassOutput)
+ {
+ uint32_t locn = get_decoration(var.self, DecorationLocation);
+ target_components = get_target_components_for_fragment_location(locn);
+ if (type_components < target_components)
+ {
+ // Make a new type here.
+ type_id = build_extended_vector_type(type_id, target_components);
+ padded_output = true;
+ }
+ }
+
+ ib_type.member_types.push_back(type_id);
+
+ // Give the member a name
+ string mbr_name = ensure_valid_name(to_expression(var.self), "m");
+ set_member_name(ib_type.self, ib_mbr_idx, mbr_name);
+
+ // Update the original variable reference to include the structure reference
+ string qual_var_name = ib_var_ref + "." + mbr_name;
+ auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
+
+ if (padded_output)
+ {
+ entry_func.add_local_variable(var.self);
+ vars_needing_early_declaration.push_back(var.self);
+
+ entry_func.fixup_hooks_out.push_back([=, &var]() {
+ SPIRType &padded_type = this->get<SPIRType>(type_id);
+ statement(qual_var_name, " = ", remap_swizzle(padded_type, type_components, to_name(var.self)), ";");
+ });
+ }
+ else if (!strip_array)
+ ir.meta[var.self].decoration.qualified_alias = qual_var_name;
+
+ if (var.storage == StorageClassOutput && var.initializer != 0)
+ {
+ entry_func.fixup_hooks_in.push_back(
+ [=, &var]() { statement(qual_var_name, " = ", to_expression(var.initializer), ";"); });
+ }
+
+ // Copy the variable location from the original variable to the member
+ if (get_decoration_bitset(var.self).get(DecorationLocation))
+ {
+ uint32_t locn = get_decoration(var.self, DecorationLocation);
+ if (storage == StorageClassInput && (get_execution_model() == ExecutionModelVertex || is_tessellation_shader()))
+ {
+ type_id = ensure_correct_attribute_type(var.basetype, locn);
+ var.basetype = type_id;
+ type_id = get_pointee_type_id(type_id);
+ if (strip_array && is_array(get<SPIRType>(type_id)))
+ type_id = get<SPIRType>(type_id).parent_type;
+ ib_type.member_types[ib_mbr_idx] = type_id;
+ }
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
+ mark_location_as_used_by_shader(locn, storage);
+ }
+ else if (is_builtin && is_tessellation_shader() && vtx_attrs_by_builtin.count(builtin))
+ {
+ uint32_t locn = vtx_attrs_by_builtin[builtin].location;
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
+ mark_location_as_used_by_shader(locn, storage);
+ }
+
+ if (get_decoration_bitset(var.self).get(DecorationComponent))
+ {
+ uint32_t comp = get_decoration(var.self, DecorationComponent);
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationComponent, comp);
+ }
+
+ if (get_decoration_bitset(var.self).get(DecorationIndex))
+ {
+ uint32_t index = get_decoration(var.self, DecorationIndex);
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationIndex, index);
+ }
+
+ // Mark the member as builtin if needed
+ if (is_builtin)
+ {
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, builtin);
+ if (builtin == BuiltInPosition && storage == StorageClassOutput)
+ qual_pos_var_name = qual_var_name;
+ }
+
+ // Copy interpolation decorations if needed
+ if (is_flat)
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat);
+ if (is_noperspective)
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective);
+ if (is_centroid)
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid);
+ if (is_sample)
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample);
+
+ set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceOrigID, var.self);
+}
+
+void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage, const string &ib_var_ref,
+ SPIRType &ib_type, SPIRVariable &var, bool strip_array)
+{
+ auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
+ auto &var_type = strip_array ? get_variable_element_type(var) : get_variable_data_type(var);
+ uint32_t elem_cnt = 0;
+
+ if (is_matrix(var_type))
+ {
+ if (is_array(var_type))
+ SPIRV_CROSS_THROW("MSL cannot emit arrays-of-matrices in input and output variables.");
+
+ elem_cnt = var_type.columns;
+ }
+ else if (is_array(var_type))
+ {
+ if (var_type.array.size() != 1)
+ SPIRV_CROSS_THROW("MSL cannot emit arrays-of-arrays in input and output variables.");
+
+ elem_cnt = to_array_size_literal(var_type);
+ }
+
+ bool is_builtin = is_builtin_variable(var);
+ BuiltIn builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn));
+ bool is_flat = has_decoration(var.self, DecorationFlat);
+ bool is_noperspective = has_decoration(var.self, DecorationNoPerspective);
+ bool is_centroid = has_decoration(var.self, DecorationCentroid);
+ bool is_sample = has_decoration(var.self, DecorationSample);
+
+ auto *usable_type = &var_type;
+ if (usable_type->pointer)
+ usable_type = &get<SPIRType>(usable_type->parent_type);
+ while (is_array(*usable_type) || is_matrix(*usable_type))
+ usable_type = &get<SPIRType>(usable_type->parent_type);
+
+ // If a builtin, force it to have the proper name.
+ if (is_builtin)
+ set_name(var.self, builtin_to_glsl(builtin, StorageClassFunction));
+
+ entry_func.add_local_variable(var.self);
+
+ // We need to declare the variable early and at entry-point scope.
+ vars_needing_early_declaration.push_back(var.self);
+
+ for (uint32_t i = 0; i < elem_cnt; i++)
+ {
+ // Add a reference to the variable type to the interface struct.
+ uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size());
+
+ uint32_t target_components = 0;
+ bool padded_output = false;
+ uint32_t type_id = usable_type->self;
+
+ // Check if we need to pad fragment output to match a certain number of components.
+ if (get_decoration_bitset(var.self).get(DecorationLocation) && msl_options.pad_fragment_output_components &&
+ get_entry_point().model == ExecutionModelFragment && storage == StorageClassOutput)
+ {
+ uint32_t locn = get_decoration(var.self, DecorationLocation) + i;
+ target_components = get_target_components_for_fragment_location(locn);
+ if (usable_type->vecsize < target_components)
+ {
+ // Make a new type here.
+ type_id = build_extended_vector_type(usable_type->self, target_components);
+ padded_output = true;
+ }
+ }
+
+ ib_type.member_types.push_back(get_pointee_type_id(type_id));
+
+ // Give the member a name
+ string mbr_name = ensure_valid_name(join(to_expression(var.self), "_", i), "m");
+ set_member_name(ib_type.self, ib_mbr_idx, mbr_name);
+
+ // There is no qualified alias since we need to flatten the internal array on return.
+ if (get_decoration_bitset(var.self).get(DecorationLocation))
+ {
+ uint32_t locn = get_decoration(var.self, DecorationLocation) + i;
+ if (storage == StorageClassInput &&
+ (get_execution_model() == ExecutionModelVertex || is_tessellation_shader()))
+ {
+ var.basetype = ensure_correct_attribute_type(var.basetype, locn);
+ uint32_t mbr_type_id = ensure_correct_attribute_type(usable_type->self, locn);
+ ib_type.member_types[ib_mbr_idx] = mbr_type_id;
+ }
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
+ mark_location_as_used_by_shader(locn, storage);
+ }
+ else if (is_builtin && is_tessellation_shader() && vtx_attrs_by_builtin.count(builtin))
+ {
+ uint32_t locn = vtx_attrs_by_builtin[builtin].location + i;
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
+ mark_location_as_used_by_shader(locn, storage);
+ }
+
+ if (get_decoration_bitset(var.self).get(DecorationIndex))
+ {
+ uint32_t index = get_decoration(var.self, DecorationIndex);
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationIndex, index);
+ }
+
+ // Copy interpolation decorations if needed
+ if (is_flat)
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat);
+ if (is_noperspective)
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective);
+ if (is_centroid)
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid);
+ if (is_sample)
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample);
+
+ set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceOrigID, var.self);
+
+ if (!strip_array)
+ {
+ switch (storage)
+ {
+ case StorageClassInput:
+ entry_func.fixup_hooks_in.push_back(
+ [=, &var]() { statement(to_name(var.self), "[", i, "] = ", ib_var_ref, ".", mbr_name, ";"); });
+ break;
+
+ case StorageClassOutput:
+ entry_func.fixup_hooks_out.push_back([=, &var]() {
+ if (padded_output)
+ {
+ auto &padded_type = this->get<SPIRType>(type_id);
+ statement(
+ ib_var_ref, ".", mbr_name, " = ",
+ remap_swizzle(padded_type, usable_type->vecsize, join(to_name(var.self), "[", i, "]")),
+ ";");
+ }
+ else
+ statement(ib_var_ref, ".", mbr_name, " = ", to_name(var.self), "[", i, "];");
+ });
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+}
+
+uint32_t CompilerMSL::get_accumulated_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array)
+{
+ auto &type = strip_array ? get_variable_element_type(var) : get_variable_data_type(var);
+ uint32_t location = get_decoration(var.self, DecorationLocation);
+
+ for (uint32_t i = 0; i < mbr_idx; i++)
+ {
+ auto &mbr_type = get<SPIRType>(type.member_types[i]);
+
+ // Start counting from any place we have a new location decoration.
+ if (has_member_decoration(type.self, mbr_idx, DecorationLocation))
+ location = get_member_decoration(type.self, mbr_idx, DecorationLocation);
+
+ uint32_t location_count = 1;
+
+ if (mbr_type.columns > 1)
+ location_count = mbr_type.columns;
+
+ if (!mbr_type.array.empty())
+ for (uint32_t j = 0; j < uint32_t(mbr_type.array.size()); j++)
+ location_count *= to_array_size_literal(mbr_type, j);
+
+ location += location_count;
+ }
+
+ return location;
+}
+
+void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass storage, const string &ib_var_ref,
+ SPIRType &ib_type, SPIRVariable &var,
+ uint32_t mbr_idx, bool strip_array)
+{
+ auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
+ auto &var_type = strip_array ? get_variable_element_type(var) : get_variable_data_type(var);
+
+ BuiltIn builtin;
+ bool is_builtin = is_member_builtin(var_type, mbr_idx, &builtin);
+ bool is_flat =
+ has_member_decoration(var_type.self, mbr_idx, DecorationFlat) || has_decoration(var.self, DecorationFlat);
+ bool is_noperspective = has_member_decoration(var_type.self, mbr_idx, DecorationNoPerspective) ||
+ has_decoration(var.self, DecorationNoPerspective);
+ bool is_centroid = has_member_decoration(var_type.self, mbr_idx, DecorationCentroid) ||
+ has_decoration(var.self, DecorationCentroid);
+ bool is_sample =
+ has_member_decoration(var_type.self, mbr_idx, DecorationSample) || has_decoration(var.self, DecorationSample);
+
+ uint32_t mbr_type_id = var_type.member_types[mbr_idx];
+ auto &mbr_type = get<SPIRType>(mbr_type_id);
+ uint32_t elem_cnt = 0;
+
+ if (is_matrix(mbr_type))
+ {
+ if (is_array(mbr_type))
+ SPIRV_CROSS_THROW("MSL cannot emit arrays-of-matrices in input and output variables.");
+
+ elem_cnt = mbr_type.columns;
+ }
+ else if (is_array(mbr_type))
+ {
+ if (mbr_type.array.size() != 1)
+ SPIRV_CROSS_THROW("MSL cannot emit arrays-of-arrays in input and output variables.");
+
+ elem_cnt = to_array_size_literal(mbr_type);
+ }
+
+ auto *usable_type = &mbr_type;
+ if (usable_type->pointer)
+ usable_type = &get<SPIRType>(usable_type->parent_type);
+ while (is_array(*usable_type) || is_matrix(*usable_type))
+ usable_type = &get<SPIRType>(usable_type->parent_type);
+
+ for (uint32_t i = 0; i < elem_cnt; i++)
+ {
+ // Add a reference to the variable type to the interface struct.
+ uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size());
+ ib_type.member_types.push_back(usable_type->self);
+
+ // Give the member a name
+ string mbr_name = ensure_valid_name(join(to_qualified_member_name(var_type, mbr_idx), "_", i), "m");
+ set_member_name(ib_type.self, ib_mbr_idx, mbr_name);
+
+ if (has_member_decoration(var_type.self, mbr_idx, DecorationLocation))
+ {
+ uint32_t locn = get_member_decoration(var_type.self, mbr_idx, DecorationLocation) + i;
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
+ mark_location_as_used_by_shader(locn, storage);
+ }
+ else if (has_decoration(var.self, DecorationLocation))
+ {
+ uint32_t locn = get_accumulated_member_location(var, mbr_idx, strip_array) + i;
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
+ mark_location_as_used_by_shader(locn, storage);
+ }
+ else if (is_builtin && is_tessellation_shader() && vtx_attrs_by_builtin.count(builtin))
+ {
+ uint32_t locn = vtx_attrs_by_builtin[builtin].location + i;
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
+ mark_location_as_used_by_shader(locn, storage);
+ }
+
+ if (has_member_decoration(var_type.self, mbr_idx, DecorationComponent))
+ SPIRV_CROSS_THROW("DecorationComponent on matrices and arrays make little sense.");
+
+ // Copy interpolation decorations if needed
+ if (is_flat)
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat);
+ if (is_noperspective)
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective);
+ if (is_centroid)
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid);
+ if (is_sample)
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample);
+
+ set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceOrigID, var.self);
+ set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceMemberIndex, mbr_idx);
+
+ // Unflatten or flatten from [[stage_in]] or [[stage_out]] as appropriate.
+ if (!strip_array)
+ {
+ switch (storage)
+ {
+ case StorageClassInput:
+ entry_func.fixup_hooks_in.push_back([=, &var, &var_type]() {
+ statement(to_name(var.self), ".", to_member_name(var_type, mbr_idx), "[", i, "] = ", ib_var_ref,
+ ".", mbr_name, ";");
+ });
+ break;
+
+ case StorageClassOutput:
+ entry_func.fixup_hooks_out.push_back([=, &var, &var_type]() {
+ statement(ib_var_ref, ".", mbr_name, " = ", to_name(var.self), ".",
+ to_member_name(var_type, mbr_idx), "[", i, "];");
+ });
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+}
+
+void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass storage, const string &ib_var_ref,
+ SPIRType &ib_type, SPIRVariable &var, uint32_t mbr_idx,
+ bool strip_array)
+{
+ auto &var_type = strip_array ? get_variable_element_type(var) : get_variable_data_type(var);
+ auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
+
+ BuiltIn builtin = BuiltInMax;
+ bool is_builtin = is_member_builtin(var_type, mbr_idx, &builtin);
+ bool is_flat =
+ has_member_decoration(var_type.self, mbr_idx, DecorationFlat) || has_decoration(var.self, DecorationFlat);
+ bool is_noperspective = has_member_decoration(var_type.self, mbr_idx, DecorationNoPerspective) ||
+ has_decoration(var.self, DecorationNoPerspective);
+ bool is_centroid = has_member_decoration(var_type.self, mbr_idx, DecorationCentroid) ||
+ has_decoration(var.self, DecorationCentroid);
+ bool is_sample =
+ has_member_decoration(var_type.self, mbr_idx, DecorationSample) || has_decoration(var.self, DecorationSample);
+
+ // Add a reference to the member to the interface struct.
+ uint32_t mbr_type_id = var_type.member_types[mbr_idx];
+ uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size());
+ mbr_type_id = ensure_correct_builtin_type(mbr_type_id, builtin);
+ var_type.member_types[mbr_idx] = mbr_type_id;
+ ib_type.member_types.push_back(mbr_type_id);
+
+ // Give the member a name
+ string mbr_name = ensure_valid_name(to_qualified_member_name(var_type, mbr_idx), "m");
+ set_member_name(ib_type.self, ib_mbr_idx, mbr_name);
+
+ // Update the original variable reference to include the structure reference
+ string qual_var_name = ib_var_ref + "." + mbr_name;
+
+ if (is_builtin && !strip_array)
+ {
+ // For the builtin gl_PerVertex, we cannot treat it as a block anyways,
+ // so redirect to qualified name.
+ set_member_qualified_name(var_type.self, mbr_idx, qual_var_name);
+ }
+ else if (!strip_array)
+ {
+ // Unflatten or flatten from [[stage_in]] or [[stage_out]] as appropriate.
+ switch (storage)
+ {
+ case StorageClassInput:
+ entry_func.fixup_hooks_in.push_back([=, &var, &var_type]() {
+ statement(to_name(var.self), ".", to_member_name(var_type, mbr_idx), " = ", qual_var_name, ";");
+ });
+ break;
+
+ case StorageClassOutput:
+ entry_func.fixup_hooks_out.push_back([=, &var, &var_type]() {
+ statement(qual_var_name, " = ", to_name(var.self), ".", to_member_name(var_type, mbr_idx), ";");
+ });
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // Copy the variable location from the original variable to the member
+ if (has_member_decoration(var_type.self, mbr_idx, DecorationLocation))
+ {
+ uint32_t locn = get_member_decoration(var_type.self, mbr_idx, DecorationLocation);
+ if (storage == StorageClassInput && (get_execution_model() == ExecutionModelVertex || is_tessellation_shader()))
+ {
+ mbr_type_id = ensure_correct_attribute_type(mbr_type_id, locn);
+ var_type.member_types[mbr_idx] = mbr_type_id;
+ ib_type.member_types[ib_mbr_idx] = mbr_type_id;
+ }
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
+ mark_location_as_used_by_shader(locn, storage);
+ }
+ else if (has_decoration(var.self, DecorationLocation))
+ {
+ // The block itself might have a location and in this case, all members of the block
+ // receive incrementing locations.
+ uint32_t locn = get_accumulated_member_location(var, mbr_idx, strip_array);
+ if (storage == StorageClassInput && (get_execution_model() == ExecutionModelVertex || is_tessellation_shader()))
+ {
+ mbr_type_id = ensure_correct_attribute_type(mbr_type_id, locn);
+ var_type.member_types[mbr_idx] = mbr_type_id;
+ ib_type.member_types[ib_mbr_idx] = mbr_type_id;
+ }
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
+ mark_location_as_used_by_shader(locn, storage);
+ }
+ else if (is_builtin && is_tessellation_shader() && vtx_attrs_by_builtin.count(builtin))
+ {
+ uint32_t locn = 0;
+ auto builtin_itr = vtx_attrs_by_builtin.find(builtin);
+ if (builtin_itr != end(vtx_attrs_by_builtin))
+ locn = builtin_itr->second.location;
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
+ mark_location_as_used_by_shader(locn, storage);
+ }
+
+ // Copy the component location, if present.
+ if (has_member_decoration(var_type.self, mbr_idx, DecorationComponent))
+ {
+ uint32_t comp = get_member_decoration(var_type.self, mbr_idx, DecorationComponent);
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationComponent, comp);
+ }
+
+ // Mark the member as builtin if needed
+ if (is_builtin)
+ {
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, builtin);
+ if (builtin == BuiltInPosition && storage == StorageClassOutput)
+ qual_pos_var_name = qual_var_name;
+ }
+
+ // Copy interpolation decorations if needed
+ if (is_flat)
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat);
+ if (is_noperspective)
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective);
+ if (is_centroid)
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid);
+ if (is_sample)
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample);
+
+ set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceOrigID, var.self);
+ set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceMemberIndex, mbr_idx);
+}
+
+// In Metal, the tessellation levels are stored as tightly packed half-precision floating point values.
+// But, stage-in attribute offsets and strides must be multiples of four, so we can't pass the levels
+// individually. Therefore, we must pass them as vectors. Triangles get a single float4, with the outer
+// levels in 'xyz' and the inner level in 'w'. Quads get a float4 containing the outer levels and a
+// float2 containing the inner levels.
+void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_var_ref, SPIRType &ib_type,
+ SPIRVariable &var)
+{
+ auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
+ auto &var_type = get_variable_element_type(var);
+
+ BuiltIn builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn));
+
+ // Force the variable to have the proper name.
+ set_name(var.self, builtin_to_glsl(builtin, StorageClassFunction));
+
+ if (get_entry_point().flags.get(ExecutionModeTriangles))
+ {
+ // Triangles are tricky, because we want only one member in the struct.
+
+ // We need to declare the variable early and at entry-point scope.
+ entry_func.add_local_variable(var.self);
+ vars_needing_early_declaration.push_back(var.self);
+
+ string mbr_name = "gl_TessLevel";
+
+ // If we already added the other one, we can skip this step.
+ if (!added_builtin_tess_level)
+ {
+ // Add a reference to the variable type to the interface struct.
+ uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size());
+
+ uint32_t type_id = build_extended_vector_type(var_type.self, 4);
+
+ ib_type.member_types.push_back(type_id);
+
+ // Give the member a name
+ set_member_name(ib_type.self, ib_mbr_idx, mbr_name);
+
+ // There is no qualified alias since we need to flatten the internal array on return.
+ if (get_decoration_bitset(var.self).get(DecorationLocation))
+ {
+ uint32_t locn = get_decoration(var.self, DecorationLocation);
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
+ mark_location_as_used_by_shader(locn, StorageClassInput);
+ }
+ else if (vtx_attrs_by_builtin.count(builtin))
+ {
+ uint32_t locn = vtx_attrs_by_builtin[builtin].location;
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
+ mark_location_as_used_by_shader(locn, StorageClassInput);
+ }
+
+ added_builtin_tess_level = true;
+ }
+
+ switch (builtin)
+ {
+ case BuiltInTessLevelOuter:
+ entry_func.fixup_hooks_in.push_back([=, &var]() {
+ statement(to_name(var.self), "[0] = ", ib_var_ref, ".", mbr_name, ".x;");
+ statement(to_name(var.self), "[1] = ", ib_var_ref, ".", mbr_name, ".y;");
+ statement(to_name(var.self), "[2] = ", ib_var_ref, ".", mbr_name, ".z;");
+ });
+ break;
+
+ case BuiltInTessLevelInner:
+ entry_func.fixup_hooks_in.push_back(
+ [=, &var]() { statement(to_name(var.self), "[0] = ", ib_var_ref, ".", mbr_name, ".w;"); });
+ break;
+
+ default:
+ assert(false);
+ break;
+ }
+ }
+ else
+ {
+ // Add a reference to the variable type to the interface struct.
+ uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size());
+
+ uint32_t type_id = build_extended_vector_type(var_type.self, builtin == BuiltInTessLevelOuter ? 4 : 2);
+ // Change the type of the variable, too.
+ uint32_t ptr_type_id = ir.increase_bound_by(1);
+ auto &new_var_type = set<SPIRType>(ptr_type_id, get<SPIRType>(type_id));
+ new_var_type.pointer = true;
+ new_var_type.storage = StorageClassInput;
+ new_var_type.parent_type = type_id;
+ var.basetype = ptr_type_id;
+
+ ib_type.member_types.push_back(type_id);
+
+ // Give the member a name
+ string mbr_name = to_expression(var.self);
+ set_member_name(ib_type.self, ib_mbr_idx, mbr_name);
+
+ // Since vectors can be indexed like arrays, there is no need to unpack this. We can
+ // just refer to the vector directly. So give it a qualified alias.
+ string qual_var_name = ib_var_ref + "." + mbr_name;
+ ir.meta[var.self].decoration.qualified_alias = qual_var_name;
+
+ if (get_decoration_bitset(var.self).get(DecorationLocation))
+ {
+ uint32_t locn = get_decoration(var.self, DecorationLocation);
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
+ mark_location_as_used_by_shader(locn, StorageClassInput);
+ }
+ else if (vtx_attrs_by_builtin.count(builtin))
+ {
+ uint32_t locn = vtx_attrs_by_builtin[builtin].location;
+ set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
+ mark_location_as_used_by_shader(locn, StorageClassInput);
+ }
+ }
+}
+
+void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const string &ib_var_ref, SPIRType &ib_type,
+ SPIRVariable &var, bool strip_array)
+{
+ auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
+ // Tessellation control I/O variables and tessellation evaluation per-point inputs are
+ // usually declared as arrays. In these cases, we want to add the element type to the
+ // interface block, since in Metal it's the interface block itself which is arrayed.
+ auto &var_type = strip_array ? get_variable_element_type(var) : get_variable_data_type(var);
+ bool is_builtin = is_builtin_variable(var);
+ auto builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn));
+
+ if (var_type.basetype == SPIRType::Struct)
+ {
+ if (!is_builtin_type(var_type) && (!capture_output_to_buffer || storage == StorageClassInput) && !strip_array)
+ {
+ // For I/O blocks or structs, we will need to pass the block itself around
+ // to functions if they are used globally in leaf functions.
+ // Rather than passing down member by member,
+ // we unflatten I/O blocks while running the shader,
+ // and pass the actual struct type down to leaf functions.
+ // We then unflatten inputs, and flatten outputs in the "fixup" stages.
+ entry_func.add_local_variable(var.self);
+ vars_needing_early_declaration.push_back(var.self);
+ }
+
+ if (capture_output_to_buffer && storage != StorageClassInput && !has_decoration(var_type.self, DecorationBlock))
+ {
+ // In Metal tessellation shaders, the interface block itself is arrayed. This makes things
+ // very complicated, since stage-in structures in MSL don't support nested structures.
+ // Luckily, for stage-out when capturing output, we can avoid this and just add
+ // composite members directly, because the stage-out structure is stored to a buffer,
+ // not returned.
+ add_plain_variable_to_interface_block(storage, ib_var_ref, ib_type, var, strip_array);
+ }
+ else
+ {
+ // Flatten the struct members into the interface struct
+ for (uint32_t mbr_idx = 0; mbr_idx < uint32_t(var_type.member_types.size()); mbr_idx++)
+ {
+ builtin = BuiltInMax;
+ is_builtin = is_member_builtin(var_type, mbr_idx, &builtin);
+ auto &mbr_type = get<SPIRType>(var_type.member_types[mbr_idx]);
+
+ if (!is_builtin || has_active_builtin(builtin, storage))
+ {
+ if ((!is_builtin ||
+ (storage == StorageClassInput && get_execution_model() != ExecutionModelFragment)) &&
+ (storage == StorageClassInput || storage == StorageClassOutput) &&
+ (is_matrix(mbr_type) || is_array(mbr_type)))
+ {
+ add_composite_member_variable_to_interface_block(storage, ib_var_ref, ib_type, var, mbr_idx,
+ strip_array);
+ }
+ else
+ {
+ add_plain_member_variable_to_interface_block(storage, ib_var_ref, ib_type, var, mbr_idx,
+ strip_array);
+ }
+ }
+ }
+ }
+ }
+ else if (get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput &&
+ !strip_array && is_builtin && (builtin == BuiltInTessLevelOuter || builtin == BuiltInTessLevelInner))
+ {
+ add_tess_level_input_to_interface_block(ib_var_ref, ib_type, var);
+ }
+ else if (var_type.basetype == SPIRType::Boolean || var_type.basetype == SPIRType::Char ||
+ type_is_integral(var_type) || type_is_floating_point(var_type) || var_type.basetype == SPIRType::Boolean)
+ {
+ if (!is_builtin || has_active_builtin(builtin, storage))
+ {
+ // MSL does not allow matrices or arrays in input or output variables, so need to handle it specially.
+ if ((!is_builtin || (storage == StorageClassInput && get_execution_model() != ExecutionModelFragment)) &&
+ (storage == StorageClassInput || (storage == StorageClassOutput && !capture_output_to_buffer)) &&
+ (is_matrix(var_type) || is_array(var_type)))
+ {
+ add_composite_variable_to_interface_block(storage, ib_var_ref, ib_type, var, strip_array);
+ }
+ else
+ {
+ add_plain_variable_to_interface_block(storage, ib_var_ref, ib_type, var, strip_array);
+ }
+ }
+ }
+}
+
+// Fix up the mapping of variables to interface member indices, which is used to compile access chains
+// for per-vertex variables in a tessellation control shader.
+void CompilerMSL::fix_up_interface_member_indices(StorageClass storage, uint32_t ib_type_id)
+{
+ // Only needed for tessellation shaders.
+ if (get_execution_model() != ExecutionModelTessellationControl &&
+ !(get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput))
+ return;
+
+ bool in_array = false;
+ for (uint32_t i = 0; i < ir.meta[ib_type_id].members.size(); i++)
+ {
+ auto &mbr_dec = ir.meta[ib_type_id].members[i];
+ uint32_t var_id = mbr_dec.extended.ib_orig_id;
+ if (!var_id)
+ continue;
+ auto &var = get<SPIRVariable>(var_id);
+
+ // Unfortunately, all this complexity is needed to handle flattened structs and/or
+ // arrays.
+ if (storage == StorageClassInput)
+ {
+ auto &type = get_variable_element_type(var);
+ if (is_array(type) || is_matrix(type))
+ {
+ if (in_array)
+ continue;
+ in_array = true;
+ set_extended_decoration(var_id, SPIRVCrossDecorationInterfaceMemberIndex, i);
+ }
+ else
+ {
+ if (type.basetype == SPIRType::Struct)
+ {
+ uint32_t mbr_idx =
+ get_extended_member_decoration(ib_type_id, i, SPIRVCrossDecorationInterfaceMemberIndex);
+ auto &mbr_type = get<SPIRType>(type.member_types[mbr_idx]);
+
+ if (is_array(mbr_type) || is_matrix(mbr_type))
+ {
+ if (in_array)
+ continue;
+ in_array = true;
+ set_extended_member_decoration(var_id, mbr_idx, SPIRVCrossDecorationInterfaceMemberIndex, i);
+ }
+ else
+ {
+ in_array = false;
+ set_extended_member_decoration(var_id, mbr_idx, SPIRVCrossDecorationInterfaceMemberIndex, i);
+ }
+ }
+ else
+ {
+ in_array = false;
+ set_extended_decoration(var_id, SPIRVCrossDecorationInterfaceMemberIndex, i);
+ }
+ }
+ }
+ else
+ set_extended_decoration(var_id, SPIRVCrossDecorationInterfaceMemberIndex, i);
+ }
+}
+
+// Add an interface structure for the type of storage, which is either StorageClassInput or StorageClassOutput.
+// Returns the ID of the newly added variable, or zero if no variable was added.
+uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
+{
+ // Accumulate the variables that should appear in the interface struct
+ vector<SPIRVariable *> vars;
+ bool incl_builtins = (storage == StorageClassOutput || is_tessellation_shader());
+
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t var_id, SPIRVariable &var) {
+ auto &type = this->get<SPIRType>(var.basetype);
+ BuiltIn bi_type = BuiltIn(get_decoration(var_id, DecorationBuiltIn));
+ if (var.storage == storage && interface_variable_exists_in_entry_point(var.self) &&
+ !is_hidden_variable(var, incl_builtins) && type.pointer &&
+ (has_decoration(var_id, DecorationPatch) || is_patch_block(type)) == patch &&
+ (!is_builtin_variable(var) || bi_type == BuiltInPosition || bi_type == BuiltInPointSize ||
+ bi_type == BuiltInClipDistance || bi_type == BuiltInCullDistance || bi_type == BuiltInLayer ||
+ bi_type == BuiltInViewportIndex || bi_type == BuiltInFragDepth || bi_type == BuiltInSampleMask ||
+ (get_execution_model() == ExecutionModelTessellationEvaluation &&
+ (bi_type == BuiltInTessLevelOuter || bi_type == BuiltInTessLevelInner))))
+ {
+ vars.push_back(&var);
+ }
+ });
+
+ // If no variables qualify, leave.
+ // For patch input in a tessellation evaluation shader, the per-vertex stage inputs
+ // are included in a special patch control point array.
+ if (vars.empty() && !(storage == StorageClassInput && patch && stage_in_var_id))
+ return 0;
+
+ // Add a new typed variable for this interface structure.
+ // The initializer expression is allocated here, but populated when the function
+ // declaraion is emitted, because it is cleared after each compilation pass.
+ uint32_t next_id = ir.increase_bound_by(3);
+ uint32_t ib_type_id = next_id++;
+ auto &ib_type = set<SPIRType>(ib_type_id);
+ ib_type.basetype = SPIRType::Struct;
+ ib_type.storage = storage;
+ set_decoration(ib_type_id, DecorationBlock);
+
+ uint32_t ib_var_id = next_id++;
+ auto &var = set<SPIRVariable>(ib_var_id, ib_type_id, storage, 0);
+ var.initializer = next_id++;
+
+ string ib_var_ref;
+ auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
+ switch (storage)
+ {
+ case StorageClassInput:
+ ib_var_ref = patch ? patch_stage_in_var_name : stage_in_var_name;
+ if (get_execution_model() == ExecutionModelTessellationControl)
+ {
+ // Add a hook to populate the shared workgroup memory containing
+ // the gl_in array.
+ entry_func.fixup_hooks_in.push_back([=]() {
+ // Can't use PatchVertices yet; the hook for that may not have run yet.
+ statement("if (", to_expression(builtin_invocation_id_id), " < ", "spvIndirectParams[0])");
+ statement(" ", input_wg_var_name, "[", to_expression(builtin_invocation_id_id), "] = ", ib_var_ref,
+ ";");
+ statement("threadgroup_barrier(mem_flags::mem_threadgroup);");
+ statement("if (", to_expression(builtin_invocation_id_id), " >= ", get_entry_point().output_vertices,
+ ")");
+ statement(" return;");
+ });
+ }
+ break;
+
+ case StorageClassOutput:
+ {
+ ib_var_ref = patch ? patch_stage_out_var_name : stage_out_var_name;
+
+ // Add the output interface struct as a local variable to the entry function.
+ // If the entry point should return the output struct, set the entry function
+ // to return the output interface struct, otherwise to return nothing.
+ // Indicate the output var requires early initialization.
+ bool ep_should_return_output = !get_is_rasterization_disabled();
+ uint32_t rtn_id = ep_should_return_output ? ib_var_id : 0;
+ if (!capture_output_to_buffer)
+ {
+ entry_func.add_local_variable(ib_var_id);
+ for (auto &blk_id : entry_func.blocks)
+ {
+ auto &blk = get<SPIRBlock>(blk_id);
+ if (blk.terminator == SPIRBlock::Return)
+ blk.return_value = rtn_id;
+ }
+ vars_needing_early_declaration.push_back(ib_var_id);
+ }
+ else
+ {
+ switch (get_execution_model())
+ {
+ case ExecutionModelVertex:
+ case ExecutionModelTessellationEvaluation:
+ // Instead of declaring a struct variable to hold the output and then
+ // copying that to the output buffer, we'll declare the output variable
+ // as a reference to the final output element in the buffer. Then we can
+ // avoid the extra copy.
+ entry_func.fixup_hooks_in.push_back([=]() {
+ if (stage_out_var_id)
+ {
+ // The first member of the indirect buffer is always the number of vertices
+ // to draw.
+ statement("device ", to_name(ir.default_entry_point), "_", ib_var_ref, "& ", ib_var_ref, " = ",
+ output_buffer_var_name, "[(", to_expression(builtin_instance_idx_id), " - ",
+ to_expression(builtin_base_instance_id), ") * spvIndirectParams[0] + ",
+ to_expression(builtin_vertex_idx_id), " - ", to_expression(builtin_base_vertex_id),
+ "];");
+ }
+ });
+ break;
+ case ExecutionModelTessellationControl:
+ if (patch)
+ entry_func.fixup_hooks_in.push_back([=]() {
+ statement("device ", to_name(ir.default_entry_point), "_", ib_var_ref, "& ", ib_var_ref, " = ",
+ patch_output_buffer_var_name, "[", to_expression(builtin_primitive_id_id), "];");
+ });
+ else
+ entry_func.fixup_hooks_in.push_back([=]() {
+ statement("device ", to_name(ir.default_entry_point), "_", ib_var_ref, "* gl_out = &",
+ output_buffer_var_name, "[", to_expression(builtin_primitive_id_id), " * ",
+ get_entry_point().output_vertices, "];");
+ });
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ set_name(ib_type_id, to_name(ir.default_entry_point) + "_" + ib_var_ref);
+ set_name(ib_var_id, ib_var_ref);
+
+ for (auto p_var : vars)
+ {
+ bool strip_array =
+ (get_execution_model() == ExecutionModelTessellationControl ||
+ (get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput)) &&
+ !patch;
+ add_variable_to_interface_block(storage, ib_var_ref, ib_type, *p_var, strip_array);
+ }
+
+ // Sort the members of the structure by their locations.
+ MemberSorter member_sorter(ib_type, ir.meta[ib_type_id], MemberSorter::Location);
+ member_sorter.sort();
+
+ // The member indices were saved to the original variables, but after the members
+ // were sorted, those indices are now likely incorrect. Fix those up now.
+ if (!patch)
+ fix_up_interface_member_indices(storage, ib_type_id);
+
+ // For patch inputs, add one more member, holding the array of control point data.
+ if (get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput && patch &&
+ stage_in_var_id)
+ {
+ uint32_t pcp_type_id = ir.increase_bound_by(1);
+ auto &pcp_type = set<SPIRType>(pcp_type_id, ib_type);
+ pcp_type.basetype = SPIRType::ControlPointArray;
+ pcp_type.parent_type = pcp_type.type_alias = get_stage_in_struct_type().self;
+ pcp_type.storage = storage;
+ ir.meta[pcp_type_id] = ir.meta[ib_type.self];
+ uint32_t mbr_idx = uint32_t(ib_type.member_types.size());
+ ib_type.member_types.push_back(pcp_type_id);
+ set_member_name(ib_type.self, mbr_idx, "gl_in");
+ }
+
+ return ib_var_id;
+}
+
+uint32_t CompilerMSL::add_interface_block_pointer(uint32_t ib_var_id, StorageClass storage)
+{
+ if (!ib_var_id)
+ return 0;
+
+ uint32_t ib_ptr_var_id;
+ uint32_t next_id = ir.increase_bound_by(3);
+ auto &ib_type = expression_type(ib_var_id);
+ if (get_execution_model() == ExecutionModelTessellationControl)
+ {
+ // Tessellation control per-vertex I/O is presented as an array, so we must
+ // do the same with our struct here.
+ uint32_t ib_ptr_type_id = next_id++;
+ auto &ib_ptr_type = set<SPIRType>(ib_ptr_type_id, ib_type);
+ ib_ptr_type.parent_type = ib_ptr_type.type_alias = ib_type.self;
+ ib_ptr_type.pointer = true;
+ ib_ptr_type.storage = storage == StorageClassInput ? StorageClassWorkgroup : StorageClassStorageBuffer;
+ ir.meta[ib_ptr_type_id] = ir.meta[ib_type.self];
+ // To ensure that get_variable_data_type() doesn't strip off the pointer,
+ // which we need, use another pointer.
+ uint32_t ib_ptr_ptr_type_id = next_id++;
+ auto &ib_ptr_ptr_type = set<SPIRType>(ib_ptr_ptr_type_id, ib_ptr_type);
+ ib_ptr_ptr_type.parent_type = ib_ptr_type_id;
+ ib_ptr_ptr_type.type_alias = ib_type.self;
+ ib_ptr_ptr_type.storage = StorageClassFunction;
+ ir.meta[ib_ptr_ptr_type_id] = ir.meta[ib_type.self];
+
+ ib_ptr_var_id = next_id;
+ set<SPIRVariable>(ib_ptr_var_id, ib_ptr_ptr_type_id, StorageClassFunction, 0);
+ set_name(ib_ptr_var_id, storage == StorageClassInput ? input_wg_var_name : "gl_out");
+ }
+ else
+ {
+ // Tessellation evaluation per-vertex inputs are also presented as arrays.
+ // But, in Metal, this array uses a very special type, 'patch_control_point<T>',
+ // which is a container that can be used to access the control point data.
+ // To represent this, a special 'ControlPointArray' type has been added to the
+ // SPIRV-Cross type system. It should only be generated by and seen in the MSL
+ // backend (i.e. this one).
+ uint32_t pcp_type_id = next_id++;
+ auto &pcp_type = set<SPIRType>(pcp_type_id, ib_type);
+ pcp_type.basetype = SPIRType::ControlPointArray;
+ pcp_type.parent_type = pcp_type.type_alias = ib_type.self;
+ pcp_type.storage = storage;
+ ir.meta[pcp_type_id] = ir.meta[ib_type.self];
+
+ ib_ptr_var_id = next_id;
+ set<SPIRVariable>(ib_ptr_var_id, pcp_type_id, storage, 0);
+ set_name(ib_ptr_var_id, "gl_in");
+ ir.meta[ib_ptr_var_id].decoration.qualified_alias = join(patch_stage_in_var_name, ".gl_in");
+ }
+ return ib_ptr_var_id;
+}
+
+// Ensure that the type is compatible with the builtin.
+// If it is, simply return the given type ID.
+// Otherwise, create a new type, and return it's ID.
+uint32_t CompilerMSL::ensure_correct_builtin_type(uint32_t type_id, BuiltIn builtin)
+{
+ auto &type = get<SPIRType>(type_id);
+
+ if ((builtin == BuiltInSampleMask && is_array(type)) ||
+ ((builtin == BuiltInLayer || builtin == BuiltInViewportIndex) && type.basetype != SPIRType::UInt))
+ {
+ uint32_t next_id = ir.increase_bound_by(type.pointer ? 2 : 1);
+ uint32_t base_type_id = next_id++;
+ auto &base_type = set<SPIRType>(base_type_id);
+ base_type.basetype = SPIRType::UInt;
+ base_type.width = 32;
+
+ if (!type.pointer)
+ return base_type_id;
+
+ uint32_t ptr_type_id = next_id++;
+ auto &ptr_type = set<SPIRType>(ptr_type_id);
+ ptr_type = base_type;
+ ptr_type.pointer = true;
+ ptr_type.storage = type.storage;
+ ptr_type.parent_type = base_type_id;
+ return ptr_type_id;
+ }
+
+ return type_id;
+}
+
+// Ensure that the type is compatible with the vertex attribute.
+// If it is, simply return the given type ID.
+// Otherwise, create a new type, and return its ID.
+uint32_t CompilerMSL::ensure_correct_attribute_type(uint32_t type_id, uint32_t location)
+{
+ auto &type = get<SPIRType>(type_id);
+
+ auto p_va = vtx_attrs_by_location.find(location);
+ if (p_va == end(vtx_attrs_by_location))
+ return type_id;
+
+ switch (p_va->second.format)
+ {
+ case MSL_VERTEX_FORMAT_UINT8:
+ {
+ switch (type.basetype)
+ {
+ case SPIRType::UByte:
+ case SPIRType::UShort:
+ case SPIRType::UInt:
+ return type_id;
+ case SPIRType::Short:
+ case SPIRType::Int:
+ break;
+ default:
+ SPIRV_CROSS_THROW("Vertex attribute type mismatch between host and shader");
+ }
+ uint32_t next_id = ir.increase_bound_by(type.pointer ? 2 : 1);
+ uint32_t base_type_id = next_id++;
+ auto &base_type = set<SPIRType>(base_type_id);
+ base_type = type;
+ base_type.basetype = type.basetype == SPIRType::Short ? SPIRType::UShort : SPIRType::UInt;
+ base_type.pointer = false;
+
+ if (!type.pointer)
+ return base_type_id;
+
+ uint32_t ptr_type_id = next_id++;
+ auto &ptr_type = set<SPIRType>(ptr_type_id);
+ ptr_type = base_type;
+ ptr_type.pointer = true;
+ ptr_type.storage = type.storage;
+ ptr_type.parent_type = base_type_id;
+ return ptr_type_id;
+ }
+
+ case MSL_VERTEX_FORMAT_UINT16:
+ {
+ switch (type.basetype)
+ {
+ case SPIRType::UShort:
+ case SPIRType::UInt:
+ return type_id;
+ case SPIRType::Int:
+ break;
+ default:
+ SPIRV_CROSS_THROW("Vertex attribute type mismatch between host and shader");
+ }
+ uint32_t next_id = ir.increase_bound_by(type.pointer ? 2 : 1);
+ uint32_t base_type_id = next_id++;
+ auto &base_type = set<SPIRType>(base_type_id);
+ base_type = type;
+ base_type.basetype = SPIRType::UInt;
+ base_type.pointer = false;
+
+ if (!type.pointer)
+ return base_type_id;
+
+ uint32_t ptr_type_id = next_id++;
+ auto &ptr_type = set<SPIRType>(ptr_type_id);
+ ptr_type = base_type;
+ ptr_type.pointer = true;
+ ptr_type.storage = type.storage;
+ ptr_type.parent_type = base_type_id;
+ return ptr_type_id;
+ }
+
+ default:
+ case MSL_VERTEX_FORMAT_OTHER:
+ break;
+ }
+
+ return type_id;
+}
+
+// Sort the members of the struct type by offset, and pack and then pad members where needed
+// to align MSL members with SPIR-V offsets. The struct members are iterated twice. Packing
+// occurs first, followed by padding, because packing a member reduces both its size and its
+// natural alignment, possibly requiring a padding member to be added ahead of it.
+void CompilerMSL::align_struct(SPIRType &ib_type)
+{
+ uint32_t &ib_type_id = ib_type.self;
+
+ // Sort the members of the interface structure by their offset.
+ // They should already be sorted per SPIR-V spec anyway.
+ MemberSorter member_sorter(ib_type, ir.meta[ib_type_id], MemberSorter::Offset);
+ member_sorter.sort();
+
+ uint32_t mbr_cnt = uint32_t(ib_type.member_types.size());
+
+ // Test the alignment of each member, and if a member should be closer to the previous
+ // member than the default spacing expects, it is likely that the previous member is in
+ // a packed format. If so, and the previous member is packable, pack it.
+ // For example...this applies to any 3-element vector that is followed by a scalar.
+ uint32_t curr_offset = 0;
+ for (uint32_t mbr_idx = 0; mbr_idx < mbr_cnt; mbr_idx++)
+ {
+ if (is_member_packable(ib_type, mbr_idx))
+ {
+ set_extended_member_decoration(ib_type_id, mbr_idx, SPIRVCrossDecorationPacked);
+ set_extended_member_decoration(ib_type_id, mbr_idx, SPIRVCrossDecorationPackedType,
+ ib_type.member_types[mbr_idx]);
+ }
+
+ // Align current offset to the current member's default alignment.
+ size_t align_mask = get_declared_struct_member_alignment(ib_type, mbr_idx) - 1;
+ uint32_t aligned_curr_offset = uint32_t((curr_offset + align_mask) & ~align_mask);
+
+ // Fetch the member offset as declared in the SPIRV.
+ uint32_t mbr_offset = get_member_decoration(ib_type_id, mbr_idx, DecorationOffset);
+ if (mbr_offset > aligned_curr_offset)
+ {
+ // Since MSL and SPIR-V have slightly different struct member alignment and
+ // size rules, we'll pad to standard C-packing rules. If the member is farther
+ // away than C-packing, expects, add an inert padding member before the the member.
+ MSLStructMemberKey key = get_struct_member_key(ib_type_id, mbr_idx);
+ struct_member_padding[key] = mbr_offset - curr_offset;
+ }
+
+ // Increment the current offset to be positioned immediately after the current member.
+ // Don't do this for the last member since it can be unsized, and it is not relevant for padding purposes here.
+ if (mbr_idx + 1 < mbr_cnt)
+ curr_offset = mbr_offset + uint32_t(get_declared_struct_member_size(ib_type, mbr_idx));
+ }
+}
+
+// Returns whether the specified struct member supports a packable type
+// variation that is smaller than the unpacked variation of that type.
+bool CompilerMSL::is_member_packable(SPIRType &ib_type, uint32_t index)
+{
+ // We've already marked it as packable
+ if (has_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPacked))
+ return true;
+
+ auto &mbr_type = get<SPIRType>(ib_type.member_types[index]);
+
+ uint32_t component_size = mbr_type.width / 8;
+ uint32_t unpacked_mbr_size;
+ if (mbr_type.vecsize == 3)
+ unpacked_mbr_size = component_size * (mbr_type.vecsize + 1) * mbr_type.columns;
+ else
+ unpacked_mbr_size = component_size * mbr_type.vecsize * mbr_type.columns;
+
+ // Special case for packing. Check for float[] or vec2[] in std140 layout. Here we actually need to pad out instead,
+ // but we will use the same mechanism.
+ if (is_array(mbr_type) && (is_scalar(mbr_type) || is_vector(mbr_type)) && mbr_type.vecsize <= 2 &&
+ type_struct_member_array_stride(ib_type, index) == 4 * component_size)
+ {
+ return true;
+ }
+
+ // Check for array of struct, where the SPIR-V declares an array stride which is larger than the struct itself.
+ // This can happen for struct A { float a }; A a[]; in std140 layout.
+ // TODO: Emit a padded struct which can be used for this purpose.
+ if (is_array(mbr_type) && mbr_type.basetype == SPIRType::Struct)
+ {
+ size_t declared_struct_size = get_declared_struct_size(mbr_type);
+ size_t alignment = get_declared_struct_member_alignment(ib_type, index);
+ declared_struct_size = (declared_struct_size + alignment - 1) & ~(alignment - 1);
+ if (type_struct_member_array_stride(ib_type, index) > declared_struct_size)
+ return true;
+ }
+
+ // TODO: Another sanity check for matrices. We currently do not support std140 matrices which need to be padded out per column.
+ //if (is_matrix(mbr_type) && mbr_type.vecsize <= 2 && type_struct_member_matrix_stride(ib_type, index) == 16)
+ // SPIRV_CROSS_THROW("Currently cannot support matrices with small vector size in std140 layout.");
+
+ // Only vectors or 3-row matrices need to be packed.
+ if (mbr_type.vecsize == 1 || (is_matrix(mbr_type) && mbr_type.vecsize != 3))
+ return false;
+
+ // Only row-major matrices need to be packed.
+ if (is_matrix(mbr_type) && !has_member_decoration(ib_type.self, index, DecorationRowMajor))
+ return false;
+
+ if (is_array(mbr_type))
+ {
+ // If member is an array, and the array stride is larger than the type needs, don't pack it.
+ // Take into consideration multi-dimentional arrays.
+ uint32_t md_elem_cnt = 1;
+ size_t last_elem_idx = mbr_type.array.size() - 1;
+ for (uint32_t i = 0; i < last_elem_idx; i++)
+ md_elem_cnt *= max(to_array_size_literal(mbr_type, i), 1u);
+
+ uint32_t unpacked_array_stride = unpacked_mbr_size * md_elem_cnt;
+ uint32_t array_stride = type_struct_member_array_stride(ib_type, index);
+ return unpacked_array_stride > array_stride;
+ }
+ else
+ {
+ uint32_t mbr_offset_curr = get_member_decoration(ib_type.self, index, DecorationOffset);
+ // For vectors, pack if the member's offset doesn't conform to the
+ // type's usual alignment. For example, a float3 at offset 4.
+ if (!is_matrix(mbr_type) && (mbr_offset_curr % unpacked_mbr_size))
+ return true;
+ // Pack if there is not enough space between this member and next.
+ // If last member, only pack if it's a row-major matrix.
+ if (index < ib_type.member_types.size() - 1)
+ {
+ uint32_t mbr_offset_next = get_member_decoration(ib_type.self, index + 1, DecorationOffset);
+ return unpacked_mbr_size > mbr_offset_next - mbr_offset_curr;
+ }
+ else
+ return is_matrix(mbr_type);
+ }
+}
+
+// Returns a combination of type ID and member index for use as hash key
+MSLStructMemberKey CompilerMSL::get_struct_member_key(uint32_t type_id, uint32_t index)
+{
+ MSLStructMemberKey k = type_id;
+ k <<= 32;
+ k += index;
+ return k;
+}
+
+void CompilerMSL::emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression)
+{
+ if (!has_extended_decoration(lhs_expression, SPIRVCrossDecorationPacked) ||
+ get_extended_decoration(lhs_expression, SPIRVCrossDecorationPackedType) == 0)
+ {
+ CompilerGLSL::emit_store_statement(lhs_expression, rhs_expression);
+ }
+ else
+ {
+ // Special handling when storing to a float[] or float2[] in std140 layout.
+
+ auto &type = get<SPIRType>(get_extended_decoration(lhs_expression, SPIRVCrossDecorationPackedType));
+ string lhs = to_dereferenced_expression(lhs_expression);
+ string rhs = to_pointer_expression(rhs_expression);
+
+ // Unpack the expression so we can store to it with a float or float2.
+ // It's still an l-value, so it's fine. Most other unpacking of expressions turn them into r-values instead.
+ if (is_scalar(type) && is_array(type))
+ lhs = enclose_expression(lhs) + ".x";
+ else if (is_vector(type) && type.vecsize == 2 && is_array(type))
+ lhs = enclose_expression(lhs) + ".xy";
+
+ if (!optimize_read_modify_write(expression_type(rhs_expression), lhs, rhs))
+ statement(lhs, " = ", rhs, ";");
+ register_write(lhs_expression);
+ }
+}
+
+// Converts the format of the current expression from packed to unpacked,
+// by wrapping the expression in a constructor of the appropriate type.
+string CompilerMSL::unpack_expression_type(string expr_str, const SPIRType &type, uint32_t packed_type_id)
+{
+ const SPIRType *packed_type = nullptr;
+ if (packed_type_id)
+ packed_type = &get<SPIRType>(packed_type_id);
+
+ // float[] and float2[] cases are really just padding, so directly swizzle from the backing float4 instead.
+ if (packed_type && is_array(*packed_type) && is_scalar(*packed_type))
+ return enclose_expression(expr_str) + ".x";
+ else if (packed_type && is_array(*packed_type) && is_vector(*packed_type) && packed_type->vecsize == 2)
+ return enclose_expression(expr_str) + ".xy";
+ else
+ return join(type_to_glsl(type), "(", expr_str, ")");
+}
+
+// Emits the file header info
+void CompilerMSL::emit_header()
+{
+ for (auto &pragma : pragma_lines)
+ statement(pragma);
+
+ if (!pragma_lines.empty())
+ statement("");
+
+ statement("#include <metal_stdlib>");
+ statement("#include <simd/simd.h>");
+
+ for (auto &header : header_lines)
+ statement(header);
+
+ statement("");
+ statement("using namespace metal;");
+ statement("");
+
+ for (auto &td : typedef_lines)
+ statement(td);
+
+ if (!typedef_lines.empty())
+ statement("");
+}
+
+void CompilerMSL::add_pragma_line(const string &line)
+{
+ auto rslt = pragma_lines.insert(line);
+ if (rslt.second)
+ force_recompile = true;
+}
+
+void CompilerMSL::add_typedef_line(const string &line)
+{
+ auto rslt = typedef_lines.insert(line);
+ if (rslt.second)
+ force_recompile = true;
+}
+
+// Emits any needed custom function bodies.
+void CompilerMSL::emit_custom_functions()
+{
+ for (uint32_t i = SPVFuncImplArrayCopyMultidimMax; i >= 2; i--)
+ if (spv_function_implementations.count(static_cast<SPVFuncImpl>(SPVFuncImplArrayCopyMultidimBase + i)))
+ spv_function_implementations.insert(static_cast<SPVFuncImpl>(SPVFuncImplArrayCopyMultidimBase + i - 1));
+
+ for (auto &spv_func : spv_function_implementations)
+ {
+ switch (spv_func)
+ {
+ case SPVFuncImplMod:
+ statement("// Implementation of the GLSL mod() function, which is slightly different than Metal fmod()");
+ statement("template<typename Tx, typename Ty>");
+ statement("Tx mod(Tx x, Ty y)");
+ begin_scope();
+ statement("return x - y * floor(x / y);");
+ end_scope();
+ statement("");
+ break;
+
+ case SPVFuncImplRadians:
+ statement("// Implementation of the GLSL radians() function");
+ statement("template<typename T>");
+ statement("T radians(T d)");
+ begin_scope();
+ statement("return d * T(0.01745329251);");
+ end_scope();
+ statement("");
+ break;
+
+ case SPVFuncImplDegrees:
+ statement("// Implementation of the GLSL degrees() function");
+ statement("template<typename T>");
+ statement("T degrees(T r)");
+ begin_scope();
+ statement("return r * T(57.2957795131);");
+ end_scope();
+ statement("");
+ break;
+
+ case SPVFuncImplFindILsb:
+ statement("// Implementation of the GLSL findLSB() function");
+ statement("template<typename T>");
+ statement("T findLSB(T x)");
+ begin_scope();
+ statement("return select(ctz(x), T(-1), x == T(0));");
+ end_scope();
+ statement("");
+ break;
+
+ case SPVFuncImplFindUMsb:
+ statement("// Implementation of the unsigned GLSL findMSB() function");
+ statement("template<typename T>");
+ statement("T findUMSB(T x)");
+ begin_scope();
+ statement("return select(clz(T(0)) - (clz(x) + T(1)), T(-1), x == T(0));");
+ end_scope();
+ statement("");
+ break;
+
+ case SPVFuncImplFindSMsb:
+ statement("// Implementation of the signed GLSL findMSB() function");
+ statement("template<typename T>");
+ statement("T findSMSB(T x)");
+ begin_scope();
+ statement("T v = select(x, T(-1) - x, x < T(0));");
+ statement("return select(clz(T(0)) - (clz(v) + T(1)), T(-1), v == T(0));");
+ end_scope();
+ statement("");
+ break;
+
+ case SPVFuncImplSSign:
+ statement("// Implementation of the GLSL sign() function for integer types");
+ statement("template<typename T, typename E = typename enable_if<is_integral<T>::value>::type>");
+ statement("T sign(T x)");
+ begin_scope();
+ statement("return select(select(select(x, T(0), x == T(0)), T(1), x > T(0)), T(-1), x < T(0));");
+ end_scope();
+ statement("");
+ break;
+
+ case SPVFuncImplArrayCopy:
+ statement("// Implementation of an array copy function to cover GLSL's ability to copy an array via "
+ "assignment.");
+ statement("template<typename T, uint N>");
+ statement("void spvArrayCopyFromStack1(thread T (&dst)[N], thread const T (&src)[N])");
+ begin_scope();
+ statement("for (uint i = 0; i < N; dst[i] = src[i], i++);");
+ end_scope();
+ statement("");
+
+ statement("template<typename T, uint N>");
+ statement("void spvArrayCopyFromConstant1(thread T (&dst)[N], constant T (&src)[N])");
+ begin_scope();
+ statement("for (uint i = 0; i < N; dst[i] = src[i], i++);");
+ end_scope();
+ statement("");
+ break;
+
+ case SPVFuncImplArrayOfArrayCopy2Dim:
+ case SPVFuncImplArrayOfArrayCopy3Dim:
+ case SPVFuncImplArrayOfArrayCopy4Dim:
+ case SPVFuncImplArrayOfArrayCopy5Dim:
+ case SPVFuncImplArrayOfArrayCopy6Dim:
+ {
+ static const char *function_name_tags[] = {
+ "FromStack",
+ "FromConstant",
+ };
+
+ static const char *src_address_space[] = {
+ "thread const",
+ "constant",
+ };
+
+ for (uint32_t variant = 0; variant < 2; variant++)
+ {
+ uint32_t dimensions = spv_func - SPVFuncImplArrayCopyMultidimBase;
+ string tmp = "template<typename T";
+ for (uint8_t i = 0; i < dimensions; i++)
+ {
+ tmp += ", uint ";
+ tmp += 'A' + i;
+ }
+ tmp += ">";
+ statement(tmp);
+
+ string array_arg;
+ for (uint8_t i = 0; i < dimensions; i++)
+ {
+ array_arg += "[";
+ array_arg += 'A' + i;
+ array_arg += "]";
+ }
+
+ statement("void spvArrayCopy", function_name_tags[variant], dimensions, "(thread T (&dst)", array_arg,
+ ", ", src_address_space[variant], " T (&src)", array_arg, ")");
+
+ begin_scope();
+ statement("for (uint i = 0; i < A; i++)");
+ begin_scope();
+ statement("spvArrayCopy", function_name_tags[variant], dimensions - 1, "(dst[i], src[i]);");
+ end_scope();
+ end_scope();
+ statement("");
+ }
+ break;
+ }
+
+ case SPVFuncImplTexelBufferCoords:
+ {
+ string tex_width_str = convert_to_string(msl_options.texel_buffer_texture_width);
+ statement("// Returns 2D texture coords corresponding to 1D texel buffer coords");
+ statement("uint2 spvTexelBufferCoord(uint tc)");
+ begin_scope();
+ statement(join("return uint2(tc % ", tex_width_str, ", tc / ", tex_width_str, ");"));
+ end_scope();
+ statement("");
+ break;
+ }
+
+ case SPVFuncImplInverse4x4:
+ statement("// Returns the determinant of a 2x2 matrix.");
+ statement("inline float spvDet2x2(float a1, float a2, float b1, float b2)");
+ begin_scope();
+ statement("return a1 * b2 - b1 * a2;");
+ end_scope();
+ statement("");
+
+ statement("// Returns the determinant of a 3x3 matrix.");
+ statement("inline float spvDet3x3(float a1, float a2, float a3, float b1, float b2, float b3, float c1, "
+ "float c2, float c3)");
+ begin_scope();
+ statement("return a1 * spvDet2x2(b2, b3, c2, c3) - b1 * spvDet2x2(a2, a3, c2, c3) + c1 * spvDet2x2(a2, a3, "
+ "b2, b3);");
+ end_scope();
+ statement("");
+ statement("// Returns the inverse of a matrix, by using the algorithm of calculating the classical");
+ statement("// adjoint and dividing by the determinant. The contents of the matrix are changed.");
+ statement("float4x4 spvInverse4x4(float4x4 m)");
+ begin_scope();
+ statement("float4x4 adj; // The adjoint matrix (inverse after dividing by determinant)");
+ statement_no_indent("");
+ statement("// Create the transpose of the cofactors, as the classical adjoint of the matrix.");
+ statement("adj[0][0] = spvDet3x3(m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], "
+ "m[3][3]);");
+ statement("adj[0][1] = -spvDet3x3(m[0][1], m[0][2], m[0][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], "
+ "m[3][3]);");
+ statement("adj[0][2] = spvDet3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[3][1], m[3][2], "
+ "m[3][3]);");
+ statement("adj[0][3] = -spvDet3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], "
+ "m[2][3]);");
+ statement_no_indent("");
+ statement("adj[1][0] = -spvDet3x3(m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], "
+ "m[3][3]);");
+ statement("adj[1][1] = spvDet3x3(m[0][0], m[0][2], m[0][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], "
+ "m[3][3]);");
+ statement("adj[1][2] = -spvDet3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[3][0], m[3][2], "
+ "m[3][3]);");
+ statement("adj[1][3] = spvDet3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], "
+ "m[2][3]);");
+ statement_no_indent("");
+ statement("adj[2][0] = spvDet3x3(m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], "
+ "m[3][3]);");
+ statement("adj[2][1] = -spvDet3x3(m[0][0], m[0][1], m[0][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], "
+ "m[3][3]);");
+ statement("adj[2][2] = spvDet3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[3][0], m[3][1], "
+ "m[3][3]);");
+ statement("adj[2][3] = -spvDet3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], "
+ "m[2][3]);");
+ statement_no_indent("");
+ statement("adj[3][0] = -spvDet3x3(m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], "
+ "m[3][2]);");
+ statement("adj[3][1] = spvDet3x3(m[0][0], m[0][1], m[0][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], "
+ "m[3][2]);");
+ statement("adj[3][2] = -spvDet3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[3][0], m[3][1], "
+ "m[3][2]);");
+ statement("adj[3][3] = spvDet3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], "
+ "m[2][2]);");
+ statement_no_indent("");
+ statement("// Calculate the determinant as a combination of the cofactors of the first row.");
+ statement("float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]) + (adj[0][3] "
+ "* m[3][0]);");
+ statement_no_indent("");
+ statement("// Divide the classical adjoint matrix by the determinant.");
+ statement("// If determinant is zero, matrix is not invertable, so leave it unchanged.");
+ statement("return (det != 0.0f) ? (adj * (1.0f / det)) : m;");
+ end_scope();
+ statement("");
+ break;
+
+ case SPVFuncImplInverse3x3:
+ if (spv_function_implementations.count(SPVFuncImplInverse4x4) == 0)
+ {
+ statement("// Returns the determinant of a 2x2 matrix.");
+ statement("inline float spvDet2x2(float a1, float a2, float b1, float b2)");
+ begin_scope();
+ statement("return a1 * b2 - b1 * a2;");
+ end_scope();
+ statement("");
+ }
+
+ statement("// Returns the inverse of a matrix, by using the algorithm of calculating the classical");
+ statement("// adjoint and dividing by the determinant. The contents of the matrix are changed.");
+ statement("float3x3 spvInverse3x3(float3x3 m)");
+ begin_scope();
+ statement("float3x3 adj; // The adjoint matrix (inverse after dividing by determinant)");
+ statement_no_indent("");
+ statement("// Create the transpose of the cofactors, as the classical adjoint of the matrix.");
+ statement("adj[0][0] = spvDet2x2(m[1][1], m[1][2], m[2][1], m[2][2]);");
+ statement("adj[0][1] = -spvDet2x2(m[0][1], m[0][2], m[2][1], m[2][2]);");
+ statement("adj[0][2] = spvDet2x2(m[0][1], m[0][2], m[1][1], m[1][2]);");
+ statement_no_indent("");
+ statement("adj[1][0] = -spvDet2x2(m[1][0], m[1][2], m[2][0], m[2][2]);");
+ statement("adj[1][1] = spvDet2x2(m[0][0], m[0][2], m[2][0], m[2][2]);");
+ statement("adj[1][2] = -spvDet2x2(m[0][0], m[0][2], m[1][0], m[1][2]);");
+ statement_no_indent("");
+ statement("adj[2][0] = spvDet2x2(m[1][0], m[1][1], m[2][0], m[2][1]);");
+ statement("adj[2][1] = -spvDet2x2(m[0][0], m[0][1], m[2][0], m[2][1]);");
+ statement("adj[2][2] = spvDet2x2(m[0][0], m[0][1], m[1][0], m[1][1]);");
+ statement_no_indent("");
+ statement("// Calculate the determinant as a combination of the cofactors of the first row.");
+ statement("float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]);");
+ statement_no_indent("");
+ statement("// Divide the classical adjoint matrix by the determinant.");
+ statement("// If determinant is zero, matrix is not invertable, so leave it unchanged.");
+ statement("return (det != 0.0f) ? (adj * (1.0f / det)) : m;");
+ end_scope();
+ statement("");
+ break;
+
+ case SPVFuncImplInverse2x2:
+ statement("// Returns the inverse of a matrix, by using the algorithm of calculating the classical");
+ statement("// adjoint and dividing by the determinant. The contents of the matrix are changed.");
+ statement("float2x2 spvInverse2x2(float2x2 m)");
+ begin_scope();
+ statement("float2x2 adj; // The adjoint matrix (inverse after dividing by determinant)");
+ statement_no_indent("");
+ statement("// Create the transpose of the cofactors, as the classical adjoint of the matrix.");
+ statement("adj[0][0] = m[1][1];");
+ statement("adj[0][1] = -m[0][1];");
+ statement_no_indent("");
+ statement("adj[1][0] = -m[1][0];");
+ statement("adj[1][1] = m[0][0];");
+ statement_no_indent("");
+ statement("// Calculate the determinant as a combination of the cofactors of the first row.");
+ statement("float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]);");
+ statement_no_indent("");
+ statement("// Divide the classical adjoint matrix by the determinant.");
+ statement("// If determinant is zero, matrix is not invertable, so leave it unchanged.");
+ statement("return (det != 0.0f) ? (adj * (1.0f / det)) : m;");
+ end_scope();
+ statement("");
+ break;
+
+ case SPVFuncImplRowMajor2x3:
+ statement("// Implementation of a conversion of matrix content from RowMajor to ColumnMajor organization.");
+ statement("float2x3 spvConvertFromRowMajor2x3(float2x3 m)");
+ begin_scope();
+ statement("return float2x3(float3(m[0][0], m[0][2], m[1][1]), float3(m[0][1], m[1][0], m[1][2]));");
+ end_scope();
+ statement("");
+ break;
+
+ case SPVFuncImplRowMajor2x4:
+ statement("// Implementation of a conversion of matrix content from RowMajor to ColumnMajor organization.");
+ statement("float2x4 spvConvertFromRowMajor2x4(float2x4 m)");
+ begin_scope();
+ statement("return float2x4(float4(m[0][0], m[0][2], m[1][0], m[1][2]), float4(m[0][1], m[0][3], m[1][1], "
+ "m[1][3]));");
+ end_scope();
+ statement("");
+ break;
+
+ case SPVFuncImplRowMajor3x2:
+ statement("// Implementation of a conversion of matrix content from RowMajor to ColumnMajor organization.");
+ statement("float3x2 spvConvertFromRowMajor3x2(float3x2 m)");
+ begin_scope();
+ statement("return float3x2(float2(m[0][0], m[1][1]), float2(m[0][1], m[2][0]), float2(m[1][0], m[2][1]));");
+ end_scope();
+ statement("");
+ break;
+
+ case SPVFuncImplRowMajor3x4:
+ statement("// Implementation of a conversion of matrix content from RowMajor to ColumnMajor organization.");
+ statement("float3x4 spvConvertFromRowMajor3x4(float3x4 m)");
+ begin_scope();
+ statement("return float3x4(float4(m[0][0], m[0][3], m[1][2], m[2][1]), float4(m[0][1], m[1][0], m[1][3], "
+ "m[2][2]), float4(m[0][2], m[1][1], m[2][0], m[2][3]));");
+ end_scope();
+ statement("");
+ break;
+
+ case SPVFuncImplRowMajor4x2:
+ statement("// Implementation of a conversion of matrix content from RowMajor to ColumnMajor organization.");
+ statement("float4x2 spvConvertFromRowMajor4x2(float4x2 m)");
+ begin_scope();
+ statement("return float4x2(float2(m[0][0], m[2][0]), float2(m[0][1], m[2][1]), float2(m[1][0], m[3][0]), "
+ "float2(m[1][1], m[3][1]));");
+ end_scope();
+ statement("");
+ break;
+
+ case SPVFuncImplRowMajor4x3:
+ statement("// Implementation of a conversion of matrix content from RowMajor to ColumnMajor organization.");
+ statement("float4x3 spvConvertFromRowMajor4x3(float4x3 m)");
+ begin_scope();
+ statement("return float4x3(float3(m[0][0], m[1][1], m[2][2]), float3(m[0][1], m[1][2], m[3][0]), "
+ "float3(m[0][2], m[2][0], m[3][1]), float3(m[1][0], m[2][1], m[3][2]));");
+ end_scope();
+ statement("");
+ break;
+
+ case SPVFuncImplTextureSwizzle:
+ statement("enum class spvSwizzle : uint");
+ begin_scope();
+ statement("none = 0,");
+ statement("zero,");
+ statement("one,");
+ statement("red,");
+ statement("green,");
+ statement("blue,");
+ statement("alpha");
+ end_scope_decl();
+ statement("");
+ statement("template<typename T> struct spvRemoveReference { typedef T type; };");
+ statement("template<typename T> struct spvRemoveReference<thread T&> { typedef T type; };");
+ statement("template<typename T> struct spvRemoveReference<thread T&&> { typedef T type; };");
+ statement("template<typename T> inline constexpr thread T&& spvForward(thread typename "
+ "spvRemoveReference<T>::type& x)");
+ begin_scope();
+ statement("return static_cast<thread T&&>(x);");
+ end_scope();
+ statement("template<typename T> inline constexpr thread T&& spvForward(thread typename "
+ "spvRemoveReference<T>::type&& x)");
+ begin_scope();
+ statement("return static_cast<thread T&&>(x);");
+ end_scope();
+ statement("");
+ statement("template<typename T>");
+ statement("inline T spvGetSwizzle(vec<T, 4> x, T c, spvSwizzle s)");
+ begin_scope();
+ statement("switch (s)");
+ begin_scope();
+ statement("case spvSwizzle::none:");
+ statement(" return c;");
+ statement("case spvSwizzle::zero:");
+ statement(" return 0;");
+ statement("case spvSwizzle::one:");
+ statement(" return 1;");
+ statement("case spvSwizzle::red:");
+ statement(" return x.r;");
+ statement("case spvSwizzle::green:");
+ statement(" return x.g;");
+ statement("case spvSwizzle::blue:");
+ statement(" return x.b;");
+ statement("case spvSwizzle::alpha:");
+ statement(" return x.a;");
+ end_scope();
+ end_scope();
+ statement("");
+ statement("// Wrapper function that swizzles texture samples and fetches.");
+ statement("template<typename T>");
+ statement("inline vec<T, 4> spvTextureSwizzle(vec<T, 4> x, uint s)");
+ begin_scope();
+ statement("if (!s)");
+ statement(" return x;");
+ statement("return vec<T, 4>(spvGetSwizzle(x, x.r, spvSwizzle((s >> 0) & 0xFF)), "
+ "spvGetSwizzle(x, x.g, spvSwizzle((s >> 8) & 0xFF)), spvGetSwizzle(x, x.b, spvSwizzle((s >> 16) "
+ "& 0xFF)), "
+ "spvGetSwizzle(x, x.a, spvSwizzle((s >> 24) & 0xFF)));");
+ end_scope();
+ statement("");
+ statement("template<typename T>");
+ statement("inline T spvTextureSwizzle(T x, uint s)");
+ begin_scope();
+ statement("return spvTextureSwizzle(vec<T, 4>(x, 0, 0, 1), s).x;");
+ end_scope();
+ statement("");
+ statement("// Wrapper function that swizzles texture gathers.");
+ statement("template<typename T, typename Tex, typename... Ts>");
+ statement(
+ "inline vec<T, 4> spvGatherSwizzle(sampler s, const thread Tex& t, Ts... params, component c, uint sw) "
+ "METAL_CONST_ARG(c)");
+ begin_scope();
+ statement("if (sw)");
+ begin_scope();
+ statement("switch (spvSwizzle((sw >> (uint(c) * 8)) & 0xFF))");
+ begin_scope();
+ statement("case spvSwizzle::none:");
+ statement(" break;");
+ statement("case spvSwizzle::zero:");
+ statement(" return vec<T, 4>(0, 0, 0, 0);");
+ statement("case spvSwizzle::one:");
+ statement(" return vec<T, 4>(1, 1, 1, 1);");
+ statement("case spvSwizzle::red:");
+ statement(" return t.gather(s, spvForward<Ts>(params)..., component::x);");
+ statement("case spvSwizzle::green:");
+ statement(" return t.gather(s, spvForward<Ts>(params)..., component::y);");
+ statement("case spvSwizzle::blue:");
+ statement(" return t.gather(s, spvForward<Ts>(params)..., component::z);");
+ statement("case spvSwizzle::alpha:");
+ statement(" return t.gather(s, spvForward<Ts>(params)..., component::w);");
+ end_scope();
+ end_scope();
+ // texture::gather insists on its component parameter being a constant
+ // expression, so we need this silly workaround just to compile the shader.
+ statement("switch (c)");
+ begin_scope();
+ statement("case component::x:");
+ statement(" return t.gather(s, spvForward<Ts>(params)..., component::x);");
+ statement("case component::y:");
+ statement(" return t.gather(s, spvForward<Ts>(params)..., component::y);");
+ statement("case component::z:");
+ statement(" return t.gather(s, spvForward<Ts>(params)..., component::z);");
+ statement("case component::w:");
+ statement(" return t.gather(s, spvForward<Ts>(params)..., component::w);");
+ end_scope();
+ end_scope();
+ statement("");
+ statement("// Wrapper function that swizzles depth texture gathers.");
+ statement("template<typename T, typename Tex, typename... Ts>");
+ statement(
+ "inline vec<T, 4> spvGatherCompareSwizzle(sampler s, const thread Tex& t, Ts... params, uint sw) ");
+ begin_scope();
+ statement("if (sw)");
+ begin_scope();
+ statement("switch (spvSwizzle(sw & 0xFF))");
+ begin_scope();
+ statement("case spvSwizzle::none:");
+ statement("case spvSwizzle::red:");
+ statement(" break;");
+ statement("case spvSwizzle::zero:");
+ statement("case spvSwizzle::green:");
+ statement("case spvSwizzle::blue:");
+ statement("case spvSwizzle::alpha:");
+ statement(" return vec<T, 4>(0, 0, 0, 0);");
+ statement("case spvSwizzle::one:");
+ statement(" return vec<T, 4>(1, 1, 1, 1);");
+ end_scope();
+ end_scope();
+ statement("return t.gather_compare(s, spvForward<Ts>(params)...);");
+ end_scope();
+ statement("");
+
+ default:
+ break;
+ }
+ }
+}
+
+// Undefined global memory is not allowed in MSL.
+// Declare constant and init to zeros. Use {}, as global constructors can break Metal.
+void CompilerMSL::declare_undefined_values()
+{
+ bool emitted = false;
+ ir.for_each_typed_id<SPIRUndef>([&](uint32_t, SPIRUndef &undef) {
+ auto &type = this->get<SPIRType>(undef.basetype);
+ statement("constant ", variable_decl(type, to_name(undef.self), undef.self), " = {};");
+ emitted = true;
+ });
+
+ if (emitted)
+ statement("");
+}
+
+void CompilerMSL::declare_constant_arrays()
+{
+ // MSL cannot declare arrays inline (except when declaring a variable), so we must move them out to
+ // global constants directly, so we are able to use constants as variable expressions.
+ bool emitted = false;
+
+ ir.for_each_typed_id<SPIRConstant>([&](uint32_t, SPIRConstant &c) {
+ if (c.specialization)
+ return;
+
+ auto &type = this->get<SPIRType>(c.constant_type);
+ if (!type.array.empty())
+ {
+ auto name = to_name(c.self);
+ statement("constant ", variable_decl(type, name), " = ", constant_expression(c), ";");
+ emitted = true;
+ }
+ });
+
+ if (emitted)
+ statement("");
+}
+
+void CompilerMSL::emit_resources()
+{
+ declare_constant_arrays();
+ declare_undefined_values();
+
+ // Emit the special [[stage_in]] and [[stage_out]] interface blocks which we created.
+ emit_interface_block(stage_out_var_id);
+ emit_interface_block(patch_stage_out_var_id);
+ emit_interface_block(stage_in_var_id);
+ emit_interface_block(patch_stage_in_var_id);
+}
+
+// Emit declarations for the specialization Metal function constants
+void CompilerMSL::emit_specialization_constants_and_structs()
+{
+ SpecializationConstant wg_x, wg_y, wg_z;
+ uint32_t workgroup_size_id = get_work_group_size_specialization_constants(wg_x, wg_y, wg_z);
+ bool emitted = false;
+
+ unordered_set<uint32_t> declared_structs;
+
+ for (auto &id_ : ir.ids_for_constant_or_type)
+ {
+ auto &id = ir.ids[id_];
+
+ if (id.get_type() == TypeConstant)
+ {
+ auto &c = id.get<SPIRConstant>();
+
+ if (c.self == workgroup_size_id)
+ {
+ // TODO: This can be expressed as a [[threads_per_threadgroup]] input semantic, but we need to know
+ // the work group size at compile time in SPIR-V, and [[threads_per_threadgroup]] would need to be passed around as a global.
+ // The work group size may be a specialization constant.
+ statement("constant uint3 ", builtin_to_glsl(BuiltInWorkgroupSize, StorageClassWorkgroup), " [[maybe_unused]] = ",
+ constant_expression(get<SPIRConstant>(workgroup_size_id)), ";");
+ emitted = true;
+ }
+ else if (c.specialization)
+ {
+ auto &type = get<SPIRType>(c.constant_type);
+ string sc_type_name = type_to_glsl(type);
+ string sc_name = to_name(c.self);
+ string sc_tmp_name = sc_name + "_tmp";
+
+ // Function constants are only supported in MSL 1.2 and later.
+ // If we don't support it just declare the "default" directly.
+ // This "default" value can be overridden to the true specialization constant by the API user.
+ // Specialization constants which are used as array length expressions cannot be function constants in MSL,
+ // so just fall back to macros.
+ if (msl_options.supports_msl_version(1, 2) && has_decoration(c.self, DecorationSpecId) &&
+ !c.is_used_as_array_length)
+ {
+ uint32_t constant_id = get_decoration(c.self, DecorationSpecId);
+ // Only scalar, non-composite values can be function constants.
+ statement("constant ", sc_type_name, " ", sc_tmp_name, " [[function_constant(", constant_id,
+ ")]];");
+ statement("constant ", sc_type_name, " ", sc_name, " = is_function_constant_defined(", sc_tmp_name,
+ ") ? ", sc_tmp_name, " : ", constant_expression(c), ";");
+ }
+ else if (has_decoration(c.self, DecorationSpecId))
+ {
+ // Fallback to macro overrides.
+ c.specialization_constant_macro_name =
+ constant_value_macro_name(get_decoration(c.self, DecorationSpecId));
+
+ statement("#ifndef ", c.specialization_constant_macro_name);
+ statement("#define ", c.specialization_constant_macro_name, " ", constant_expression(c));
+ statement("#endif");
+ statement("constant ", sc_type_name, " ", sc_name, " = ", c.specialization_constant_macro_name,
+ ";");
+ }
+ else
+ {
+ // Composite specialization constants must be built from other specialization constants.
+ statement("constant ", sc_type_name, " ", sc_name, " = ", constant_expression(c), ";");
+ }
+ emitted = true;
+ }
+ }
+ else if (id.get_type() == TypeConstantOp)
+ {
+ auto &c = id.get<SPIRConstantOp>();
+ auto &type = get<SPIRType>(c.basetype);
+ auto name = to_name(c.self);
+ statement("constant ", variable_decl(type, name), " = ", constant_op_expression(c), ";");
+ emitted = true;
+ }
+ else if (id.get_type() == TypeType)
+ {
+ // Output non-builtin interface structs. These include local function structs
+ // and structs nested within uniform and read-write buffers.
+ auto &type = id.get<SPIRType>();
+ uint32_t type_id = type.self;
+
+ bool is_struct = (type.basetype == SPIRType::Struct) && type.array.empty();
+ bool is_block =
+ has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock);
+
+ bool is_builtin_block = is_block && is_builtin_type(type);
+ bool is_declarable_struct = is_struct && !is_builtin_block;
+
+ // We'll declare this later.
+ if (stage_out_var_id && get_stage_out_struct_type().self == type_id)
+ is_declarable_struct = false;
+ if (patch_stage_out_var_id && get_patch_stage_out_struct_type().self == type_id)
+ is_declarable_struct = false;
+ if (stage_in_var_id && get_stage_in_struct_type().self == type_id)
+ is_declarable_struct = false;
+ if (patch_stage_in_var_id && get_patch_stage_in_struct_type().self == type_id)
+ is_declarable_struct = false;
+
+ // Align and emit declarable structs...but avoid declaring each more than once.
+ if (is_declarable_struct && declared_structs.count(type_id) == 0)
+ {
+ if (emitted)
+ statement("");
+ emitted = false;
+
+ declared_structs.insert(type_id);
+
+ if (has_extended_decoration(type_id, SPIRVCrossDecorationPacked))
+ align_struct(type);
+
+ // Make sure we declare the underlying struct type, and not the "decorated" type with pointers, etc.
+ emit_struct(get<SPIRType>(type_id));
+ }
+ }
+ }
+
+ if (emitted)
+ statement("");
+}
+
+void CompilerMSL::emit_binary_unord_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1,
+ const char *op)
+{
+ bool forward = should_forward(op0) && should_forward(op1);
+ emit_op(result_type, result_id,
+ join("(isunordered(", to_enclosed_unpacked_expression(op0), ", ", to_enclosed_unpacked_expression(op1),
+ ") || ", to_enclosed_unpacked_expression(op0), " ", op, " ", to_enclosed_unpacked_expression(op1),
+ ")"),
+ forward);
+
+ inherit_expression_dependencies(result_id, op0);
+ inherit_expression_dependencies(result_id, op1);
+}
+
+bool CompilerMSL::emit_tessellation_access_chain(const uint32_t *ops, uint32_t length)
+{
+ // If this is a per-vertex output, remap it to the I/O array buffer.
+ auto *var = maybe_get<SPIRVariable>(ops[2]);
+ BuiltIn bi_type = BuiltIn(get_decoration(ops[2], DecorationBuiltIn));
+ if (var &&
+ (var->storage == StorageClassInput ||
+ (get_execution_model() == ExecutionModelTessellationControl && var->storage == StorageClassOutput)) &&
+ !(has_decoration(ops[2], DecorationPatch) || is_patch_block(get_variable_data_type(*var))) &&
+ (!is_builtin_variable(*var) || bi_type == BuiltInPosition || bi_type == BuiltInPointSize ||
+ bi_type == BuiltInClipDistance || bi_type == BuiltInCullDistance ||
+ get_variable_data_type(*var).basetype == SPIRType::Struct))
+ {
+ AccessChainMeta meta;
+ std::vector<uint32_t> indices;
+ uint32_t next_id = ir.increase_bound_by(2);
+
+ indices.reserve(length - 3 + 1);
+ uint32_t type_id = next_id++;
+ SPIRType new_uint_type;
+ new_uint_type.basetype = SPIRType::UInt;
+ new_uint_type.width = 32;
+ set<SPIRType>(type_id, new_uint_type);
+
+ indices.push_back(ops[3]);
+
+ uint32_t const_mbr_id = next_id++;
+ uint32_t index = get_extended_decoration(ops[2], SPIRVCrossDecorationInterfaceMemberIndex);
+ uint32_t ptr = var->storage == StorageClassInput ? stage_in_ptr_var_id : stage_out_ptr_var_id;
+ if (var->storage == StorageClassInput || has_decoration(get_variable_element_type(*var).self, DecorationBlock))
+ {
+ uint32_t i = 4;
+ auto *type = &get_variable_element_type(*var);
+ if (index == uint32_t(-1) && length >= 5)
+ {
+ // Maybe this is a struct type in the input class, in which case
+ // we put it as a decoration on the corresponding member.
+ index = get_extended_member_decoration(ops[2], get_constant(ops[4]).scalar(),
+ SPIRVCrossDecorationInterfaceMemberIndex);
+ assert(index != uint32_t(-1));
+ i++;
+ type = &get<SPIRType>(type->member_types[get_constant(ops[4]).scalar()]);
+ }
+ // In this case, we flattened structures and arrays, so now we have to
+ // combine the following indices. If we encounter a non-constant index,
+ // we're hosed.
+ for (; i < length; ++i)
+ {
+ if (!is_array(*type) && !is_matrix(*type) && type->basetype != SPIRType::Struct)
+ break;
+
+ auto &c = get_constant(ops[i]);
+ index += c.scalar();
+ if (type->parent_type)
+ type = &get<SPIRType>(type->parent_type);
+ else if (type->basetype == SPIRType::Struct)
+ type = &get<SPIRType>(type->member_types[c.scalar()]);
+ }
+ // If the access chain terminates at a composite type, the composite
+ // itself might be copied. In that case, we must unflatten it.
+ if (is_matrix(*type) || is_array(*type) || type->basetype == SPIRType::Struct)
+ {
+ std::string temp_name = join(to_name(var->self), "_", ops[1]);
+ statement(variable_decl(*type, temp_name, var->self), ";");
+ // Set up the initializer for this temporary variable.
+ indices.push_back(const_mbr_id);
+ if (type->basetype == SPIRType::Struct)
+ {
+ for (uint32_t j = 0; j < type->member_types.size(); j++)
+ {
+ index = get_extended_member_decoration(ops[2], j, SPIRVCrossDecorationInterfaceMemberIndex);
+ const auto &mbr_type = get<SPIRType>(type->member_types[j]);
+ if (is_matrix(mbr_type))
+ {
+ for (uint32_t k = 0; k < mbr_type.columns; k++, index++)
+ {
+ set<SPIRConstant>(const_mbr_id, type_id, index, false);
+ auto e = access_chain(ptr, indices.data(), uint32_t(indices.size()), mbr_type, nullptr,
+ true);
+ statement(temp_name, ".", to_member_name(*type, j), "[", k, "] = ", e, ";");
+ }
+ }
+ else if (is_array(mbr_type))
+ {
+ for (uint32_t k = 0; k < mbr_type.array[0]; k++, index++)
+ {
+ set<SPIRConstant>(const_mbr_id, type_id, index, false);
+ auto e = access_chain(ptr, indices.data(), uint32_t(indices.size()), mbr_type, nullptr,
+ true);
+ statement(temp_name, ".", to_member_name(*type, j), "[", k, "] = ", e, ";");
+ }
+ }
+ else
+ {
+ set<SPIRConstant>(const_mbr_id, type_id, index, false);
+ auto e =
+ access_chain(ptr, indices.data(), uint32_t(indices.size()), mbr_type, nullptr, true);
+ statement(temp_name, ".", to_member_name(*type, j), " = ", e, ";");
+ }
+ }
+ }
+ else if (is_matrix(*type))
+ {
+ for (uint32_t j = 0; j < type->columns; j++, index++)
+ {
+ set<SPIRConstant>(const_mbr_id, type_id, index, false);
+ auto e = access_chain(ptr, indices.data(), uint32_t(indices.size()), *type, nullptr, true);
+ statement(temp_name, "[", j, "] = ", e, ";");
+ }
+ }
+ else // Must be an array
+ {
+ assert(is_array(*type));
+ for (uint32_t j = 0; j < type->array[0]; j++, index++)
+ {
+ set<SPIRConstant>(const_mbr_id, type_id, index, false);
+ auto e = access_chain(ptr, indices.data(), uint32_t(indices.size()), *type, nullptr, true);
+ statement(temp_name, "[", j, "] = ", e, ";");
+ }
+ }
+
+ // This needs to be a variable instead of an expression so we don't
+ // try to dereference this as a variable pointer.
+ set<SPIRVariable>(ops[1], ops[0], var->storage);
+ ir.meta[ops[1]] = ir.meta[ops[2]];
+ set_name(ops[1], temp_name);
+ if (has_decoration(var->self, DecorationInvariant))
+ set_decoration(ops[1], DecorationInvariant);
+ for (uint32_t j = 2; j < length; j++)
+ inherit_expression_dependencies(ops[1], ops[j]);
+ return true;
+ }
+ else
+ {
+ set<SPIRConstant>(const_mbr_id, type_id, index, false);
+ indices.push_back(const_mbr_id);
+
+ if (i < length)
+ indices.insert(indices.end(), ops + i, ops + length);
+ }
+ }
+ else
+ {
+ assert(index != uint32_t(-1));
+ set<SPIRConstant>(const_mbr_id, type_id, index, false);
+ indices.push_back(const_mbr_id);
+
+ indices.insert(indices.end(), ops + 4, ops + length);
+ }
+
+ // We use the pointer to the base of the input/output array here,
+ // so this is always a pointer chain.
+ auto e = access_chain(ptr, indices.data(), uint32_t(indices.size()), get<SPIRType>(ops[0]), &meta, true);
+ auto &expr = set<SPIRExpression>(ops[1], move(e), ops[0], should_forward(ops[2]));
+ expr.loaded_from = var->self;
+ expr.need_transpose = meta.need_transpose;
+ expr.access_chain = true;
+
+ // Mark the result as being packed if necessary.
+ if (meta.storage_is_packed)
+ set_extended_decoration(ops[1], SPIRVCrossDecorationPacked);
+ if (meta.storage_packed_type != 0)
+ set_extended_decoration(ops[1], SPIRVCrossDecorationPackedType, meta.storage_packed_type);
+ if (meta.storage_is_invariant)
+ set_decoration(ops[1], DecorationInvariant);
+
+ for (uint32_t i = 2; i < length; i++)
+ {
+ inherit_expression_dependencies(ops[1], ops[i]);
+ add_implied_read_expression(expr, ops[i]);
+ }
+
+ return true;
+ }
+
+ // If this is the inner tessellation level, and we're tessellating triangles,
+ // drop the last index. It isn't an array in this case, so we can't have an
+ // array reference here. We need to make this ID a variable instead of an
+ // expression so we don't try to dereference it as a variable pointer.
+ // Don't do this if the index is a constant 1, though. We need to drop stores
+ // to that one.
+ auto *m = ir.find_meta(var ? var->self : 0);
+ if (get_execution_model() == ExecutionModelTessellationControl && var && m &&
+ m->decoration.builtin_type == BuiltInTessLevelInner && get_entry_point().flags.get(ExecutionModeTriangles))
+ {
+ auto *c = maybe_get<SPIRConstant>(ops[3]);
+ if (c && c->scalar() == 1)
+ return false;
+ auto &dest_var = set<SPIRVariable>(ops[1], *var);
+ dest_var.basetype = ops[0];
+ ir.meta[ops[1]] = ir.meta[ops[2]];
+ inherit_expression_dependencies(ops[1], ops[2]);
+ return true;
+ }
+
+ return false;
+}
+
+bool CompilerMSL::is_out_of_bounds_tessellation_level(uint32_t id_lhs)
+{
+ if (!get_entry_point().flags.get(ExecutionModeTriangles))
+ return false;
+
+ // In SPIR-V, TessLevelInner always has two elements and TessLevelOuter always has
+ // four. This is true even if we are tessellating triangles. This allows clients
+ // to use a single tessellation control shader with multiple tessellation evaluation
+ // shaders.
+ // In Metal, however, only the first element of TessLevelInner and the first three
+ // of TessLevelOuter are accessible. This stems from how in Metal, the tessellation
+ // levels must be stored to a dedicated buffer in a particular format that depends
+ // on the patch type. Therefore, in Triangles mode, any access to the second
+ // inner level or the fourth outer level must be dropped.
+ const auto *e = maybe_get<SPIRExpression>(id_lhs);
+ if (!e || !e->access_chain)
+ return false;
+ BuiltIn builtin = BuiltIn(get_decoration(e->loaded_from, DecorationBuiltIn));
+ if (builtin != BuiltInTessLevelInner && builtin != BuiltInTessLevelOuter)
+ return false;
+ auto *c = maybe_get<SPIRConstant>(e->implied_read_expressions[1]);
+ if (!c)
+ return false;
+ return (builtin == BuiltInTessLevelInner && c->scalar() == 1) ||
+ (builtin == BuiltInTessLevelOuter && c->scalar() == 3);
+}
+
+// Override for MSL-specific syntax instructions
+void CompilerMSL::emit_instruction(const Instruction &instruction)
+{
+#define MSL_BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op)
+#define MSL_BOP_CAST(op, type) \
+ emit_binary_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, opcode_is_sign_invariant(opcode))
+#define MSL_UOP(op) emit_unary_op(ops[0], ops[1], ops[2], #op)
+#define MSL_QFOP(op) emit_quaternary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], #op)
+#define MSL_TFOP(op) emit_trinary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], #op)
+#define MSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op)
+#define MSL_BFOP_CAST(op, type) \
+ emit_binary_func_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, opcode_is_sign_invariant(opcode))
+#define MSL_UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op)
+#define MSL_UNORD_BOP(op) emit_binary_unord_op(ops[0], ops[1], ops[2], ops[3], #op)
+
+ auto ops = stream(instruction);
+ auto opcode = static_cast<Op>(instruction.op);
+
+ // If we need to do implicit bitcasts, make sure we do it with the correct type.
+ uint32_t integer_width = get_integer_width_for_instruction(instruction);
+ auto int_type = to_signed_basetype(integer_width);
+ auto uint_type = to_unsigned_basetype(integer_width);
+
+ switch (opcode)
+ {
+
+ // Comparisons
+ case OpIEqual:
+ MSL_BOP_CAST(==, int_type);
+ break;
+
+ case OpLogicalEqual:
+ case OpFOrdEqual:
+ MSL_BOP(==);
+ break;
+
+ case OpINotEqual:
+ MSL_BOP_CAST(!=, int_type);
+ break;
+
+ case OpLogicalNotEqual:
+ case OpFOrdNotEqual:
+ MSL_BOP(!=);
+ break;
+
+ case OpUGreaterThan:
+ MSL_BOP_CAST(>, uint_type);
+ break;
+
+ case OpSGreaterThan:
+ MSL_BOP_CAST(>, int_type);
+ break;
+
+ case OpFOrdGreaterThan:
+ MSL_BOP(>);
+ break;
+
+ case OpUGreaterThanEqual:
+ MSL_BOP_CAST(>=, uint_type);
+ break;
+
+ case OpSGreaterThanEqual:
+ MSL_BOP_CAST(>=, int_type);
+ break;
+
+ case OpFOrdGreaterThanEqual:
+ MSL_BOP(>=);
+ break;
+
+ case OpULessThan:
+ MSL_BOP_CAST(<, uint_type);
+ break;
+
+ case OpSLessThan:
+ MSL_BOP_CAST(<, int_type);
+ break;
+
+ case OpFOrdLessThan:
+ MSL_BOP(<);
+ break;
+
+ case OpULessThanEqual:
+ MSL_BOP_CAST(<=, uint_type);
+ break;
+
+ case OpSLessThanEqual:
+ MSL_BOP_CAST(<=, int_type);
+ break;
+
+ case OpFOrdLessThanEqual:
+ MSL_BOP(<=);
+ break;
+
+ case OpFUnordEqual:
+ MSL_UNORD_BOP(==);
+ break;
+
+ case OpFUnordNotEqual:
+ MSL_UNORD_BOP(!=);
+ break;
+
+ case OpFUnordGreaterThan:
+ MSL_UNORD_BOP(>);
+ break;
+
+ case OpFUnordGreaterThanEqual:
+ MSL_UNORD_BOP(>=);
+ break;
+
+ case OpFUnordLessThan:
+ MSL_UNORD_BOP(<);
+ break;
+
+ case OpFUnordLessThanEqual:
+ MSL_UNORD_BOP(<=);
+ break;
+
+ // Derivatives
+ case OpDPdx:
+ case OpDPdxFine:
+ case OpDPdxCoarse:
+ MSL_UFOP(dfdx);
+ register_control_dependent_expression(ops[1]);
+ break;
+
+ case OpDPdy:
+ case OpDPdyFine:
+ case OpDPdyCoarse:
+ MSL_UFOP(dfdy);
+ register_control_dependent_expression(ops[1]);
+ break;
+
+ case OpFwidth:
+ case OpFwidthCoarse:
+ case OpFwidthFine:
+ MSL_UFOP(fwidth);
+ register_control_dependent_expression(ops[1]);
+ break;
+
+ // Bitfield
+ case OpBitFieldInsert:
+ MSL_QFOP(insert_bits);
+ break;
+
+ case OpBitFieldSExtract:
+ case OpBitFieldUExtract:
+ MSL_TFOP(extract_bits);
+ break;
+
+ case OpBitReverse:
+ MSL_UFOP(reverse_bits);
+ break;
+
+ case OpBitCount:
+ MSL_UFOP(popcount);
+ break;
+
+ case OpFRem:
+ MSL_BFOP(fmod);
+ break;
+
+ // Atomics
+ case OpAtomicExchange:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ uint32_t ptr = ops[2];
+ uint32_t mem_sem = ops[4];
+ uint32_t val = ops[5];
+ emit_atomic_func_op(result_type, id, "atomic_exchange_explicit", mem_sem, mem_sem, false, ptr, val);
+ break;
+ }
+
+ case OpAtomicCompareExchange:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ uint32_t ptr = ops[2];
+ uint32_t mem_sem_pass = ops[4];
+ uint32_t mem_sem_fail = ops[5];
+ uint32_t val = ops[6];
+ uint32_t comp = ops[7];
+ emit_atomic_func_op(result_type, id, "atomic_compare_exchange_weak_explicit", mem_sem_pass, mem_sem_fail, true,
+ ptr, comp, true, false, val);
+ break;
+ }
+
+ case OpAtomicCompareExchangeWeak:
+ SPIRV_CROSS_THROW("OpAtomicCompareExchangeWeak is only supported in kernel profile.");
+
+ case OpAtomicLoad:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ uint32_t ptr = ops[2];
+ uint32_t mem_sem = ops[4];
+ emit_atomic_func_op(result_type, id, "atomic_load_explicit", mem_sem, mem_sem, false, ptr, 0);
+ break;
+ }
+
+ case OpAtomicStore:
+ {
+ uint32_t result_type = expression_type(ops[0]).self;
+ uint32_t id = ops[0];
+ uint32_t ptr = ops[0];
+ uint32_t mem_sem = ops[2];
+ uint32_t val = ops[3];
+ emit_atomic_func_op(result_type, id, "atomic_store_explicit", mem_sem, mem_sem, false, ptr, val);
+ break;
+ }
+
+#define MSL_AFMO_IMPL(op, valsrc, valconst) \
+ do \
+ { \
+ uint32_t result_type = ops[0]; \
+ uint32_t id = ops[1]; \
+ uint32_t ptr = ops[2]; \
+ uint32_t mem_sem = ops[4]; \
+ uint32_t val = valsrc; \
+ emit_atomic_func_op(result_type, id, "atomic_fetch_" #op "_explicit", mem_sem, mem_sem, false, ptr, val, \
+ false, valconst); \
+ } while (false)
+
+#define MSL_AFMO(op) MSL_AFMO_IMPL(op, ops[5], false)
+#define MSL_AFMIO(op) MSL_AFMO_IMPL(op, 1, true)
+
+ case OpAtomicIIncrement:
+ MSL_AFMIO(add);
+ break;
+
+ case OpAtomicIDecrement:
+ MSL_AFMIO(sub);
+ break;
+
+ case OpAtomicIAdd:
+ MSL_AFMO(add);
+ break;
+
+ case OpAtomicISub:
+ MSL_AFMO(sub);
+ break;
+
+ case OpAtomicSMin:
+ case OpAtomicUMin:
+ MSL_AFMO(min);
+ break;
+
+ case OpAtomicSMax:
+ case OpAtomicUMax:
+ MSL_AFMO(max);
+ break;
+
+ case OpAtomicAnd:
+ MSL_AFMO(and);
+ break;
+
+ case OpAtomicOr:
+ MSL_AFMO(or);
+ break;
+
+ case OpAtomicXor:
+ MSL_AFMO(xor);
+ break;
+
+ // Images
+
+ // Reads == Fetches in Metal
+ case OpImageRead:
+ {
+ // Mark that this shader reads from this image
+ uint32_t img_id = ops[2];
+ auto &type = expression_type(img_id);
+ if (type.image.dim != DimSubpassData)
+ {
+ auto *p_var = maybe_get_backing_variable(img_id);
+ if (p_var && has_decoration(p_var->self, DecorationNonReadable))
+ {
+ unset_decoration(p_var->self, DecorationNonReadable);
+ force_recompile = true;
+ }
+ }
+
+ emit_texture_op(instruction);
+ break;
+ }
+
+ case OpImageWrite:
+ {
+ uint32_t img_id = ops[0];
+ uint32_t coord_id = ops[1];
+ uint32_t texel_id = ops[2];
+ const uint32_t *opt = &ops[3];
+ uint32_t length = instruction.length - 3;
+
+ // Bypass pointers because we need the real image struct
+ auto &type = expression_type(img_id);
+ auto &img_type = get<SPIRType>(type.self);
+
+ // Ensure this image has been marked as being written to and force a
+ // recommpile so that the image type output will include write access
+ auto *p_var = maybe_get_backing_variable(img_id);
+ if (p_var && has_decoration(p_var->self, DecorationNonWritable))
+ {
+ unset_decoration(p_var->self, DecorationNonWritable);
+ force_recompile = true;
+ }
+
+ bool forward = false;
+ uint32_t bias = 0;
+ uint32_t lod = 0;
+ uint32_t flags = 0;
+
+ if (length)
+ {
+ flags = *opt++;
+ length--;
+ }
+
+ auto test = [&](uint32_t &v, uint32_t flag) {
+ if (length && (flags & flag))
+ {
+ v = *opt++;
+ length--;
+ }
+ };
+
+ test(bias, ImageOperandsBiasMask);
+ test(lod, ImageOperandsLodMask);
+
+ auto &texel_type = expression_type(texel_id);
+ auto store_type = texel_type;
+ store_type.vecsize = 4;
+
+ statement(join(
+ to_expression(img_id), ".write(", remap_swizzle(store_type, texel_type.vecsize, to_expression(texel_id)),
+ ", ",
+ to_function_args(img_id, img_type, true, false, false, coord_id, 0, 0, 0, 0, lod, 0, 0, 0, 0, 0, &forward),
+ ");"));
+
+ if (p_var && variable_storage_is_aliased(*p_var))
+ flush_all_aliased_variables();
+
+ break;
+ }
+
+ case OpImageQuerySize:
+ case OpImageQuerySizeLod:
+ {
+ uint32_t rslt_type_id = ops[0];
+ auto &rslt_type = get<SPIRType>(rslt_type_id);
+
+ uint32_t id = ops[1];
+
+ uint32_t img_id = ops[2];
+ string img_exp = to_expression(img_id);
+ auto &img_type = expression_type(img_id);
+ Dim img_dim = img_type.image.dim;
+ bool img_is_array = img_type.image.arrayed;
+
+ if (img_type.basetype != SPIRType::Image)
+ SPIRV_CROSS_THROW("Invalid type for OpImageQuerySize.");
+
+ string lod;
+ if (opcode == OpImageQuerySizeLod)
+ {
+ // LOD index defaults to zero, so don't bother outputing level zero index
+ string decl_lod = to_expression(ops[3]);
+ if (decl_lod != "0")
+ lod = decl_lod;
+ }
+
+ string expr = type_to_glsl(rslt_type) + "(";
+ expr += img_exp + ".get_width(" + lod + ")";
+
+ if (img_dim == Dim2D || img_dim == DimCube || img_dim == Dim3D)
+ expr += ", " + img_exp + ".get_height(" + lod + ")";
+
+ if (img_dim == Dim3D)
+ expr += ", " + img_exp + ".get_depth(" + lod + ")";
+
+ if (img_is_array)
+ expr += ", " + img_exp + ".get_array_size()";
+
+ expr += ")";
+
+ emit_op(rslt_type_id, id, expr, should_forward(img_id));
+
+ break;
+ }
+
+ case OpImageQueryLod:
+ SPIRV_CROSS_THROW("MSL does not support textureQueryLod().");
+
+#define MSL_ImgQry(qrytype) \
+ do \
+ { \
+ uint32_t rslt_type_id = ops[0]; \
+ auto &rslt_type = get<SPIRType>(rslt_type_id); \
+ uint32_t id = ops[1]; \
+ uint32_t img_id = ops[2]; \
+ string img_exp = to_expression(img_id); \
+ string expr = type_to_glsl(rslt_type) + "(" + img_exp + ".get_num_" #qrytype "())"; \
+ emit_op(rslt_type_id, id, expr, should_forward(img_id)); \
+ } while (false)
+
+ case OpImageQueryLevels:
+ MSL_ImgQry(mip_levels);
+ break;
+
+ case OpImageQuerySamples:
+ MSL_ImgQry(samples);
+ break;
+
+ case OpImage:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ auto *combined = maybe_get<SPIRCombinedImageSampler>(ops[2]);
+
+ if (combined)
+ {
+ auto &e = emit_op(result_type, id, to_expression(combined->image), true, true);
+ auto *var = maybe_get_backing_variable(combined->image);
+ if (var)
+ e.loaded_from = var->self;
+ }
+ else
+ {
+ auto &e = emit_op(result_type, id, to_expression(ops[2]), true, true);
+ auto *var = maybe_get_backing_variable(ops[2]);
+ if (var)
+ e.loaded_from = var->self;
+ }
+ break;
+ }
+
+ // Casting
+ case OpQuantizeToF16:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ uint32_t arg = ops[2];
+
+ string exp;
+ auto &type = get<SPIRType>(result_type);
+
+ switch (type.vecsize)
+ {
+ case 1:
+ exp = join("float(half(", to_expression(arg), "))");
+ break;
+ case 2:
+ exp = join("float2(half2(", to_expression(arg), "))");
+ break;
+ case 3:
+ exp = join("float3(half3(", to_expression(arg), "))");
+ break;
+ case 4:
+ exp = join("float4(half4(", to_expression(arg), "))");
+ break;
+ default:
+ SPIRV_CROSS_THROW("Illegal argument to OpQuantizeToF16.");
+ }
+
+ emit_op(result_type, id, exp, should_forward(arg));
+ break;
+ }
+
+ case OpInBoundsAccessChain:
+ case OpAccessChain:
+ case OpPtrAccessChain:
+ if (is_tessellation_shader())
+ {
+ if (!emit_tessellation_access_chain(ops, instruction.length))
+ CompilerGLSL::emit_instruction(instruction);
+ }
+ else
+ CompilerGLSL::emit_instruction(instruction);
+ break;
+
+ case OpStore:
+ if (is_out_of_bounds_tessellation_level(ops[0]))
+ break;
+
+ if (maybe_emit_array_assignment(ops[0], ops[1]))
+ break;
+
+ CompilerGLSL::emit_instruction(instruction);
+ break;
+
+ // Compute barriers
+ case OpMemoryBarrier:
+ emit_barrier(0, ops[0], ops[1]);
+ break;
+
+ case OpControlBarrier:
+ // In GLSL a memory barrier is often followed by a control barrier.
+ // But in MSL, memory barriers are also control barriers, so don't
+ // emit a simple control barrier if a memory barrier has just been emitted.
+ if (previous_instruction_opcode != OpMemoryBarrier)
+ emit_barrier(ops[0], ops[1], ops[2]);
+ break;
+
+ case OpVectorTimesMatrix:
+ case OpMatrixTimesVector:
+ {
+ // If the matrix needs transpose and it is square or packed, just flip the multiply order.
+ uint32_t mtx_id = ops[opcode == OpMatrixTimesVector ? 2 : 3];
+ auto *e = maybe_get<SPIRExpression>(mtx_id);
+ auto &t = expression_type(mtx_id);
+ bool is_packed = has_extended_decoration(mtx_id, SPIRVCrossDecorationPacked);
+ if (e && e->need_transpose && (t.columns == t.vecsize || is_packed))
+ {
+ e->need_transpose = false;
+
+ // This is important for matrices. Packed matrices
+ // are generally transposed, so unpacking using a constructor argument
+ // will result in an error.
+ // The simplest solution for now is to just avoid unpacking the matrix in this operation.
+ unset_extended_decoration(mtx_id, SPIRVCrossDecorationPacked);
+
+ emit_binary_op(ops[0], ops[1], ops[3], ops[2], "*");
+ if (is_packed)
+ set_extended_decoration(mtx_id, SPIRVCrossDecorationPacked);
+ e->need_transpose = true;
+ }
+ else
+ MSL_BOP(*);
+ break;
+ }
+
+ // OpOuterProduct
+
+ case OpIAddCarry:
+ case OpISubBorrow:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t result_id = ops[1];
+ uint32_t op0 = ops[2];
+ uint32_t op1 = ops[3];
+ forced_temporaries.insert(result_id);
+ auto &type = get<SPIRType>(result_type);
+ statement(variable_decl(type, to_name(result_id)), ";");
+ set<SPIRExpression>(result_id, to_name(result_id), result_type, true);
+
+ auto &res_type = get<SPIRType>(type.member_types[1]);
+ if (opcode == OpIAddCarry)
+ {
+ statement(to_expression(result_id), ".", to_member_name(type, 0), " = ", to_enclosed_expression(op0), " + ",
+ to_enclosed_expression(op1), ";");
+ statement(to_expression(result_id), ".", to_member_name(type, 1), " = select(", type_to_glsl(res_type),
+ "(1), ", type_to_glsl(res_type), "(0), ", to_expression(result_id), ".", to_member_name(type, 0),
+ " >= max(", to_expression(op0), ", ", to_expression(op1), "));");
+ }
+ else
+ {
+ statement(to_expression(result_id), ".", to_member_name(type, 0), " = ", to_enclosed_expression(op0), " - ",
+ to_enclosed_expression(op1), ";");
+ statement(to_expression(result_id), ".", to_member_name(type, 1), " = select(", type_to_glsl(res_type),
+ "(1), ", type_to_glsl(res_type), "(0), ", to_enclosed_expression(op0),
+ " >= ", to_enclosed_expression(op1), ");");
+ }
+ break;
+ }
+
+ case OpUMulExtended:
+ case OpSMulExtended:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t result_id = ops[1];
+ uint32_t op0 = ops[2];
+ uint32_t op1 = ops[3];
+ forced_temporaries.insert(result_id);
+ auto &type = get<SPIRType>(result_type);
+ statement(variable_decl(type, to_name(result_id)), ";");
+ set<SPIRExpression>(result_id, to_name(result_id), result_type, true);
+
+ statement(to_expression(result_id), ".", to_member_name(type, 0), " = ", to_enclosed_expression(op0), " * ",
+ to_enclosed_expression(op1), ";");
+ statement(to_expression(result_id), ".", to_member_name(type, 1), " = mulhi(", to_expression(op0), ", ",
+ to_expression(op1), ");");
+ break;
+ }
+
+ default:
+ CompilerGLSL::emit_instruction(instruction);
+ break;
+ }
+
+ previous_instruction_opcode = opcode;
+}
+
+void CompilerMSL::emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uint32_t id_mem_sem)
+{
+ if (get_execution_model() != ExecutionModelGLCompute && get_execution_model() != ExecutionModelTessellationControl)
+ return;
+
+ string bar_stmt = "threadgroup_barrier(mem_flags::";
+
+ uint32_t mem_sem = id_mem_sem ? get<SPIRConstant>(id_mem_sem).scalar() : uint32_t(MemorySemanticsMaskNone);
+
+ if (get_execution_model() == ExecutionModelTessellationControl)
+ // For tesc shaders, this also affects objects in the Output storage class.
+ // Since in Metal, these are placed in a device buffer, we have to sync device memory here.
+ bar_stmt += "mem_device";
+ else if (mem_sem & MemorySemanticsCrossWorkgroupMemoryMask)
+ bar_stmt += "mem_device";
+ else if (mem_sem & (MemorySemanticsSubgroupMemoryMask | MemorySemanticsWorkgroupMemoryMask |
+ MemorySemanticsAtomicCounterMemoryMask))
+ bar_stmt += "mem_threadgroup";
+ else if (mem_sem & MemorySemanticsImageMemoryMask)
+ bar_stmt += "mem_texture";
+ else
+ bar_stmt += "mem_none";
+
+ if (msl_options.is_ios() && (msl_options.supports_msl_version(2) && !msl_options.supports_msl_version(2, 1)))
+ {
+ bar_stmt += ", ";
+
+ // Use the wider of the two scopes (smaller value)
+ uint32_t exe_scope = id_exe_scope ? get<SPIRConstant>(id_exe_scope).scalar() : uint32_t(ScopeInvocation);
+ uint32_t mem_scope = id_mem_scope ? get<SPIRConstant>(id_mem_scope).scalar() : uint32_t(ScopeInvocation);
+ uint32_t scope = min(exe_scope, mem_scope);
+ switch (scope)
+ {
+ case ScopeCrossDevice:
+ case ScopeDevice:
+ bar_stmt += "memory_scope_device";
+ break;
+
+ case ScopeSubgroup:
+ case ScopeInvocation:
+ bar_stmt += "memory_scope_simdgroup";
+ break;
+
+ case ScopeWorkgroup:
+ default:
+ bar_stmt += "memory_scope_threadgroup";
+ break;
+ }
+ }
+
+ bar_stmt += ");";
+
+ statement(bar_stmt);
+
+ assert(current_emitting_block);
+ flush_control_dependent_expressions(current_emitting_block->self);
+ flush_all_active_variables();
+}
+
+void CompilerMSL::emit_array_copy(const string &lhs, uint32_t rhs_id)
+{
+ // Assignment from an array initializer is fine.
+ auto &type = expression_type(rhs_id);
+ auto *var = maybe_get_backing_variable(rhs_id);
+
+ // Unfortunately, we cannot template on address space in MSL,
+ // so explicit address space redirection it is ...
+ bool is_constant = false;
+ if (ir.ids[rhs_id].get_type() == TypeConstant)
+ {
+ is_constant = true;
+ }
+ else if (var && var->remapped_variable && var->statically_assigned &&
+ ir.ids[var->static_expression].get_type() == TypeConstant)
+ {
+ is_constant = true;
+ }
+
+ const char *tag = is_constant ? "FromConstant" : "FromStack";
+ statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ", ", to_expression(rhs_id), ");");
+}
+
+// Since MSL does not allow arrays to be copied via simple variable assignment,
+// if the LHS and RHS represent an assignment of an entire array, it must be
+// implemented by calling an array copy function.
+// Returns whether the struct assignment was emitted.
+bool CompilerMSL::maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs)
+{
+ // We only care about assignments of an entire array
+ auto &type = expression_type(id_rhs);
+ if (type.array.size() == 0)
+ return false;
+
+ auto *var = maybe_get<SPIRVariable>(id_lhs);
+
+ // Is this a remapped, static constant? Don't do anything.
+ if (var && var->remapped_variable && var->statically_assigned)
+ return true;
+
+ if (ir.ids[id_rhs].get_type() == TypeConstant && var && var->deferred_declaration)
+ {
+ // Special case, if we end up declaring a variable when assigning the constant array,
+ // we can avoid the copy by directly assigning the constant expression.
+ // This is likely necessary to be able to use a variable as a true look-up table, as it is unlikely
+ // the compiler will be able to optimize the spvArrayCopy() into a constant LUT.
+ // After a variable has been declared, we can no longer assign constant arrays in MSL unfortunately.
+ statement(to_expression(id_lhs), " = ", constant_expression(get<SPIRConstant>(id_rhs)), ";");
+ return true;
+ }
+
+ // Ensure the LHS variable has been declared
+ auto *p_v_lhs = maybe_get_backing_variable(id_lhs);
+ if (p_v_lhs)
+ flush_variable_declaration(p_v_lhs->self);
+
+ emit_array_copy(to_expression(id_lhs), id_rhs);
+ register_write(id_lhs);
+
+ return true;
+}
+
+// Emits one of the atomic functions. In MSL, the atomic functions operate on pointers
+void CompilerMSL::emit_atomic_func_op(uint32_t result_type, uint32_t result_id, const char *op, uint32_t mem_order_1,
+ uint32_t mem_order_2, bool has_mem_order_2, uint32_t obj, uint32_t op1,
+ bool op1_is_pointer, bool op1_is_literal, uint32_t op2)
+{
+ forced_temporaries.insert(result_id);
+
+ string exp = string(op) + "(";
+
+ auto &type = get_pointee_type(expression_type(obj));
+ exp += "(volatile ";
+ auto *var = maybe_get_backing_variable(obj);
+ if (!var)
+ SPIRV_CROSS_THROW("No backing variable for atomic operation.");
+ exp += get_argument_address_space(*var);
+ exp += " atomic_";
+ exp += type_to_glsl(type);
+ exp += "*)";
+
+ exp += "&";
+ exp += to_enclosed_expression(obj);
+
+ bool is_atomic_compare_exchange_strong = op1_is_pointer && op1;
+
+ if (is_atomic_compare_exchange_strong)
+ {
+ assert(strcmp(op, "atomic_compare_exchange_weak_explicit") == 0);
+ assert(op2);
+ assert(has_mem_order_2);
+ exp += ", &";
+ exp += to_name(result_id);
+ exp += ", ";
+ exp += to_expression(op2);
+ exp += ", ";
+ exp += get_memory_order(mem_order_1);
+ exp += ", ";
+ exp += get_memory_order(mem_order_2);
+ exp += ")";
+
+ // MSL only supports the weak atomic compare exchange,
+ // so emit a CAS loop here.
+ statement(variable_decl(type, to_name(result_id)), ";");
+ statement("do");
+ begin_scope();
+ statement(to_name(result_id), " = ", to_expression(op1), ";");
+ end_scope_decl(join("while (!", exp, ")"));
+ set<SPIRExpression>(result_id, to_name(result_id), result_type, true);
+ }
+ else
+ {
+ assert(strcmp(op, "atomic_compare_exchange_weak_explicit") != 0);
+ if (op1)
+ {
+ if (op1_is_literal)
+ exp += join(", ", op1);
+ else
+ exp += ", " + to_expression(op1);
+ }
+ if (op2)
+ exp += ", " + to_expression(op2);
+
+ exp += string(", ") + get_memory_order(mem_order_1);
+ if (has_mem_order_2)
+ exp += string(", ") + get_memory_order(mem_order_2);
+
+ exp += ")";
+ emit_op(result_type, result_id, exp, false);
+ }
+
+ flush_all_atomic_capable_variables();
+}
+
+// Metal only supports relaxed memory order for now
+const char *CompilerMSL::get_memory_order(uint32_t)
+{
+ return "memory_order_relaxed";
+}
+
+// Override for MSL-specific extension syntax instructions
+void CompilerMSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop, const uint32_t *args, uint32_t count)
+{
+ auto op = static_cast<GLSLstd450>(eop);
+
+ // If we need to do implicit bitcasts, make sure we do it with the correct type.
+ uint32_t integer_width = get_integer_width_for_glsl_instruction(op, args, count);
+ auto int_type = to_signed_basetype(integer_width);
+ auto uint_type = to_unsigned_basetype(integer_width);
+
+ switch (op)
+ {
+ case GLSLstd450Atan2:
+ emit_binary_func_op(result_type, id, args[0], args[1], "atan2");
+ break;
+ case GLSLstd450InverseSqrt:
+ emit_unary_func_op(result_type, id, args[0], "rsqrt");
+ break;
+ case GLSLstd450RoundEven:
+ emit_unary_func_op(result_type, id, args[0], "rint");
+ break;
+
+ case GLSLstd450FindSMsb:
+ emit_unary_func_op_cast(result_type, id, args[0], "findSMSB", int_type, int_type);
+ break;
+
+ case GLSLstd450FindUMsb:
+ emit_unary_func_op_cast(result_type, id, args[0], "findUMSB", uint_type, uint_type);
+ break;
+
+ case GLSLstd450PackSnorm4x8:
+ emit_unary_func_op(result_type, id, args[0], "pack_float_to_snorm4x8");
+ break;
+ case GLSLstd450PackUnorm4x8:
+ emit_unary_func_op(result_type, id, args[0], "pack_float_to_unorm4x8");
+ break;
+ case GLSLstd450PackSnorm2x16:
+ emit_unary_func_op(result_type, id, args[0], "pack_float_to_snorm2x16");
+ break;
+ case GLSLstd450PackUnorm2x16:
+ emit_unary_func_op(result_type, id, args[0], "pack_float_to_unorm2x16");
+ break;
+
+ case GLSLstd450PackHalf2x16:
+ {
+ auto expr = join("as_type<uint>(half2(", to_expression(args[0]), "))");
+ emit_op(result_type, id, expr, should_forward(args[0]));
+ inherit_expression_dependencies(id, args[0]);
+ break;
+ }
+
+ case GLSLstd450UnpackSnorm4x8:
+ emit_unary_func_op(result_type, id, args[0], "unpack_snorm4x8_to_float");
+ break;
+ case GLSLstd450UnpackUnorm4x8:
+ emit_unary_func_op(result_type, id, args[0], "unpack_unorm4x8_to_float");
+ break;
+ case GLSLstd450UnpackSnorm2x16:
+ emit_unary_func_op(result_type, id, args[0], "unpack_snorm2x16_to_float");
+ break;
+ case GLSLstd450UnpackUnorm2x16:
+ emit_unary_func_op(result_type, id, args[0], "unpack_unorm2x16_to_float");
+ break;
+
+ case GLSLstd450UnpackHalf2x16:
+ {
+ auto expr = join("float2(as_type<half2>(", to_expression(args[0]), "))");
+ emit_op(result_type, id, expr, should_forward(args[0]));
+ inherit_expression_dependencies(id, args[0]);
+ break;
+ }
+
+ case GLSLstd450PackDouble2x32:
+ emit_unary_func_op(result_type, id, args[0], "unsupported_GLSLstd450PackDouble2x32"); // Currently unsupported
+ break;
+ case GLSLstd450UnpackDouble2x32:
+ emit_unary_func_op(result_type, id, args[0], "unsupported_GLSLstd450UnpackDouble2x32"); // Currently unsupported
+ break;
+
+ case GLSLstd450MatrixInverse:
+ {
+ auto &mat_type = get<SPIRType>(result_type);
+ switch (mat_type.columns)
+ {
+ case 2:
+ emit_unary_func_op(result_type, id, args[0], "spvInverse2x2");
+ break;
+ case 3:
+ emit_unary_func_op(result_type, id, args[0], "spvInverse3x3");
+ break;
+ case 4:
+ emit_unary_func_op(result_type, id, args[0], "spvInverse4x4");
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ case GLSLstd450FMin:
+ // If the result type isn't float, don't bother calling the specific
+ // precise::/fast:: version. Metal doesn't have those for half and
+ // double types.
+ if (get<SPIRType>(result_type).basetype != SPIRType::Float)
+ emit_binary_func_op(result_type, id, args[0], args[1], "min");
+ else
+ emit_binary_func_op(result_type, id, args[0], args[1], "fast::min");
+ break;
+
+ case GLSLstd450FMax:
+ if (get<SPIRType>(result_type).basetype != SPIRType::Float)
+ emit_binary_func_op(result_type, id, args[0], args[1], "max");
+ else
+ emit_binary_func_op(result_type, id, args[0], args[1], "fast::max");
+ break;
+
+ case GLSLstd450FClamp:
+ // TODO: If args[1] is 0 and args[2] is 1, emit a saturate() call.
+ if (get<SPIRType>(result_type).basetype != SPIRType::Float)
+ emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "clamp");
+ else
+ emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "fast::clamp");
+ break;
+
+ case GLSLstd450NMin:
+ if (get<SPIRType>(result_type).basetype != SPIRType::Float)
+ emit_binary_func_op(result_type, id, args[0], args[1], "min");
+ else
+ emit_binary_func_op(result_type, id, args[0], args[1], "precise::min");
+ break;
+
+ case GLSLstd450NMax:
+ if (get<SPIRType>(result_type).basetype != SPIRType::Float)
+ emit_binary_func_op(result_type, id, args[0], args[1], "max");
+ else
+ emit_binary_func_op(result_type, id, args[0], args[1], "precise::max");
+ break;
+
+ case GLSLstd450NClamp:
+ // TODO: If args[1] is 0 and args[2] is 1, emit a saturate() call.
+ if (get<SPIRType>(result_type).basetype != SPIRType::Float)
+ emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "clamp");
+ else
+ emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "precise::clamp");
+ break;
+
+ // TODO:
+ // GLSLstd450InterpolateAtCentroid (centroid_no_perspective qualifier)
+ // GLSLstd450InterpolateAtSample (sample_no_perspective qualifier)
+ // GLSLstd450InterpolateAtOffset
+
+ default:
+ CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count);
+ break;
+ }
+}
+
+// Emit a structure declaration for the specified interface variable.
+void CompilerMSL::emit_interface_block(uint32_t ib_var_id)
+{
+ if (ib_var_id)
+ {
+ auto &ib_var = get<SPIRVariable>(ib_var_id);
+ auto &ib_type = get_variable_data_type(ib_var);
+ assert(ib_type.basetype == SPIRType::Struct && !ib_type.member_types.empty());
+ emit_struct(ib_type);
+ }
+}
+
+// Emits the declaration signature of the specified function.
+// If this is the entry point function, Metal-specific return value and function arguments are added.
+void CompilerMSL::emit_function_prototype(SPIRFunction &func, const Bitset &)
+{
+ if (func.self != ir.default_entry_point)
+ add_function_overload(func);
+
+ local_variable_names = resource_names;
+ string decl;
+
+ processing_entry_point = (func.self == ir.default_entry_point);
+
+ auto &type = get<SPIRType>(func.return_type);
+
+ if (type.array.empty())
+ {
+ decl += func_type_decl(type);
+ }
+ else
+ {
+ // We cannot return arrays in MSL, so "return" through an out variable.
+ decl = "void";
+ }
+
+ decl += " ";
+ decl += to_name(func.self);
+ decl += "(";
+
+ if (!type.array.empty())
+ {
+ // Fake arrays returns by writing to an out array instead.
+ decl += "thread ";
+ decl += type_to_glsl(type);
+ decl += " (&SPIRV_Cross_return_value)";
+ decl += type_to_array_glsl(type);
+ if (!func.arguments.empty())
+ decl += ", ";
+ }
+
+ if (processing_entry_point)
+ {
+ if (msl_options.argument_buffers)
+ decl += entry_point_args_argument_buffer(!func.arguments.empty());
+ else
+ decl += entry_point_args_classic(!func.arguments.empty());
+
+ // If entry point function has variables that require early declaration,
+ // ensure they each have an empty initializer, creating one if needed.
+ // This is done at this late stage because the initialization expression
+ // is cleared after each compilation pass.
+ for (auto var_id : vars_needing_early_declaration)
+ {
+ auto &ed_var = get<SPIRVariable>(var_id);
+ uint32_t &initializer = ed_var.initializer;
+ if (!initializer)
+ initializer = ir.increase_bound_by(1);
+
+ // Do not override proper initializers.
+ if (ir.ids[initializer].get_type() == TypeNone || ir.ids[initializer].get_type() == TypeExpression)
+ set<SPIRExpression>(ed_var.initializer, "{}", ed_var.basetype, true);
+ }
+ }
+
+ for (auto &arg : func.arguments)
+ {
+ uint32_t name_id = arg.id;
+
+ auto *var = maybe_get<SPIRVariable>(arg.id);
+ if (var)
+ {
+ // If we need to modify the name of the variable, make sure we modify the original variable.
+ // Our alias is just a shadow variable.
+ if (arg.alias_global_variable && var->basevariable)
+ name_id = var->basevariable;
+
+ var->parameter = &arg; // Hold a pointer to the parameter so we can invalidate the readonly field if needed.
+ }
+
+ add_local_variable_name(name_id);
+
+ decl += argument_decl(arg);
+
+ // Manufacture automatic sampler arg for SampledImage texture
+ auto &arg_type = get<SPIRType>(arg.type);
+ if (arg_type.basetype == SPIRType::SampledImage && arg_type.image.dim != DimBuffer)
+ decl += join(", thread const ", sampler_type(arg_type), " ", to_sampler_expression(arg.id));
+
+ // Manufacture automatic swizzle arg.
+ if (msl_options.swizzle_texture_samples && has_sampled_images && is_sampled_image_type(arg_type))
+ decl += join(", constant uint32_t& ", to_swizzle_expression(arg.id));
+
+ if (&arg != &func.arguments.back())
+ decl += ", ";
+ }
+
+ decl += ")";
+ statement(decl);
+}
+
+// Returns the texture sampling function string for the specified image and sampling characteristics.
+string CompilerMSL::to_function_name(uint32_t img, const SPIRType &imgtype, bool is_fetch, bool is_gather, bool, bool,
+ bool has_offset, bool, bool has_dref, uint32_t)
+{
+ // Special-case gather. We have to alter the component being looked up
+ // in the swizzle case.
+ if (msl_options.swizzle_texture_samples && is_gather)
+ {
+ string fname = imgtype.image.depth ? "spvGatherCompareSwizzle" : "spvGatherSwizzle";
+ fname += "<" + type_to_glsl(get<SPIRType>(imgtype.image.type)) + ", metal::" + type_to_glsl(imgtype);
+ // Add the arg types ourselves. Yes, this sucks, but Clang can't
+ // deduce template pack parameters in the middle of an argument list.
+ switch (imgtype.image.dim)
+ {
+ case Dim2D:
+ fname += ", float2";
+ if (imgtype.image.arrayed)
+ fname += ", uint";
+ if (imgtype.image.depth)
+ fname += ", float";
+ if (!imgtype.image.depth || has_offset)
+ fname += ", int2";
+ break;
+ case DimCube:
+ fname += ", float3";
+ if (imgtype.image.arrayed)
+ fname += ", uint";
+ if (imgtype.image.depth)
+ fname += ", float";
+ break;
+ default:
+ SPIRV_CROSS_THROW("Invalid texture dimension for gather op.");
+ }
+ fname += ">";
+ return fname;
+ }
+
+ auto *combined = maybe_get<SPIRCombinedImageSampler>(img);
+
+ // Texture reference
+ string fname = to_expression(combined ? combined->image : img) + ".";
+ if (msl_options.swizzle_texture_samples && !is_gather && is_sampled_image_type(imgtype))
+ fname = "spvTextureSwizzle(" + fname;
+
+ // Texture function and sampler
+ if (is_fetch)
+ fname += "read";
+ else if (is_gather)
+ fname += "gather";
+ else
+ fname += "sample";
+
+ if (has_dref)
+ fname += "_compare";
+
+ return fname;
+}
+
+// Returns the function args for a texture sampling function for the specified image and sampling characteristics.
+string CompilerMSL::to_function_args(uint32_t img, const SPIRType &imgtype, bool is_fetch, bool is_gather, bool is_proj,
+ uint32_t coord, uint32_t, uint32_t dref, uint32_t grad_x, uint32_t grad_y,
+ uint32_t lod, uint32_t coffset, uint32_t offset, uint32_t bias, uint32_t comp,
+ uint32_t sample, bool *p_forward)
+{
+ string farg_str;
+ if (!is_fetch)
+ farg_str += to_sampler_expression(img);
+
+ if (msl_options.swizzle_texture_samples && is_gather)
+ {
+ if (!farg_str.empty())
+ farg_str += ", ";
+
+ auto *combined = maybe_get<SPIRCombinedImageSampler>(img);
+ farg_str += to_expression(combined ? combined->image : img);
+ }
+
+ // Texture coordinates
+ bool forward = should_forward(coord);
+ auto coord_expr = to_enclosed_expression(coord);
+ auto &coord_type = expression_type(coord);
+ bool coord_is_fp = type_is_floating_point(coord_type);
+ bool is_cube_fetch = false;
+
+ string tex_coords = coord_expr;
+ uint32_t alt_coord_component = 0;
+
+ switch (imgtype.image.dim)
+ {
+
+ case Dim1D:
+ if (coord_type.vecsize > 1)
+ tex_coords = enclose_expression(tex_coords) + ".x";
+
+ if (is_fetch)
+ tex_coords = "uint(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ")";
+
+ alt_coord_component = 1;
+ break;
+
+ case DimBuffer:
+ if (coord_type.vecsize > 1)
+ tex_coords = enclose_expression(tex_coords) + ".x";
+
+ // Metal texel buffer textures are 2D, so convert 1D coord to 2D.
+ if (is_fetch)
+ tex_coords = "spvTexelBufferCoord(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ")";
+
+ alt_coord_component = 1;
+ break;
+
+ case DimSubpassData:
+ if (imgtype.image.ms)
+ tex_coords = "uint2(gl_FragCoord.xy)";
+ else
+ tex_coords = join("uint2(gl_FragCoord.xy), 0");
+ break;
+
+ case Dim2D:
+ if (coord_type.vecsize > 2)
+ tex_coords = enclose_expression(tex_coords) + ".xy";
+
+ if (is_fetch)
+ tex_coords = "uint2(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ")";
+
+ alt_coord_component = 2;
+ break;
+
+ case Dim3D:
+ if (coord_type.vecsize > 3)
+ tex_coords = enclose_expression(tex_coords) + ".xyz";
+
+ if (is_fetch)
+ tex_coords = "uint3(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ")";
+
+ alt_coord_component = 3;
+ break;
+
+ case DimCube:
+ if (is_fetch)
+ {
+ is_cube_fetch = true;
+ tex_coords += ".xy";
+ tex_coords = "uint2(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ")";
+ }
+ else
+ {
+ if (coord_type.vecsize > 3)
+ tex_coords = enclose_expression(tex_coords) + ".xyz";
+ }
+
+ alt_coord_component = 3;
+ break;
+
+ default:
+ break;
+ }
+
+ if (is_fetch && offset)
+ {
+ // Fetch offsets must be applied directly to the coordinate.
+ forward = forward && should_forward(offset);
+ auto &type = expression_type(offset);
+ if (type.basetype != SPIRType::UInt)
+ tex_coords += " + " + bitcast_expression(SPIRType::UInt, offset);
+ else
+ tex_coords += " + " + to_enclosed_expression(offset);
+ }
+ else if (is_fetch && coffset)
+ {
+ // Fetch offsets must be applied directly to the coordinate.
+ forward = forward && should_forward(coffset);
+ auto &type = expression_type(coffset);
+ if (type.basetype != SPIRType::UInt)
+ tex_coords += " + " + bitcast_expression(SPIRType::UInt, coffset);
+ else
+ tex_coords += " + " + to_enclosed_expression(coffset);
+ }
+
+ // If projection, use alt coord as divisor
+ if (is_proj)
+ tex_coords += " / " + to_extract_component_expression(coord, alt_coord_component);
+
+ if (!farg_str.empty())
+ farg_str += ", ";
+ farg_str += tex_coords;
+
+ // If fetch from cube, add face explicitly
+ if (is_cube_fetch)
+ {
+ // Special case for cube arrays, face and layer are packed in one dimension.
+ if (imgtype.image.arrayed)
+ farg_str += ", uint(" + to_extract_component_expression(coord, 2) + ") % 6u";
+ else
+ farg_str += ", uint(" + round_fp_tex_coords(to_extract_component_expression(coord, 2), coord_is_fp) + ")";
+ }
+
+ // If array, use alt coord
+ if (imgtype.image.arrayed)
+ {
+ // Special case for cube arrays, face and layer are packed in one dimension.
+ if (imgtype.image.dim == DimCube && is_fetch)
+ farg_str += ", uint(" + to_extract_component_expression(coord, 2) + ") / 6u";
+ else
+ farg_str += ", uint(" +
+ round_fp_tex_coords(to_extract_component_expression(coord, alt_coord_component), coord_is_fp) +
+ ")";
+ }
+
+ // Depth compare reference value
+ if (dref)
+ {
+ forward = forward && should_forward(dref);
+ farg_str += ", ";
+
+ if (is_proj)
+ farg_str +=
+ to_enclosed_expression(dref) + " / " + to_extract_component_expression(coord, alt_coord_component);
+ else
+ farg_str += to_expression(dref);
+
+ if (msl_options.is_macos() && (grad_x || grad_y))
+ {
+ // For sample compare, MSL does not support gradient2d for all targets (only iOS apparently according to docs).
+ // However, the most common case here is to have a constant gradient of 0, as that is the only way to express
+ // LOD == 0 in GLSL with sampler2DArrayShadow (cascaded shadow mapping).
+ // We will detect a compile-time constant 0 value for gradient and promote that to level(0) on MSL.
+ bool constant_zero_x = !grad_x || expression_is_constant_null(grad_x);
+ bool constant_zero_y = !grad_y || expression_is_constant_null(grad_y);
+ if (constant_zero_x && constant_zero_y)
+ {
+ lod = 0;
+ grad_x = 0;
+ grad_y = 0;
+ farg_str += ", level(0)";
+ }
+ else
+ {
+ SPIRV_CROSS_THROW("Using non-constant 0.0 gradient() qualifier for sample_compare. This is not "
+ "supported in MSL macOS.");
+ }
+ }
+
+ if (msl_options.is_macos() && bias)
+ {
+ // Bias is not supported either on macOS with sample_compare.
+ // Verify it is compile-time zero, and drop the argument.
+ if (expression_is_constant_null(bias))
+ {
+ bias = 0;
+ }
+ else
+ {
+ SPIRV_CROSS_THROW(
+ "Using non-constant 0.0 bias() qualifier for sample_compare. This is not supported in MSL macOS.");
+ }
+ }
+ }
+
+ // LOD Options
+ // Metal does not support LOD for 1D textures.
+ if (bias && imgtype.image.dim != Dim1D)
+ {
+ forward = forward && should_forward(bias);
+ farg_str += ", bias(" + to_expression(bias) + ")";
+ }
+
+ // Metal does not support LOD for 1D textures.
+ if (lod && imgtype.image.dim != Dim1D)
+ {
+ forward = forward && should_forward(lod);
+ if (is_fetch)
+ {
+ farg_str += ", " + to_expression(lod);
+ }
+ else
+ {
+ farg_str += ", level(" + to_expression(lod) + ")";
+ }
+ }
+ else if (is_fetch && !lod && imgtype.image.dim != Dim1D && imgtype.image.dim != DimBuffer && !imgtype.image.ms &&
+ imgtype.image.sampled != 2)
+ {
+ // Lod argument is optional in OpImageFetch, but we require a LOD value, pick 0 as the default.
+ // Check for sampled type as well, because is_fetch is also used for OpImageRead in MSL.
+ farg_str += ", 0";
+ }
+
+ // Metal does not support LOD for 1D textures.
+ if ((grad_x || grad_y) && imgtype.image.dim != Dim1D)
+ {
+ forward = forward && should_forward(grad_x);
+ forward = forward && should_forward(grad_y);
+ string grad_opt;
+ switch (imgtype.image.dim)
+ {
+ case Dim2D:
+ grad_opt = "2d";
+ break;
+ case Dim3D:
+ grad_opt = "3d";
+ break;
+ case DimCube:
+ grad_opt = "cube";
+ break;
+ default:
+ grad_opt = "unsupported_gradient_dimension";
+ break;
+ }
+ farg_str += ", gradient" + grad_opt + "(" + to_expression(grad_x) + ", " + to_expression(grad_y) + ")";
+ }
+
+ // Add offsets
+ string offset_expr;
+ if (coffset && !is_fetch)
+ {
+ forward = forward && should_forward(coffset);
+ offset_expr = to_expression(coffset);
+ }
+ else if (offset && !is_fetch)
+ {
+ forward = forward && should_forward(offset);
+ offset_expr = to_expression(offset);
+ }
+
+ if (!offset_expr.empty())
+ {
+ switch (imgtype.image.dim)
+ {
+ case Dim2D:
+ if (coord_type.vecsize > 2)
+ offset_expr = enclose_expression(offset_expr) + ".xy";
+
+ farg_str += ", " + offset_expr;
+ break;
+
+ case Dim3D:
+ if (coord_type.vecsize > 3)
+ offset_expr = enclose_expression(offset_expr) + ".xyz";
+
+ farg_str += ", " + offset_expr;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (comp)
+ {
+ // If 2D has gather component, ensure it also has an offset arg
+ if (imgtype.image.dim == Dim2D && offset_expr.empty())
+ farg_str += ", int2(0)";
+
+ forward = forward && should_forward(comp);
+ farg_str += ", " + to_component_argument(comp);
+ }
+
+ if (sample)
+ {
+ farg_str += ", ";
+ farg_str += to_expression(sample);
+ }
+
+ if (msl_options.swizzle_texture_samples && is_sampled_image_type(imgtype))
+ {
+ // Add the swizzle constant from the swizzle buffer.
+ if (!is_gather)
+ farg_str += ")";
+ farg_str += ", " + to_swizzle_expression(img);
+ used_aux_buffer = true;
+ }
+
+ *p_forward = forward;
+
+ return farg_str;
+}
+
+// If the texture coordinates are floating point, invokes MSL round() function to round them.
+string CompilerMSL::round_fp_tex_coords(string tex_coords, bool coord_is_fp)
+{
+ return coord_is_fp ? ("round(" + tex_coords + ")") : tex_coords;
+}
+
+// Returns a string to use in an image sampling function argument.
+// The ID must be a scalar constant.
+string CompilerMSL::to_component_argument(uint32_t id)
+{
+ if (ir.ids[id].get_type() != TypeConstant)
+ {
+ SPIRV_CROSS_THROW("ID " + to_string(id) + " is not an OpConstant.");
+ return "component::x";
+ }
+
+ uint32_t component_index = get<SPIRConstant>(id).scalar();
+ switch (component_index)
+ {
+ case 0:
+ return "component::x";
+ case 1:
+ return "component::y";
+ case 2:
+ return "component::z";
+ case 3:
+ return "component::w";
+
+ default:
+ SPIRV_CROSS_THROW("The value (" + to_string(component_index) + ") of OpConstant ID " + to_string(id) +
+ " is not a valid Component index, which must be one of 0, 1, 2, or 3.");
+ return "component::x";
+ }
+}
+
+// Establish sampled image as expression object and assign the sampler to it.
+void CompilerMSL::emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id)
+{
+ set<SPIRCombinedImageSampler>(result_id, result_type, image_id, samp_id);
+}
+
+// Returns a string representation of the ID, usable as a function arg.
+// Manufacture automatic sampler arg for SampledImage texture.
+string CompilerMSL::to_func_call_arg(uint32_t id)
+{
+ string arg_str;
+
+ auto *c = maybe_get<SPIRConstant>(id);
+ if (c && !get<SPIRType>(c->constant_type).array.empty())
+ {
+ // If we are passing a constant array directly to a function for some reason,
+ // the callee will expect an argument in thread const address space
+ // (since we can only bind to arrays with references in MSL).
+ // To resolve this, we must emit a copy in this address space.
+ // This kind of code gen should be rare enough that performance is not a real concern.
+ // Inline the SPIR-V to avoid this kind of suboptimal codegen.
+ //
+ // We risk calling this inside a continue block (invalid code),
+ // so just create a thread local copy in the current function.
+ arg_str = join("_", id, "_array_copy");
+ auto &constants = current_function->constant_arrays_needed_on_stack;
+ auto itr = find(begin(constants), end(constants), id);
+ if (itr == end(constants))
+ {
+ force_recompile = true;
+ constants.push_back(id);
+ }
+ }
+ else
+ arg_str = CompilerGLSL::to_func_call_arg(id);
+
+ // Manufacture automatic sampler arg if the arg is a SampledImage texture.
+ auto &type = expression_type(id);
+ if (type.basetype == SPIRType::SampledImage && type.image.dim != DimBuffer)
+ {
+ // Need to check the base variable in case we need to apply a qualified alias.
+ uint32_t var_id = 0;
+ auto *sampler_var = maybe_get<SPIRVariable>(id);
+ if (sampler_var)
+ var_id = sampler_var->basevariable;
+
+ arg_str += ", " + to_sampler_expression(var_id ? var_id : id);
+ }
+ if (msl_options.swizzle_texture_samples && has_sampled_images && is_sampled_image_type(type))
+ arg_str += ", " + to_swizzle_expression(id);
+
+ return arg_str;
+}
+
+// If the ID represents a sampled image that has been assigned a sampler already,
+// generate an expression for the sampler, otherwise generate a fake sampler name
+// by appending a suffix to the expression constructed from the ID.
+string CompilerMSL::to_sampler_expression(uint32_t id)
+{
+ auto *combined = maybe_get<SPIRCombinedImageSampler>(id);
+ auto expr = to_expression(combined ? combined->image : id);
+ auto index = expr.find_first_of('[');
+
+ uint32_t samp_id = 0;
+ if (combined)
+ samp_id = combined->sampler;
+
+ if (index == string::npos)
+ return samp_id ? to_expression(samp_id) : expr + sampler_name_suffix;
+ else
+ {
+ auto image_expr = expr.substr(0, index);
+ auto array_expr = expr.substr(index);
+ return samp_id ? to_expression(samp_id) : (image_expr + sampler_name_suffix + array_expr);
+ }
+}
+
+string CompilerMSL::to_swizzle_expression(uint32_t id)
+{
+ auto *combined = maybe_get<SPIRCombinedImageSampler>(id);
+ auto expr = to_expression(combined ? combined->image : id);
+ auto index = expr.find_first_of('[');
+
+ if (index == string::npos)
+ return expr + swizzle_name_suffix;
+ else
+ {
+ auto image_expr = expr.substr(0, index);
+ auto array_expr = expr.substr(index);
+ return image_expr + swizzle_name_suffix + array_expr;
+ }
+}
+
+// Checks whether the type is a Block all of whose members have DecorationPatch.
+bool CompilerMSL::is_patch_block(const SPIRType &type)
+{
+ if (!has_decoration(type.self, DecorationBlock))
+ return false;
+
+ for (uint32_t i = 0; i < type.member_types.size(); i++)
+ {
+ if (!has_member_decoration(type.self, i, DecorationPatch))
+ return false;
+ }
+
+ return true;
+}
+
+// Checks whether the ID is a row_major matrix that requires conversion before use
+bool CompilerMSL::is_non_native_row_major_matrix(uint32_t id)
+{
+ // Natively supported row-major matrices do not need to be converted.
+ if (backend.native_row_major_matrix)
+ return false;
+
+ // Non-matrix or column-major matrix types do not need to be converted.
+ if (!has_decoration(id, DecorationRowMajor))
+ return false;
+
+ // Generate a function that will swap matrix elements from row-major to column-major.
+ // Packed row-matrix should just use transpose() function.
+ if (!has_extended_decoration(id, SPIRVCrossDecorationPacked))
+ {
+ const auto type = expression_type(id);
+ add_convert_row_major_matrix_function(type.columns, type.vecsize);
+ }
+
+ return true;
+}
+
+// Checks whether the member is a row_major matrix that requires conversion before use
+bool CompilerMSL::member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index)
+{
+ // Natively supported row-major matrices do not need to be converted.
+ if (backend.native_row_major_matrix)
+ return false;
+
+ // Non-matrix or column-major matrix types do not need to be converted.
+ if (!has_member_decoration(type.self, index, DecorationRowMajor))
+ return false;
+
+ // Generate a function that will swap matrix elements from row-major to column-major.
+ // Packed row-matrix should just use transpose() function.
+ if (!has_extended_member_decoration(type.self, index, SPIRVCrossDecorationPacked))
+ {
+ const auto mbr_type = get<SPIRType>(type.member_types[index]);
+ add_convert_row_major_matrix_function(mbr_type.columns, mbr_type.vecsize);
+ }
+
+ return true;
+}
+
+// Adds a function suitable for converting a non-square row-major matrix to a column-major matrix.
+void CompilerMSL::add_convert_row_major_matrix_function(uint32_t cols, uint32_t rows)
+{
+ SPVFuncImpl spv_func;
+ if (cols == rows) // Square matrix...just use transpose() function
+ return;
+ else if (cols == 2 && rows == 3)
+ spv_func = SPVFuncImplRowMajor2x3;
+ else if (cols == 2 && rows == 4)
+ spv_func = SPVFuncImplRowMajor2x4;
+ else if (cols == 3 && rows == 2)
+ spv_func = SPVFuncImplRowMajor3x2;
+ else if (cols == 3 && rows == 4)
+ spv_func = SPVFuncImplRowMajor3x4;
+ else if (cols == 4 && rows == 2)
+ spv_func = SPVFuncImplRowMajor4x2;
+ else if (cols == 4 && rows == 3)
+ spv_func = SPVFuncImplRowMajor4x3;
+ else
+ SPIRV_CROSS_THROW("Could not convert row-major matrix.");
+
+ auto rslt = spv_function_implementations.insert(spv_func);
+ if (rslt.second)
+ {
+ add_pragma_line("#pragma clang diagnostic ignored \"-Wmissing-prototypes\"");
+ force_recompile = true;
+ }
+}
+
+// Wraps the expression string in a function call that converts the
+// row_major matrix result of the expression to a column_major matrix.
+string CompilerMSL::convert_row_major_matrix(string exp_str, const SPIRType &exp_type, bool is_packed)
+{
+ strip_enclosed_expression(exp_str);
+
+ string func_name;
+
+ // Square and packed matrices can just use transpose
+ if (exp_type.columns == exp_type.vecsize || is_packed)
+ func_name = "transpose";
+ else
+ func_name = string("spvConvertFromRowMajor") + to_string(exp_type.columns) + "x" + to_string(exp_type.vecsize);
+
+ return join(func_name, "(", exp_str, ")");
+}
+
+// Called automatically at the end of the entry point function
+void CompilerMSL::emit_fixup()
+{
+ if ((get_execution_model() == ExecutionModelVertex ||
+ get_execution_model() == ExecutionModelTessellationEvaluation) &&
+ stage_out_var_id && !qual_pos_var_name.empty() && !capture_output_to_buffer)
+ {
+ if (options.vertex.fixup_clipspace)
+ statement(qual_pos_var_name, ".z = (", qual_pos_var_name, ".z + ", qual_pos_var_name,
+ ".w) * 0.5; // Adjust clip-space for Metal");
+
+ if (options.vertex.flip_vert_y)
+ statement(qual_pos_var_name, ".y = -(", qual_pos_var_name, ".y);", " // Invert Y-axis for Metal");
+ }
+}
+
+// Return a string defining a structure member, with padding and packing.
+string CompilerMSL::to_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index,
+ const string &qualifier)
+{
+ auto &membertype = get<SPIRType>(member_type_id);
+
+ // If this member requires padding to maintain alignment, emit a dummy padding member.
+ MSLStructMemberKey key = get_struct_member_key(type.self, index);
+ uint32_t pad_len = struct_member_padding[key];
+ if (pad_len > 0)
+ statement("char _m", index, "_pad", "[", to_string(pad_len), "];");
+
+ // If this member is packed, mark it as so.
+ string pack_pfx = "";
+
+ const SPIRType *effective_membertype = &membertype;
+ SPIRType override_type;
+
+ uint32_t orig_id = 0;
+ if (has_extended_member_decoration(type.self, index, SPIRVCrossDecorationInterfaceOrigID))
+ orig_id = get_extended_member_decoration(type.self, index, SPIRVCrossDecorationInterfaceOrigID);
+
+ if (member_is_packed_type(type, index))
+ {
+ // If we're packing a matrix, output an appropriate typedef
+ if (membertype.basetype == SPIRType::Struct)
+ {
+ pack_pfx = "/* FIXME: A padded struct is needed here. If you see this message, file a bug! */ ";
+ }
+ else if (membertype.vecsize > 1 && membertype.columns > 1)
+ {
+ pack_pfx = "packed_";
+ string base_type = membertype.width == 16 ? "half" : "float";
+ string td_line = "typedef ";
+ td_line += base_type + to_string(membertype.vecsize) + "x" + to_string(membertype.columns);
+ td_line += " " + pack_pfx;
+ td_line += base_type + to_string(membertype.columns) + "x" + to_string(membertype.vecsize);
+ td_line += ";";
+ add_typedef_line(td_line);
+ }
+ else if (is_array(membertype) && membertype.vecsize <= 2 && membertype.basetype != SPIRType::Struct)
+ {
+ // A "packed" float array, but we pad here instead to 4-vector.
+ override_type = membertype;
+ override_type.vecsize = 4;
+ effective_membertype = &override_type;
+ }
+ else
+ pack_pfx = "packed_";
+ }
+
+ // Very specifically, image load-store in argument buffers are disallowed on MSL on iOS.
+ if (msl_options.is_ios() && membertype.basetype == SPIRType::Image && membertype.image.sampled == 2)
+ {
+ if (!has_decoration(orig_id, DecorationNonWritable))
+ SPIRV_CROSS_THROW("Writable images are not allowed in argument buffers on iOS.");
+ }
+
+ // Array information is baked into these types.
+ string array_type;
+ if (membertype.basetype != SPIRType::Image && membertype.basetype != SPIRType::Sampler &&
+ membertype.basetype != SPIRType::SampledImage)
+ {
+ array_type = type_to_array_glsl(membertype);
+ }
+
+ return join(pack_pfx, type_to_glsl(*effective_membertype, orig_id), " ", qualifier, to_member_name(type, index),
+ member_attribute_qualifier(type, index), array_type, ";");
+}
+
+// Emit a structure member, padding and packing to maintain the correct memeber alignments.
+void CompilerMSL::emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index,
+ const string &qualifier, uint32_t)
+{
+ statement(to_struct_member(type, member_type_id, index, qualifier));
+}
+
+// Return a MSL qualifier for the specified function attribute member
+string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t index)
+{
+ auto &execution = get_entry_point();
+
+ uint32_t mbr_type_id = type.member_types[index];
+ auto &mbr_type = get<SPIRType>(mbr_type_id);
+
+ BuiltIn builtin = BuiltInMax;
+ bool is_builtin = is_member_builtin(type, index, &builtin);
+
+ if (has_extended_member_decoration(type.self, index, SPIRVCrossDecorationArgumentBufferID))
+ return join(" [[id(", get_extended_member_decoration(type.self, index, SPIRVCrossDecorationArgumentBufferID),
+ ")]]");
+
+ // Vertex function inputs
+ if (execution.model == ExecutionModelVertex && type.storage == StorageClassInput)
+ {
+ if (is_builtin)
+ {
+ switch (builtin)
+ {
+ case BuiltInVertexId:
+ case BuiltInVertexIndex:
+ case BuiltInBaseVertex:
+ case BuiltInInstanceId:
+ case BuiltInInstanceIndex:
+ case BuiltInBaseInstance:
+ return string(" [[") + builtin_qualifier(builtin) + "]]";
+
+ case BuiltInDrawIndex:
+ SPIRV_CROSS_THROW("DrawIndex is not supported in MSL.");
+
+ default:
+ return "";
+ }
+ }
+ uint32_t locn = get_ordered_member_location(type.self, index);
+ if (locn != k_unknown_location)
+ return string(" [[attribute(") + convert_to_string(locn) + ")]]";
+ }
+
+ // Vertex and tessellation evaluation function outputs
+ if ((execution.model == ExecutionModelVertex || execution.model == ExecutionModelTessellationEvaluation) &&
+ type.storage == StorageClassOutput)
+ {
+ if (is_builtin)
+ {
+ switch (builtin)
+ {
+ case BuiltInPointSize:
+ // Only mark the PointSize builtin if really rendering points.
+ // Some shaders may include a PointSize builtin even when used to render
+ // non-point topologies, and Metal will reject this builtin when compiling
+ // the shader into a render pipeline that uses a non-point topology.
+ return msl_options.enable_point_size_builtin ? (string(" [[") + builtin_qualifier(builtin) + "]]") : "";
+
+ case BuiltInViewportIndex:
+ if (!msl_options.supports_msl_version(2, 0))
+ SPIRV_CROSS_THROW("ViewportIndex requires Metal 2.0.");
+ /* fallthrough */
+ case BuiltInPosition:
+ case BuiltInLayer:
+ case BuiltInClipDistance:
+ return string(" [[") + builtin_qualifier(builtin) + "]]" + (mbr_type.array.empty() ? "" : " ");
+
+ default:
+ return "";
+ }
+ }
+ uint32_t comp;
+ uint32_t locn = get_ordered_member_location(type.self, index, &comp);
+ if (locn != k_unknown_location)
+ {
+ if (comp != k_unknown_component)
+ return string(" [[user(locn") + convert_to_string(locn) + "_" + convert_to_string(comp) + ")]]";
+ else
+ return string(" [[user(locn") + convert_to_string(locn) + ")]]";
+ }
+ }
+
+ // Tessellation control function inputs
+ if (execution.model == ExecutionModelTessellationControl && type.storage == StorageClassInput)
+ {
+ if (is_builtin)
+ {
+ switch (builtin)
+ {
+ case BuiltInInvocationId:
+ case BuiltInPrimitiveId:
+ return string(" [[") + builtin_qualifier(builtin) + "]]" + (mbr_type.array.empty() ? "" : " ");
+ case BuiltInPatchVertices:
+ return "";
+ // Others come from stage input.
+ default:
+ break;
+ }
+ }
+ uint32_t locn = get_ordered_member_location(type.self, index);
+ if (locn != k_unknown_location)
+ return string(" [[attribute(") + convert_to_string(locn) + ")]]";
+ }
+
+ // Tessellation control function outputs
+ if (execution.model == ExecutionModelTessellationControl && type.storage == StorageClassOutput)
+ {
+ // For this type of shader, we always arrange for it to capture its
+ // output to a buffer. For this reason, qualifiers are irrelevant here.
+ return "";
+ }
+
+ // Tessellation evaluation function inputs
+ if (execution.model == ExecutionModelTessellationEvaluation && type.storage == StorageClassInput)
+ {
+ if (is_builtin)
+ {
+ switch (builtin)
+ {
+ case BuiltInPrimitiveId:
+ case BuiltInTessCoord:
+ return string(" [[") + builtin_qualifier(builtin) + "]]";
+ case BuiltInPatchVertices:
+ return "";
+ // Others come from stage input.
+ default:
+ break;
+ }
+ }
+ // The special control point array must not be marked with an attribute.
+ if (get_type(type.member_types[index]).basetype == SPIRType::ControlPointArray)
+ return "";
+ uint32_t locn = get_ordered_member_location(type.self, index);
+ if (locn != k_unknown_location)
+ return string(" [[attribute(") + convert_to_string(locn) + ")]]";
+ }
+
+ // Tessellation evaluation function outputs were handled above.
+
+ // Fragment function inputs
+ if (execution.model == ExecutionModelFragment && type.storage == StorageClassInput)
+ {
+ string quals = "";
+ if (is_builtin)
+ {
+ switch (builtin)
+ {
+ case BuiltInFrontFacing:
+ case BuiltInPointCoord:
+ case BuiltInFragCoord:
+ case BuiltInSampleId:
+ case BuiltInSampleMask:
+ case BuiltInLayer:
+ quals = builtin_qualifier(builtin);
+
+ default:
+ break;
+ }
+ }
+ else
+ {
+ uint32_t comp;
+ uint32_t locn = get_ordered_member_location(type.self, index, &comp);
+ if (locn != k_unknown_location)
+ {
+ if (comp != k_unknown_component)
+ quals = string("user(locn") + convert_to_string(locn) + "_" + convert_to_string(comp) + ")";
+ else
+ quals = string("user(locn") + convert_to_string(locn) + ")";
+ }
+ }
+ // Don't bother decorating integers with the 'flat' attribute; it's
+ // the default (in fact, the only option). Also don't bother with the
+ // FragCoord builtin; it's always noperspective on Metal.
+ if (!type_is_integral(mbr_type) && (!is_builtin || builtin != BuiltInFragCoord))
+ {
+ if (has_member_decoration(type.self, index, DecorationFlat))
+ {
+ if (!quals.empty())
+ quals += ", ";
+ quals += "flat";
+ }
+ else if (has_member_decoration(type.self, index, DecorationCentroid))
+ {
+ if (!quals.empty())
+ quals += ", ";
+ if (has_member_decoration(type.self, index, DecorationNoPerspective))
+ quals += "centroid_no_perspective";
+ else
+ quals += "centroid_perspective";
+ }
+ else if (has_member_decoration(type.self, index, DecorationSample))
+ {
+ if (!quals.empty())
+ quals += ", ";
+ if (has_member_decoration(type.self, index, DecorationNoPerspective))
+ quals += "sample_no_perspective";
+ else
+ quals += "sample_perspective";
+ }
+ else if (has_member_decoration(type.self, index, DecorationNoPerspective))
+ {
+ if (!quals.empty())
+ quals += ", ";
+ quals += "center_no_perspective";
+ }
+ }
+ if (!quals.empty())
+ return " [[" + quals + "]]";
+ }
+
+ // Fragment function outputs
+ if (execution.model == ExecutionModelFragment && type.storage == StorageClassOutput)
+ {
+ if (is_builtin)
+ {
+ switch (builtin)
+ {
+ case BuiltInSampleMask:
+ case BuiltInFragDepth:
+ return string(" [[") + builtin_qualifier(builtin) + "]]";
+
+ default:
+ return "";
+ }
+ }
+ uint32_t locn = get_ordered_member_location(type.self, index);
+ if (locn != k_unknown_location && has_member_decoration(type.self, index, DecorationIndex))
+ return join(" [[color(", locn, "), index(", get_member_decoration(type.self, index, DecorationIndex),
+ ")]]");
+ else if (locn != k_unknown_location)
+ return join(" [[color(", locn, ")]]");
+ else if (has_member_decoration(type.self, index, DecorationIndex))
+ return join(" [[index(", get_member_decoration(type.self, index, DecorationIndex), ")]]");
+ else
+ return "";
+ }
+
+ // Compute function inputs
+ if (execution.model == ExecutionModelGLCompute && type.storage == StorageClassInput)
+ {
+ if (is_builtin)
+ {
+ switch (builtin)
+ {
+ case BuiltInGlobalInvocationId:
+ case BuiltInWorkgroupId:
+ case BuiltInNumWorkgroups:
+ case BuiltInLocalInvocationId:
+ case BuiltInLocalInvocationIndex:
+ return string(" [[") + builtin_qualifier(builtin) + "]]";
+
+ default:
+ return "";
+ }
+ }
+ }
+
+ return "";
+}
+
+// Returns the location decoration of the member with the specified index in the specified type.
+// If the location of the member has been explicitly set, that location is used. If not, this
+// function assumes the members are ordered in their location order, and simply returns the
+// index as the location.
+uint32_t CompilerMSL::get_ordered_member_location(uint32_t type_id, uint32_t index, uint32_t *comp)
+{
+ auto &m = ir.meta[type_id];
+ if (index < m.members.size())
+ {
+ auto &dec = m.members[index];
+ if (comp)
+ {
+ if (dec.decoration_flags.get(DecorationComponent))
+ *comp = dec.component;
+ else
+ *comp = k_unknown_component;
+ }
+ if (dec.decoration_flags.get(DecorationLocation))
+ return dec.location;
+ }
+
+ return index;
+}
+
+// Returns the type declaration for a function, including the
+// entry type if the current function is the entry point function
+string CompilerMSL::func_type_decl(SPIRType &type)
+{
+ // The regular function return type. If not processing the entry point function, that's all we need
+ string return_type = type_to_glsl(type) + type_to_array_glsl(type);
+ if (!processing_entry_point)
+ return return_type;
+
+ // If an outgoing interface block has been defined, and it should be returned, override the entry point return type
+ bool ep_should_return_output = !get_is_rasterization_disabled();
+ if (stage_out_var_id && ep_should_return_output)
+ return_type = type_to_glsl(get_stage_out_struct_type()) + type_to_array_glsl(type);
+
+ // Prepend a entry type, based on the execution model
+ string entry_type;
+ auto &execution = get_entry_point();
+ switch (execution.model)
+ {
+ case ExecutionModelVertex:
+ entry_type = "vertex";
+ break;
+ case ExecutionModelTessellationEvaluation:
+ if (!msl_options.supports_msl_version(1, 2))
+ SPIRV_CROSS_THROW("Tessellation requires Metal 1.2.");
+ if (execution.flags.get(ExecutionModeIsolines))
+ SPIRV_CROSS_THROW("Metal does not support isoline tessellation.");
+ if (msl_options.is_ios())
+ entry_type =
+ join("[[ patch(", execution.flags.get(ExecutionModeTriangles) ? "triangle" : "quad", ") ]] vertex");
+ else
+ entry_type = join("[[ patch(", execution.flags.get(ExecutionModeTriangles) ? "triangle" : "quad", ", ",
+ execution.output_vertices, ") ]] vertex");
+ break;
+ case ExecutionModelFragment:
+ entry_type =
+ execution.flags.get(ExecutionModeEarlyFragmentTests) ? "[[ early_fragment_tests ]] fragment" : "fragment";
+ break;
+ case ExecutionModelTessellationControl:
+ if (!msl_options.supports_msl_version(1, 2))
+ SPIRV_CROSS_THROW("Tessellation requires Metal 1.2.");
+ if (execution.flags.get(ExecutionModeIsolines))
+ SPIRV_CROSS_THROW("Metal does not support isoline tessellation.");
+ /* fallthrough */
+ case ExecutionModelGLCompute:
+ case ExecutionModelKernel:
+ entry_type = "kernel";
+ break;
+ default:
+ entry_type = "unknown";
+ break;
+ }
+
+ return entry_type + " " + return_type;
+}
+
+// In MSL, address space qualifiers are required for all pointer or reference variables
+string CompilerMSL::get_argument_address_space(const SPIRVariable &argument)
+{
+ const auto &type = get<SPIRType>(argument.basetype);
+
+ switch (type.storage)
+ {
+ case StorageClassWorkgroup:
+ return "threadgroup";
+
+ case StorageClassStorageBuffer:
+ {
+ // For arguments from variable pointers, we use the write count deduction, so
+ // we should not assume any constness here. Only for global SSBOs.
+ bool readonly = false;
+ if (has_decoration(type.self, DecorationBlock))
+ readonly = ir.get_buffer_block_flags(argument).get(DecorationNonWritable);
+
+ return readonly ? "const device" : "device";
+ }
+
+ case StorageClassUniform:
+ case StorageClassUniformConstant:
+ case StorageClassPushConstant:
+ if (type.basetype == SPIRType::Struct)
+ {
+ bool ssbo = has_decoration(type.self, DecorationBufferBlock);
+ if (ssbo)
+ {
+ bool readonly = ir.get_buffer_block_flags(argument).get(DecorationNonWritable);
+ return readonly ? "const device" : "device";
+ }
+ else
+ return "constant";
+ }
+ break;
+
+ case StorageClassFunction:
+ case StorageClassGeneric:
+ // No address space for plain values.
+ return type.pointer ? "thread" : "";
+
+ case StorageClassInput:
+ if (get_execution_model() == ExecutionModelTessellationControl && argument.basevariable == stage_in_ptr_var_id)
+ return "threadgroup";
+ break;
+
+ case StorageClassOutput:
+ if (capture_output_to_buffer)
+ return "device";
+ break;
+
+ default:
+ break;
+ }
+
+ return "thread";
+}
+
+string CompilerMSL::get_type_address_space(const SPIRType &type, uint32_t id)
+{
+ switch (type.storage)
+ {
+ case StorageClassWorkgroup:
+ return "threadgroup";
+
+ case StorageClassStorageBuffer:
+ {
+ // This can be called for variable pointer contexts as well, so be very careful about which method we choose.
+ Bitset flags;
+ if (ir.ids[id].get_type() == TypeVariable && has_decoration(type.self, DecorationBlock))
+ flags = get_buffer_block_flags(id);
+ else
+ flags = get_decoration_bitset(id);
+
+ return flags.get(DecorationNonWritable) ? "const device" : "device";
+ }
+
+ case StorageClassUniform:
+ case StorageClassUniformConstant:
+ case StorageClassPushConstant:
+ if (type.basetype == SPIRType::Struct)
+ {
+ bool ssbo = has_decoration(type.self, DecorationBufferBlock);
+ if (ssbo)
+ {
+ // This can be called for variable pointer contexts as well, so be very careful about which method we choose.
+ Bitset flags;
+ if (ir.ids[id].get_type() == TypeVariable && has_decoration(type.self, DecorationBlock))
+ flags = get_buffer_block_flags(id);
+ else
+ flags = get_decoration_bitset(id);
+
+ return flags.get(DecorationNonWritable) ? "const device" : "device";
+ }
+ else
+ return "constant";
+ }
+ break;
+
+ case StorageClassFunction:
+ case StorageClassGeneric:
+ // No address space for plain values.
+ return type.pointer ? "thread" : "";
+
+ case StorageClassOutput:
+ if (capture_output_to_buffer)
+ return "device";
+ break;
+
+ default:
+ break;
+ }
+
+ return "thread";
+}
+
+string CompilerMSL::entry_point_arg_stage_in()
+{
+ string decl;
+
+ // Stage-in structure
+ uint32_t stage_in_id;
+ if (get_execution_model() == ExecutionModelTessellationEvaluation)
+ stage_in_id = patch_stage_in_var_id;
+ else
+ stage_in_id = stage_in_var_id;
+
+ if (stage_in_id)
+ {
+ auto &var = get<SPIRVariable>(stage_in_id);
+ auto &type = get_variable_data_type(var);
+
+ add_resource_name(var.self);
+ decl = join(type_to_glsl(type), " ", to_name(var.self), " [[stage_in]]");
+ }
+
+ return decl;
+}
+
+void CompilerMSL::entry_point_args_builtin(string &ep_args)
+{
+ // Builtin variables
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t var_id, SPIRVariable &var) {
+ BuiltIn bi_type = ir.meta[var_id].decoration.builtin_type;
+
+ // Don't emit SamplePosition as a separate parameter. In the entry
+ // point, we get that by calling get_sample_position() on the sample ID.
+ if (var.storage == StorageClassInput && is_builtin_variable(var) &&
+ get_variable_data_type(var).basetype != SPIRType::Struct &&
+ get_variable_data_type(var).basetype != SPIRType::ControlPointArray)
+ {
+ if (bi_type != BuiltInSamplePosition && bi_type != BuiltInHelperInvocation &&
+ bi_type != BuiltInPatchVertices && bi_type != BuiltInTessLevelInner &&
+ bi_type != BuiltInTessLevelOuter && bi_type != BuiltInPosition && bi_type != BuiltInPointSize &&
+ bi_type != BuiltInClipDistance && bi_type != BuiltInCullDistance)
+ {
+ if (!ep_args.empty())
+ ep_args += ", ";
+
+ ep_args += builtin_type_decl(bi_type) + " " + to_expression(var_id);
+ ep_args += " [[" + builtin_qualifier(bi_type) + "]]";
+ }
+ }
+ });
+
+ // Vertex and instance index built-ins
+ if (needs_vertex_idx_arg)
+ ep_args += built_in_func_arg(BuiltInVertexIndex, !ep_args.empty());
+
+ if (needs_instance_idx_arg)
+ ep_args += built_in_func_arg(BuiltInInstanceIndex, !ep_args.empty());
+
+ if (capture_output_to_buffer)
+ {
+ // Add parameters to hold the indirect draw parameters and the shader output. This has to be handled
+ // specially because it needs to be a pointer, not a reference.
+ if (stage_out_var_id)
+ {
+ if (!ep_args.empty())
+ ep_args += ", ";
+ ep_args += join("device ", type_to_glsl(get_stage_out_struct_type()), "* ", output_buffer_var_name,
+ " [[buffer(", msl_options.shader_output_buffer_index, ")]]");
+ }
+
+ if (stage_out_var_id || get_execution_model() == ExecutionModelTessellationControl)
+ {
+ if (!ep_args.empty())
+ ep_args += ", ";
+ ep_args +=
+ join("device uint* spvIndirectParams [[buffer(", msl_options.indirect_params_buffer_index, ")]]");
+ }
+
+ // Tessellation control shaders get three additional parameters:
+ // a buffer to hold the per-patch data, a buffer to hold the per-patch
+ // tessellation levels, and a block of workgroup memory to hold the
+ // input control point data.
+ if (get_execution_model() == ExecutionModelTessellationControl)
+ {
+ if (patch_stage_out_var_id)
+ {
+ if (!ep_args.empty())
+ ep_args += ", ";
+ ep_args +=
+ join("device ", type_to_glsl(get_patch_stage_out_struct_type()), "* ", patch_output_buffer_var_name,
+ " [[buffer(", convert_to_string(msl_options.shader_patch_output_buffer_index), ")]]");
+ }
+ if (!ep_args.empty())
+ ep_args += ", ";
+ ep_args += join("device ", get_tess_factor_struct_name(), "* ", tess_factor_buffer_var_name, " [[buffer(",
+ convert_to_string(msl_options.shader_tess_factor_buffer_index), ")]]");
+ if (stage_in_var_id)
+ {
+ if (!ep_args.empty())
+ ep_args += ", ";
+ ep_args += join("threadgroup ", type_to_glsl(get_stage_in_struct_type()), "* ", input_wg_var_name,
+ " [[threadgroup(", convert_to_string(msl_options.shader_input_wg_index), ")]]");
+ }
+ }
+ }
+}
+
+string CompilerMSL::entry_point_args_argument_buffer(bool append_comma)
+{
+ string ep_args = entry_point_arg_stage_in();
+
+ for (uint32_t i = 0; i < kMaxArgumentBuffers; i++)
+ {
+ uint32_t id = argument_buffer_ids[i];
+ if (id == 0)
+ continue;
+
+ add_resource_name(id);
+ auto &var = get<SPIRVariable>(id);
+ auto &type = get_variable_data_type(var);
+
+ if (!ep_args.empty())
+ ep_args += ", ";
+
+ ep_args += get_argument_address_space(var) + " " + type_to_glsl(type) + "& " + to_name(id);
+ ep_args += " [[buffer(" + convert_to_string(i) + ")]]";
+
+ // Makes it more practical for testing, since the push constant block can occupy the first available
+ // buffer slot if it's not bound explicitly.
+ next_metal_resource_index_buffer = i + 1;
+ }
+
+ entry_point_args_discrete_descriptors(ep_args);
+ entry_point_args_builtin(ep_args);
+
+ if (!ep_args.empty() && append_comma)
+ ep_args += ", ";
+
+ return ep_args;
+}
+
+void CompilerMSL::entry_point_args_discrete_descriptors(string &ep_args)
+{
+ // Output resources, sorted by resource index & type
+ // We need to sort to work around a bug on macOS 10.13 with NVidia drivers where switching between shaders
+ // with different order of buffers can result in issues with buffer assignments inside the driver.
+ struct Resource
+ {
+ SPIRVariable *var;
+ string name;
+ SPIRType::BaseType basetype;
+ uint32_t index;
+ };
+
+ vector<Resource> resources;
+
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ if ((var.storage == StorageClassUniform || var.storage == StorageClassUniformConstant ||
+ var.storage == StorageClassPushConstant || var.storage == StorageClassStorageBuffer) &&
+ !is_hidden_variable(var))
+ {
+ auto &type = get_variable_data_type(var);
+ uint32_t var_id = var.self;
+
+ if (var.storage != StorageClassPushConstant)
+ {
+ uint32_t desc_set = get_decoration(var_id, DecorationDescriptorSet);
+ if (descriptor_set_is_argument_buffer(desc_set))
+ return;
+ }
+
+ if (type.basetype == SPIRType::SampledImage)
+ {
+ add_resource_name(var_id);
+ resources.push_back(
+ { &var, to_name(var_id), SPIRType::Image, get_metal_resource_index(var, SPIRType::Image) });
+
+ if (type.image.dim != DimBuffer && constexpr_samplers.count(var_id) == 0)
+ {
+ resources.push_back({ &var, to_sampler_expression(var_id), SPIRType::Sampler,
+ get_metal_resource_index(var, SPIRType::Sampler) });
+ }
+ }
+ else if (constexpr_samplers.count(var_id) == 0)
+ {
+ // constexpr samplers are not declared as resources.
+ add_resource_name(var_id);
+ resources.push_back(
+ { &var, to_name(var_id), type.basetype, get_metal_resource_index(var, type.basetype) });
+ }
+ }
+ });
+
+ sort(resources.begin(), resources.end(), [](const Resource &lhs, const Resource &rhs) {
+ return tie(lhs.basetype, lhs.index) < tie(rhs.basetype, rhs.index);
+ });
+
+ for (auto &r : resources)
+ {
+ auto &var = *r.var;
+ auto &type = get_variable_data_type(var);
+
+ uint32_t var_id = var.self;
+
+ switch (r.basetype)
+ {
+ case SPIRType::Struct:
+ {
+ auto &m = ir.meta[type.self];
+ if (m.members.size() == 0)
+ break;
+ if (!type.array.empty())
+ {
+ if (type.array.size() > 1)
+ SPIRV_CROSS_THROW("Arrays of arrays of buffers are not supported.");
+
+ // Metal doesn't directly support this, so we must expand the
+ // array. We'll declare a local array to hold these elements
+ // later.
+ uint32_t array_size = to_array_size_literal(type);
+
+ if (array_size == 0)
+ SPIRV_CROSS_THROW("Unsized arrays of buffers are not supported in MSL.");
+
+ buffer_arrays.push_back(var_id);
+ for (uint32_t i = 0; i < array_size; ++i)
+ {
+ if (!ep_args.empty())
+ ep_args += ", ";
+ ep_args += get_argument_address_space(var) + " " + type_to_glsl(type) + "* " + r.name + "_" +
+ convert_to_string(i);
+ ep_args += " [[buffer(" + convert_to_string(r.index + i) + ")]]";
+ }
+ }
+ else
+ {
+ if (!ep_args.empty())
+ ep_args += ", ";
+ ep_args += get_argument_address_space(var) + " " + type_to_glsl(type) + "& " + r.name;
+ ep_args += " [[buffer(" + convert_to_string(r.index) + ")]]";
+ }
+ break;
+ }
+ case SPIRType::Sampler:
+ if (!ep_args.empty())
+ ep_args += ", ";
+ ep_args += sampler_type(type) + " " + r.name;
+ ep_args += " [[sampler(" + convert_to_string(r.index) + ")]]";
+ break;
+ case SPIRType::Image:
+ if (!ep_args.empty())
+ ep_args += ", ";
+ ep_args += image_type_glsl(type, var_id) + " " + r.name;
+ ep_args += " [[texture(" + convert_to_string(r.index) + ")]]";
+ break;
+ default:
+ SPIRV_CROSS_THROW("Unexpected resource type");
+ break;
+ }
+ }
+}
+
+// Returns a string containing a comma-delimited list of args for the entry point function
+// This is the "classic" method of MSL 1 when we don't have argument buffer support.
+string CompilerMSL::entry_point_args_classic(bool append_comma)
+{
+ string ep_args = entry_point_arg_stage_in();
+ entry_point_args_discrete_descriptors(ep_args);
+ entry_point_args_builtin(ep_args);
+
+ if (!ep_args.empty() && append_comma)
+ ep_args += ", ";
+
+ return ep_args;
+}
+
+void CompilerMSL::fix_up_shader_inputs_outputs()
+{
+ // Look for sampled images. Add hooks to set up the swizzle constants.
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ auto &type = get_variable_data_type(var);
+
+ uint32_t var_id = var.self;
+
+ if ((var.storage == StorageClassUniform || var.storage == StorageClassUniformConstant ||
+ var.storage == StorageClassPushConstant || var.storage == StorageClassStorageBuffer) &&
+ !is_hidden_variable(var))
+ {
+ if (msl_options.swizzle_texture_samples && has_sampled_images && is_sampled_image_type(type))
+ {
+ auto &entry_func = this->get<SPIRFunction>(ir.default_entry_point);
+ entry_func.fixup_hooks_in.push_back([this, &var, var_id]() {
+ auto &aux_type = expression_type(aux_buffer_id);
+ statement("constant uint32_t& ", to_swizzle_expression(var_id), " = ", to_name(aux_buffer_id), ".",
+ to_member_name(aux_type, k_aux_mbr_idx_swizzle_const), "[",
+ convert_to_string(get_metal_resource_index(var, SPIRType::Image)), "];");
+ });
+ }
+ }
+ });
+
+ // Builtin variables
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
+ uint32_t var_id = var.self;
+ BuiltIn bi_type = ir.meta[var_id].decoration.builtin_type;
+
+ if (var.storage == StorageClassInput && is_builtin_variable(var))
+ {
+ auto &entry_func = this->get<SPIRFunction>(ir.default_entry_point);
+ switch (bi_type)
+ {
+ case BuiltInSamplePosition:
+ entry_func.fixup_hooks_in.push_back([=]() {
+ statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = get_sample_position(",
+ to_expression(builtin_sample_id_id), ");");
+ });
+ break;
+ case BuiltInHelperInvocation:
+ if (msl_options.is_ios())
+ SPIRV_CROSS_THROW("simd_is_helper_thread() is only supported on macOS.");
+ else if (msl_options.is_macos() && !msl_options.supports_msl_version(2, 1))
+ SPIRV_CROSS_THROW("simd_is_helper_thread() requires version 2.1 on macOS.");
+
+ entry_func.fixup_hooks_in.push_back([=]() {
+ statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = simd_is_helper_thread();");
+ });
+ break;
+ case BuiltInPatchVertices:
+ if (get_execution_model() == ExecutionModelTessellationEvaluation)
+ entry_func.fixup_hooks_in.push_back([=]() {
+ statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ",
+ to_expression(patch_stage_in_var_id), ".gl_in.size();");
+ });
+ else
+ entry_func.fixup_hooks_in.push_back([=]() {
+ statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = spvIndirectParams[0];");
+ });
+ break;
+ case BuiltInTessCoord:
+ // Emit a fixup to account for the shifted domain. Don't do this for triangles;
+ // MoltenVK will just reverse the winding order instead.
+ if (msl_options.tess_domain_origin_lower_left && !get_entry_point().flags.get(ExecutionModeTriangles))
+ {
+ string tc = to_expression(var_id);
+ entry_func.fixup_hooks_in.push_back([=]() { statement(tc, ".y = 1.0 - ", tc, ".y;"); });
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ });
+}
+
+// Returns the Metal index of the resource of the specified type as used by the specified variable.
+uint32_t CompilerMSL::get_metal_resource_index(SPIRVariable &var, SPIRType::BaseType basetype)
+{
+ auto &execution = get_entry_point();
+ auto &var_dec = ir.meta[var.self].decoration;
+ uint32_t var_desc_set = (var.storage == StorageClassPushConstant) ? kPushConstDescSet : var_dec.set;
+ uint32_t var_binding = (var.storage == StorageClassPushConstant) ? kPushConstBinding : var_dec.binding;
+
+ // If a matching binding has been specified, find and use it
+ auto itr = find_if(begin(resource_bindings), end(resource_bindings),
+ [&](const pair<MSLResourceBinding, bool> &resource) -> bool {
+ return var_desc_set == resource.first.desc_set && var_binding == resource.first.binding &&
+ execution.model == resource.first.stage;
+ });
+
+ if (itr != end(resource_bindings))
+ {
+ itr->second = true;
+ switch (basetype)
+ {
+ case SPIRType::Struct:
+ return itr->first.msl_buffer;
+ case SPIRType::Image:
+ return itr->first.msl_texture;
+ case SPIRType::Sampler:
+ return itr->first.msl_sampler;
+ default:
+ return 0;
+ }
+ }
+
+ // If there is no explicit mapping of bindings to MSL, use the declared binding.
+ if (has_decoration(var.self, DecorationBinding))
+ return get_decoration(var.self, DecorationBinding);
+
+ uint32_t binding_stride = 1;
+ auto &type = get<SPIRType>(var.basetype);
+ for (uint32_t i = 0; i < uint32_t(type.array.size()); i++)
+ binding_stride *= type.array_size_literal[i] ? type.array[i] : get<SPIRConstant>(type.array[i]).scalar();
+
+ // If a binding has not been specified, revert to incrementing resource indices
+ uint32_t resource_index;
+ switch (basetype)
+ {
+ case SPIRType::Struct:
+ resource_index = next_metal_resource_index_buffer;
+ next_metal_resource_index_buffer += binding_stride;
+ break;
+ case SPIRType::Image:
+ resource_index = next_metal_resource_index_texture;
+ next_metal_resource_index_texture += binding_stride;
+ break;
+ case SPIRType::Sampler:
+ resource_index = next_metal_resource_index_sampler;
+ next_metal_resource_index_sampler += binding_stride;
+ break;
+ default:
+ resource_index = 0;
+ break;
+ }
+ return resource_index;
+}
+
+string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
+{
+ auto &var = get<SPIRVariable>(arg.id);
+ auto &type = get_variable_data_type(var);
+ auto &var_type = get<SPIRType>(arg.type);
+ StorageClass storage = var_type.storage;
+ bool is_pointer = var_type.pointer;
+
+ // If we need to modify the name of the variable, make sure we use the original variable.
+ // Our alias is just a shadow variable.
+ uint32_t name_id = var.self;
+ if (arg.alias_global_variable && var.basevariable)
+ name_id = var.basevariable;
+
+ bool constref = !arg.alias_global_variable && is_pointer && arg.write_count == 0;
+
+ bool type_is_image = type.basetype == SPIRType::Image || type.basetype == SPIRType::SampledImage ||
+ type.basetype == SPIRType::Sampler;
+
+ // Arrays of images/samplers in MSL are always const.
+ if (!type.array.empty() && type_is_image)
+ constref = true;
+
+ string decl;
+ if (constref)
+ decl += "const ";
+
+ bool builtin = is_builtin_variable(var);
+ if (var.basevariable == stage_in_ptr_var_id || var.basevariable == stage_out_ptr_var_id)
+ decl += type_to_glsl(type, arg.id);
+ else if (builtin)
+ decl += builtin_type_decl(static_cast<BuiltIn>(get_decoration(arg.id, DecorationBuiltIn)));
+ else if ((storage == StorageClassUniform || storage == StorageClassStorageBuffer) && is_array(type))
+ decl += join(type_to_glsl(type, arg.id), "*");
+ else
+ decl += type_to_glsl(type, arg.id);
+
+ bool opaque_handle = storage == StorageClassUniformConstant;
+
+ string address_space = get_argument_address_space(var);
+
+ if (!builtin && !opaque_handle && !is_pointer &&
+ (storage == StorageClassFunction || storage == StorageClassGeneric))
+ {
+ // If the argument is a pure value and not an opaque type, we will pass by value.
+ if (is_array(type))
+ {
+ // We are receiving an array by value. This is problematic.
+ // We cannot be sure of the target address space since we are supposed to receive a copy,
+ // but this is not possible with MSL without some extra work.
+ // We will have to assume we're getting a reference in thread address space.
+ // If we happen to get a reference in constant address space, the caller must emit a copy and pass that.
+ // Thread const therefore becomes the only logical choice, since we cannot "create" a constant array from
+ // non-constant arrays, but we can create thread const from constant.
+ decl = string("thread const ") + decl;
+ decl += " (&";
+ decl += to_expression(name_id);
+ decl += ")";
+ decl += type_to_array_glsl(type);
+ }
+ else
+ {
+ if (!address_space.empty())
+ decl = join(address_space, " ", decl);
+ decl += " ";
+ decl += to_expression(name_id);
+ }
+ }
+ else if (is_array(type) && !type_is_image)
+ {
+ // Arrays of images and samplers are special cased.
+ if (!address_space.empty())
+ decl = join(address_space, " ", decl);
+
+ if (msl_options.argument_buffers)
+ {
+ // An awkward case where we need to emit *more* address space declarations (yay!).
+ // An example is where we pass down an array of buffer pointers to leaf functions.
+ // It's a constant array containing pointers to constants.
+ // The pointer array is always constant however. E.g.
+ // device SSBO * constant (&array)[N].
+ // const device SSBO * constant (&array)[N].
+ // constant SSBO * constant (&array)[N].
+ // However, this only matters for argument buffers, since for MSL 1.0 style codegen,
+ // we emit the buffer array on stack instead, and that seems to work just fine apparently.
+ if (storage == StorageClassUniform || storage == StorageClassStorageBuffer)
+ decl += " constant";
+ }
+
+ decl += " (&";
+ decl += to_expression(name_id);
+ decl += ")";
+ decl += type_to_array_glsl(type);
+ }
+ else if (!opaque_handle)
+ {
+ // If this is going to be a reference to a variable pointer, the address space
+ // for the reference has to go before the '&', but after the '*'.
+ if (!address_space.empty())
+ {
+ if (decl.back() == '*')
+ decl += join(" ", address_space, " ");
+ else
+ decl = join(address_space, " ", decl);
+ }
+ decl += "&";
+ decl += " ";
+ decl += to_expression(name_id);
+ }
+ else
+ {
+ if (!address_space.empty())
+ decl = join(address_space, " ", decl);
+ decl += " ";
+ decl += to_expression(name_id);
+ }
+
+ return decl;
+}
+
+// If we're currently in the entry point function, and the object
+// has a qualified name, use it, otherwise use the standard name.
+string CompilerMSL::to_name(uint32_t id, bool allow_alias) const
+{
+ if (current_function && (current_function->self == ir.default_entry_point))
+ {
+ auto *m = ir.find_meta(id);
+ if (m && !m->decoration.qualified_alias.empty())
+ return m->decoration.qualified_alias;
+ }
+ return Compiler::to_name(id, allow_alias);
+}
+
+// Returns a name that combines the name of the struct with the name of the member, except for Builtins
+string CompilerMSL::to_qualified_member_name(const SPIRType &type, uint32_t index)
+{
+ // Don't qualify Builtin names because they are unique and are treated as such when building expressions
+ BuiltIn builtin = BuiltInMax;
+ if (is_member_builtin(type, index, &builtin))
+ return builtin_to_glsl(builtin, type.storage);
+
+ // Strip any underscore prefix from member name
+ string mbr_name = to_member_name(type, index);
+ size_t startPos = mbr_name.find_first_not_of("_");
+ mbr_name = (startPos != string::npos) ? mbr_name.substr(startPos) : "";
+ return join(to_name(type.self), "_", mbr_name);
+}
+
+// Ensures that the specified name is permanently usable by prepending a prefix
+// if the first chars are _ and a digit, which indicate a transient name.
+string CompilerMSL::ensure_valid_name(string name, string pfx)
+{
+ return (name.size() >= 2 && name[0] == '_' && isdigit(name[1])) ? (pfx + name) : name;
+}
+
+// Replace all names that match MSL keywords or Metal Standard Library functions.
+void CompilerMSL::replace_illegal_names()
+{
+ // FIXME: MSL and GLSL are doing two different things here.
+ // Agree on convention and remove this override.
+ static const unordered_set<string> keywords = {
+ "kernel",
+ "vertex",
+ "fragment",
+ "compute",
+ "bias",
+ "assert",
+ "VARIABLE_TRACEPOINT",
+ "STATIC_DATA_TRACEPOINT",
+ "STATIC_DATA_TRACEPOINT_V",
+ "METAL_ALIGN",
+ "METAL_ASM",
+ "METAL_CONST",
+ "METAL_DEPRECATED",
+ "METAL_ENABLE_IF",
+ "METAL_FUNC",
+ "METAL_INTERNAL",
+ "METAL_NON_NULL_RETURN",
+ "METAL_NORETURN",
+ "METAL_NOTHROW",
+ "METAL_PURE",
+ "METAL_UNAVAILABLE",
+ "METAL_IMPLICIT",
+ "METAL_EXPLICIT",
+ "METAL_CONST_ARG",
+ "METAL_ARG_UNIFORM",
+ "METAL_ZERO_ARG",
+ "METAL_VALID_LOD_ARG",
+ "METAL_VALID_LEVEL_ARG",
+ "METAL_VALID_STORE_ORDER",
+ "METAL_VALID_LOAD_ORDER",
+ "METAL_VALID_COMPARE_EXCHANGE_FAILURE_ORDER",
+ "METAL_COMPATIBLE_COMPARE_EXCHANGE_ORDERS",
+ "METAL_VALID_RENDER_TARGET",
+ "is_function_constant_defined",
+ "CHAR_BIT",
+ "SCHAR_MAX",
+ "SCHAR_MIN",
+ "UCHAR_MAX",
+ "CHAR_MAX",
+ "CHAR_MIN",
+ "USHRT_MAX",
+ "SHRT_MAX",
+ "SHRT_MIN",
+ "UINT_MAX",
+ "INT_MAX",
+ "INT_MIN",
+ "FLT_DIG",
+ "FLT_MANT_DIG",
+ "FLT_MAX_10_EXP",
+ "FLT_MAX_EXP",
+ "FLT_MIN_10_EXP",
+ "FLT_MIN_EXP",
+ "FLT_RADIX",
+ "FLT_MAX",
+ "FLT_MIN",
+ "FLT_EPSILON",
+ "FP_ILOGB0",
+ "FP_ILOGBNAN",
+ "MAXFLOAT",
+ "HUGE_VALF",
+ "INFINITY",
+ "NAN",
+ "M_E_F",
+ "M_LOG2E_F",
+ "M_LOG10E_F",
+ "M_LN2_F",
+ "M_LN10_F",
+ "M_PI_F",
+ "M_PI_2_F",
+ "M_PI_4_F",
+ "M_1_PI_F",
+ "M_2_PI_F",
+ "M_2_SQRTPI_F",
+ "M_SQRT2_F",
+ "M_SQRT1_2_F",
+ "HALF_DIG",
+ "HALF_MANT_DIG",
+ "HALF_MAX_10_EXP",
+ "HALF_MAX_EXP",
+ "HALF_MIN_10_EXP",
+ "HALF_MIN_EXP",
+ "HALF_RADIX",
+ "HALF_MAX",
+ "HALF_MIN",
+ "HALF_EPSILON",
+ "MAXHALF",
+ "HUGE_VALH",
+ "M_E_H",
+ "M_LOG2E_H",
+ "M_LOG10E_H",
+ "M_LN2_H",
+ "M_LN10_H",
+ "M_PI_H",
+ "M_PI_2_H",
+ "M_PI_4_H",
+ "M_1_PI_H",
+ "M_2_PI_H",
+ "M_2_SQRTPI_H",
+ "M_SQRT2_H",
+ "M_SQRT1_2_H",
+ "DBL_DIG",
+ "DBL_MANT_DIG",
+ "DBL_MAX_10_EXP",
+ "DBL_MAX_EXP",
+ "DBL_MIN_10_EXP",
+ "DBL_MIN_EXP",
+ "DBL_RADIX",
+ "DBL_MAX",
+ "DBL_MIN",
+ "DBL_EPSILON",
+ "HUGE_VAL",
+ "M_E",
+ "M_LOG2E",
+ "M_LOG10E",
+ "M_LN2",
+ "M_LN10",
+ "M_PI",
+ "M_PI_2",
+ "M_PI_4",
+ "M_1_PI",
+ "M_2_PI",
+ "M_2_SQRTPI",
+ "M_SQRT2",
+ "M_SQRT1_2",
+ };
+
+ static const unordered_set<string> illegal_func_names = {
+ "main",
+ "saturate",
+ "assert",
+ "VARIABLE_TRACEPOINT",
+ "STATIC_DATA_TRACEPOINT",
+ "STATIC_DATA_TRACEPOINT_V",
+ "METAL_ALIGN",
+ "METAL_ASM",
+ "METAL_CONST",
+ "METAL_DEPRECATED",
+ "METAL_ENABLE_IF",
+ "METAL_FUNC",
+ "METAL_INTERNAL",
+ "METAL_NON_NULL_RETURN",
+ "METAL_NORETURN",
+ "METAL_NOTHROW",
+ "METAL_PURE",
+ "METAL_UNAVAILABLE",
+ "METAL_IMPLICIT",
+ "METAL_EXPLICIT",
+ "METAL_CONST_ARG",
+ "METAL_ARG_UNIFORM",
+ "METAL_ZERO_ARG",
+ "METAL_VALID_LOD_ARG",
+ "METAL_VALID_LEVEL_ARG",
+ "METAL_VALID_STORE_ORDER",
+ "METAL_VALID_LOAD_ORDER",
+ "METAL_VALID_COMPARE_EXCHANGE_FAILURE_ORDER",
+ "METAL_COMPATIBLE_COMPARE_EXCHANGE_ORDERS",
+ "METAL_VALID_RENDER_TARGET",
+ "is_function_constant_defined",
+ "CHAR_BIT",
+ "SCHAR_MAX",
+ "SCHAR_MIN",
+ "UCHAR_MAX",
+ "CHAR_MAX",
+ "CHAR_MIN",
+ "USHRT_MAX",
+ "SHRT_MAX",
+ "SHRT_MIN",
+ "UINT_MAX",
+ "INT_MAX",
+ "INT_MIN",
+ "FLT_DIG",
+ "FLT_MANT_DIG",
+ "FLT_MAX_10_EXP",
+ "FLT_MAX_EXP",
+ "FLT_MIN_10_EXP",
+ "FLT_MIN_EXP",
+ "FLT_RADIX",
+ "FLT_MAX",
+ "FLT_MIN",
+ "FLT_EPSILON",
+ "FP_ILOGB0",
+ "FP_ILOGBNAN",
+ "MAXFLOAT",
+ "HUGE_VALF",
+ "INFINITY",
+ "NAN",
+ "M_E_F",
+ "M_LOG2E_F",
+ "M_LOG10E_F",
+ "M_LN2_F",
+ "M_LN10_F",
+ "M_PI_F",
+ "M_PI_2_F",
+ "M_PI_4_F",
+ "M_1_PI_F",
+ "M_2_PI_F",
+ "M_2_SQRTPI_F",
+ "M_SQRT2_F",
+ "M_SQRT1_2_F",
+ "HALF_DIG",
+ "HALF_MANT_DIG",
+ "HALF_MAX_10_EXP",
+ "HALF_MAX_EXP",
+ "HALF_MIN_10_EXP",
+ "HALF_MIN_EXP",
+ "HALF_RADIX",
+ "HALF_MAX",
+ "HALF_MIN",
+ "HALF_EPSILON",
+ "MAXHALF",
+ "HUGE_VALH",
+ "M_E_H",
+ "M_LOG2E_H",
+ "M_LOG10E_H",
+ "M_LN2_H",
+ "M_LN10_H",
+ "M_PI_H",
+ "M_PI_2_H",
+ "M_PI_4_H",
+ "M_1_PI_H",
+ "M_2_PI_H",
+ "M_2_SQRTPI_H",
+ "M_SQRT2_H",
+ "M_SQRT1_2_H",
+ "DBL_DIG",
+ "DBL_MANT_DIG",
+ "DBL_MAX_10_EXP",
+ "DBL_MAX_EXP",
+ "DBL_MIN_10_EXP",
+ "DBL_MIN_EXP",
+ "DBL_RADIX",
+ "DBL_MAX",
+ "DBL_MIN",
+ "DBL_EPSILON",
+ "HUGE_VAL",
+ "M_E",
+ "M_LOG2E",
+ "M_LOG10E",
+ "M_LN2",
+ "M_LN10",
+ "M_PI",
+ "M_PI_2",
+ "M_PI_4",
+ "M_1_PI",
+ "M_2_PI",
+ "M_2_SQRTPI",
+ "M_SQRT2",
+ "M_SQRT1_2",
+ };
+
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t self, SPIRVariable &) {
+ auto &dec = ir.meta[self].decoration;
+ if (keywords.find(dec.alias) != end(keywords))
+ dec.alias += "0";
+ });
+
+ ir.for_each_typed_id<SPIRFunction>([&](uint32_t self, SPIRFunction &) {
+ auto &dec = ir.meta[self].decoration;
+ if (illegal_func_names.find(dec.alias) != end(illegal_func_names))
+ dec.alias += "0";
+ });
+
+ ir.for_each_typed_id<SPIRType>([&](uint32_t self, SPIRType &) {
+ for (auto &mbr_dec : ir.meta[self].members)
+ if (keywords.find(mbr_dec.alias) != end(keywords))
+ mbr_dec.alias += "0";
+ });
+
+ for (auto &entry : ir.entry_points)
+ {
+ // Change both the entry point name and the alias, to keep them synced.
+ string &ep_name = entry.second.name;
+ if (illegal_func_names.find(ep_name) != end(illegal_func_names))
+ ep_name += "0";
+
+ // Always write this because entry point might have been renamed earlier.
+ ir.meta[entry.first].decoration.alias = ep_name;
+ }
+
+ CompilerGLSL::replace_illegal_names();
+}
+
+string CompilerMSL::to_member_reference(uint32_t base, const SPIRType &type, uint32_t index, bool ptr_chain)
+{
+ auto *var = maybe_get<SPIRVariable>(base);
+ // If this is a buffer array, we have to dereference the buffer pointers.
+ // Otherwise, if this is a pointer expression, dereference it.
+
+ bool declared_as_pointer = false;
+
+ if (var)
+ {
+ bool is_buffer_variable = var->storage == StorageClassUniform || var->storage == StorageClassStorageBuffer;
+ declared_as_pointer = is_buffer_variable && is_array(get<SPIRType>(var->basetype));
+ }
+
+ if (declared_as_pointer || (!ptr_chain && should_dereference(base)))
+ return join("->", to_member_name(type, index));
+ else
+ return join(".", to_member_name(type, index));
+}
+
+string CompilerMSL::to_qualifiers_glsl(uint32_t id)
+{
+ string quals;
+
+ auto &type = expression_type(id);
+ if (type.storage == StorageClassWorkgroup)
+ quals += "threadgroup ";
+
+ return quals;
+}
+
+// The optional id parameter indicates the object whose type we are trying
+// to find the description for. It is optional. Most type descriptions do not
+// depend on a specific object's use of that type.
+string CompilerMSL::type_to_glsl(const SPIRType &type, uint32_t id)
+{
+ string type_name;
+
+ // Pointer?
+ if (type.pointer)
+ {
+ type_name = join(get_type_address_space(type, id), " ", type_to_glsl(get<SPIRType>(type.parent_type), id));
+ switch (type.basetype)
+ {
+ case SPIRType::Image:
+ case SPIRType::SampledImage:
+ case SPIRType::Sampler:
+ // These are handles.
+ break;
+ default:
+ // Anything else can be a raw pointer.
+ type_name += "*";
+ break;
+ }
+ return type_name;
+ }
+
+ switch (type.basetype)
+ {
+ case SPIRType::Struct:
+ // Need OpName lookup here to get a "sensible" name for a struct.
+ return to_name(type.self);
+
+ case SPIRType::Image:
+ case SPIRType::SampledImage:
+ return image_type_glsl(type, id);
+
+ case SPIRType::Sampler:
+ return sampler_type(type);
+
+ case SPIRType::Void:
+ return "void";
+
+ case SPIRType::AtomicCounter:
+ return "atomic_uint";
+
+ case SPIRType::ControlPointArray:
+ return join("patch_control_point<", type_to_glsl(get<SPIRType>(type.parent_type), id), ">");
+
+ // Scalars
+ case SPIRType::Boolean:
+ type_name = "bool";
+ break;
+ case SPIRType::Char:
+ case SPIRType::SByte:
+ type_name = "char";
+ break;
+ case SPIRType::UByte:
+ type_name = "uchar";
+ break;
+ case SPIRType::Short:
+ type_name = "short";
+ break;
+ case SPIRType::UShort:
+ type_name = "ushort";
+ break;
+ case SPIRType::Int:
+ type_name = "int";
+ break;
+ case SPIRType::UInt:
+ type_name = "uint";
+ break;
+ case SPIRType::Int64:
+ type_name = "long"; // Currently unsupported
+ break;
+ case SPIRType::UInt64:
+ type_name = "size_t";
+ break;
+ case SPIRType::Half:
+ type_name = "half";
+ break;
+ case SPIRType::Float:
+ type_name = "float";
+ break;
+ case SPIRType::Double:
+ type_name = "double"; // Currently unsupported
+ break;
+
+ default:
+ return "unknown_type";
+ }
+
+ // Matrix?
+ if (type.columns > 1)
+ type_name += to_string(type.columns) + "x";
+
+ // Vector or Matrix?
+ if (type.vecsize > 1)
+ type_name += to_string(type.vecsize);
+
+ return type_name;
+}
+
+std::string CompilerMSL::sampler_type(const SPIRType &type)
+{
+ if (!type.array.empty())
+ {
+ if (!msl_options.supports_msl_version(2))
+ SPIRV_CROSS_THROW("MSL 2.0 or greater is required for arrays of samplers.");
+
+ if (type.array.size() > 1)
+ SPIRV_CROSS_THROW("Arrays of arrays of samplers are not supported in MSL.");
+
+ // Arrays of samplers in MSL must be declared with a special array<T, N> syntax ala C++11 std::array.
+ uint32_t array_size = to_array_size_literal(type);
+ if (array_size == 0)
+ SPIRV_CROSS_THROW("Unsized array of samplers is not supported in MSL.");
+
+ auto &parent = get<SPIRType>(get_pointee_type(type).parent_type);
+ return join("array<", sampler_type(parent), ", ", array_size, ">");
+ }
+ else
+ return "sampler";
+}
+
+// Returns an MSL string describing the SPIR-V image type
+string CompilerMSL::image_type_glsl(const SPIRType &type, uint32_t id)
+{
+ auto *var = maybe_get<SPIRVariable>(id);
+ if (var && var->basevariable)
+ {
+ // For comparison images, check against the base variable,
+ // and not the fake ID which might have been generated for this variable.
+ id = var->basevariable;
+ }
+
+ if (!type.array.empty())
+ {
+ uint32_t major = 2, minor = 0;
+ if (msl_options.is_ios())
+ {
+ major = 1;
+ minor = 2;
+ }
+ if (!msl_options.supports_msl_version(major, minor))
+ {
+ if (msl_options.is_ios())
+ SPIRV_CROSS_THROW("MSL 1.2 or greater is required for arrays of textures.");
+ else
+ SPIRV_CROSS_THROW("MSL 2.0 or greater is required for arrays of textures.");
+ }
+
+ if (type.array.size() > 1)
+ SPIRV_CROSS_THROW("Arrays of arrays of textures are not supported in MSL.");
+
+ // Arrays of images in MSL must be declared with a special array<T, N> syntax ala C++11 std::array.
+ uint32_t array_size = to_array_size_literal(type);
+ if (array_size == 0)
+ SPIRV_CROSS_THROW("Unsized array of images is not supported in MSL.");
+
+ auto &parent = get<SPIRType>(get_pointee_type(type).parent_type);
+ return join("array<", image_type_glsl(parent, id), ", ", array_size, ">");
+ }
+
+ string img_type_name;
+
+ // Bypass pointers because we need the real image struct
+ auto &img_type = get<SPIRType>(type.self).image;
+ if (image_is_comparison(type, id))
+ {
+ switch (img_type.dim)
+ {
+ case Dim1D:
+ img_type_name += "depth1d_unsupported_by_metal";
+ break;
+ case Dim2D:
+ if (img_type.ms && img_type.arrayed)
+ {
+ if (!msl_options.supports_msl_version(2, 1))
+ SPIRV_CROSS_THROW("Multisampled array textures are supported from 2.1.");
+ img_type_name += "depth2d_ms_array";
+ }
+ else if (img_type.ms)
+ img_type_name += "depth2d_ms";
+ else if (img_type.arrayed)
+ img_type_name += "depth2d_array";
+ else
+ img_type_name += "depth2d";
+ break;
+ case Dim3D:
+ img_type_name += "depth3d_unsupported_by_metal";
+ break;
+ case DimCube:
+ img_type_name += (img_type.arrayed ? "depthcube_array" : "depthcube");
+ break;
+ default:
+ img_type_name += "unknown_depth_texture_type";
+ break;
+ }
+ }
+ else
+ {
+ switch (img_type.dim)
+ {
+ case Dim1D:
+ img_type_name += (img_type.arrayed ? "texture1d_array" : "texture1d");
+ break;
+ case DimBuffer:
+ case Dim2D:
+ case DimSubpassData:
+ if (img_type.ms && img_type.arrayed)
+ {
+ if (!msl_options.supports_msl_version(2, 1))
+ SPIRV_CROSS_THROW("Multisampled array textures are supported from 2.1.");
+ img_type_name += "texture2d_ms_array";
+ }
+ else if (img_type.ms)
+ img_type_name += "texture2d_ms";
+ else if (img_type.arrayed)
+ img_type_name += "texture2d_array";
+ else
+ img_type_name += "texture2d";
+ break;
+ case Dim3D:
+ img_type_name += "texture3d";
+ break;
+ case DimCube:
+ img_type_name += (img_type.arrayed ? "texturecube_array" : "texturecube");
+ break;
+ default:
+ img_type_name += "unknown_texture_type";
+ break;
+ }
+ }
+
+ // Append the pixel type
+ img_type_name += "<";
+ img_type_name += type_to_glsl(get<SPIRType>(img_type.type));
+
+ // For unsampled images, append the sample/read/write access qualifier.
+ // For kernel images, the access qualifier my be supplied directly by SPIR-V.
+ // Otherwise it may be set based on whether the image is read from or written to within the shader.
+ if (type.basetype == SPIRType::Image && type.image.sampled == 2 && type.image.dim != DimSubpassData)
+ {
+ switch (img_type.access)
+ {
+ case AccessQualifierReadOnly:
+ img_type_name += ", access::read";
+ break;
+
+ case AccessQualifierWriteOnly:
+ img_type_name += ", access::write";
+ break;
+
+ case AccessQualifierReadWrite:
+ img_type_name += ", access::read_write";
+ break;
+
+ default:
+ {
+ auto *p_var = maybe_get_backing_variable(id);
+ if (p_var && p_var->basevariable)
+ p_var = maybe_get<SPIRVariable>(p_var->basevariable);
+ if (p_var && !has_decoration(p_var->self, DecorationNonWritable))
+ {
+ img_type_name += ", access::";
+
+ if (!has_decoration(p_var->self, DecorationNonReadable))
+ img_type_name += "read_";
+
+ img_type_name += "write";
+ }
+ break;
+ }
+ }
+ }
+
+ img_type_name += ">";
+
+ return img_type_name;
+}
+
+string CompilerMSL::bitcast_glsl_op(const SPIRType &out_type, const SPIRType &in_type)
+{
+ if (out_type.basetype == in_type.basetype)
+ return "";
+
+ assert(out_type.basetype != SPIRType::Boolean);
+ assert(in_type.basetype != SPIRType::Boolean);
+
+ bool integral_cast = type_is_integral(out_type) && type_is_integral(in_type);
+ bool same_size_cast = out_type.width == in_type.width;
+
+ if (integral_cast && same_size_cast)
+ {
+ // Trivial bitcast case, casts between integers.
+ return type_to_glsl(out_type);
+ }
+ else
+ {
+ // Fall back to the catch-all bitcast in MSL.
+ return "as_type<" + type_to_glsl(out_type) + ">";
+ }
+}
+
+// Returns an MSL string identifying the name of a SPIR-V builtin.
+// Output builtins are qualified with the name of the stage out structure.
+string CompilerMSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage)
+{
+ switch (builtin)
+ {
+
+ // Override GLSL compiler strictness
+ case BuiltInVertexId:
+ return "gl_VertexID";
+ case BuiltInInstanceId:
+ return "gl_InstanceID";
+ case BuiltInVertexIndex:
+ return "gl_VertexIndex";
+ case BuiltInInstanceIndex:
+ return "gl_InstanceIndex";
+ case BuiltInBaseVertex:
+ return "gl_BaseVertex";
+ case BuiltInBaseInstance:
+ return "gl_BaseInstance";
+ case BuiltInDrawIndex:
+ SPIRV_CROSS_THROW("DrawIndex is not supported in MSL.");
+
+ // When used in the entry function, output builtins are qualified with output struct name.
+ // Test storage class as NOT Input, as output builtins might be part of generic type.
+ // Also don't do this for tessellation control shaders.
+ case BuiltInViewportIndex:
+ if (!msl_options.supports_msl_version(2, 0))
+ SPIRV_CROSS_THROW("ViewportIndex requires Metal 2.0.");
+ /* fallthrough */
+ case BuiltInPosition:
+ case BuiltInPointSize:
+ case BuiltInClipDistance:
+ case BuiltInCullDistance:
+ case BuiltInLayer:
+ case BuiltInFragDepth:
+ case BuiltInSampleMask:
+ if (get_execution_model() == ExecutionModelTessellationControl)
+ break;
+ if (storage != StorageClassInput && current_function && (current_function->self == ir.default_entry_point))
+ return stage_out_var_name + "." + CompilerGLSL::builtin_to_glsl(builtin, storage);
+
+ break;
+
+ case BuiltInTessLevelOuter:
+ if (get_execution_model() == ExecutionModelTessellationEvaluation)
+ {
+ if (storage != StorageClassOutput && !get_entry_point().flags.get(ExecutionModeTriangles) &&
+ current_function && (current_function->self == ir.default_entry_point))
+ return join(patch_stage_in_var_name, ".", CompilerGLSL::builtin_to_glsl(builtin, storage));
+ else
+ break;
+ }
+ if (storage != StorageClassInput && current_function && (current_function->self == ir.default_entry_point))
+ return join(tess_factor_buffer_var_name, "[", to_expression(builtin_primitive_id_id),
+ "].edgeTessellationFactor");
+ break;
+
+ case BuiltInTessLevelInner:
+ if (get_execution_model() == ExecutionModelTessellationEvaluation)
+ {
+ if (storage != StorageClassOutput && !get_entry_point().flags.get(ExecutionModeTriangles) &&
+ current_function && (current_function->self == ir.default_entry_point))
+ return join(patch_stage_in_var_name, ".", CompilerGLSL::builtin_to_glsl(builtin, storage));
+ else
+ break;
+ }
+ if (storage != StorageClassInput && current_function && (current_function->self == ir.default_entry_point))
+ return join(tess_factor_buffer_var_name, "[", to_expression(builtin_primitive_id_id),
+ "].insideTessellationFactor");
+ break;
+
+ default:
+ break;
+ }
+
+ return CompilerGLSL::builtin_to_glsl(builtin, storage);
+}
+
+// Returns an MSL string attribute qualifer for a SPIR-V builtin
+string CompilerMSL::builtin_qualifier(BuiltIn builtin)
+{
+ auto &execution = get_entry_point();
+
+ switch (builtin)
+ {
+ // Vertex function in
+ case BuiltInVertexId:
+ return "vertex_id";
+ case BuiltInVertexIndex:
+ return "vertex_id";
+ case BuiltInBaseVertex:
+ return "base_vertex";
+ case BuiltInInstanceId:
+ return "instance_id";
+ case BuiltInInstanceIndex:
+ return "instance_id";
+ case BuiltInBaseInstance:
+ return "base_instance";
+ case BuiltInDrawIndex:
+ SPIRV_CROSS_THROW("DrawIndex is not supported in MSL.");
+
+ // Vertex function out
+ case BuiltInClipDistance:
+ return "clip_distance";
+ case BuiltInPointSize:
+ return "point_size";
+ case BuiltInPosition:
+ return "position";
+ case BuiltInLayer:
+ return "render_target_array_index";
+ case BuiltInViewportIndex:
+ if (!msl_options.supports_msl_version(2, 0))
+ SPIRV_CROSS_THROW("ViewportIndex requires Metal 2.0.");
+ return "viewport_array_index";
+
+ // Tess. control function in
+ case BuiltInInvocationId:
+ return "thread_index_in_threadgroup";
+ case BuiltInPatchVertices:
+ // Shouldn't be reached.
+ SPIRV_CROSS_THROW("PatchVertices is derived from the auxiliary buffer in MSL.");
+ case BuiltInPrimitiveId:
+ switch (execution.model)
+ {
+ case ExecutionModelTessellationControl:
+ return "threadgroup_position_in_grid";
+ case ExecutionModelTessellationEvaluation:
+ return "patch_id";
+ default:
+ SPIRV_CROSS_THROW("PrimitiveId is not supported in this execution model.");
+ }
+
+ // Tess. control function out
+ case BuiltInTessLevelOuter:
+ case BuiltInTessLevelInner:
+ // Shouldn't be reached.
+ SPIRV_CROSS_THROW("Tessellation levels are handled specially in MSL.");
+
+ // Tess. evaluation function in
+ case BuiltInTessCoord:
+ return "position_in_patch";
+
+ // Fragment function in
+ case BuiltInFrontFacing:
+ return "front_facing";
+ case BuiltInPointCoord:
+ return "point_coord";
+ case BuiltInFragCoord:
+ return "position";
+ case BuiltInSampleId:
+ return "sample_id";
+ case BuiltInSampleMask:
+ return "sample_mask";
+ case BuiltInSamplePosition:
+ // Shouldn't be reached.
+ SPIRV_CROSS_THROW("Sample position is retrieved by a function in MSL.");
+
+ // Fragment function out
+ case BuiltInFragDepth:
+ if (execution.flags.get(ExecutionModeDepthGreater))
+ return "depth(greater)";
+ else if (execution.flags.get(ExecutionModeDepthLess))
+ return "depth(less)";
+ else
+ return "depth(any)";
+
+ // Compute function in
+ case BuiltInGlobalInvocationId:
+ return "thread_position_in_grid";
+
+ case BuiltInWorkgroupId:
+ return "threadgroup_position_in_grid";
+
+ case BuiltInNumWorkgroups:
+ return "threadgroups_per_grid";
+
+ case BuiltInLocalInvocationId:
+ return "thread_position_in_threadgroup";
+
+ case BuiltInLocalInvocationIndex:
+ return "thread_index_in_threadgroup";
+
+ default:
+ return "unsupported-built-in";
+ }
+}
+
+// Returns an MSL string type declaration for a SPIR-V builtin
+string CompilerMSL::builtin_type_decl(BuiltIn builtin)
+{
+ const SPIREntryPoint &execution = get_entry_point();
+ switch (builtin)
+ {
+ // Vertex function in
+ case BuiltInVertexId:
+ return "uint";
+ case BuiltInVertexIndex:
+ return "uint";
+ case BuiltInBaseVertex:
+ return "uint";
+ case BuiltInInstanceId:
+ return "uint";
+ case BuiltInInstanceIndex:
+ return "uint";
+ case BuiltInBaseInstance:
+ return "uint";
+ case BuiltInDrawIndex:
+ SPIRV_CROSS_THROW("DrawIndex is not supported in MSL.");
+
+ // Vertex function out
+ case BuiltInClipDistance:
+ return "float";
+ case BuiltInPointSize:
+ return "float";
+ case BuiltInPosition:
+ return "float4";
+ case BuiltInLayer:
+ return "uint";
+ case BuiltInViewportIndex:
+ if (!msl_options.supports_msl_version(2, 0))
+ SPIRV_CROSS_THROW("ViewportIndex requires Metal 2.0.");
+ return "uint";
+
+ // Tess. control function in
+ case BuiltInInvocationId:
+ return "uint";
+ case BuiltInPatchVertices:
+ return "uint";
+ case BuiltInPrimitiveId:
+ return "uint";
+
+ // Tess. control function out
+ case BuiltInTessLevelInner:
+ if (execution.model == ExecutionModelTessellationEvaluation)
+ return !execution.flags.get(ExecutionModeTriangles) ? "float2" : "float";
+ return "half";
+ case BuiltInTessLevelOuter:
+ if (execution.model == ExecutionModelTessellationEvaluation)
+ return !execution.flags.get(ExecutionModeTriangles) ? "float4" : "float";
+ return "half";
+
+ // Tess. evaluation function in
+ case BuiltInTessCoord:
+ return execution.flags.get(ExecutionModeTriangles) ? "float3" : "float2";
+
+ // Fragment function in
+ case BuiltInFrontFacing:
+ return "bool";
+ case BuiltInPointCoord:
+ return "float2";
+ case BuiltInFragCoord:
+ return "float4";
+ case BuiltInSampleId:
+ return "uint";
+ case BuiltInSampleMask:
+ return "uint";
+ case BuiltInSamplePosition:
+ return "float2";
+
+ // Fragment function out
+ case BuiltInFragDepth:
+ return "float";
+
+ // Compute function in
+ case BuiltInGlobalInvocationId:
+ case BuiltInLocalInvocationId:
+ case BuiltInNumWorkgroups:
+ case BuiltInWorkgroupId:
+ return "uint3";
+ case BuiltInLocalInvocationIndex:
+ return "uint";
+
+ case BuiltInHelperInvocation:
+ return "bool";
+
+ default:
+ return "unsupported-built-in-type";
+ }
+}
+
+// Returns the declaration of a built-in argument to a function
+string CompilerMSL::built_in_func_arg(BuiltIn builtin, bool prefix_comma)
+{
+ string bi_arg;
+ if (prefix_comma)
+ bi_arg += ", ";
+
+ bi_arg += builtin_type_decl(builtin);
+ bi_arg += " " + builtin_to_glsl(builtin, StorageClassInput);
+ bi_arg += " [[" + builtin_qualifier(builtin) + "]]";
+
+ return bi_arg;
+}
+
+// Returns the byte size of a struct member.
+size_t CompilerMSL::get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const
+{
+ auto &type = get<SPIRType>(struct_type.member_types[index]);
+
+ switch (type.basetype)
+ {
+ case SPIRType::Unknown:
+ case SPIRType::Void:
+ case SPIRType::AtomicCounter:
+ case SPIRType::Image:
+ case SPIRType::SampledImage:
+ case SPIRType::Sampler:
+ SPIRV_CROSS_THROW("Querying size of opaque object.");
+
+ default:
+ {
+ // For arrays, we can use ArrayStride to get an easy check.
+ // Runtime arrays will have zero size so force to min of one.
+ if (!type.array.empty())
+ {
+ uint32_t array_size = to_array_size_literal(type);
+ return type_struct_member_array_stride(struct_type, index) * max(array_size, 1u);
+ }
+
+ if (type.basetype == SPIRType::Struct)
+ {
+ // The size of a struct in Metal is aligned up to its natural alignment.
+ auto size = get_declared_struct_size(type);
+ auto alignment = get_declared_struct_member_alignment(struct_type, index);
+ return (size + alignment - 1) & ~(alignment - 1);
+ }
+
+ uint32_t component_size = type.width / 8;
+ uint32_t vecsize = type.vecsize;
+ uint32_t columns = type.columns;
+
+ // An unpacked 3-element vector or matrix column is the same memory size as a 4-element.
+ if (vecsize == 3 && !has_extended_member_decoration(struct_type.self, index, SPIRVCrossDecorationPacked))
+ vecsize = 4;
+
+ return component_size * vecsize * columns;
+ }
+ }
+}
+
+// Returns the byte alignment of a struct member.
+size_t CompilerMSL::get_declared_struct_member_alignment(const SPIRType &struct_type, uint32_t index) const
+{
+ auto &type = get<SPIRType>(struct_type.member_types[index]);
+
+ switch (type.basetype)
+ {
+ case SPIRType::Unknown:
+ case SPIRType::Void:
+ case SPIRType::AtomicCounter:
+ case SPIRType::Image:
+ case SPIRType::SampledImage:
+ case SPIRType::Sampler:
+ SPIRV_CROSS_THROW("Querying alignment of opaque object.");
+
+ case SPIRType::Struct:
+ {
+ // In MSL, a struct's alignment is equal to the maximum alignment of any of its members.
+ uint32_t alignment = 1;
+ for (uint32_t i = 0; i < type.member_types.size(); i++)
+ alignment = max(alignment, uint32_t(get_declared_struct_member_alignment(type, i)));
+ return alignment;
+ }
+
+ default:
+ {
+ // Alignment of packed type is the same as the underlying component or column size.
+ // Alignment of unpacked type is the same as the vector size.
+ // Alignment of 3-elements vector is the same as 4-elements (including packed using column).
+ if (member_is_packed_type(struct_type, index))
+ {
+ // This is getting pretty complicated.
+ // The special case of array of float/float2 needs to be handled here.
+ uint32_t packed_type_id =
+ get_extended_member_decoration(struct_type.self, index, SPIRVCrossDecorationPackedType);
+ const SPIRType *packed_type = packed_type_id != 0 ? &get<SPIRType>(packed_type_id) : nullptr;
+ if (packed_type && is_array(*packed_type) && !is_matrix(*packed_type) &&
+ packed_type->basetype != SPIRType::Struct)
+ return (packed_type->width / 8) * 4;
+ else
+ return (type.width / 8) * (type.columns == 3 ? 4 : type.columns);
+ }
+ else
+ return (type.width / 8) * (type.vecsize == 3 ? 4 : type.vecsize);
+ }
+ }
+}
+
+bool CompilerMSL::skip_argument(uint32_t) const
+{
+ return false;
+}
+
+void CompilerMSL::analyze_sampled_image_usage()
+{
+ if (msl_options.swizzle_texture_samples)
+ {
+ SampledImageScanner scanner(*this);
+ traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), scanner);
+ }
+}
+
+bool CompilerMSL::SampledImageScanner::handle(spv::Op opcode, const uint32_t *args, uint32_t length)
+{
+ switch (opcode)
+ {
+ case OpLoad:
+ case OpImage:
+ case OpSampledImage:
+ {
+ if (length < 3)
+ return false;
+
+ uint32_t result_type = args[0];
+ auto &type = compiler.get<SPIRType>(result_type);
+ if ((type.basetype != SPIRType::Image && type.basetype != SPIRType::SampledImage) || type.image.sampled != 1)
+ return true;
+
+ uint32_t id = args[1];
+ compiler.set<SPIRExpression>(id, "", result_type, true);
+ break;
+ }
+ case OpImageSampleExplicitLod:
+ case OpImageSampleProjExplicitLod:
+ case OpImageSampleDrefExplicitLod:
+ case OpImageSampleProjDrefExplicitLod:
+ case OpImageSampleImplicitLod:
+ case OpImageSampleProjImplicitLod:
+ case OpImageSampleDrefImplicitLod:
+ case OpImageSampleProjDrefImplicitLod:
+ case OpImageFetch:
+ case OpImageGather:
+ case OpImageDrefGather:
+ compiler.has_sampled_images =
+ compiler.has_sampled_images || compiler.is_sampled_image_type(compiler.expression_type(args[2]));
+ compiler.needs_aux_buffer_def = compiler.needs_aux_buffer_def || compiler.has_sampled_images;
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+bool CompilerMSL::OpCodePreprocessor::handle(Op opcode, const uint32_t *args, uint32_t length)
+{
+ // Since MSL exists in a single execution scope, function prototype declarations are not
+ // needed, and clutter the output. If secondary functions are output (either as a SPIR-V
+ // function implementation or as indicated by the presence of OpFunctionCall), then set
+ // suppress_missing_prototypes to suppress compiler warnings of missing function prototypes.
+
+ // Mark if the input requires the implementation of an SPIR-V function that does not exist in Metal.
+ SPVFuncImpl spv_func = get_spv_func_impl(opcode, args);
+ if (spv_func != SPVFuncImplNone)
+ {
+ compiler.spv_function_implementations.insert(spv_func);
+ suppress_missing_prototypes = true;
+ }
+
+ switch (opcode)
+ {
+
+ case OpFunctionCall:
+ suppress_missing_prototypes = true;
+ break;
+
+ case OpImageWrite:
+ uses_resource_write = true;
+ break;
+
+ case OpStore:
+ check_resource_write(args[0]);
+ break;
+
+ case OpAtomicExchange:
+ case OpAtomicCompareExchange:
+ case OpAtomicCompareExchangeWeak:
+ case OpAtomicIIncrement:
+ case OpAtomicIDecrement:
+ case OpAtomicIAdd:
+ case OpAtomicISub:
+ case OpAtomicSMin:
+ case OpAtomicUMin:
+ case OpAtomicSMax:
+ case OpAtomicUMax:
+ case OpAtomicAnd:
+ case OpAtomicOr:
+ case OpAtomicXor:
+ uses_atomics = true;
+ check_resource_write(args[2]);
+ break;
+
+ case OpAtomicLoad:
+ uses_atomics = true;
+ break;
+
+ default:
+ break;
+ }
+
+ // If it has one, keep track of the instruction's result type, mapped by ID
+ uint32_t result_type, result_id;
+ if (compiler.instruction_to_result_type(result_type, result_id, opcode, args, length))
+ result_types[result_id] = result_type;
+
+ return true;
+}
+
+// If the variable is a Uniform or StorageBuffer, mark that a resource has been written to.
+void CompilerMSL::OpCodePreprocessor::check_resource_write(uint32_t var_id)
+{
+ auto *p_var = compiler.maybe_get_backing_variable(var_id);
+ StorageClass sc = p_var ? p_var->storage : StorageClassMax;
+ if (sc == StorageClassUniform || sc == StorageClassStorageBuffer)
+ uses_resource_write = true;
+}
+
+// Returns an enumeration of a SPIR-V function that needs to be output for certain Op codes.
+CompilerMSL::SPVFuncImpl CompilerMSL::OpCodePreprocessor::get_spv_func_impl(Op opcode, const uint32_t *args)
+{
+ switch (opcode)
+ {
+ case OpFMod:
+ return SPVFuncImplMod;
+
+ case OpFunctionCall:
+ {
+ auto &return_type = compiler.get<SPIRType>(args[0]);
+ if (return_type.array.size() > 1)
+ {
+ if (return_type.array.size() > SPVFuncImplArrayCopyMultidimMax)
+ SPIRV_CROSS_THROW("Cannot support this many dimensions for arrays of arrays.");
+ return static_cast<SPVFuncImpl>(SPVFuncImplArrayCopyMultidimBase + return_type.array.size());
+ }
+ else if (return_type.array.size() > 0)
+ return SPVFuncImplArrayCopy;
+
+ break;
+ }
+
+ case OpStore:
+ {
+ // Get the result type of the RHS. Since this is run as a pre-processing stage,
+ // we must extract the result type directly from the Instruction, rather than the ID.
+ uint32_t id_lhs = args[0];
+ uint32_t id_rhs = args[1];
+
+ const SPIRType *type = nullptr;
+ if (compiler.ir.ids[id_rhs].get_type() != TypeNone)
+ {
+ // Could be a constant, or similar.
+ type = &compiler.expression_type(id_rhs);
+ }
+ else
+ {
+ // Or ... an expression.
+ uint32_t tid = result_types[id_rhs];
+ if (tid)
+ type = &compiler.get<SPIRType>(tid);
+ }
+
+ auto *var = compiler.maybe_get<SPIRVariable>(id_lhs);
+
+ // Are we simply assigning to a statically assigned variable which takes a constant?
+ // Don't bother emitting this function.
+ bool static_expression_lhs =
+ var && var->storage == StorageClassFunction && var->statically_assigned && var->remapped_variable;
+ if (type && compiler.is_array(*type) && !static_expression_lhs)
+ {
+ if (type->array.size() > 1)
+ {
+ if (type->array.size() > SPVFuncImplArrayCopyMultidimMax)
+ SPIRV_CROSS_THROW("Cannot support this many dimensions for arrays of arrays.");
+ return static_cast<SPVFuncImpl>(SPVFuncImplArrayCopyMultidimBase + type->array.size());
+ }
+ else
+ return SPVFuncImplArrayCopy;
+ }
+
+ break;
+ }
+
+ case OpImageFetch:
+ case OpImageRead:
+ case OpImageWrite:
+ {
+ // Retrieve the image type, and if it's a Buffer, emit a texel coordinate function
+ uint32_t tid = result_types[args[opcode == OpImageWrite ? 0 : 2]];
+ if (tid && compiler.get<SPIRType>(tid).image.dim == DimBuffer)
+ return SPVFuncImplTexelBufferCoords;
+
+ if (opcode == OpImageFetch && compiler.msl_options.swizzle_texture_samples)
+ return SPVFuncImplTextureSwizzle;
+
+ break;
+ }
+
+ case OpImageSampleExplicitLod:
+ case OpImageSampleProjExplicitLod:
+ case OpImageSampleDrefExplicitLod:
+ case OpImageSampleProjDrefExplicitLod:
+ case OpImageSampleImplicitLod:
+ case OpImageSampleProjImplicitLod:
+ case OpImageSampleDrefImplicitLod:
+ case OpImageSampleProjDrefImplicitLod:
+ case OpImageGather:
+ case OpImageDrefGather:
+ if (compiler.msl_options.swizzle_texture_samples)
+ return SPVFuncImplTextureSwizzle;
+ break;
+
+ case OpCompositeConstruct:
+ {
+ auto &type = compiler.get<SPIRType>(args[0]);
+ if (type.array.size() > 1) // We need to use copies to build the composite.
+ return static_cast<SPVFuncImpl>(SPVFuncImplArrayCopyMultidimBase + type.array.size() - 1);
+ break;
+ }
+
+ case OpExtInst:
+ {
+ uint32_t extension_set = args[2];
+ if (compiler.get<SPIRExtension>(extension_set).ext == SPIRExtension::GLSL)
+ {
+ GLSLstd450 op_450 = static_cast<GLSLstd450>(args[3]);
+ switch (op_450)
+ {
+ case GLSLstd450Radians:
+ return SPVFuncImplRadians;
+ case GLSLstd450Degrees:
+ return SPVFuncImplDegrees;
+ case GLSLstd450FindILsb:
+ return SPVFuncImplFindILsb;
+ case GLSLstd450FindSMsb:
+ return SPVFuncImplFindSMsb;
+ case GLSLstd450FindUMsb:
+ return SPVFuncImplFindUMsb;
+ case GLSLstd450SSign:
+ return SPVFuncImplSSign;
+ case GLSLstd450MatrixInverse:
+ {
+ auto &mat_type = compiler.get<SPIRType>(args[0]);
+ switch (mat_type.columns)
+ {
+ case 2:
+ return SPVFuncImplInverse2x2;
+ case 3:
+ return SPVFuncImplInverse3x3;
+ case 4:
+ return SPVFuncImplInverse4x4;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ return SPVFuncImplNone;
+}
+
+// Sort both type and meta member content based on builtin status (put builtins at end),
+// then by the required sorting aspect.
+void CompilerMSL::MemberSorter::sort()
+{
+ // Create a temporary array of consecutive member indices and sort it based on how
+ // the members should be reordered, based on builtin and sorting aspect meta info.
+ size_t mbr_cnt = type.member_types.size();
+ vector<uint32_t> mbr_idxs(mbr_cnt);
+ iota(mbr_idxs.begin(), mbr_idxs.end(), 0); // Fill with consecutive indices
+ std::sort(mbr_idxs.begin(), mbr_idxs.end(), *this); // Sort member indices based on sorting aspect
+
+ // Move type and meta member info to the order defined by the sorted member indices.
+ // This is done by creating temporary copies of both member types and meta, and then
+ // copying back to the original content at the sorted indices.
+ auto mbr_types_cpy = type.member_types;
+ auto mbr_meta_cpy = meta.members;
+ for (uint32_t mbr_idx = 0; mbr_idx < mbr_cnt; mbr_idx++)
+ {
+ type.member_types[mbr_idx] = mbr_types_cpy[mbr_idxs[mbr_idx]];
+ meta.members[mbr_idx] = mbr_meta_cpy[mbr_idxs[mbr_idx]];
+ }
+}
+
+// Sort first by builtin status (put builtins at end), then by the sorting aspect.
+bool CompilerMSL::MemberSorter::operator()(uint32_t mbr_idx1, uint32_t mbr_idx2)
+{
+ auto &mbr_meta1 = meta.members[mbr_idx1];
+ auto &mbr_meta2 = meta.members[mbr_idx2];
+ if (mbr_meta1.builtin != mbr_meta2.builtin)
+ return mbr_meta2.builtin;
+ else
+ switch (sort_aspect)
+ {
+ case Location:
+ return mbr_meta1.location < mbr_meta2.location;
+ case LocationReverse:
+ return mbr_meta1.location > mbr_meta2.location;
+ case Offset:
+ return mbr_meta1.offset < mbr_meta2.offset;
+ case OffsetThenLocationReverse:
+ return (mbr_meta1.offset < mbr_meta2.offset) ||
+ ((mbr_meta1.offset == mbr_meta2.offset) && (mbr_meta1.location > mbr_meta2.location));
+ case Alphabetical:
+ return mbr_meta1.alias < mbr_meta2.alias;
+ default:
+ return false;
+ }
+}
+
+CompilerMSL::MemberSorter::MemberSorter(SPIRType &t, Meta &m, SortAspect sa)
+ : type(t)
+ , meta(m)
+ , sort_aspect(sa)
+{
+ // Ensure enough meta info is available
+ meta.members.resize(max(type.member_types.size(), meta.members.size()));
+}
+
+void CompilerMSL::remap_constexpr_sampler(uint32_t id, const MSLConstexprSampler &sampler)
+{
+ auto &type = get<SPIRType>(get<SPIRVariable>(id).basetype);
+ if (type.basetype != SPIRType::SampledImage && type.basetype != SPIRType::Sampler)
+ SPIRV_CROSS_THROW("Can only remap SampledImage and Sampler type.");
+ if (!type.array.empty())
+ SPIRV_CROSS_THROW("Can not remap array of samplers.");
+ constexpr_samplers[id] = sampler;
+}
+
+void CompilerMSL::bitcast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type)
+{
+ auto *var = maybe_get_backing_variable(source_id);
+ if (var)
+ source_id = var->self;
+
+ // Only interested in standalone builtin variables.
+ if (!has_decoration(source_id, DecorationBuiltIn))
+ return;
+
+ auto builtin = static_cast<BuiltIn>(get_decoration(source_id, DecorationBuiltIn));
+ auto expected_type = expr_type.basetype;
+ switch (builtin)
+ {
+ case BuiltInGlobalInvocationId:
+ case BuiltInLocalInvocationId:
+ case BuiltInWorkgroupId:
+ case BuiltInLocalInvocationIndex:
+ case BuiltInWorkgroupSize:
+ case BuiltInNumWorkgroups:
+ case BuiltInLayer:
+ case BuiltInViewportIndex:
+ expected_type = SPIRType::UInt;
+ break;
+
+ case BuiltInTessLevelInner:
+ case BuiltInTessLevelOuter:
+ if (get_execution_model() == ExecutionModelTessellationControl)
+ expected_type = SPIRType::Half;
+ break;
+
+ default:
+ break;
+ }
+
+ if (expected_type != expr_type.basetype)
+ expr = bitcast_expression(expr_type, expected_type, expr);
+
+ if (builtin == BuiltInTessCoord && get_entry_point().flags.get(ExecutionModeQuads) && expr_type.vecsize == 3)
+ {
+ // In SPIR-V, this is always a vec3, even for quads. In Metal, though, it's a float2 for quads.
+ // The code is expecting a float3, so we need to widen this.
+ expr = join("float3(", expr, ", 0)");
+ }
+}
+
+void CompilerMSL::bitcast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type)
+{
+ auto *var = maybe_get_backing_variable(target_id);
+ if (var)
+ target_id = var->self;
+
+ // Only interested in standalone builtin variables.
+ if (!has_decoration(target_id, DecorationBuiltIn))
+ return;
+
+ auto builtin = static_cast<BuiltIn>(get_decoration(target_id, DecorationBuiltIn));
+ auto expected_type = expr_type.basetype;
+ switch (builtin)
+ {
+ case BuiltInLayer:
+ case BuiltInViewportIndex:
+ expected_type = SPIRType::UInt;
+ break;
+
+ case BuiltInTessLevelInner:
+ case BuiltInTessLevelOuter:
+ expected_type = SPIRType::Half;
+ break;
+
+ default:
+ break;
+ }
+
+ if (expected_type != expr_type.basetype)
+ {
+ if (expected_type == SPIRType::Half && expr_type.basetype == SPIRType::Float)
+ {
+ // These are of different widths, so we cannot do a straight bitcast.
+ expr = join("half(", expr, ")");
+ }
+ else
+ {
+ auto type = expr_type;
+ type.basetype = expected_type;
+ expr = bitcast_expression(type, expr_type.basetype, expr);
+ }
+ }
+}
+
+std::string CompilerMSL::to_initializer_expression(const SPIRVariable &var)
+{
+ // We risk getting an array initializer here with MSL. If we have an array.
+ // FIXME: We cannot handle non-constant arrays being initialized.
+ // We will need to inject spvArrayCopy here somehow ...
+ auto &type = get<SPIRType>(var.basetype);
+ if (ir.ids[var.initializer].get_type() == TypeConstant &&
+ (!type.array.empty() || type.basetype == SPIRType::Struct))
+ return constant_expression(get<SPIRConstant>(var.initializer));
+ else
+ return CompilerGLSL::to_initializer_expression(var);
+}
+
+bool CompilerMSL::descriptor_set_is_argument_buffer(uint32_t desc_set) const
+{
+ if (!msl_options.argument_buffers)
+ return false;
+ if (desc_set >= kMaxArgumentBuffers)
+ return false;
+
+ return (argument_buffer_discrete_mask & (1u << desc_set)) == 0;
+}
+
+void CompilerMSL::analyze_argument_buffers()
+{
+ // Gather all used resources and sort them out into argument buffers.
+ // Each argument buffer corresponds to a descriptor set in SPIR-V.
+ // The [[id(N)]] values used correspond to the resource mapping we have for MSL.
+ // Otherwise, the binding number is used, but this is generally not safe some types like
+ // combined image samplers and arrays of resources. Metal needs different indices here,
+ // while SPIR-V can have one descriptor set binding. To use argument buffers in practice,
+ // you will need to use the remapping from the API.
+ for (auto &id : argument_buffer_ids)
+ id = 0;
+
+ // Output resources, sorted by resource index & type.
+ struct Resource
+ {
+ SPIRVariable *var;
+ string name;
+ SPIRType::BaseType basetype;
+ uint32_t index;
+ };
+ vector<Resource> resources_in_set[kMaxArgumentBuffers];
+
+ ir.for_each_typed_id<SPIRVariable>([&](uint32_t self, SPIRVariable &var) {
+ if ((var.storage == StorageClassUniform || var.storage == StorageClassUniformConstant ||
+ var.storage == StorageClassStorageBuffer) &&
+ !is_hidden_variable(var))
+ {
+ uint32_t desc_set = get_decoration(self, DecorationDescriptorSet);
+ // Ignore if it's part of a push descriptor set.
+ if (!descriptor_set_is_argument_buffer(desc_set))
+ return;
+
+ uint32_t var_id = var.self;
+ auto &type = get_variable_data_type(var);
+
+ if (desc_set >= kMaxArgumentBuffers)
+ SPIRV_CROSS_THROW("Descriptor set index is out of range.");
+
+ if (type.basetype == SPIRType::SampledImage)
+ {
+ add_resource_name(var_id);
+
+ uint32_t image_resource_index = get_metal_resource_index(var, SPIRType::Image);
+ uint32_t sampler_resource_index = get_metal_resource_index(var, SPIRType::Sampler);
+
+ // Avoid trivial conflicts where we didn't remap.
+ // This will let us at least compile test cases without having to instrument remaps.
+ if (sampler_resource_index == image_resource_index)
+ sampler_resource_index += type.array.empty() ? 1 : to_array_size_literal(type);
+
+ resources_in_set[desc_set].push_back({ &var, to_name(var_id), SPIRType::Image, image_resource_index });
+
+ if (type.image.dim != DimBuffer && constexpr_samplers.count(var_id) == 0)
+ {
+ resources_in_set[desc_set].push_back(
+ { &var, to_sampler_expression(var_id), SPIRType::Sampler, sampler_resource_index });
+ }
+ }
+ else if (constexpr_samplers.count(var_id) == 0)
+ {
+ // constexpr samplers are not declared as resources.
+ add_resource_name(var_id);
+ resources_in_set[desc_set].push_back(
+ { &var, to_name(var_id), type.basetype, get_metal_resource_index(var, type.basetype) });
+ }
+ }
+ });
+
+ for (uint32_t desc_set = 0; desc_set < kMaxArgumentBuffers; desc_set++)
+ {
+ auto &resources = resources_in_set[desc_set];
+ if (resources.empty())
+ continue;
+
+ assert(descriptor_set_is_argument_buffer(desc_set));
+
+ uint32_t next_id = ir.increase_bound_by(3);
+ uint32_t type_id = next_id + 1;
+ uint32_t ptr_type_id = next_id + 2;
+ argument_buffer_ids[desc_set] = next_id;
+
+ auto &buffer_type = set<SPIRType>(type_id);
+ buffer_type.storage = StorageClassUniform;
+ buffer_type.basetype = SPIRType::Struct;
+ set_name(type_id, join("spvDescriptorSetBuffer", desc_set));
+
+ auto &ptr_type = set<SPIRType>(ptr_type_id);
+ ptr_type = buffer_type;
+ ptr_type.pointer = true;
+ ptr_type.pointer_depth = 1;
+ ptr_type.parent_type = type_id;
+
+ uint32_t buffer_variable_id = next_id;
+ set<SPIRVariable>(buffer_variable_id, ptr_type_id, StorageClassUniform);
+ set_name(buffer_variable_id, join("spvDescriptorSet", desc_set));
+
+ // Ids must be emitted in ID order.
+ sort(begin(resources), end(resources), [&](const Resource &lhs, const Resource &rhs) -> bool {
+ return tie(lhs.index, lhs.basetype) < tie(rhs.index, rhs.basetype);
+ });
+
+ uint32_t member_index = 0;
+ for (auto &resource : resources)
+ {
+ auto &var = *resource.var;
+ auto &type = get_variable_data_type(var);
+ string mbr_name = ensure_valid_name(resource.name, "m");
+ set_member_name(buffer_type.self, member_index, mbr_name);
+
+ if (resource.basetype == SPIRType::Sampler && type.basetype != SPIRType::Sampler)
+ {
+ // Have to synthesize a sampler type here.
+
+ bool type_is_array = !type.array.empty();
+ uint32_t sampler_type_id = ir.increase_bound_by(type_is_array ? 2 : 1);
+ auto &new_sampler_type = set<SPIRType>(sampler_type_id);
+ new_sampler_type.basetype = SPIRType::Sampler;
+ new_sampler_type.storage = StorageClassUniformConstant;
+
+ if (type_is_array)
+ {
+ uint32_t sampler_type_array_id = sampler_type_id + 1;
+ auto &sampler_type_array = set<SPIRType>(sampler_type_array_id);
+ sampler_type_array = new_sampler_type;
+ sampler_type_array.array = type.array;
+ sampler_type_array.array_size_literal = type.array_size_literal;
+ sampler_type_array.parent_type = sampler_type_id;
+ buffer_type.member_types.push_back(sampler_type_array_id);
+ }
+ else
+ buffer_type.member_types.push_back(sampler_type_id);
+ }
+ else
+ {
+ if (resource.basetype == SPIRType::Image || resource.basetype == SPIRType::Sampler ||
+ resource.basetype == SPIRType::SampledImage)
+ {
+ // Drop pointer information when we emit the resources into a struct.
+ buffer_type.member_types.push_back(get_variable_data_type_id(var));
+ set_qualified_name(var.self, join(to_name(buffer_variable_id), ".", mbr_name));
+ }
+ else
+ {
+ // Resources will be declared as pointers not references, so automatically dereference as appropriate.
+ buffer_type.member_types.push_back(var.basetype);
+ if (type.array.empty())
+ set_qualified_name(var.self, join("(*", to_name(buffer_variable_id), ".", mbr_name, ")"));
+ else
+ set_qualified_name(var.self, join(to_name(buffer_variable_id), ".", mbr_name));
+ }
+ }
+
+ set_extended_member_decoration(buffer_type.self, member_index, SPIRVCrossDecorationArgumentBufferID,
+ resource.index);
+ set_extended_member_decoration(buffer_type.self, member_index, SPIRVCrossDecorationInterfaceOrigID,
+ var.self);
+ member_index++;
+ }
+ }
+}
diff --git a/src/3rdparty/SPIRV-Cross/spirv_msl.hpp b/src/3rdparty/SPIRV-Cross/spirv_msl.hpp
new file mode 100644
index 0000000..38610e1
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_msl.hpp
@@ -0,0 +1,600 @@
+/*
+ * Copyright 2016-2019 The Brenwill Workshop Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SPIRV_CROSS_MSL_HPP
+#define SPIRV_CROSS_MSL_HPP
+
+#include "spirv_glsl.hpp"
+#include <map>
+#include <set>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+namespace spirv_cross
+{
+
+// Indicates the format of the vertex attribute. Currently limited to specifying
+// if the attribute is an 8-bit unsigned integer, 16-bit unsigned integer, or
+// some other format.
+enum MSLVertexFormat
+{
+ MSL_VERTEX_FORMAT_OTHER = 0,
+ MSL_VERTEX_FORMAT_UINT8 = 1,
+ MSL_VERTEX_FORMAT_UINT16 = 2,
+ MSL_VERTEX_FORMAT_INT_MAX = 0x7fffffff
+};
+
+// Defines MSL characteristics of a vertex attribute at a particular location.
+// After compilation, it is possible to query whether or not this location was used.
+struct MSLVertexAttr
+{
+ uint32_t location = 0;
+ uint32_t msl_buffer = 0;
+ uint32_t msl_offset = 0;
+ uint32_t msl_stride = 0;
+ bool per_instance = false;
+ MSLVertexFormat format = MSL_VERTEX_FORMAT_OTHER;
+ spv::BuiltIn builtin = spv::BuiltInMax;
+};
+
+// Matches the binding index of a MSL resource for a binding within a descriptor set.
+// Taken together, the stage, desc_set and binding combine to form a reference to a resource
+// descriptor used in a particular shading stage.
+// If using MSL 2.0 argument buffers, and the descriptor set is not marked as a discrete descriptor set,
+// the binding reference we remap to will become an [[id(N)]] attribute within
+// the "descriptor set" argument buffer structure.
+// For resources which are bound in the "classic" MSL 1.0 way or discrete descriptors, the remap will become a
+// [[buffer(N)]], [[texture(N)]] or [[sampler(N)]] depending on the resource types used.
+struct MSLResourceBinding
+{
+ spv::ExecutionModel stage = spv::ExecutionModelMax;
+ uint32_t desc_set = 0;
+ uint32_t binding = 0;
+ uint32_t msl_buffer = 0;
+ uint32_t msl_texture = 0;
+ uint32_t msl_sampler = 0;
+};
+
+enum MSLSamplerCoord
+{
+ MSL_SAMPLER_COORD_NORMALIZED = 0,
+ MSL_SAMPLER_COORD_PIXEL = 1,
+ MSL_SAMPLER_INT_MAX = 0x7fffffff
+};
+
+enum MSLSamplerFilter
+{
+ MSL_SAMPLER_FILTER_NEAREST = 0,
+ MSL_SAMPLER_FILTER_LINEAR = 1,
+ MSL_SAMPLER_FILTER_INT_MAX = 0x7fffffff
+};
+
+enum MSLSamplerMipFilter
+{
+ MSL_SAMPLER_MIP_FILTER_NONE = 0,
+ MSL_SAMPLER_MIP_FILTER_NEAREST = 1,
+ MSL_SAMPLER_MIP_FILTER_LINEAR = 2,
+ MSL_SAMPLER_MIP_FILTER_INT_MAX = 0x7fffffff
+};
+
+enum MSLSamplerAddress
+{
+ MSL_SAMPLER_ADDRESS_CLAMP_TO_ZERO = 0,
+ MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE = 1,
+ MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER = 2,
+ MSL_SAMPLER_ADDRESS_REPEAT = 3,
+ MSL_SAMPLER_ADDRESS_MIRRORED_REPEAT = 4,
+ MSL_SAMPLER_ADDRESS_INT_MAX = 0x7fffffff
+};
+
+enum MSLSamplerCompareFunc
+{
+ MSL_SAMPLER_COMPARE_FUNC_NEVER = 0,
+ MSL_SAMPLER_COMPARE_FUNC_LESS = 1,
+ MSL_SAMPLER_COMPARE_FUNC_LESS_EQUAL = 2,
+ MSL_SAMPLER_COMPARE_FUNC_GREATER = 3,
+ MSL_SAMPLER_COMPARE_FUNC_GREATER_EQUAL = 4,
+ MSL_SAMPLER_COMPARE_FUNC_EQUAL = 5,
+ MSL_SAMPLER_COMPARE_FUNC_NOT_EQUAL = 6,
+ MSL_SAMPLER_COMPARE_FUNC_ALWAYS = 7,
+ MSL_SAMPLER_COMPARE_FUNC_INT_MAX = 0x7fffffff
+};
+
+enum MSLSamplerBorderColor
+{
+ MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK = 0,
+ MSL_SAMPLER_BORDER_COLOR_OPAQUE_BLACK = 1,
+ MSL_SAMPLER_BORDER_COLOR_OPAQUE_WHITE = 2,
+ MSL_SAMPLER_BORDER_COLOR_INT_MAX = 0x7fffffff
+};
+
+struct MSLConstexprSampler
+{
+ MSLSamplerCoord coord = MSL_SAMPLER_COORD_NORMALIZED;
+ MSLSamplerFilter min_filter = MSL_SAMPLER_FILTER_NEAREST;
+ MSLSamplerFilter mag_filter = MSL_SAMPLER_FILTER_NEAREST;
+ MSLSamplerMipFilter mip_filter = MSL_SAMPLER_MIP_FILTER_NONE;
+ MSLSamplerAddress s_address = MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE;
+ MSLSamplerAddress t_address = MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE;
+ MSLSamplerAddress r_address = MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE;
+ MSLSamplerCompareFunc compare_func = MSL_SAMPLER_COMPARE_FUNC_NEVER;
+ MSLSamplerBorderColor border_color = MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK;
+ float lod_clamp_min = 0.0f;
+ float lod_clamp_max = 1000.0f;
+ int max_anisotropy = 1;
+
+ bool compare_enable = false;
+ bool lod_clamp_enable = false;
+ bool anisotropy_enable = false;
+};
+
+// Tracks the type ID and member index of a struct member
+using MSLStructMemberKey = uint64_t;
+
+// Special constant used in a MSLResourceBinding desc_set
+// element to indicate the bindings for the push constants.
+static const uint32_t kPushConstDescSet = ~(0u);
+
+// Special constant used in a MSLResourceBinding binding
+// element to indicate the bindings for the push constants.
+static const uint32_t kPushConstBinding = 0;
+
+static const uint32_t kMaxArgumentBuffers = 8;
+
+// The current version of the aux buffer structure. It must be incremented any time a
+// new field is added to the aux buffer.
+#define SPIRV_CROSS_MSL_AUX_BUFFER_STRUCT_VERSION 1
+
+// Decompiles SPIR-V to Metal Shading Language
+class CompilerMSL : public CompilerGLSL
+{
+public:
+ // Options for compiling to Metal Shading Language
+ struct Options
+ {
+ typedef enum
+ {
+ iOS = 0,
+ macOS = 1
+ } Platform;
+
+ Platform platform = macOS;
+ uint32_t msl_version = make_msl_version(1, 2);
+ uint32_t texel_buffer_texture_width = 4096; // Width of 2D Metal textures used as 1D texel buffers
+ uint32_t aux_buffer_index = 30;
+ uint32_t indirect_params_buffer_index = 29;
+ uint32_t shader_output_buffer_index = 28;
+ uint32_t shader_patch_output_buffer_index = 27;
+ uint32_t shader_tess_factor_buffer_index = 26;
+ uint32_t shader_input_wg_index = 0;
+ bool enable_point_size_builtin = true;
+ bool disable_rasterization = false;
+ bool capture_output_to_buffer = false;
+ bool swizzle_texture_samples = false;
+ bool tess_domain_origin_lower_left = false;
+
+ // Enable use of MSL 2.0 indirect argument buffers.
+ // MSL 2.0 must also be enabled.
+ bool argument_buffers = false;
+
+ // Fragment output in MSL must have at least as many components as the render pass.
+ // Add support to explicit pad out components.
+ bool pad_fragment_output_components = false;
+
+ bool is_ios()
+ {
+ return platform == iOS;
+ }
+
+ bool is_macos()
+ {
+ return platform == macOS;
+ }
+
+ void set_msl_version(uint32_t major, uint32_t minor = 0, uint32_t patch = 0)
+ {
+ msl_version = make_msl_version(major, minor, patch);
+ }
+
+ bool supports_msl_version(uint32_t major, uint32_t minor = 0, uint32_t patch = 0)
+ {
+ return msl_version >= make_msl_version(major, minor, patch);
+ }
+
+ static uint32_t make_msl_version(uint32_t major, uint32_t minor = 0, uint32_t patch = 0)
+ {
+ return (major * 10000) + (minor * 100) + patch;
+ }
+ };
+
+ const Options &get_msl_options() const
+ {
+ return msl_options;
+ }
+
+ void set_msl_options(const Options &opts)
+ {
+ msl_options = opts;
+ }
+
+ // Provide feedback to calling API to allow runtime to disable pipeline
+ // rasterization if vertex shader requires rasterization to be disabled.
+ bool get_is_rasterization_disabled() const
+ {
+ return is_rasterization_disabled && (get_entry_point().model == spv::ExecutionModelVertex ||
+ get_entry_point().model == spv::ExecutionModelTessellationControl ||
+ get_entry_point().model == spv::ExecutionModelTessellationEvaluation);
+ }
+
+ // Provide feedback to calling API to allow it to pass an auxiliary
+ // buffer if the shader needs it.
+ bool needs_aux_buffer() const
+ {
+ return used_aux_buffer;
+ }
+
+ // Provide feedback to calling API to allow it to pass an output
+ // buffer if the shader needs it.
+ bool needs_output_buffer() const
+ {
+ return capture_output_to_buffer && stage_out_var_id != 0;
+ }
+
+ // Provide feedback to calling API to allow it to pass a patch output
+ // buffer if the shader needs it.
+ bool needs_patch_output_buffer() const
+ {
+ return capture_output_to_buffer && patch_stage_out_var_id != 0;
+ }
+
+ // Provide feedback to calling API to allow it to pass an input threadgroup
+ // buffer if the shader needs it.
+ bool needs_input_threadgroup_mem() const
+ {
+ return capture_output_to_buffer && stage_in_var_id != 0;
+ }
+
+ explicit CompilerMSL(std::vector<uint32_t> spirv);
+ CompilerMSL(const uint32_t *ir, size_t word_count);
+ explicit CompilerMSL(const ParsedIR &ir);
+ explicit CompilerMSL(ParsedIR &&ir);
+
+ // attr is a vertex attribute binding used to match
+ // vertex content locations to MSL attributes. If vertex attributes are provided,
+ // is_msl_vertex_attribute_used() will return true after calling ::compile() if
+ // the location was used by the MSL code.
+ void add_msl_vertex_attribute(const MSLVertexAttr &attr);
+
+ // resource is a resource binding to indicate the MSL buffer,
+ // texture or sampler index to use for a particular SPIR-V description set
+ // and binding. If resource bindings are provided,
+ // is_msl_resource_binding_used() will return true after calling ::compile() if
+ // the set/binding combination was used by the MSL code.
+ void add_msl_resource_binding(const MSLResourceBinding &resource);
+
+ // When using MSL argument buffers, we can force "classic" MSL 1.0 binding schemes for certain descriptor sets.
+ // This corresponds to VK_KHR_push_descriptor in Vulkan.
+ void add_discrete_descriptor_set(uint32_t desc_set);
+
+ // Query after compilation is done. This allows you to check if a location or set/binding combination was used by the shader.
+ bool is_msl_vertex_attribute_used(uint32_t location);
+ bool is_msl_resource_binding_used(spv::ExecutionModel model, uint32_t set, uint32_t binding);
+
+ // Compiles the SPIR-V code into Metal Shading Language.
+ std::string compile() override;
+
+ // Remap a sampler with ID to a constexpr sampler.
+ // Older iOS targets must use constexpr samplers in certain cases (PCF),
+ // so a static sampler must be used.
+ // The sampler will not consume a binding, but be declared in the entry point as a constexpr sampler.
+ // This can be used on both combined image/samplers (sampler2D) or standalone samplers.
+ // The remapped sampler must not be an array of samplers.
+ void remap_constexpr_sampler(uint32_t id, const MSLConstexprSampler &sampler);
+
+ // If using CompilerMSL::Options::pad_fragment_output_components, override the number of components we expect
+ // to use for a particular location. The default is 4 if number of components is not overridden.
+ void set_fragment_output_components(uint32_t location, uint32_t components);
+
+protected:
+ // An enum of SPIR-V functions that are implemented in additional
+ // source code that is added to the shader if necessary.
+ enum SPVFuncImpl
+ {
+ SPVFuncImplNone,
+ SPVFuncImplMod,
+ SPVFuncImplRadians,
+ SPVFuncImplDegrees,
+ SPVFuncImplFindILsb,
+ SPVFuncImplFindSMsb,
+ SPVFuncImplFindUMsb,
+ SPVFuncImplSSign,
+ SPVFuncImplArrayCopyMultidimBase,
+ // Unfortunately, we cannot use recursive templates in the MSL compiler properly,
+ // so stamp out variants up to some arbitrary maximum.
+ SPVFuncImplArrayCopy = SPVFuncImplArrayCopyMultidimBase + 1,
+ SPVFuncImplArrayOfArrayCopy2Dim = SPVFuncImplArrayCopyMultidimBase + 2,
+ SPVFuncImplArrayOfArrayCopy3Dim = SPVFuncImplArrayCopyMultidimBase + 3,
+ SPVFuncImplArrayOfArrayCopy4Dim = SPVFuncImplArrayCopyMultidimBase + 4,
+ SPVFuncImplArrayOfArrayCopy5Dim = SPVFuncImplArrayCopyMultidimBase + 5,
+ SPVFuncImplArrayOfArrayCopy6Dim = SPVFuncImplArrayCopyMultidimBase + 6,
+ SPVFuncImplTexelBufferCoords,
+ SPVFuncImplInverse4x4,
+ SPVFuncImplInverse3x3,
+ SPVFuncImplInverse2x2,
+ SPVFuncImplRowMajor2x3,
+ SPVFuncImplRowMajor2x4,
+ SPVFuncImplRowMajor3x2,
+ SPVFuncImplRowMajor3x4,
+ SPVFuncImplRowMajor4x2,
+ SPVFuncImplRowMajor4x3,
+ SPVFuncImplTextureSwizzle,
+ SPVFuncImplArrayCopyMultidimMax = 6
+ };
+
+ void emit_binary_unord_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op);
+ void emit_instruction(const Instruction &instr) override;
+ void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args,
+ uint32_t count) override;
+ void emit_header() override;
+ void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override;
+ void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override;
+ void emit_fixup() override;
+ std::string to_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index,
+ const std::string &qualifier = "");
+ void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index,
+ const std::string &qualifier = "", uint32_t base_offset = 0) override;
+ std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override;
+ std::string image_type_glsl(const SPIRType &type, uint32_t id = 0) override;
+ std::string sampler_type(const SPIRType &type);
+ std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) override;
+ size_t get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const override;
+ std::string to_func_call_arg(uint32_t id) override;
+ std::string to_name(uint32_t id, bool allow_alias = true) const override;
+ std::string to_function_name(uint32_t img, const SPIRType &imgtype, bool is_fetch, bool is_gather, bool is_proj,
+ bool has_array_offsets, bool has_offset, bool has_grad, bool has_dref,
+ uint32_t lod) override;
+ std::string to_function_args(uint32_t img, const SPIRType &imgtype, bool is_fetch, bool is_gather, bool is_proj,
+ uint32_t coord, uint32_t coord_components, uint32_t dref, uint32_t grad_x,
+ uint32_t grad_y, uint32_t lod, uint32_t coffset, uint32_t offset, uint32_t bias,
+ uint32_t comp, uint32_t sample, bool *p_forward) override;
+ std::string to_initializer_expression(const SPIRVariable &var) override;
+ std::string unpack_expression_type(std::string expr_str, const SPIRType &type, uint32_t packed_type_id) override;
+ std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type) override;
+ bool skip_argument(uint32_t id) const override;
+ std::string to_member_reference(uint32_t base, const SPIRType &type, uint32_t index, bool ptr_chain) override;
+ std::string to_qualifiers_glsl(uint32_t id) override;
+ void replace_illegal_names() override;
+ void declare_undefined_values() override;
+ void declare_constant_arrays();
+ bool is_patch_block(const SPIRType &type);
+ bool is_non_native_row_major_matrix(uint32_t id) override;
+ bool member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index) override;
+ std::string convert_row_major_matrix(std::string exp_str, const SPIRType &exp_type, bool is_packed) override;
+
+ void preprocess_op_codes();
+ void localize_global_variables();
+ void extract_global_variables_from_functions();
+ void mark_packable_structs();
+ void mark_as_packable(SPIRType &type);
+
+ std::unordered_map<uint32_t, std::set<uint32_t>> function_global_vars;
+ void extract_global_variables_from_function(uint32_t func_id, std::set<uint32_t> &added_arg_ids,
+ std::unordered_set<uint32_t> &global_var_ids,
+ std::unordered_set<uint32_t> &processed_func_ids);
+ uint32_t add_interface_block(spv::StorageClass storage, bool patch = false);
+ uint32_t add_interface_block_pointer(uint32_t ib_var_id, spv::StorageClass storage);
+
+ void add_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, SPIRType &ib_type,
+ SPIRVariable &var, bool strip_array);
+ void add_composite_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
+ SPIRType &ib_type, SPIRVariable &var, bool strip_array);
+ void add_plain_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
+ SPIRType &ib_type, SPIRVariable &var, bool strip_array);
+ void add_plain_member_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
+ SPIRType &ib_type, SPIRVariable &var, uint32_t index,
+ bool strip_array);
+ void add_composite_member_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
+ SPIRType &ib_type, SPIRVariable &var, uint32_t index,
+ bool strip_array);
+ uint32_t get_accumulated_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array);
+ void add_tess_level_input_to_interface_block(const std::string &ib_var_ref, SPIRType &ib_type, SPIRVariable &var);
+
+ void fix_up_interface_member_indices(spv::StorageClass storage, uint32_t ib_type_id);
+
+ void mark_location_as_used_by_shader(uint32_t location, spv::StorageClass storage);
+ uint32_t ensure_correct_builtin_type(uint32_t type_id, spv::BuiltIn builtin);
+ uint32_t ensure_correct_attribute_type(uint32_t type_id, uint32_t location);
+
+ void emit_custom_functions();
+ void emit_resources();
+ void emit_specialization_constants_and_structs();
+ void emit_interface_block(uint32_t ib_var_id);
+ bool maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs);
+ void add_convert_row_major_matrix_function(uint32_t cols, uint32_t rows);
+ void fix_up_shader_inputs_outputs();
+
+ std::string func_type_decl(SPIRType &type);
+ std::string entry_point_args_classic(bool append_comma);
+ std::string entry_point_args_argument_buffer(bool append_comma);
+ std::string entry_point_arg_stage_in();
+ void entry_point_args_builtin(std::string &args);
+ void entry_point_args_discrete_descriptors(std::string &args);
+ std::string to_qualified_member_name(const SPIRType &type, uint32_t index);
+ std::string ensure_valid_name(std::string name, std::string pfx);
+ std::string to_sampler_expression(uint32_t id);
+ std::string to_swizzle_expression(uint32_t id);
+ std::string builtin_qualifier(spv::BuiltIn builtin);
+ std::string builtin_type_decl(spv::BuiltIn builtin);
+ std::string built_in_func_arg(spv::BuiltIn builtin, bool prefix_comma);
+ std::string member_attribute_qualifier(const SPIRType &type, uint32_t index);
+ std::string argument_decl(const SPIRFunction::Parameter &arg);
+ std::string round_fp_tex_coords(std::string tex_coords, bool coord_is_fp);
+ uint32_t get_metal_resource_index(SPIRVariable &var, SPIRType::BaseType basetype);
+ uint32_t get_ordered_member_location(uint32_t type_id, uint32_t index, uint32_t *comp = nullptr);
+ size_t get_declared_struct_member_alignment(const SPIRType &struct_type, uint32_t index) const;
+ std::string to_component_argument(uint32_t id);
+ void align_struct(SPIRType &ib_type);
+ bool is_member_packable(SPIRType &ib_type, uint32_t index);
+ MSLStructMemberKey get_struct_member_key(uint32_t type_id, uint32_t index);
+ std::string get_argument_address_space(const SPIRVariable &argument);
+ std::string get_type_address_space(const SPIRType &type, uint32_t id);
+ SPIRType &get_stage_in_struct_type();
+ SPIRType &get_stage_out_struct_type();
+ SPIRType &get_patch_stage_in_struct_type();
+ SPIRType &get_patch_stage_out_struct_type();
+ std::string get_tess_factor_struct_name();
+ void emit_atomic_func_op(uint32_t result_type, uint32_t result_id, const char *op, uint32_t mem_order_1,
+ uint32_t mem_order_2, bool has_mem_order_2, uint32_t op0, uint32_t op1 = 0,
+ bool op1_is_pointer = false, bool op1_is_literal = false, uint32_t op2 = 0);
+ const char *get_memory_order(uint32_t spv_mem_sem);
+ void add_pragma_line(const std::string &line);
+ void add_typedef_line(const std::string &line);
+ void emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uint32_t id_mem_sem);
+ void emit_array_copy(const std::string &lhs, uint32_t rhs_id) override;
+ void build_implicit_builtins();
+ void emit_entry_point_declarations() override;
+ uint32_t builtin_frag_coord_id = 0;
+ uint32_t builtin_sample_id_id = 0;
+ uint32_t builtin_vertex_idx_id = 0;
+ uint32_t builtin_base_vertex_id = 0;
+ uint32_t builtin_instance_idx_id = 0;
+ uint32_t builtin_base_instance_id = 0;
+ uint32_t builtin_invocation_id_id = 0;
+ uint32_t builtin_primitive_id_id = 0;
+ uint32_t aux_buffer_id = 0;
+
+ void bitcast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type) override;
+ void bitcast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type) override;
+ void emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression) override;
+
+ void analyze_sampled_image_usage();
+
+ bool emit_tessellation_access_chain(const uint32_t *ops, uint32_t length);
+ bool is_out_of_bounds_tessellation_level(uint32_t id_lhs);
+
+ Options msl_options;
+ std::set<SPVFuncImpl> spv_function_implementations;
+ std::unordered_map<uint32_t, MSLVertexAttr> vtx_attrs_by_location;
+ std::unordered_map<uint32_t, MSLVertexAttr> vtx_attrs_by_builtin;
+ std::unordered_set<uint32_t> vtx_attrs_in_use;
+ std::unordered_map<uint32_t, uint32_t> fragment_output_components;
+ std::unordered_map<MSLStructMemberKey, uint32_t> struct_member_padding;
+ std::set<std::string> pragma_lines;
+ std::set<std::string> typedef_lines;
+ std::vector<uint32_t> vars_needing_early_declaration;
+
+ std::vector<std::pair<MSLResourceBinding, bool>> resource_bindings;
+ uint32_t next_metal_resource_index_buffer = 0;
+ uint32_t next_metal_resource_index_texture = 0;
+ uint32_t next_metal_resource_index_sampler = 0;
+
+ uint32_t stage_in_var_id = 0;
+ uint32_t stage_out_var_id = 0;
+ uint32_t patch_stage_in_var_id = 0;
+ uint32_t patch_stage_out_var_id = 0;
+ uint32_t stage_in_ptr_var_id = 0;
+ uint32_t stage_out_ptr_var_id = 0;
+ bool has_sampled_images = false;
+ bool needs_vertex_idx_arg = false;
+ bool needs_instance_idx_arg = false;
+ bool is_rasterization_disabled = false;
+ bool capture_output_to_buffer = false;
+ bool needs_aux_buffer_def = false;
+ bool used_aux_buffer = false;
+ bool added_builtin_tess_level = false;
+ std::string qual_pos_var_name;
+ std::string stage_in_var_name = "in";
+ std::string stage_out_var_name = "out";
+ std::string patch_stage_in_var_name = "patchIn";
+ std::string patch_stage_out_var_name = "patchOut";
+ std::string sampler_name_suffix = "Smplr";
+ std::string swizzle_name_suffix = "Swzl";
+ std::string input_wg_var_name = "gl_in";
+ std::string output_buffer_var_name = "spvOut";
+ std::string patch_output_buffer_var_name = "spvPatchOut";
+ std::string tess_factor_buffer_var_name = "spvTessLevel";
+ spv::Op previous_instruction_opcode = spv::OpNop;
+
+ std::unordered_map<uint32_t, MSLConstexprSampler> constexpr_samplers;
+ std::vector<uint32_t> buffer_arrays;
+
+ uint32_t argument_buffer_ids[kMaxArgumentBuffers];
+ uint32_t argument_buffer_discrete_mask = 0;
+ void analyze_argument_buffers();
+ bool descriptor_set_is_argument_buffer(uint32_t desc_set) const;
+
+ uint32_t get_target_components_for_fragment_location(uint32_t location) const;
+ uint32_t build_extended_vector_type(uint32_t type_id, uint32_t components);
+
+ // OpcodeHandler that handles several MSL preprocessing operations.
+ struct OpCodePreprocessor : OpcodeHandler
+ {
+ OpCodePreprocessor(CompilerMSL &compiler_)
+ : compiler(compiler_)
+ {
+ }
+
+ bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override;
+ CompilerMSL::SPVFuncImpl get_spv_func_impl(spv::Op opcode, const uint32_t *args);
+ void check_resource_write(uint32_t var_id);
+
+ CompilerMSL &compiler;
+ std::unordered_map<uint32_t, uint32_t> result_types;
+ bool suppress_missing_prototypes = false;
+ bool uses_atomics = false;
+ bool uses_resource_write = false;
+ };
+
+ // OpcodeHandler that scans for uses of sampled images
+ struct SampledImageScanner : OpcodeHandler
+ {
+ SampledImageScanner(CompilerMSL &compiler_)
+ : compiler(compiler_)
+ {
+ }
+
+ bool handle(spv::Op opcode, const uint32_t *args, uint32_t) override;
+
+ CompilerMSL &compiler;
+ };
+
+ // Sorts the members of a SPIRType and associated Meta info based on a settable sorting
+ // aspect, which defines which aspect of the struct members will be used to sort them.
+ // Regardless of the sorting aspect, built-in members always appear at the end of the struct.
+ struct MemberSorter
+ {
+ enum SortAspect
+ {
+ Location,
+ LocationReverse,
+ Offset,
+ OffsetThenLocationReverse,
+ Alphabetical
+ };
+
+ void sort();
+ bool operator()(uint32_t mbr_idx1, uint32_t mbr_idx2);
+ MemberSorter(SPIRType &t, Meta &m, SortAspect sa);
+
+ SPIRType &type;
+ Meta &meta;
+ SortAspect sort_aspect;
+ };
+};
+} // namespace spirv_cross
+
+#endif
diff --git a/src/3rdparty/SPIRV-Cross/spirv_parser.cpp b/src/3rdparty/SPIRV-Cross/spirv_parser.cpp
new file mode 100644
index 0000000..fa87fa3
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_parser.cpp
@@ -0,0 +1,1121 @@
+/*
+ * Copyright 2018-2019 Arm Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "spirv_parser.hpp"
+#include <assert.h>
+
+using namespace std;
+using namespace spv;
+
+namespace spirv_cross
+{
+Parser::Parser(std::vector<uint32_t> spirv)
+{
+ ir.spirv = move(spirv);
+}
+
+Parser::Parser(const uint32_t *spirv_data, size_t word_count)
+{
+ ir.spirv = vector<uint32_t>(spirv_data, spirv_data + word_count);
+}
+
+static bool decoration_is_string(Decoration decoration)
+{
+ switch (decoration)
+ {
+ case DecorationHlslSemanticGOOGLE:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static inline uint32_t swap_endian(uint32_t v)
+{
+ return ((v >> 24) & 0x000000ffu) | ((v >> 8) & 0x0000ff00u) | ((v << 8) & 0x00ff0000u) | ((v << 24) & 0xff000000u);
+}
+
+static bool is_valid_spirv_version(uint32_t version)
+{
+ switch (version)
+ {
+ // Allow v99 since it tends to just work.
+ case 99:
+ case 0x10000: // SPIR-V 1.0
+ case 0x10100: // SPIR-V 1.1
+ case 0x10200: // SPIR-V 1.2
+ case 0x10300: // SPIR-V 1.3
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+void Parser::parse()
+{
+ auto &spirv = ir.spirv;
+
+ auto len = spirv.size();
+ if (len < 5)
+ SPIRV_CROSS_THROW("SPIRV file too small.");
+
+ auto s = spirv.data();
+
+ // Endian-swap if we need to.
+ if (s[0] == swap_endian(MagicNumber))
+ transform(begin(spirv), end(spirv), begin(spirv), [](uint32_t c) { return swap_endian(c); });
+
+ if (s[0] != MagicNumber || !is_valid_spirv_version(s[1]))
+ SPIRV_CROSS_THROW("Invalid SPIRV format.");
+
+ uint32_t bound = s[3];
+ ir.set_id_bounds(bound);
+
+ uint32_t offset = 5;
+
+ vector<Instruction> instructions;
+ while (offset < len)
+ {
+ Instruction instr = {};
+ instr.op = spirv[offset] & 0xffff;
+ instr.count = (spirv[offset] >> 16) & 0xffff;
+
+ if (instr.count == 0)
+ SPIRV_CROSS_THROW("SPIR-V instructions cannot consume 0 words. Invalid SPIR-V file.");
+
+ instr.offset = offset + 1;
+ instr.length = instr.count - 1;
+
+ offset += instr.count;
+
+ if (offset > spirv.size())
+ SPIRV_CROSS_THROW("SPIR-V instruction goes out of bounds.");
+
+ instructions.push_back(instr);
+ }
+
+ for (auto &i : instructions)
+ parse(i);
+
+ if (current_function)
+ SPIRV_CROSS_THROW("Function was not terminated.");
+ if (current_block)
+ SPIRV_CROSS_THROW("Block was not terminated.");
+}
+
+const uint32_t *Parser::stream(const Instruction &instr) const
+{
+ // If we're not going to use any arguments, just return nullptr.
+ // We want to avoid case where we return an out of range pointer
+ // that trips debug assertions on some platforms.
+ if (!instr.length)
+ return nullptr;
+
+ if (instr.offset + instr.length > ir.spirv.size())
+ SPIRV_CROSS_THROW("Compiler::stream() out of range.");
+ return &ir.spirv[instr.offset];
+}
+
+static string extract_string(const vector<uint32_t> &spirv, uint32_t offset)
+{
+ string ret;
+ for (uint32_t i = offset; i < spirv.size(); i++)
+ {
+ uint32_t w = spirv[i];
+
+ for (uint32_t j = 0; j < 4; j++, w >>= 8)
+ {
+ char c = w & 0xff;
+ if (c == '\0')
+ return ret;
+ ret += c;
+ }
+ }
+
+ SPIRV_CROSS_THROW("String was not terminated before EOF");
+}
+
+void Parser::parse(const Instruction &instruction)
+{
+ auto *ops = stream(instruction);
+ auto op = static_cast<Op>(instruction.op);
+ uint32_t length = instruction.length;
+
+ switch (op)
+ {
+ case OpMemoryModel:
+ case OpSourceContinued:
+ case OpSourceExtension:
+ case OpNop:
+ case OpLine:
+ case OpNoLine:
+ case OpString:
+ case OpModuleProcessed:
+ break;
+
+ case OpSource:
+ {
+ auto lang = static_cast<SourceLanguage>(ops[0]);
+ switch (lang)
+ {
+ case SourceLanguageESSL:
+ ir.source.es = true;
+ ir.source.version = ops[1];
+ ir.source.known = true;
+ ir.source.hlsl = false;
+ break;
+
+ case SourceLanguageGLSL:
+ ir.source.es = false;
+ ir.source.version = ops[1];
+ ir.source.known = true;
+ ir.source.hlsl = false;
+ break;
+
+ case SourceLanguageHLSL:
+ // For purposes of cross-compiling, this is GLSL 450.
+ ir.source.es = false;
+ ir.source.version = 450;
+ ir.source.known = true;
+ ir.source.hlsl = true;
+ break;
+
+ default:
+ ir.source.known = false;
+ break;
+ }
+ break;
+ }
+
+ case OpUndef:
+ {
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ set<SPIRUndef>(id, result_type);
+ break;
+ }
+
+ case OpCapability:
+ {
+ uint32_t cap = ops[0];
+ if (cap == CapabilityKernel)
+ SPIRV_CROSS_THROW("Kernel capability not supported.");
+
+ ir.declared_capabilities.push_back(static_cast<Capability>(ops[0]));
+ break;
+ }
+
+ case OpExtension:
+ {
+ auto ext = extract_string(ir.spirv, instruction.offset);
+ ir.declared_extensions.push_back(move(ext));
+ break;
+ }
+
+ case OpExtInstImport:
+ {
+ uint32_t id = ops[0];
+ auto ext = extract_string(ir.spirv, instruction.offset + 1);
+ if (ext == "GLSL.std.450")
+ set<SPIRExtension>(id, SPIRExtension::GLSL);
+ else if (ext == "SPV_AMD_shader_ballot")
+ set<SPIRExtension>(id, SPIRExtension::SPV_AMD_shader_ballot);
+ else if (ext == "SPV_AMD_shader_explicit_vertex_parameter")
+ set<SPIRExtension>(id, SPIRExtension::SPV_AMD_shader_explicit_vertex_parameter);
+ else if (ext == "SPV_AMD_shader_trinary_minmax")
+ set<SPIRExtension>(id, SPIRExtension::SPV_AMD_shader_trinary_minmax);
+ else if (ext == "SPV_AMD_gcn_shader")
+ set<SPIRExtension>(id, SPIRExtension::SPV_AMD_gcn_shader);
+ else
+ set<SPIRExtension>(id, SPIRExtension::Unsupported);
+
+ // Other SPIR-V extensions which have ExtInstrs are currently not supported.
+
+ break;
+ }
+
+ case OpEntryPoint:
+ {
+ auto itr =
+ ir.entry_points.insert(make_pair(ops[1], SPIREntryPoint(ops[1], static_cast<ExecutionModel>(ops[0]),
+ extract_string(ir.spirv, instruction.offset + 2))));
+ auto &e = itr.first->second;
+
+ // Strings need nul-terminator and consume the whole word.
+ uint32_t strlen_words = uint32_t((e.name.size() + 1 + 3) >> 2);
+ e.interface_variables.insert(end(e.interface_variables), ops + strlen_words + 2, ops + instruction.length);
+
+ // Set the name of the entry point in case OpName is not provided later.
+ ir.set_name(ops[1], e.name);
+
+ // If we don't have an entry, make the first one our "default".
+ if (!ir.default_entry_point)
+ ir.default_entry_point = ops[1];
+ break;
+ }
+
+ case OpExecutionMode:
+ {
+ auto &execution = ir.entry_points[ops[0]];
+ auto mode = static_cast<ExecutionMode>(ops[1]);
+ execution.flags.set(mode);
+
+ switch (mode)
+ {
+ case ExecutionModeInvocations:
+ execution.invocations = ops[2];
+ break;
+
+ case ExecutionModeLocalSize:
+ execution.workgroup_size.x = ops[2];
+ execution.workgroup_size.y = ops[3];
+ execution.workgroup_size.z = ops[4];
+ break;
+
+ case ExecutionModeOutputVertices:
+ execution.output_vertices = ops[2];
+ break;
+
+ default:
+ break;
+ }
+ break;
+ }
+
+ case OpName:
+ {
+ uint32_t id = ops[0];
+ ir.set_name(id, extract_string(ir.spirv, instruction.offset + 1));
+ break;
+ }
+
+ case OpMemberName:
+ {
+ uint32_t id = ops[0];
+ uint32_t member = ops[1];
+ ir.set_member_name(id, member, extract_string(ir.spirv, instruction.offset + 2));
+ break;
+ }
+
+ case OpDecorationGroup:
+ {
+ // Noop, this simply means an ID should be a collector of decorations.
+ // The meta array is already a flat array of decorations which will contain the relevant decorations.
+ break;
+ }
+
+ case OpGroupDecorate:
+ {
+ uint32_t group_id = ops[0];
+ auto &decorations = ir.meta[group_id].decoration;
+ auto &flags = decorations.decoration_flags;
+
+ // Copies decorations from one ID to another. Only copy decorations which are set in the group,
+ // i.e., we cannot just copy the meta structure directly.
+ for (uint32_t i = 1; i < length; i++)
+ {
+ uint32_t target = ops[i];
+ flags.for_each_bit([&](uint32_t bit) {
+ auto decoration = static_cast<Decoration>(bit);
+
+ if (decoration_is_string(decoration))
+ {
+ ir.set_decoration_string(target, decoration, ir.get_decoration_string(group_id, decoration));
+ }
+ else
+ {
+ ir.meta[target].decoration_word_offset[decoration] =
+ ir.meta[group_id].decoration_word_offset[decoration];
+ ir.set_decoration(target, decoration, ir.get_decoration(group_id, decoration));
+ }
+ });
+ }
+ break;
+ }
+
+ case OpGroupMemberDecorate:
+ {
+ uint32_t group_id = ops[0];
+ auto &flags = ir.meta[group_id].decoration.decoration_flags;
+
+ // Copies decorations from one ID to another. Only copy decorations which are set in the group,
+ // i.e., we cannot just copy the meta structure directly.
+ for (uint32_t i = 1; i + 1 < length; i += 2)
+ {
+ uint32_t target = ops[i + 0];
+ uint32_t index = ops[i + 1];
+ flags.for_each_bit([&](uint32_t bit) {
+ auto decoration = static_cast<Decoration>(bit);
+
+ if (decoration_is_string(decoration))
+ ir.set_member_decoration_string(target, index, decoration,
+ ir.get_decoration_string(group_id, decoration));
+ else
+ ir.set_member_decoration(target, index, decoration, ir.get_decoration(group_id, decoration));
+ });
+ }
+ break;
+ }
+
+ case OpDecorate:
+ case OpDecorateId:
+ {
+ // OpDecorateId technically supports an array of arguments, but our only supported decorations are single uint,
+ // so merge decorate and decorate-id here.
+ uint32_t id = ops[0];
+
+ auto decoration = static_cast<Decoration>(ops[1]);
+ if (length >= 3)
+ {
+ ir.meta[id].decoration_word_offset[decoration] = uint32_t(&ops[2] - ir.spirv.data());
+ ir.set_decoration(id, decoration, ops[2]);
+ }
+ else
+ ir.set_decoration(id, decoration);
+
+ break;
+ }
+
+ case OpDecorateStringGOOGLE:
+ {
+ uint32_t id = ops[0];
+ auto decoration = static_cast<Decoration>(ops[1]);
+ ir.set_decoration_string(id, decoration, extract_string(ir.spirv, instruction.offset + 2));
+ break;
+ }
+
+ case OpMemberDecorate:
+ {
+ uint32_t id = ops[0];
+ uint32_t member = ops[1];
+ auto decoration = static_cast<Decoration>(ops[2]);
+ if (length >= 4)
+ ir.set_member_decoration(id, member, decoration, ops[3]);
+ else
+ ir.set_member_decoration(id, member, decoration);
+ break;
+ }
+
+ case OpMemberDecorateStringGOOGLE:
+ {
+ uint32_t id = ops[0];
+ uint32_t member = ops[1];
+ auto decoration = static_cast<Decoration>(ops[2]);
+ ir.set_member_decoration_string(id, member, decoration, extract_string(ir.spirv, instruction.offset + 3));
+ break;
+ }
+
+ // Build up basic types.
+ case OpTypeVoid:
+ {
+ uint32_t id = ops[0];
+ auto &type = set<SPIRType>(id);
+ type.basetype = SPIRType::Void;
+ break;
+ }
+
+ case OpTypeBool:
+ {
+ uint32_t id = ops[0];
+ auto &type = set<SPIRType>(id);
+ type.basetype = SPIRType::Boolean;
+ type.width = 1;
+ break;
+ }
+
+ case OpTypeFloat:
+ {
+ uint32_t id = ops[0];
+ uint32_t width = ops[1];
+ auto &type = set<SPIRType>(id);
+ if (width == 64)
+ type.basetype = SPIRType::Double;
+ else if (width == 32)
+ type.basetype = SPIRType::Float;
+ else if (width == 16)
+ type.basetype = SPIRType::Half;
+ else
+ SPIRV_CROSS_THROW("Unrecognized bit-width of floating point type.");
+ type.width = width;
+ break;
+ }
+
+ case OpTypeInt:
+ {
+ uint32_t id = ops[0];
+ uint32_t width = ops[1];
+ bool signedness = ops[2] != 0;
+ auto &type = set<SPIRType>(id);
+ type.basetype = signedness ? to_signed_basetype(width) : to_unsigned_basetype(width);
+ type.width = width;
+ break;
+ }
+
+ // Build composite types by "inheriting".
+ // NOTE: The self member is also copied! For pointers and array modifiers this is a good thing
+ // since we can refer to decorations on pointee classes which is needed for UBO/SSBO, I/O blocks in geometry/tess etc.
+ case OpTypeVector:
+ {
+ uint32_t id = ops[0];
+ uint32_t vecsize = ops[2];
+
+ auto &base = get<SPIRType>(ops[1]);
+ auto &vecbase = set<SPIRType>(id);
+
+ vecbase = base;
+ vecbase.vecsize = vecsize;
+ vecbase.self = id;
+ vecbase.parent_type = ops[1];
+ break;
+ }
+
+ case OpTypeMatrix:
+ {
+ uint32_t id = ops[0];
+ uint32_t colcount = ops[2];
+
+ auto &base = get<SPIRType>(ops[1]);
+ auto &matrixbase = set<SPIRType>(id);
+
+ matrixbase = base;
+ matrixbase.columns = colcount;
+ matrixbase.self = id;
+ matrixbase.parent_type = ops[1];
+ break;
+ }
+
+ case OpTypeArray:
+ {
+ uint32_t id = ops[0];
+ auto &arraybase = set<SPIRType>(id);
+
+ uint32_t tid = ops[1];
+ auto &base = get<SPIRType>(tid);
+
+ arraybase = base;
+ arraybase.parent_type = tid;
+
+ uint32_t cid = ops[2];
+ ir.mark_used_as_array_length(cid);
+ auto *c = maybe_get<SPIRConstant>(cid);
+ bool literal = c && !c->specialization;
+
+ arraybase.array_size_literal.push_back(literal);
+ arraybase.array.push_back(literal ? c->scalar() : cid);
+ // Do NOT set arraybase.self!
+ break;
+ }
+
+ case OpTypeRuntimeArray:
+ {
+ uint32_t id = ops[0];
+
+ auto &base = get<SPIRType>(ops[1]);
+ auto &arraybase = set<SPIRType>(id);
+
+ arraybase = base;
+ arraybase.array.push_back(0);
+ arraybase.array_size_literal.push_back(true);
+ arraybase.parent_type = ops[1];
+ // Do NOT set arraybase.self!
+ break;
+ }
+
+ case OpTypeImage:
+ {
+ uint32_t id = ops[0];
+ auto &type = set<SPIRType>(id);
+ type.basetype = SPIRType::Image;
+ type.image.type = ops[1];
+ type.image.dim = static_cast<Dim>(ops[2]);
+ type.image.depth = ops[3] == 1;
+ type.image.arrayed = ops[4] != 0;
+ type.image.ms = ops[5] != 0;
+ type.image.sampled = ops[6];
+ type.image.format = static_cast<ImageFormat>(ops[7]);
+ type.image.access = (length >= 9) ? static_cast<AccessQualifier>(ops[8]) : AccessQualifierMax;
+
+ if (type.image.sampled == 0)
+ SPIRV_CROSS_THROW("OpTypeImage Sampled parameter must not be zero.");
+
+ break;
+ }
+
+ case OpTypeSampledImage:
+ {
+ uint32_t id = ops[0];
+ uint32_t imagetype = ops[1];
+ auto &type = set<SPIRType>(id);
+ type = get<SPIRType>(imagetype);
+ type.basetype = SPIRType::SampledImage;
+ type.self = id;
+ break;
+ }
+
+ case OpTypeSampler:
+ {
+ uint32_t id = ops[0];
+ auto &type = set<SPIRType>(id);
+ type.basetype = SPIRType::Sampler;
+ break;
+ }
+
+ case OpTypePointer:
+ {
+ uint32_t id = ops[0];
+
+ auto &base = get<SPIRType>(ops[2]);
+ auto &ptrbase = set<SPIRType>(id);
+
+ ptrbase = base;
+ ptrbase.pointer = true;
+ ptrbase.pointer_depth++;
+ ptrbase.storage = static_cast<StorageClass>(ops[1]);
+
+ if (ptrbase.storage == StorageClassAtomicCounter)
+ ptrbase.basetype = SPIRType::AtomicCounter;
+
+ ptrbase.parent_type = ops[2];
+
+ // Do NOT set ptrbase.self!
+ break;
+ }
+
+ case OpTypeStruct:
+ {
+ uint32_t id = ops[0];
+ auto &type = set<SPIRType>(id);
+ type.basetype = SPIRType::Struct;
+ for (uint32_t i = 1; i < length; i++)
+ type.member_types.push_back(ops[i]);
+
+ // Check if we have seen this struct type before, with just different
+ // decorations.
+ //
+ // Add workaround for issue #17 as well by looking at OpName for the struct
+ // types, which we shouldn't normally do.
+ // We should not normally have to consider type aliases like this to begin with
+ // however ... glslang issues #304, #307 cover this.
+
+ // For stripped names, never consider struct type aliasing.
+ // We risk declaring the same struct multiple times, but type-punning is not allowed
+ // so this is safe.
+ bool consider_aliasing = !ir.get_name(type.self).empty();
+ if (consider_aliasing)
+ {
+ for (auto &other : global_struct_cache)
+ {
+ if (ir.get_name(type.self) == ir.get_name(other) &&
+ types_are_logically_equivalent(type, get<SPIRType>(other)))
+ {
+ type.type_alias = other;
+ break;
+ }
+ }
+
+ if (type.type_alias == 0)
+ global_struct_cache.push_back(id);
+ }
+ break;
+ }
+
+ case OpTypeFunction:
+ {
+ uint32_t id = ops[0];
+ uint32_t ret = ops[1];
+
+ auto &func = set<SPIRFunctionPrototype>(id, ret);
+ for (uint32_t i = 2; i < length; i++)
+ func.parameter_types.push_back(ops[i]);
+ break;
+ }
+
+ case OpTypeAccelerationStructureNV:
+ {
+ uint32_t id = ops[0];
+ auto &type = set<SPIRType>(id);
+ type.basetype = SPIRType::AccelerationStructureNV;
+ break;
+ }
+
+ // Variable declaration
+ // All variables are essentially pointers with a storage qualifier.
+ case OpVariable:
+ {
+ uint32_t type = ops[0];
+ uint32_t id = ops[1];
+ auto storage = static_cast<StorageClass>(ops[2]);
+ uint32_t initializer = length == 4 ? ops[3] : 0;
+
+ if (storage == StorageClassFunction)
+ {
+ if (!current_function)
+ SPIRV_CROSS_THROW("No function currently in scope");
+ current_function->add_local_variable(id);
+ }
+
+ set<SPIRVariable>(id, type, storage, initializer);
+
+ // hlsl based shaders don't have those decorations. force them and then reset when reading/writing images
+ auto &ttype = get<SPIRType>(type);
+ if (ttype.basetype == SPIRType::BaseType::Image)
+ {
+ ir.set_decoration(id, DecorationNonWritable);
+ ir.set_decoration(id, DecorationNonReadable);
+ }
+
+ break;
+ }
+
+ // OpPhi
+ // OpPhi is a fairly magical opcode.
+ // It selects temporary variables based on which parent block we *came from*.
+ // In high-level languages we can "de-SSA" by creating a function local, and flush out temporaries to this function-local
+ // variable to emulate SSA Phi.
+ case OpPhi:
+ {
+ if (!current_function)
+ SPIRV_CROSS_THROW("No function currently in scope");
+ if (!current_block)
+ SPIRV_CROSS_THROW("No block currently in scope");
+
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+
+ // Instead of a temporary, create a new function-wide temporary with this ID instead.
+ auto &var = set<SPIRVariable>(id, result_type, spv::StorageClassFunction);
+ var.phi_variable = true;
+
+ current_function->add_local_variable(id);
+
+ for (uint32_t i = 2; i + 2 <= length; i += 2)
+ current_block->phi_variables.push_back({ ops[i], ops[i + 1], id });
+ break;
+ }
+
+ // Constants
+ case OpSpecConstant:
+ case OpConstant:
+ {
+ uint32_t id = ops[1];
+ auto &type = get<SPIRType>(ops[0]);
+
+ if (type.width > 32)
+ set<SPIRConstant>(id, ops[0], ops[2] | (uint64_t(ops[3]) << 32), op == OpSpecConstant);
+ else
+ set<SPIRConstant>(id, ops[0], ops[2], op == OpSpecConstant);
+ break;
+ }
+
+ case OpSpecConstantFalse:
+ case OpConstantFalse:
+ {
+ uint32_t id = ops[1];
+ set<SPIRConstant>(id, ops[0], uint32_t(0), op == OpSpecConstantFalse);
+ break;
+ }
+
+ case OpSpecConstantTrue:
+ case OpConstantTrue:
+ {
+ uint32_t id = ops[1];
+ set<SPIRConstant>(id, ops[0], uint32_t(1), op == OpSpecConstantTrue);
+ break;
+ }
+
+ case OpConstantNull:
+ {
+ uint32_t id = ops[1];
+ uint32_t type = ops[0];
+ make_constant_null(id, type);
+ break;
+ }
+
+ case OpSpecConstantComposite:
+ case OpConstantComposite:
+ {
+ uint32_t id = ops[1];
+ uint32_t type = ops[0];
+
+ auto &ctype = get<SPIRType>(type);
+
+ // We can have constants which are structs and arrays.
+ // In this case, our SPIRConstant will be a list of other SPIRConstant ids which we
+ // can refer to.
+ if (ctype.basetype == SPIRType::Struct || !ctype.array.empty())
+ {
+ set<SPIRConstant>(id, type, ops + 2, length - 2, op == OpSpecConstantComposite);
+ }
+ else
+ {
+ uint32_t elements = length - 2;
+ if (elements > 4)
+ SPIRV_CROSS_THROW("OpConstantComposite only supports 1, 2, 3 and 4 elements.");
+
+ SPIRConstant remapped_constant_ops[4];
+ const SPIRConstant *c[4];
+ for (uint32_t i = 0; i < elements; i++)
+ {
+ // Specialization constants operations can also be part of this.
+ // We do not know their value, so any attempt to query SPIRConstant later
+ // will fail. We can only propagate the ID of the expression and use to_expression on it.
+ auto *constant_op = maybe_get<SPIRConstantOp>(ops[2 + i]);
+ auto *undef_op = maybe_get<SPIRUndef>(ops[2 + i]);
+ if (constant_op)
+ {
+ if (op == OpConstantComposite)
+ SPIRV_CROSS_THROW("Specialization constant operation used in OpConstantComposite.");
+
+ remapped_constant_ops[i].make_null(get<SPIRType>(constant_op->basetype));
+ remapped_constant_ops[i].self = constant_op->self;
+ remapped_constant_ops[i].constant_type = constant_op->basetype;
+ remapped_constant_ops[i].specialization = true;
+ c[i] = &remapped_constant_ops[i];
+ }
+ else if (undef_op)
+ {
+ // Undefined, just pick 0.
+ remapped_constant_ops[i].make_null(get<SPIRType>(undef_op->basetype));
+ remapped_constant_ops[i].constant_type = undef_op->basetype;
+ c[i] = &remapped_constant_ops[i];
+ }
+ else
+ c[i] = &get<SPIRConstant>(ops[2 + i]);
+ }
+ set<SPIRConstant>(id, type, c, elements, op == OpSpecConstantComposite);
+ }
+ break;
+ }
+
+ // Functions
+ case OpFunction:
+ {
+ uint32_t res = ops[0];
+ uint32_t id = ops[1];
+ // Control
+ uint32_t type = ops[3];
+
+ if (current_function)
+ SPIRV_CROSS_THROW("Must end a function before starting a new one!");
+
+ current_function = &set<SPIRFunction>(id, res, type);
+ break;
+ }
+
+ case OpFunctionParameter:
+ {
+ uint32_t type = ops[0];
+ uint32_t id = ops[1];
+
+ if (!current_function)
+ SPIRV_CROSS_THROW("Must be in a function!");
+
+ current_function->add_parameter(type, id);
+ set<SPIRVariable>(id, type, StorageClassFunction);
+ break;
+ }
+
+ case OpFunctionEnd:
+ {
+ if (current_block)
+ {
+ // Very specific error message, but seems to come up quite often.
+ SPIRV_CROSS_THROW(
+ "Cannot end a function before ending the current block.\n"
+ "Likely cause: If this SPIR-V was created from glslang HLSL, make sure the entry point is valid.");
+ }
+ current_function = nullptr;
+ break;
+ }
+
+ // Blocks
+ case OpLabel:
+ {
+ // OpLabel always starts a block.
+ if (!current_function)
+ SPIRV_CROSS_THROW("Blocks cannot exist outside functions!");
+
+ uint32_t id = ops[0];
+
+ current_function->blocks.push_back(id);
+ if (!current_function->entry_block)
+ current_function->entry_block = id;
+
+ if (current_block)
+ SPIRV_CROSS_THROW("Cannot start a block before ending the current block.");
+
+ current_block = &set<SPIRBlock>(id);
+ break;
+ }
+
+ // Branch instructions end blocks.
+ case OpBranch:
+ {
+ if (!current_block)
+ SPIRV_CROSS_THROW("Trying to end a non-existing block.");
+
+ uint32_t target = ops[0];
+ current_block->terminator = SPIRBlock::Direct;
+ current_block->next_block = target;
+ current_block = nullptr;
+ break;
+ }
+
+ case OpBranchConditional:
+ {
+ if (!current_block)
+ SPIRV_CROSS_THROW("Trying to end a non-existing block.");
+
+ current_block->condition = ops[0];
+ current_block->true_block = ops[1];
+ current_block->false_block = ops[2];
+
+ current_block->terminator = SPIRBlock::Select;
+ current_block = nullptr;
+ break;
+ }
+
+ case OpSwitch:
+ {
+ if (!current_block)
+ SPIRV_CROSS_THROW("Trying to end a non-existing block.");
+
+ current_block->terminator = SPIRBlock::MultiSelect;
+
+ current_block->condition = ops[0];
+ current_block->default_block = ops[1];
+
+ for (uint32_t i = 2; i + 2 <= length; i += 2)
+ current_block->cases.push_back({ ops[i], ops[i + 1] });
+
+ // If we jump to next block, make it break instead since we're inside a switch case block at that point.
+ ir.block_meta[current_block->next_block] |= ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT;
+
+ current_block = nullptr;
+ break;
+ }
+
+ case OpKill:
+ {
+ if (!current_block)
+ SPIRV_CROSS_THROW("Trying to end a non-existing block.");
+ current_block->terminator = SPIRBlock::Kill;
+ current_block = nullptr;
+ break;
+ }
+
+ case OpReturn:
+ {
+ if (!current_block)
+ SPIRV_CROSS_THROW("Trying to end a non-existing block.");
+ current_block->terminator = SPIRBlock::Return;
+ current_block = nullptr;
+ break;
+ }
+
+ case OpReturnValue:
+ {
+ if (!current_block)
+ SPIRV_CROSS_THROW("Trying to end a non-existing block.");
+ current_block->terminator = SPIRBlock::Return;
+ current_block->return_value = ops[0];
+ current_block = nullptr;
+ break;
+ }
+
+ case OpUnreachable:
+ {
+ if (!current_block)
+ SPIRV_CROSS_THROW("Trying to end a non-existing block.");
+ current_block->terminator = SPIRBlock::Unreachable;
+ current_block = nullptr;
+ break;
+ }
+
+ case OpSelectionMerge:
+ {
+ if (!current_block)
+ SPIRV_CROSS_THROW("Trying to modify a non-existing block.");
+
+ current_block->next_block = ops[0];
+ current_block->merge = SPIRBlock::MergeSelection;
+ ir.block_meta[current_block->next_block] |= ParsedIR::BLOCK_META_SELECTION_MERGE_BIT;
+
+ if (length >= 2)
+ {
+ if (ops[1] & SelectionControlFlattenMask)
+ current_block->hint = SPIRBlock::HintFlatten;
+ else if (ops[1] & SelectionControlDontFlattenMask)
+ current_block->hint = SPIRBlock::HintDontFlatten;
+ }
+ break;
+ }
+
+ case OpLoopMerge:
+ {
+ if (!current_block)
+ SPIRV_CROSS_THROW("Trying to modify a non-existing block.");
+
+ current_block->merge_block = ops[0];
+ current_block->continue_block = ops[1];
+ current_block->merge = SPIRBlock::MergeLoop;
+
+ ir.block_meta[current_block->self] |= ParsedIR::BLOCK_META_LOOP_HEADER_BIT;
+ ir.block_meta[current_block->merge_block] |= ParsedIR::BLOCK_META_LOOP_MERGE_BIT;
+
+ ir.continue_block_to_loop_header[current_block->continue_block] = current_block->self;
+
+ // Don't add loop headers to continue blocks,
+ // which would make it impossible branch into the loop header since
+ // they are treated as continues.
+ if (current_block->continue_block != current_block->self)
+ ir.block_meta[current_block->continue_block] |= ParsedIR::BLOCK_META_CONTINUE_BIT;
+
+ if (length >= 3)
+ {
+ if (ops[2] & LoopControlUnrollMask)
+ current_block->hint = SPIRBlock::HintUnroll;
+ else if (ops[2] & LoopControlDontUnrollMask)
+ current_block->hint = SPIRBlock::HintDontUnroll;
+ }
+ break;
+ }
+
+ case OpSpecConstantOp:
+ {
+ if (length < 3)
+ SPIRV_CROSS_THROW("OpSpecConstantOp not enough arguments.");
+
+ uint32_t result_type = ops[0];
+ uint32_t id = ops[1];
+ auto spec_op = static_cast<Op>(ops[2]);
+
+ set<SPIRConstantOp>(id, result_type, spec_op, ops + 3, length - 3);
+ break;
+ }
+
+ // Actual opcodes.
+ default:
+ {
+ if (!current_block)
+ SPIRV_CROSS_THROW("Currently no block to insert opcode.");
+
+ current_block->ops.push_back(instruction);
+ break;
+ }
+ }
+}
+
+bool Parser::types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const
+{
+ if (a.basetype != b.basetype)
+ return false;
+ if (a.width != b.width)
+ return false;
+ if (a.vecsize != b.vecsize)
+ return false;
+ if (a.columns != b.columns)
+ return false;
+ if (a.array.size() != b.array.size())
+ return false;
+
+ size_t array_count = a.array.size();
+ if (array_count && memcmp(a.array.data(), b.array.data(), array_count * sizeof(uint32_t)) != 0)
+ return false;
+
+ if (a.basetype == SPIRType::Image || a.basetype == SPIRType::SampledImage)
+ {
+ if (memcmp(&a.image, &b.image, sizeof(SPIRType::Image)) != 0)
+ return false;
+ }
+
+ if (a.member_types.size() != b.member_types.size())
+ return false;
+
+ size_t member_types = a.member_types.size();
+ for (size_t i = 0; i < member_types; i++)
+ {
+ if (!types_are_logically_equivalent(get<SPIRType>(a.member_types[i]), get<SPIRType>(b.member_types[i])))
+ return false;
+ }
+
+ return true;
+}
+
+bool Parser::variable_storage_is_aliased(const SPIRVariable &v) const
+{
+ auto &type = get<SPIRType>(v.basetype);
+
+ auto *type_meta = ir.find_meta(type.self);
+
+ bool ssbo = v.storage == StorageClassStorageBuffer ||
+ (type_meta && type_meta->decoration.decoration_flags.get(DecorationBufferBlock));
+ bool image = type.basetype == SPIRType::Image;
+ bool counter = type.basetype == SPIRType::AtomicCounter;
+
+ bool is_restrict;
+ if (ssbo)
+ is_restrict = ir.get_buffer_block_flags(v).get(DecorationRestrict);
+ else
+ is_restrict = ir.has_decoration(v.self, DecorationRestrict);
+
+ return !is_restrict && (ssbo || image || counter);
+}
+
+void Parser::make_constant_null(uint32_t id, uint32_t type)
+{
+ auto &constant_type = get<SPIRType>(type);
+
+ if (constant_type.pointer)
+ {
+ auto &constant = set<SPIRConstant>(id, type);
+ constant.make_null(constant_type);
+ }
+ else if (!constant_type.array.empty())
+ {
+ assert(constant_type.parent_type);
+ uint32_t parent_id = ir.increase_bound_by(1);
+ make_constant_null(parent_id, constant_type.parent_type);
+
+ if (!constant_type.array_size_literal.back())
+ SPIRV_CROSS_THROW("Array size of OpConstantNull must be a literal.");
+
+ vector<uint32_t> elements(constant_type.array.back());
+ for (uint32_t i = 0; i < constant_type.array.back(); i++)
+ elements[i] = parent_id;
+ set<SPIRConstant>(id, type, elements.data(), uint32_t(elements.size()), false);
+ }
+ else if (!constant_type.member_types.empty())
+ {
+ uint32_t member_ids = ir.increase_bound_by(uint32_t(constant_type.member_types.size()));
+ vector<uint32_t> elements(constant_type.member_types.size());
+ for (uint32_t i = 0; i < constant_type.member_types.size(); i++)
+ {
+ make_constant_null(member_ids + i, constant_type.member_types[i]);
+ elements[i] = member_ids + i;
+ }
+ set<SPIRConstant>(id, type, elements.data(), uint32_t(elements.size()), false);
+ }
+ else
+ {
+ auto &constant = set<SPIRConstant>(id, type);
+ constant.make_null(constant_type);
+ }
+}
+
+} // namespace spirv_cross
diff --git a/src/3rdparty/SPIRV-Cross/spirv_parser.hpp b/src/3rdparty/SPIRV-Cross/spirv_parser.hpp
new file mode 100644
index 0000000..cc15315
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_parser.hpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2018-2019 Arm Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SPIRV_CROSS_PARSER_HPP
+#define SPIRV_CROSS_PARSER_HPP
+
+#include "spirv_cross_parsed_ir.hpp"
+#include <stdint.h>
+#include <vector>
+
+namespace spirv_cross
+{
+class Parser
+{
+public:
+ Parser(const uint32_t *spirv_data, size_t word_count);
+ Parser(std::vector<uint32_t> spirv);
+
+ void parse();
+
+ ParsedIR &get_parsed_ir()
+ {
+ return ir;
+ }
+
+private:
+ ParsedIR ir;
+ SPIRFunction *current_function = nullptr;
+ SPIRBlock *current_block = nullptr;
+
+ void parse(const Instruction &instr);
+ const uint32_t *stream(const Instruction &instr) const;
+
+ template <typename T, typename... P>
+ T &set(uint32_t id, P &&... args)
+ {
+ ir.add_typed_id(static_cast<Types>(T::type), id);
+ auto &var = variant_set<T>(ir.ids[id], std::forward<P>(args)...);
+ var.self = id;
+ return var;
+ }
+
+ template <typename T>
+ T &get(uint32_t id)
+ {
+ return variant_get<T>(ir.ids[id]);
+ }
+
+ template <typename T>
+ T *maybe_get(uint32_t id)
+ {
+ if (ir.ids[id].get_type() == static_cast<Types>(T::type))
+ return &get<T>(id);
+ else
+ return nullptr;
+ }
+
+ template <typename T>
+ const T &get(uint32_t id) const
+ {
+ return variant_get<T>(ir.ids[id]);
+ }
+
+ template <typename T>
+ const T *maybe_get(uint32_t id) const
+ {
+ if (ir.ids[id].get_type() == T::type)
+ return &get<T>(id);
+ else
+ return nullptr;
+ }
+
+ // This must be an ordered data structure so we always pick the same type aliases.
+ std::vector<uint32_t> global_struct_cache;
+
+ bool types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const;
+ bool variable_storage_is_aliased(const SPIRVariable &v) const;
+ void make_constant_null(uint32_t id, uint32_t type);
+};
+} // namespace spirv_cross
+
+#endif
diff --git a/src/3rdparty/SPIRV-Cross/spirv_reflect.cpp b/src/3rdparty/SPIRV-Cross/spirv_reflect.cpp
new file mode 100644
index 0000000..c6cd3be
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_reflect.cpp
@@ -0,0 +1,594 @@
+/*
+ * Copyright 2018-2019 Bradley Austin Davis
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "spirv_reflect.hpp"
+#include "spirv_glsl.hpp"
+#include <iomanip>
+
+using namespace spv;
+using namespace spirv_cross;
+using namespace std;
+
+namespace simple_json
+{
+enum class Type
+{
+ Object,
+ Array,
+};
+
+using State = std::pair<Type, bool>;
+using Stack = std::stack<State>;
+
+class Stream
+{
+ Stack stack;
+ std::ostringstream buffer;
+ uint32_t indent{ 0 };
+
+public:
+ void begin_json_object();
+ void end_json_object();
+ void emit_json_key(const std::string &key);
+ void emit_json_key_value(const std::string &key, const std::string &value);
+ void emit_json_key_value(const std::string &key, bool value);
+ void emit_json_key_value(const std::string &key, uint32_t value);
+ void emit_json_key_value(const std::string &key, int32_t value);
+ void emit_json_key_value(const std::string &key, float value);
+ void emit_json_key_object(const std::string &key);
+ void emit_json_key_array(const std::string &key);
+
+ void begin_json_array();
+ void end_json_array();
+ void emit_json_array_value(const std::string &value);
+ void emit_json_array_value(uint32_t value);
+
+ std::string str() const
+ {
+ return buffer.str();
+ }
+
+private:
+ inline void statement_indent()
+ {
+ for (uint32_t i = 0; i < indent; i++)
+ buffer << " ";
+ }
+
+ template <typename T>
+ inline void statement_inner(T &&t)
+ {
+ buffer << std::forward<T>(t);
+ }
+
+ template <typename T, typename... Ts>
+ inline void statement_inner(T &&t, Ts &&... ts)
+ {
+ buffer << std::forward<T>(t);
+ statement_inner(std::forward<Ts>(ts)...);
+ }
+
+ template <typename... Ts>
+ inline void statement(Ts &&... ts)
+ {
+ statement_indent();
+ statement_inner(std::forward<Ts>(ts)...);
+ buffer << '\n';
+ }
+
+ template <typename... Ts>
+ void statement_no_return(Ts &&... ts)
+ {
+ statement_indent();
+ statement_inner(std::forward<Ts>(ts)...);
+ }
+};
+} // namespace simple_json
+
+using namespace simple_json;
+
+// Hackery to emit JSON without using nlohmann/json C++ library (which requires a
+// higher level of compiler compliance than is required by SPIRV-Cross
+void Stream::begin_json_array()
+{
+ if (!stack.empty() && stack.top().second)
+ {
+ statement_inner(",\n");
+ }
+ statement("[");
+ ++indent;
+ stack.emplace(Type::Array, false);
+}
+
+void Stream::end_json_array()
+{
+ if (stack.empty() || stack.top().first != Type::Array)
+ SPIRV_CROSS_THROW("Invalid JSON state");
+ if (stack.top().second)
+ {
+ statement_inner("\n");
+ }
+ --indent;
+ statement_no_return("]");
+ stack.pop();
+ if (!stack.empty())
+ {
+ stack.top().second = true;
+ }
+}
+
+void Stream::emit_json_array_value(const std::string &value)
+{
+ if (stack.empty() || stack.top().first != Type::Array)
+ SPIRV_CROSS_THROW("Invalid JSON state");
+
+ if (stack.top().second)
+ statement_inner(",\n");
+
+ statement_no_return("\"", value, "\"");
+ stack.top().second = true;
+}
+
+void Stream::emit_json_array_value(uint32_t value)
+{
+ if (stack.empty() || stack.top().first != Type::Array)
+ SPIRV_CROSS_THROW("Invalid JSON state");
+ if (stack.top().second)
+ statement_inner(",\n");
+ statement_no_return(std::to_string(value));
+ stack.top().second = true;
+}
+
+void Stream::begin_json_object()
+{
+ if (!stack.empty() && stack.top().second)
+ {
+ statement_inner(",\n");
+ }
+ statement("{");
+ ++indent;
+ stack.emplace(Type::Object, false);
+}
+
+void Stream::end_json_object()
+{
+ if (stack.empty() || stack.top().first != Type::Object)
+ SPIRV_CROSS_THROW("Invalid JSON state");
+ if (stack.top().second)
+ {
+ statement_inner("\n");
+ }
+ --indent;
+ statement_no_return("}");
+ stack.pop();
+ if (!stack.empty())
+ {
+ stack.top().second = true;
+ }
+}
+
+void Stream::emit_json_key(const std::string &key)
+{
+ if (stack.empty() || stack.top().first != Type::Object)
+ SPIRV_CROSS_THROW("Invalid JSON state");
+
+ if (stack.top().second)
+ statement_inner(",\n");
+ statement_no_return("\"", key, "\" : ");
+ stack.top().second = true;
+}
+
+void Stream::emit_json_key_value(const std::string &key, const std::string &value)
+{
+ emit_json_key(key);
+ statement_inner("\"", value, "\"");
+}
+
+void Stream::emit_json_key_value(const std::string &key, uint32_t value)
+{
+ emit_json_key(key);
+ statement_inner(value);
+}
+
+void Stream::emit_json_key_value(const std::string &key, int32_t value)
+{
+ emit_json_key(key);
+ statement_inner(value);
+}
+
+void Stream::emit_json_key_value(const std::string &key, float value)
+{
+ emit_json_key(key);
+ statement_inner(value);
+}
+
+void Stream::emit_json_key_value(const std::string &key, bool value)
+{
+ emit_json_key(key);
+ statement_inner(value ? "true" : "false");
+}
+
+void Stream::emit_json_key_object(const std::string &key)
+{
+ emit_json_key(key);
+ statement_inner("{\n");
+ ++indent;
+ stack.emplace(Type::Object, false);
+}
+
+void Stream::emit_json_key_array(const std::string &key)
+{
+ emit_json_key(key);
+ statement_inner("[\n");
+ ++indent;
+ stack.emplace(Type::Array, false);
+}
+
+void CompilerReflection::set_format(const std::string &format)
+{
+ if (format != "json")
+ {
+ SPIRV_CROSS_THROW("Unsupported format");
+ }
+}
+
+string CompilerReflection::compile()
+{
+ // Move constructor for this type is broken on GCC 4.9 ...
+ json_stream = std::make_shared<simple_json::Stream>();
+ json_stream->begin_json_object();
+ emit_entry_points();
+ emit_types();
+ emit_resources();
+ emit_specialization_constants();
+ json_stream->end_json_object();
+ return json_stream->str();
+}
+
+void CompilerReflection::emit_types()
+{
+ bool emitted_open_tag = false;
+
+ ir.for_each_typed_id<SPIRType>([&](uint32_t, SPIRType &type) {
+ if (type.basetype == SPIRType::Struct && !type.pointer && type.array.empty())
+ emit_type(type, emitted_open_tag);
+ });
+
+ if (emitted_open_tag)
+ {
+ json_stream->end_json_object();
+ }
+}
+
+void CompilerReflection::emit_type(const SPIRType &type, bool &emitted_open_tag)
+{
+ auto name = type_to_glsl(type);
+
+ if (type.type_alias != 0)
+ return;
+
+ if (!emitted_open_tag)
+ {
+ json_stream->emit_json_key_object("types");
+ emitted_open_tag = true;
+ }
+ json_stream->emit_json_key_object("_" + std::to_string(type.self));
+ json_stream->emit_json_key_value("name", name);
+ json_stream->emit_json_key_array("members");
+ // FIXME ideally we'd like to emit the size of a structure as a
+ // convenience to people parsing the reflected JSON. The problem
+ // is that there's no implicit size for a type. It's final size
+ // will be determined by the top level declaration in which it's
+ // included. So there might be one size for the struct if it's
+ // included in a std140 uniform block and another if it's included
+ // in a std430 uniform block.
+ // The solution is to include *all* potential sizes as a map of
+ // layout type name to integer, but that will probably require
+ // some additional logic being written in this class, or in the
+ // parent CompilerGLSL class.
+ auto size = type.member_types.size();
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ emit_type_member(type, i);
+ }
+ json_stream->end_json_array();
+ json_stream->end_json_object();
+}
+
+void CompilerReflection::emit_type_member(const SPIRType &type, uint32_t index)
+{
+ auto &membertype = get<SPIRType>(type.member_types[index]);
+ json_stream->begin_json_object();
+ auto name = to_member_name(type, index);
+ // FIXME we'd like to emit the offset of each member, but such offsets are
+ // context dependent. See the comment above regarding structure sizes
+ json_stream->emit_json_key_value("name", name);
+ if (membertype.basetype == SPIRType::Struct)
+ {
+ json_stream->emit_json_key_value("type", "_" + std::to_string(membertype.self));
+ }
+ else
+ {
+ json_stream->emit_json_key_value("type", type_to_glsl(membertype));
+ }
+ emit_type_member_qualifiers(type, index);
+ json_stream->end_json_object();
+}
+
+void CompilerReflection::emit_type_array(const SPIRType &type)
+{
+ if (!type.array.empty())
+ {
+ json_stream->emit_json_key_array("array");
+ // Note that we emit the zeros here as a means of identifying
+ // unbounded arrays. This is necessary as otherwise there would
+ // be no way of differentiating between float[4] and float[4][]
+ for (const auto &value : type.array)
+ json_stream->emit_json_array_value(value);
+ json_stream->end_json_array();
+ }
+}
+
+void CompilerReflection::emit_type_member_qualifiers(const SPIRType &type, uint32_t index)
+{
+ auto flags = combined_decoration_for_member(type, index);
+ if (flags.get(DecorationRowMajor))
+ json_stream->emit_json_key_value("row_major", true);
+
+ auto &membertype = get<SPIRType>(type.member_types[index]);
+ emit_type_array(membertype);
+ auto &memb = ir.meta[type.self].members;
+ if (index < memb.size())
+ {
+ auto &dec = memb[index];
+ if (dec.decoration_flags.get(DecorationLocation))
+ json_stream->emit_json_key_value("location", dec.location);
+ if (dec.decoration_flags.get(DecorationOffset))
+ json_stream->emit_json_key_value("offset", dec.offset);
+ }
+}
+
+string CompilerReflection::execution_model_to_str(spv::ExecutionModel model)
+{
+ switch (model)
+ {
+ case ExecutionModelVertex:
+ return "vert";
+ case ExecutionModelTessellationControl:
+ return "tesc";
+ case ExecutionModelTessellationEvaluation:
+ return "tese";
+ case ExecutionModelGeometry:
+ return "geom";
+ case ExecutionModelFragment:
+ return "frag";
+ case ExecutionModelGLCompute:
+ return "comp";
+ case ExecutionModelRayGenerationNV:
+ return "rgen";
+ case ExecutionModelIntersectionNV:
+ return "rint";
+ case ExecutionModelAnyHitNV:
+ return "rahit";
+ case ExecutionModelClosestHitNV:
+ return "rchit";
+ case ExecutionModelMissNV:
+ return "rmiss";
+ case ExecutionModelCallableNV:
+ return "rcall";
+ default:
+ return "???";
+ }
+}
+
+// FIXME include things like the local_size dimensions, geometry output vertex count, etc
+void CompilerReflection::emit_entry_points()
+{
+ auto entries = get_entry_points_and_stages();
+ if (!entries.empty())
+ {
+ // Needed to make output deterministic.
+ sort(begin(entries), end(entries), [](const EntryPoint &a, const EntryPoint &b) -> bool {
+ if (a.execution_model < b.execution_model)
+ return true;
+ else if (a.execution_model > b.execution_model)
+ return false;
+ else
+ return a.name < b.name;
+ });
+
+ json_stream->emit_json_key_array("entryPoints");
+ for (auto &e : entries)
+ {
+ json_stream->begin_json_object();
+ json_stream->emit_json_key_value("name", e.name);
+ json_stream->emit_json_key_value("mode", execution_model_to_str(e.execution_model));
+ json_stream->end_json_object();
+ }
+ json_stream->end_json_array();
+ }
+}
+
+void CompilerReflection::emit_resources()
+{
+ auto res = get_shader_resources();
+ emit_resources("subpass_inputs", res.subpass_inputs);
+ emit_resources("inputs", res.stage_inputs);
+ emit_resources("outputs", res.stage_outputs);
+ emit_resources("textures", res.sampled_images);
+ emit_resources("separate_images", res.separate_images);
+ emit_resources("separate_samplers", res.separate_samplers);
+ emit_resources("images", res.storage_images);
+ emit_resources("ssbos", res.storage_buffers);
+ emit_resources("ubos", res.uniform_buffers);
+ emit_resources("push_constants", res.push_constant_buffers);
+ emit_resources("counters", res.atomic_counters);
+ emit_resources("acceleration_structures", res.acceleration_structures);
+}
+
+void CompilerReflection::emit_resources(const char *tag, const vector<Resource> &resources)
+{
+ if (resources.empty())
+ {
+ return;
+ }
+
+ json_stream->emit_json_key_array(tag);
+ for (auto &res : resources)
+ {
+ auto &type = get_type(res.type_id);
+ auto typeflags = ir.meta[type.self].decoration.decoration_flags;
+ auto &mask = get_decoration_bitset(res.id);
+
+ // If we don't have a name, use the fallback for the type instead of the variable
+ // for SSBOs and UBOs since those are the only meaningful names to use externally.
+ // Push constant blocks are still accessed by name and not block name, even though they are technically Blocks.
+ bool is_push_constant = get_storage_class(res.id) == StorageClassPushConstant;
+ bool is_block = get_decoration_bitset(type.self).get(DecorationBlock) ||
+ get_decoration_bitset(type.self).get(DecorationBufferBlock);
+
+ uint32_t fallback_id = !is_push_constant && is_block ? res.base_type_id : res.id;
+
+ json_stream->begin_json_object();
+
+ if (type.basetype == SPIRType::Struct)
+ {
+ json_stream->emit_json_key_value("type", "_" + std::to_string(res.base_type_id));
+ }
+ else
+ {
+ json_stream->emit_json_key_value("type", type_to_glsl(type));
+ }
+
+ json_stream->emit_json_key_value("name", !res.name.empty() ? res.name : get_fallback_name(fallback_id));
+ {
+ bool ssbo_block = type.storage == StorageClassStorageBuffer ||
+ (type.storage == StorageClassUniform && typeflags.get(DecorationBufferBlock));
+ if (ssbo_block)
+ {
+ auto buffer_flags = get_buffer_block_flags(res.id);
+ if (buffer_flags.get(DecorationNonReadable))
+ json_stream->emit_json_key_value("writeonly", true);
+ if (buffer_flags.get(DecorationNonWritable))
+ json_stream->emit_json_key_value("readonly", true);
+ if (buffer_flags.get(DecorationRestrict))
+ json_stream->emit_json_key_value("restrict", true);
+ if (buffer_flags.get(DecorationCoherent))
+ json_stream->emit_json_key_value("coherent", true);
+ }
+ }
+
+ emit_type_array(type);
+
+ {
+ bool is_sized_block = is_block && (get_storage_class(res.id) == StorageClassUniform ||
+ get_storage_class(res.id) == StorageClassUniformConstant ||
+ get_storage_class(res.id) == StorageClassStorageBuffer);
+ if (is_sized_block)
+ {
+ uint32_t block_size = uint32_t(get_declared_struct_size(get_type(res.base_type_id)));
+ json_stream->emit_json_key_value("block_size", block_size);
+ }
+ }
+
+ if (type.storage == StorageClassPushConstant)
+ json_stream->emit_json_key_value("push_constant", true);
+ if (mask.get(DecorationLocation))
+ json_stream->emit_json_key_value("location", get_decoration(res.id, DecorationLocation));
+ if (mask.get(DecorationRowMajor))
+ json_stream->emit_json_key_value("row_major", true);
+ if (mask.get(DecorationColMajor))
+ json_stream->emit_json_key_value("column_major", true);
+ if (mask.get(DecorationIndex))
+ json_stream->emit_json_key_value("index", get_decoration(res.id, DecorationIndex));
+ if (type.storage != StorageClassPushConstant && mask.get(DecorationDescriptorSet))
+ json_stream->emit_json_key_value("set", get_decoration(res.id, DecorationDescriptorSet));
+ if (mask.get(DecorationBinding))
+ json_stream->emit_json_key_value("binding", get_decoration(res.id, DecorationBinding));
+ if (mask.get(DecorationInputAttachmentIndex))
+ json_stream->emit_json_key_value("input_attachment_index",
+ get_decoration(res.id, DecorationInputAttachmentIndex));
+ if (mask.get(DecorationOffset))
+ json_stream->emit_json_key_value("offset", get_decoration(res.id, DecorationOffset));
+
+ // For images, the type itself adds a layout qualifer.
+ // Only emit the format for storage images.
+ if (type.basetype == SPIRType::Image && type.image.sampled == 2)
+ {
+ const char *fmt = format_to_glsl(type.image.format);
+ if (fmt != nullptr)
+ json_stream->emit_json_key_value("format", std::string(fmt));
+ }
+ json_stream->end_json_object();
+ }
+ json_stream->end_json_array();
+}
+
+void CompilerReflection::emit_specialization_constants()
+{
+ auto specialization_constants = get_specialization_constants();
+ if (specialization_constants.empty())
+ return;
+
+ json_stream->emit_json_key_array("specialization_constants");
+ for (const auto spec_const : specialization_constants)
+ {
+ auto &c = get<SPIRConstant>(spec_const.id);
+ auto type = get<SPIRType>(c.constant_type);
+ json_stream->begin_json_object();
+ json_stream->emit_json_key_value("id", spec_const.constant_id);
+ json_stream->emit_json_key_value("type", type_to_glsl(type));
+ switch (type.basetype)
+ {
+ case SPIRType::UInt:
+ json_stream->emit_json_key_value("default_value", c.scalar());
+ break;
+
+ case SPIRType::Int:
+ json_stream->emit_json_key_value("default_value", c.scalar_i32());
+ break;
+
+ case SPIRType::Float:
+ json_stream->emit_json_key_value("default_value", c.scalar_f32());
+ break;
+
+ case SPIRType::Boolean:
+ json_stream->emit_json_key_value("default_value", c.scalar() != 0);
+ break;
+
+ default:
+ break;
+ }
+ json_stream->end_json_object();
+ }
+ json_stream->end_json_array();
+}
+
+string CompilerReflection::to_member_name(const SPIRType &type, uint32_t index) const
+{
+ auto *type_meta = ir.find_meta(type.self);
+
+ if (type_meta)
+ {
+ auto &memb = type_meta->members;
+ if (index < memb.size() && !memb[index].alias.empty())
+ return memb[index].alias;
+ else
+ return join("_m", index);
+ }
+ else
+ return join("_m", index);
+}
diff --git a/src/3rdparty/SPIRV-Cross/spirv_reflect.hpp b/src/3rdparty/SPIRV-Cross/spirv_reflect.hpp
new file mode 100644
index 0000000..13b5b43
--- /dev/null
+++ b/src/3rdparty/SPIRV-Cross/spirv_reflect.hpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2018-2019 Bradley Austin Davis
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SPIRV_CROSS_REFLECT_HPP
+#define SPIRV_CROSS_REFLECT_HPP
+
+#include "spirv_glsl.hpp"
+#include <utility>
+#include <vector>
+
+namespace simple_json
+{
+class Stream;
+}
+
+namespace spirv_cross
+{
+class CompilerReflection : public CompilerGLSL
+{
+ using Parent = CompilerGLSL;
+
+public:
+ explicit CompilerReflection(std::vector<uint32_t> spirv_)
+ : Parent(move(spirv_))
+ {
+ options.vulkan_semantics = true;
+ }
+
+ CompilerReflection(const uint32_t *ir_, size_t word_count)
+ : Parent(ir_, word_count)
+ {
+ options.vulkan_semantics = true;
+ }
+
+ explicit CompilerReflection(const ParsedIR &ir_)
+ : CompilerGLSL(ir_)
+ {
+ options.vulkan_semantics = true;
+ }
+
+ explicit CompilerReflection(ParsedIR &&ir_)
+ : CompilerGLSL(std::move(ir_))
+ {
+ options.vulkan_semantics = true;
+ }
+
+ void set_format(const std::string &format);
+ std::string compile() override;
+
+private:
+ static std::string execution_model_to_str(spv::ExecutionModel model);
+
+ void emit_entry_points();
+ void emit_types();
+ void emit_resources();
+ void emit_specialization_constants();
+
+ void emit_type(const SPIRType &type, bool &emitted_open_tag);
+ void emit_type_member(const SPIRType &type, uint32_t index);
+ void emit_type_member_qualifiers(const SPIRType &type, uint32_t index);
+ void emit_type_array(const SPIRType &type);
+ void emit_resources(const char *tag, const std::vector<Resource> &resources);
+
+ std::string to_member_name(const SPIRType &type, uint32_t index) const;
+
+ std::shared_ptr<simple_json::Stream> json_stream;
+};
+
+} // namespace spirv_cross
+
+#endif
diff --git a/src/3rdparty/glslang/OGLCompilersDLL/InitializeDll.cpp b/src/3rdparty/glslang/OGLCompilersDLL/InitializeDll.cpp
new file mode 100644
index 0000000..abea910
--- /dev/null
+++ b/src/3rdparty/glslang/OGLCompilersDLL/InitializeDll.cpp
@@ -0,0 +1,165 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#define SH_EXPORTING
+
+#include <cassert>
+
+#include "InitializeDll.h"
+#include "../glslang/Include/InitializeGlobals.h"
+#include "../glslang/Public/ShaderLang.h"
+#include "../glslang/Include/PoolAlloc.h"
+
+namespace glslang {
+
+OS_TLSIndex ThreadInitializeIndex = OS_INVALID_TLS_INDEX;
+
+// Per-process initialization.
+// Needs to be called at least once before parsing, etc. is done.
+// Will also do thread initialization for the calling thread; other
+// threads will need to do that explicitly.
+bool InitProcess()
+{
+ glslang::GetGlobalLock();
+
+ if (ThreadInitializeIndex != OS_INVALID_TLS_INDEX) {
+ //
+ // Function is re-entrant.
+ //
+
+ glslang::ReleaseGlobalLock();
+ return true;
+ }
+
+ ThreadInitializeIndex = OS_AllocTLSIndex();
+
+ if (ThreadInitializeIndex == OS_INVALID_TLS_INDEX) {
+ assert(0 && "InitProcess(): Failed to allocate TLS area for init flag");
+
+ glslang::ReleaseGlobalLock();
+ return false;
+ }
+
+ if (! InitializePoolIndex()) {
+ assert(0 && "InitProcess(): Failed to initialize global pool");
+
+ glslang::ReleaseGlobalLock();
+ return false;
+ }
+
+ if (! InitThread()) {
+ assert(0 && "InitProcess(): Failed to initialize thread");
+
+ glslang::ReleaseGlobalLock();
+ return false;
+ }
+
+ glslang::ReleaseGlobalLock();
+ return true;
+}
+
+// Per-thread scoped initialization.
+// Must be called at least once by each new thread sharing the
+// symbol tables, etc., needed to parse.
+bool InitThread()
+{
+ //
+ // This function is re-entrant
+ //
+ if (ThreadInitializeIndex == OS_INVALID_TLS_INDEX) {
+ assert(0 && "InitThread(): Process hasn't been initalised.");
+ return false;
+ }
+
+ if (OS_GetTLSValue(ThreadInitializeIndex) != 0)
+ return true;
+
+ if (! OS_SetTLSValue(ThreadInitializeIndex, (void *)1)) {
+ assert(0 && "InitThread(): Unable to set init flag.");
+ return false;
+ }
+
+ glslang::SetThreadPoolAllocator(nullptr);
+
+ return true;
+}
+
+// Not necessary to call this: InitThread() is reentrant, and the need
+// to do per thread tear down has been removed.
+//
+// This is kept, with memory management removed, to satisfy any exiting
+// calls to it that rely on it.
+bool DetachThread()
+{
+ bool success = true;
+
+ if (ThreadInitializeIndex == OS_INVALID_TLS_INDEX)
+ return true;
+
+ //
+ // Function is re-entrant and this thread may not have been initialized.
+ //
+ if (OS_GetTLSValue(ThreadInitializeIndex) != 0) {
+ if (!OS_SetTLSValue(ThreadInitializeIndex, (void *)0)) {
+ assert(0 && "DetachThread(): Unable to clear init flag.");
+ success = false;
+ }
+ }
+
+ return success;
+}
+
+// Not necessary to call this: InitProcess() is reentrant.
+//
+// This is kept, with memory management removed, to satisfy any exiting
+// calls to it that rely on it.
+//
+// Users of glslang should call shFinalize() or glslang::FinalizeProcess() for
+// process-scoped memory tear down.
+bool DetachProcess()
+{
+ bool success = true;
+
+ if (ThreadInitializeIndex == OS_INVALID_TLS_INDEX)
+ return true;
+
+ success = DetachThread();
+
+ OS_FreeTLSIndex(ThreadInitializeIndex);
+ ThreadInitializeIndex = OS_INVALID_TLS_INDEX;
+
+ return success;
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/OGLCompilersDLL/InitializeDll.h b/src/3rdparty/glslang/OGLCompilersDLL/InitializeDll.h
new file mode 100644
index 0000000..661cee4
--- /dev/null
+++ b/src/3rdparty/glslang/OGLCompilersDLL/InitializeDll.h
@@ -0,0 +1,49 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+#ifndef __INITIALIZEDLL_H
+#define __INITIALIZEDLL_H
+
+#include "../glslang/OSDependent/osinclude.h"
+
+namespace glslang {
+
+bool InitProcess();
+bool InitThread();
+bool DetachThread(); // not called from standalone, perhaps other tools rely on parts of it
+bool DetachProcess(); // not called from standalone, perhaps other tools rely on parts of it
+
+} // end namespace glslang
+
+#endif // __INITIALIZEDLL_H
+
diff --git a/src/3rdparty/glslang/README.md b/src/3rdparty/glslang/README.md
new file mode 100644
index 0000000..544081d
--- /dev/null
+++ b/src/3rdparty/glslang/README.md
@@ -0,0 +1,332 @@
+Also see the Khronos landing page for glslang as a reference front end:
+
+https://www.khronos.org/opengles/sdk/tools/Reference-Compiler/
+
+The above page includes where to get binaries, and is kept up to date
+regarding the feature level of glslang.
+
+glslang
+=======
+
+[![Build Status](https://travis-ci.org/KhronosGroup/glslang.svg?branch=master)](https://travis-ci.org/KhronosGroup/glslang)
+[![Build status](https://ci.appveyor.com/api/projects/status/q6fi9cb0qnhkla68/branch/master?svg=true)](https://ci.appveyor.com/project/Khronoswebmaster/glslang/branch/master)
+
+An OpenGL and OpenGL ES shader front end and validator.
+
+There are several components:
+
+1. A GLSL/ESSL front-end for reference validation and translation of GLSL/ESSL into an AST.
+
+2. An HLSL front-end for translation of a broad generic HLL into the AST. See [issue 362](https://github.com/KhronosGroup/glslang/issues/362) and [issue 701](https://github.com/KhronosGroup/glslang/issues/701) for current status.
+
+3. A SPIR-V back end for translating the AST to SPIR-V.
+
+4. A standalone wrapper, `glslangValidator`, that can be used as a command-line tool for the above.
+
+How to add a feature protected by a version/extension/stage/profile: See the
+comment in `glslang/MachineIndependent/Versions.cpp`.
+
+Tasks waiting to be done are documented as GitHub issues.
+
+Execution of Standalone Wrapper
+-------------------------------
+
+To use the standalone binary form, execute `glslangValidator`, and it will print
+a usage statement. Basic operation is to give it a file containing a shader,
+and it will print out warnings/errors and optionally an AST.
+
+The applied stage-specific rules are based on the file extension:
+* `.vert` for a vertex shader
+* `.tesc` for a tessellation control shader
+* `.tese` for a tessellation evaluation shader
+* `.geom` for a geometry shader
+* `.frag` for a fragment shader
+* `.comp` for a compute shader
+
+There is also a non-shader extension
+* `.conf` for a configuration file of limits, see usage statement for example
+
+Building
+--------
+
+Instead of building manually, you can also download the binaries for your
+platform directly from the [master-tot release][master-tot-release] on GitHub.
+Those binaries are automatically uploaded by the buildbots after successful
+testing and they always reflect the current top of the tree of the master
+branch.
+
+### Dependencies
+
+* A C++11 compiler.
+ (For MSVS: 2015 is recommended, 2013 is fully supported/tested, and 2010 support is attempted, but not tested.)
+* [CMake][cmake]: for generating compilation targets.
+* make: _Linux_, ninja is an alternative, if configured.
+* [Python 2.7][python]: for executing SPIRV-Tools scripts. (Optional if not using SPIRV-Tools.)
+* [bison][bison]: _optional_, but needed when changing the grammar (glslang.y).
+* [googletest][googletest]: _optional_, but should use if making any changes to glslang.
+
+### Build steps
+
+The following steps assume a Bash shell. On Windows, that could be the Git Bash
+shell or some other shell of your choosing.
+
+#### 1) Check-Out this project
+
+```bash
+cd <parent of where you want glslang to be>
+git clone https://github.com/KhronosGroup/glslang.git
+```
+
+#### 2) Check-Out External Projects
+
+```bash
+cd <the directory glslang was cloned to, "External" will be a subdirectory>
+git clone https://github.com/google/googletest.git External/googletest
+```
+
+If you wish to assure that SPIR-V generated from HLSL is legal for Vulkan,
+or wish to invoke -Os to reduce SPIR-V size from HLSL or GLSL, install
+spirv-tools with this:
+
+```bash
+./update_glslang_sources.py
+```
+
+#### 3) Configure
+
+Assume the source directory is `$SOURCE_DIR` and the build directory is
+`$BUILD_DIR`. First ensure the build directory exists, then navigate to it:
+
+```bash
+mkdir -p $BUILD_DIR
+cd $BUILD_DIR
+```
+
+For building on Linux:
+
+```bash
+cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$(pwd)/install" $SOURCE_DIR
+# "Release" (for CMAKE_BUILD_TYPE) could also be "Debug" or "RelWithDebInfo"
+```
+
+For building on Windows:
+
+```bash
+cmake $SOURCE_DIR -DCMAKE_INSTALL_PREFIX="$(pwd)/install"
+# The CMAKE_INSTALL_PREFIX part is for testing (explained later).
+```
+
+The CMake GUI also works for Windows (version 3.4.1 tested).
+
+#### 4) Build and Install
+
+```bash
+# for Linux:
+make -j4 install
+
+# for Windows:
+cmake --build . --config Release --target install
+# "Release" (for --config) could also be "Debug", "MinSizeRel", or "RelWithDebInfo"
+```
+
+If using MSVC, after running CMake to configure, use the
+Configuration Manager to check the `INSTALL` project.
+
+### If you need to change the GLSL grammar
+
+The grammar in `glslang/MachineIndependent/glslang.y` has to be recompiled with
+bison if it changes, the output files are committed to the repo to avoid every
+developer needing to have bison configured to compile the project when grammar
+changes are quite infrequent. For windows you can get binaries from
+[GnuWin32][bison-gnu-win32].
+
+The command to rebuild is:
+
+```bash
+bison --defines=MachineIndependent/glslang_tab.cpp.h
+ -t MachineIndependent/glslang.y
+ -o MachineIndependent/glslang_tab.cpp
+```
+
+The above command is also available in the bash script at
+`glslang/updateGrammar`.
+
+Testing
+-------
+
+Right now, there are two test harnesses existing in glslang: one is [Google
+Test](gtests/), one is the [`runtests` script](Test/runtests). The former
+runs unit tests and single-shader single-threaded integration tests, while
+the latter runs multiple-shader linking tests and multi-threaded tests.
+
+### Running tests
+
+The [`runtests` script](Test/runtests) requires compiled binaries to be
+installed into `$BUILD_DIR/install`. Please make sure you have supplied the
+correct configuration to CMake (using `-DCMAKE_INSTALL_PREFIX`) when building;
+otherwise, you may want to modify the path in the `runtests` script.
+
+Running Google Test-backed tests:
+
+```bash
+cd $BUILD_DIR
+
+# for Linux:
+ctest
+
+# for Windows:
+ctest -C {Debug|Release|RelWithDebInfo|MinSizeRel}
+
+# or, run the test binary directly
+# (which gives more fine-grained control like filtering):
+<dir-to-glslangtests-in-build-dir>/glslangtests
+```
+
+Running `runtests` script-backed tests:
+
+```bash
+cd $SOURCE_DIR/Test && ./runtests
+```
+
+### Contributing tests
+
+Test results should always be included with a pull request that modifies
+functionality.
+
+If you are writing unit tests, please use the Google Test framework and
+place the tests under the `gtests/` directory.
+
+Integration tests are placed in the `Test/` directory. It contains test input
+and a subdirectory `baseResults/` that contains the expected results of the
+tests. Both the tests and `baseResults/` are under source-code control.
+
+Google Test runs those integration tests by reading the test input, compiling
+them, and then compare against the expected results in `baseResults/`. The
+integration tests to run via Google Test is registered in various
+`gtests/*.FromFile.cpp` source files. `glslangtests` provides a command-line
+option `--update-mode`, which, if supplied, will overwrite the golden files
+under the `baseResults/` directory with real output from that invocation.
+For more information, please check `gtests/` directory's
+[README](gtests/README.md).
+
+For the `runtests` script, it will generate current results in the
+`localResults/` directory and `diff` them against the `baseResults/`.
+When you want to update the tracked test results, they need to be
+copied from `localResults/` to `baseResults/`. This can be done by
+the `bump` shell script.
+
+You can add your own private list of tests, not tracked publicly, by using
+`localtestlist` to list non-tracked tests. This is automatically read
+by `runtests` and included in the `diff` and `bump` process.
+
+Programmatic Interfaces
+-----------------------
+
+Another piece of software can programmatically translate shaders to an AST
+using one of two different interfaces:
+* A new C++ class-oriented interface, or
+* The original C functional interface
+
+The `main()` in `StandAlone/StandAlone.cpp` shows examples using both styles.
+
+### C++ Class Interface (new, preferred)
+
+This interface is in roughly the last 1/3 of `ShaderLang.h`. It is in the
+glslang namespace and contains the following.
+
+```cxx
+const char* GetEsslVersionString();
+const char* GetGlslVersionString();
+bool InitializeProcess();
+void FinalizeProcess();
+
+class TShader
+ setStrings(...);
+ setEnvInput(EShSourceHlsl or EShSourceGlsl, stage, EShClientVulkan or EShClientOpenGL, 100);
+ setEnvClient(EShClientVulkan or EShClientOpenGL, EShTargetVulkan_1_0 or EShTargetVulkan_1_1 or EShTargetOpenGL_450);
+ setEnvTarget(EShTargetSpv, EShTargetSpv_1_0 or EShTargetSpv_1_3);
+ bool parse(...);
+ const char* getInfoLog();
+
+class TProgram
+ void addShader(...);
+ bool link(...);
+ const char* getInfoLog();
+ Reflection queries
+```
+
+See `ShaderLang.h` and the usage of it in `StandAlone/StandAlone.cpp` for more
+details.
+
+### C Functional Interface (orignal)
+
+This interface is in roughly the first 2/3 of `ShaderLang.h`, and referred to
+as the `Sh*()` interface, as all the entry points start `Sh`.
+
+The `Sh*()` interface takes a "compiler" call-back object, which it calls after
+building call back that is passed the AST and can then execute a backend on it.
+
+The following is a simplified resulting run-time call stack:
+
+```c
+ShCompile(shader, compiler) -> compiler(AST) -> <back end>
+```
+
+In practice, `ShCompile()` takes shader strings, default version, and
+warning/error and other options for controlling compilation.
+
+Basic Internal Operation
+------------------------
+
+* Initial lexical analysis is done by the preprocessor in
+ `MachineIndependent/Preprocessor`, and then refined by a GLSL scanner
+ in `MachineIndependent/Scan.cpp`. There is currently no use of flex.
+
+* Code is parsed using bison on `MachineIndependent/glslang.y` with the
+ aid of a symbol table and an AST. The symbol table is not passed on to
+ the back-end; the intermediate representation stands on its own.
+ The tree is built by the grammar productions, many of which are
+ offloaded into `ParseHelper.cpp`, and by `Intermediate.cpp`.
+
+* The intermediate representation is very high-level, and represented
+ as an in-memory tree. This serves to lose no information from the
+ original program, and to have efficient transfer of the result from
+ parsing to the back-end. In the AST, constants are propogated and
+ folded, and a very small amount of dead code is eliminated.
+
+ To aid linking and reflection, the last top-level branch in the AST
+ lists all global symbols.
+
+* The primary algorithm of the back-end compiler is to traverse the
+ tree (high-level intermediate representation), and create an internal
+ object code representation. There is an example of how to do this
+ in `MachineIndependent/intermOut.cpp`.
+
+* Reduction of the tree to a linear byte-code style low-level intermediate
+ representation is likely a good way to generate fully optimized code.
+
+* There is currently some dead old-style linker-type code still lying around.
+
+* Memory pool: parsing uses types derived from C++ `std` types, using a
+ custom allocator that puts them in a memory pool. This makes allocation
+ of individual container/contents just few cycles and deallocation free.
+ This pool is popped after the AST is made and processed.
+
+ The use is simple: if you are going to call `new`, there are three cases:
+
+ - the object comes from the pool (its base class has the macro
+ `POOL_ALLOCATOR_NEW_DELETE` in it) and you do not have to call `delete`
+
+ - it is a `TString`, in which case call `NewPoolTString()`, which gets
+ it from the pool, and there is no corresponding `delete`
+
+ - the object does not come from the pool, and you have to do normal
+ C++ memory management of what you `new`
+
+
+[cmake]: https://cmake.org/
+[python]: https://www.python.org/
+[bison]: https://www.gnu.org/software/bison/
+[googletest]: https://github.com/google/googletest
+[bison-gnu-win32]: http://gnuwin32.sourceforge.net/packages/bison.htm
+[master-tot-release]: https://github.com/KhronosGroup/glslang/releases/tag/master-tot
diff --git a/src/3rdparty/glslang/SPIRV/GLSL.ext.AMD.h b/src/3rdparty/glslang/SPIRV/GLSL.ext.AMD.h
new file mode 100644
index 0000000..009d2f1
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/GLSL.ext.AMD.h
@@ -0,0 +1,108 @@
+/*
+** Copyright (c) 2014-2016 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and/or associated documentation files (the "Materials"),
+** to deal in the Materials without restriction, including without limitation
+** the rights to use, copy, modify, merge, publish, distribute, sublicense,
+** and/or sell copies of the Materials, and to permit persons to whom the
+** Materials are furnished to do so, subject to the following conditions:
+**
+** The above copyright notice and this permission notice shall be included in
+** all copies or substantial portions of the Materials.
+**
+** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
+** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
+** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
+** IN THE MATERIALS.
+*/
+
+#ifndef GLSLextAMD_H
+#define GLSLextAMD_H
+
+static const int GLSLextAMDVersion = 100;
+static const int GLSLextAMDRevision = 7;
+
+// SPV_AMD_shader_ballot
+static const char* const E_SPV_AMD_shader_ballot = "SPV_AMD_shader_ballot";
+
+enum ShaderBallotAMD {
+ ShaderBallotBadAMD = 0, // Don't use
+
+ SwizzleInvocationsAMD = 1,
+ SwizzleInvocationsMaskedAMD = 2,
+ WriteInvocationAMD = 3,
+ MbcntAMD = 4,
+
+ ShaderBallotCountAMD
+};
+
+// SPV_AMD_shader_trinary_minmax
+static const char* const E_SPV_AMD_shader_trinary_minmax = "SPV_AMD_shader_trinary_minmax";
+
+enum ShaderTrinaryMinMaxAMD {
+ ShaderTrinaryMinMaxBadAMD = 0, // Don't use
+
+ FMin3AMD = 1,
+ UMin3AMD = 2,
+ SMin3AMD = 3,
+ FMax3AMD = 4,
+ UMax3AMD = 5,
+ SMax3AMD = 6,
+ FMid3AMD = 7,
+ UMid3AMD = 8,
+ SMid3AMD = 9,
+
+ ShaderTrinaryMinMaxCountAMD
+};
+
+// SPV_AMD_shader_explicit_vertex_parameter
+static const char* const E_SPV_AMD_shader_explicit_vertex_parameter = "SPV_AMD_shader_explicit_vertex_parameter";
+
+enum ShaderExplicitVertexParameterAMD {
+ ShaderExplicitVertexParameterBadAMD = 0, // Don't use
+
+ InterpolateAtVertexAMD = 1,
+
+ ShaderExplicitVertexParameterCountAMD
+};
+
+// SPV_AMD_gcn_shader
+static const char* const E_SPV_AMD_gcn_shader = "SPV_AMD_gcn_shader";
+
+enum GcnShaderAMD {
+ GcnShaderBadAMD = 0, // Don't use
+
+ CubeFaceIndexAMD = 1,
+ CubeFaceCoordAMD = 2,
+ TimeAMD = 3,
+
+ GcnShaderCountAMD
+};
+
+// SPV_AMD_gpu_shader_half_float
+static const char* const E_SPV_AMD_gpu_shader_half_float = "SPV_AMD_gpu_shader_half_float";
+
+// SPV_AMD_texture_gather_bias_lod
+static const char* const E_SPV_AMD_texture_gather_bias_lod = "SPV_AMD_texture_gather_bias_lod";
+
+// SPV_AMD_gpu_shader_int16
+static const char* const E_SPV_AMD_gpu_shader_int16 = "SPV_AMD_gpu_shader_int16";
+
+// SPV_AMD_shader_image_load_store_lod
+static const char* const E_SPV_AMD_shader_image_load_store_lod = "SPV_AMD_shader_image_load_store_lod";
+
+// SPV_AMD_shader_fragment_mask
+static const char* const E_SPV_AMD_shader_fragment_mask = "SPV_AMD_shader_fragment_mask";
+
+// SPV_AMD_gpu_shader_half_float_fetch
+static const char* const E_SPV_AMD_gpu_shader_half_float_fetch = "SPV_AMD_gpu_shader_half_float_fetch";
+
+#endif // #ifndef GLSLextAMD_H
diff --git a/src/3rdparty/glslang/SPIRV/GLSL.ext.EXT.h b/src/3rdparty/glslang/SPIRV/GLSL.ext.EXT.h
new file mode 100644
index 0000000..e29c055
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/GLSL.ext.EXT.h
@@ -0,0 +1,38 @@
+/*
+** Copyright (c) 2014-2016 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and/or associated documentation files (the "Materials"),
+** to deal in the Materials without restriction, including without limitation
+** the rights to use, copy, modify, merge, publish, distribute, sublicense,
+** and/or sell copies of the Materials, and to permit persons to whom the
+** Materials are furnished to do so, subject to the following conditions:
+**
+** The above copyright notice and this permission notice shall be included in
+** all copies or substantial portions of the Materials.
+**
+** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
+** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
+** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
+** IN THE MATERIALS.
+*/
+
+#ifndef GLSLextEXT_H
+#define GLSLextEXT_H
+
+static const int GLSLextEXTVersion = 100;
+static const int GLSLextEXTRevision = 2;
+
+static const char* const E_SPV_EXT_shader_stencil_export = "SPV_EXT_shader_stencil_export";
+static const char* const E_SPV_EXT_shader_viewport_index_layer = "SPV_EXT_shader_viewport_index_layer";
+static const char* const E_SPV_EXT_fragment_fully_covered = "SPV_EXT_fragment_fully_covered";
+static const char* const E_SPV_EXT_fragment_invocation_density = "SPV_EXT_fragment_invocation_density";
+
+#endif // #ifndef GLSLextEXT_H
diff --git a/src/3rdparty/glslang/SPIRV/GLSL.ext.KHR.h b/src/3rdparty/glslang/SPIRV/GLSL.ext.KHR.h
new file mode 100644
index 0000000..333442b
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/GLSL.ext.KHR.h
@@ -0,0 +1,45 @@
+/*
+** Copyright (c) 2014-2016 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and/or associated documentation files (the "Materials"),
+** to deal in the Materials without restriction, including without limitation
+** the rights to use, copy, modify, merge, publish, distribute, sublicense,
+** and/or sell copies of the Materials, and to permit persons to whom the
+** Materials are furnished to do so, subject to the following conditions:
+**
+** The above copyright notice and this permission notice shall be included in
+** all copies or substantial portions of the Materials.
+**
+** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
+** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
+** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
+** IN THE MATERIALS.
+*/
+
+#ifndef GLSLextKHR_H
+#define GLSLextKHR_H
+
+static const int GLSLextKHRVersion = 100;
+static const int GLSLextKHRRevision = 2;
+
+static const char* const E_SPV_KHR_shader_ballot = "SPV_KHR_shader_ballot";
+static const char* const E_SPV_KHR_subgroup_vote = "SPV_KHR_subgroup_vote";
+static const char* const E_SPV_KHR_device_group = "SPV_KHR_device_group";
+static const char* const E_SPV_KHR_multiview = "SPV_KHR_multiview";
+static const char* const E_SPV_KHR_shader_draw_parameters = "SPV_KHR_shader_draw_parameters";
+static const char* const E_SPV_KHR_16bit_storage = "SPV_KHR_16bit_storage";
+static const char* const E_SPV_KHR_8bit_storage = "SPV_KHR_8bit_storage";
+static const char* const E_SPV_KHR_storage_buffer_storage_class = "SPV_KHR_storage_buffer_storage_class";
+static const char* const E_SPV_KHR_post_depth_coverage = "SPV_KHR_post_depth_coverage";
+static const char* const E_SPV_KHR_vulkan_memory_model = "SPV_KHR_vulkan_memory_model";
+static const char* const E_SPV_EXT_physical_storage_buffer = "SPV_EXT_physical_storage_buffer";
+
+#endif // #ifndef GLSLextKHR_H
diff --git a/src/3rdparty/glslang/SPIRV/GLSL.ext.NV.h b/src/3rdparty/glslang/SPIRV/GLSL.ext.NV.h
new file mode 100644
index 0000000..ede2c57
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/GLSL.ext.NV.h
@@ -0,0 +1,78 @@
+/*
+** Copyright (c) 2014-2017 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and/or associated documentation files (the "Materials"),
+** to deal in the Materials without restriction, including without limitation
+** the rights to use, copy, modify, merge, publish, distribute, sublicense,
+** and/or sell copies of the Materials, and to permit persons to whom the
+** Materials are furnished to do so, subject to the following conditions:
+**
+** The above copyright notice and this permission notice shall be included in
+** all copies or substantial portions of the Materials.
+**
+** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
+** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
+** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
+** IN THE MATERIALS.
+*/
+
+#ifndef GLSLextNV_H
+#define GLSLextNV_H
+
+enum BuiltIn;
+enum Decoration;
+enum Op;
+enum Capability;
+
+static const int GLSLextNVVersion = 100;
+static const int GLSLextNVRevision = 11;
+
+//SPV_NV_sample_mask_override_coverage
+const char* const E_SPV_NV_sample_mask_override_coverage = "SPV_NV_sample_mask_override_coverage";
+
+//SPV_NV_geometry_shader_passthrough
+const char* const E_SPV_NV_geometry_shader_passthrough = "SPV_NV_geometry_shader_passthrough";
+
+//SPV_NV_viewport_array2
+const char* const E_SPV_NV_viewport_array2 = "SPV_NV_viewport_array2";
+const char* const E_ARB_shader_viewport_layer_array = "SPV_ARB_shader_viewport_layer_array";
+
+//SPV_NV_stereo_view_rendering
+const char* const E_SPV_NV_stereo_view_rendering = "SPV_NV_stereo_view_rendering";
+
+//SPV_NVX_multiview_per_view_attributes
+const char* const E_SPV_NVX_multiview_per_view_attributes = "SPV_NVX_multiview_per_view_attributes";
+
+//SPV_NV_shader_subgroup_partitioned
+const char* const E_SPV_NV_shader_subgroup_partitioned = "SPV_NV_shader_subgroup_partitioned";
+
+//SPV_NV_fragment_shader_barycentric
+const char* const E_SPV_NV_fragment_shader_barycentric = "SPV_NV_fragment_shader_barycentric";
+
+//SPV_NV_compute_shader_derivatives
+const char* const E_SPV_NV_compute_shader_derivatives = "SPV_NV_compute_shader_derivatives";
+
+//SPV_NV_shader_image_footprint
+const char* const E_SPV_NV_shader_image_footprint = "SPV_NV_shader_image_footprint";
+
+//SPV_NV_mesh_shader
+const char* const E_SPV_NV_mesh_shader = "SPV_NV_mesh_shader";
+
+//SPV_NV_raytracing
+const char* const E_SPV_NV_ray_tracing = "SPV_NV_ray_tracing";
+
+//SPV_NV_shading_rate
+const char* const E_SPV_NV_shading_rate = "SPV_NV_shading_rate";
+
+//SPV_NV_cooperative_matrix
+const char* const E_SPV_NV_cooperative_matrix = "SPV_NV_cooperative_matrix";
+
+#endif // #ifndef GLSLextNV_H
diff --git a/src/3rdparty/glslang/SPIRV/GLSL.std.450.h b/src/3rdparty/glslang/SPIRV/GLSL.std.450.h
new file mode 100644
index 0000000..df31092
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/GLSL.std.450.h
@@ -0,0 +1,131 @@
+/*
+** Copyright (c) 2014-2016 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and/or associated documentation files (the "Materials"),
+** to deal in the Materials without restriction, including without limitation
+** the rights to use, copy, modify, merge, publish, distribute, sublicense,
+** and/or sell copies of the Materials, and to permit persons to whom the
+** Materials are furnished to do so, subject to the following conditions:
+**
+** The above copyright notice and this permission notice shall be included in
+** all copies or substantial portions of the Materials.
+**
+** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
+** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
+** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
+** IN THE MATERIALS.
+*/
+
+#ifndef GLSLstd450_H
+#define GLSLstd450_H
+
+static const int GLSLstd450Version = 100;
+static const int GLSLstd450Revision = 1;
+
+enum GLSLstd450 {
+ GLSLstd450Bad = 0, // Don't use
+
+ GLSLstd450Round = 1,
+ GLSLstd450RoundEven = 2,
+ GLSLstd450Trunc = 3,
+ GLSLstd450FAbs = 4,
+ GLSLstd450SAbs = 5,
+ GLSLstd450FSign = 6,
+ GLSLstd450SSign = 7,
+ GLSLstd450Floor = 8,
+ GLSLstd450Ceil = 9,
+ GLSLstd450Fract = 10,
+
+ GLSLstd450Radians = 11,
+ GLSLstd450Degrees = 12,
+ GLSLstd450Sin = 13,
+ GLSLstd450Cos = 14,
+ GLSLstd450Tan = 15,
+ GLSLstd450Asin = 16,
+ GLSLstd450Acos = 17,
+ GLSLstd450Atan = 18,
+ GLSLstd450Sinh = 19,
+ GLSLstd450Cosh = 20,
+ GLSLstd450Tanh = 21,
+ GLSLstd450Asinh = 22,
+ GLSLstd450Acosh = 23,
+ GLSLstd450Atanh = 24,
+ GLSLstd450Atan2 = 25,
+
+ GLSLstd450Pow = 26,
+ GLSLstd450Exp = 27,
+ GLSLstd450Log = 28,
+ GLSLstd450Exp2 = 29,
+ GLSLstd450Log2 = 30,
+ GLSLstd450Sqrt = 31,
+ GLSLstd450InverseSqrt = 32,
+
+ GLSLstd450Determinant = 33,
+ GLSLstd450MatrixInverse = 34,
+
+ GLSLstd450Modf = 35, // second operand needs an OpVariable to write to
+ GLSLstd450ModfStruct = 36, // no OpVariable operand
+ GLSLstd450FMin = 37,
+ GLSLstd450UMin = 38,
+ GLSLstd450SMin = 39,
+ GLSLstd450FMax = 40,
+ GLSLstd450UMax = 41,
+ GLSLstd450SMax = 42,
+ GLSLstd450FClamp = 43,
+ GLSLstd450UClamp = 44,
+ GLSLstd450SClamp = 45,
+ GLSLstd450FMix = 46,
+ GLSLstd450IMix = 47, // Reserved
+ GLSLstd450Step = 48,
+ GLSLstd450SmoothStep = 49,
+
+ GLSLstd450Fma = 50,
+ GLSLstd450Frexp = 51, // second operand needs an OpVariable to write to
+ GLSLstd450FrexpStruct = 52, // no OpVariable operand
+ GLSLstd450Ldexp = 53,
+
+ GLSLstd450PackSnorm4x8 = 54,
+ GLSLstd450PackUnorm4x8 = 55,
+ GLSLstd450PackSnorm2x16 = 56,
+ GLSLstd450PackUnorm2x16 = 57,
+ GLSLstd450PackHalf2x16 = 58,
+ GLSLstd450PackDouble2x32 = 59,
+ GLSLstd450UnpackSnorm2x16 = 60,
+ GLSLstd450UnpackUnorm2x16 = 61,
+ GLSLstd450UnpackHalf2x16 = 62,
+ GLSLstd450UnpackSnorm4x8 = 63,
+ GLSLstd450UnpackUnorm4x8 = 64,
+ GLSLstd450UnpackDouble2x32 = 65,
+
+ GLSLstd450Length = 66,
+ GLSLstd450Distance = 67,
+ GLSLstd450Cross = 68,
+ GLSLstd450Normalize = 69,
+ GLSLstd450FaceForward = 70,
+ GLSLstd450Reflect = 71,
+ GLSLstd450Refract = 72,
+
+ GLSLstd450FindILsb = 73,
+ GLSLstd450FindSMsb = 74,
+ GLSLstd450FindUMsb = 75,
+
+ GLSLstd450InterpolateAtCentroid = 76,
+ GLSLstd450InterpolateAtSample = 77,
+ GLSLstd450InterpolateAtOffset = 78,
+
+ GLSLstd450NMin = 79,
+ GLSLstd450NMax = 80,
+ GLSLstd450NClamp = 81,
+
+ GLSLstd450Count
+};
+
+#endif // #ifndef GLSLstd450_H
diff --git a/src/3rdparty/glslang/SPIRV/GlslangToSpv.cpp b/src/3rdparty/glslang/SPIRV/GlslangToSpv.cpp
new file mode 100644
index 0000000..25ef210
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/GlslangToSpv.cpp
@@ -0,0 +1,7980 @@
+//
+// Copyright (C) 2014-2016 LunarG, Inc.
+// Copyright (C) 2015-2018 Google, Inc.
+// Copyright (C) 2017 ARM Limited.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+//
+// Visit the nodes in the glslang intermediate tree representation to
+// translate them to SPIR-V.
+//
+
+#include "spirv.hpp"
+#include "GlslangToSpv.h"
+#include "SpvBuilder.h"
+namespace spv {
+ #include "GLSL.std.450.h"
+ #include "GLSL.ext.KHR.h"
+ #include "GLSL.ext.EXT.h"
+#ifdef AMD_EXTENSIONS
+ #include "GLSL.ext.AMD.h"
+#endif
+ #include "GLSL.ext.NV.h"
+}
+
+// Glslang includes
+#include "../glslang/MachineIndependent/localintermediate.h"
+#include "../glslang/MachineIndependent/SymbolTable.h"
+#include "../glslang/Include/Common.h"
+#include "../glslang/Include/revision.h"
+
+#include <fstream>
+#include <iomanip>
+#include <list>
+#include <map>
+#include <stack>
+#include <string>
+#include <vector>
+
+namespace {
+
+namespace {
+class SpecConstantOpModeGuard {
+public:
+ SpecConstantOpModeGuard(spv::Builder* builder)
+ : builder_(builder) {
+ previous_flag_ = builder->isInSpecConstCodeGenMode();
+ }
+ ~SpecConstantOpModeGuard() {
+ previous_flag_ ? builder_->setToSpecConstCodeGenMode()
+ : builder_->setToNormalCodeGenMode();
+ }
+ void turnOnSpecConstantOpMode() {
+ builder_->setToSpecConstCodeGenMode();
+ }
+
+private:
+ spv::Builder* builder_;
+ bool previous_flag_;
+};
+
+struct OpDecorations {
+ spv::Decoration precision;
+ spv::Decoration noContraction;
+ spv::Decoration nonUniform;
+};
+
+} // namespace
+
+//
+// The main holder of information for translating glslang to SPIR-V.
+//
+// Derives from the AST walking base class.
+//
+class TGlslangToSpvTraverser : public glslang::TIntermTraverser {
+public:
+ TGlslangToSpvTraverser(unsigned int spvVersion, const glslang::TIntermediate*, spv::SpvBuildLogger* logger,
+ glslang::SpvOptions& options);
+ virtual ~TGlslangToSpvTraverser() { }
+
+ bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate*);
+ bool visitBinary(glslang::TVisit, glslang::TIntermBinary*);
+ void visitConstantUnion(glslang::TIntermConstantUnion*);
+ bool visitSelection(glslang::TVisit, glslang::TIntermSelection*);
+ bool visitSwitch(glslang::TVisit, glslang::TIntermSwitch*);
+ void visitSymbol(glslang::TIntermSymbol* symbol);
+ bool visitUnary(glslang::TVisit, glslang::TIntermUnary*);
+ bool visitLoop(glslang::TVisit, glslang::TIntermLoop*);
+ bool visitBranch(glslang::TVisit visit, glslang::TIntermBranch*);
+
+ void finishSpv();
+ void dumpSpv(std::vector<unsigned int>& out);
+
+protected:
+ TGlslangToSpvTraverser(TGlslangToSpvTraverser&);
+ TGlslangToSpvTraverser& operator=(TGlslangToSpvTraverser&);
+
+ spv::Decoration TranslateInterpolationDecoration(const glslang::TQualifier& qualifier);
+ spv::Decoration TranslateAuxiliaryStorageDecoration(const glslang::TQualifier& qualifier);
+ spv::Decoration TranslateNonUniformDecoration(const glslang::TQualifier& qualifier);
+ spv::Builder::AccessChain::CoherentFlags TranslateCoherent(const glslang::TType& type);
+ spv::MemoryAccessMask TranslateMemoryAccess(const spv::Builder::AccessChain::CoherentFlags &coherentFlags);
+ spv::ImageOperandsMask TranslateImageOperands(const spv::Builder::AccessChain::CoherentFlags &coherentFlags);
+ spv::Scope TranslateMemoryScope(const spv::Builder::AccessChain::CoherentFlags &coherentFlags);
+ spv::BuiltIn TranslateBuiltInDecoration(glslang::TBuiltInVariable, bool memberDeclaration);
+ spv::ImageFormat TranslateImageFormat(const glslang::TType& type);
+ spv::SelectionControlMask TranslateSelectionControl(const glslang::TIntermSelection&) const;
+ spv::SelectionControlMask TranslateSwitchControl(const glslang::TIntermSwitch&) const;
+ spv::LoopControlMask TranslateLoopControl(const glslang::TIntermLoop&, unsigned int& dependencyLength) const;
+ spv::StorageClass TranslateStorageClass(const glslang::TType&);
+ void addIndirectionIndexCapabilities(const glslang::TType& baseType, const glslang::TType& indexType);
+ spv::Id createSpvVariable(const glslang::TIntermSymbol*);
+ spv::Id getSampledType(const glslang::TSampler&);
+ spv::Id getInvertedSwizzleType(const glslang::TIntermTyped&);
+ spv::Id createInvertedSwizzle(spv::Decoration precision, const glslang::TIntermTyped&, spv::Id parentResult);
+ void convertSwizzle(const glslang::TIntermAggregate&, std::vector<unsigned>& swizzle);
+ spv::Id convertGlslangToSpvType(const glslang::TType& type, bool forwardReferenceOnly = false);
+ spv::Id convertGlslangToSpvType(const glslang::TType& type, glslang::TLayoutPacking, const glslang::TQualifier&,
+ bool lastBufferBlockMember, bool forwardReferenceOnly = false);
+ bool filterMember(const glslang::TType& member);
+ spv::Id convertGlslangStructToSpvType(const glslang::TType&, const glslang::TTypeList* glslangStruct,
+ glslang::TLayoutPacking, const glslang::TQualifier&);
+ void decorateStructType(const glslang::TType&, const glslang::TTypeList* glslangStruct, glslang::TLayoutPacking,
+ const glslang::TQualifier&, spv::Id);
+ spv::Id makeArraySizeId(const glslang::TArraySizes&, int dim);
+ spv::Id accessChainLoad(const glslang::TType& type);
+ void accessChainStore(const glslang::TType& type, spv::Id rvalue);
+ void multiTypeStore(const glslang::TType&, spv::Id rValue);
+ glslang::TLayoutPacking getExplicitLayout(const glslang::TType& type) const;
+ int getArrayStride(const glslang::TType& arrayType, glslang::TLayoutPacking, glslang::TLayoutMatrix);
+ int getMatrixStride(const glslang::TType& matrixType, glslang::TLayoutPacking, glslang::TLayoutMatrix);
+ void updateMemberOffset(const glslang::TType& structType, const glslang::TType& memberType, int& currentOffset,
+ int& nextOffset, glslang::TLayoutPacking, glslang::TLayoutMatrix);
+ void declareUseOfStructMember(const glslang::TTypeList& members, int glslangMember);
+
+ bool isShaderEntryPoint(const glslang::TIntermAggregate* node);
+ bool writableParam(glslang::TStorageQualifier) const;
+ bool originalParam(glslang::TStorageQualifier, const glslang::TType&, bool implicitThisParam);
+ void makeFunctions(const glslang::TIntermSequence&);
+ void makeGlobalInitializers(const glslang::TIntermSequence&);
+ void visitFunctions(const glslang::TIntermSequence&);
+ void handleFunctionEntry(const glslang::TIntermAggregate* node);
+ void translateArguments(const glslang::TIntermAggregate& node, std::vector<spv::Id>& arguments);
+ void translateArguments(glslang::TIntermUnary& node, std::vector<spv::Id>& arguments);
+ spv::Id createImageTextureFunctionCall(glslang::TIntermOperator* node);
+ spv::Id handleUserFunctionCall(const glslang::TIntermAggregate*);
+
+ spv::Id createBinaryOperation(glslang::TOperator op, OpDecorations&, spv::Id typeId, spv::Id left, spv::Id right,
+ glslang::TBasicType typeProxy, bool reduceComparison = true);
+ spv::Id createBinaryMatrixOperation(spv::Op, OpDecorations&, spv::Id typeId, spv::Id left, spv::Id right);
+ spv::Id createUnaryOperation(glslang::TOperator op, OpDecorations&, spv::Id typeId, spv::Id operand,
+ glslang::TBasicType typeProxy);
+ spv::Id createUnaryMatrixOperation(spv::Op op, OpDecorations&, spv::Id typeId, spv::Id operand,
+ glslang::TBasicType typeProxy);
+ spv::Id createConversion(glslang::TOperator op, OpDecorations&, spv::Id destTypeId, spv::Id operand,
+ glslang::TBasicType typeProxy);
+ spv::Id createIntWidthConversion(glslang::TOperator op, spv::Id operand, int vectorSize);
+ spv::Id makeSmearedConstant(spv::Id constant, int vectorSize);
+ spv::Id createAtomicOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector<spv::Id>& operands, glslang::TBasicType typeProxy);
+ spv::Id createInvocationsOperation(glslang::TOperator op, spv::Id typeId, std::vector<spv::Id>& operands, glslang::TBasicType typeProxy);
+ spv::Id CreateInvocationsVectorOperation(spv::Op op, spv::GroupOperation groupOperation, spv::Id typeId, std::vector<spv::Id>& operands);
+ spv::Id createSubgroupOperation(glslang::TOperator op, spv::Id typeId, std::vector<spv::Id>& operands, glslang::TBasicType typeProxy);
+ spv::Id createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector<spv::Id>& operands, glslang::TBasicType typeProxy);
+ spv::Id createNoArgOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId);
+ spv::Id getSymbolId(const glslang::TIntermSymbol* node);
+#ifdef NV_EXTENSIONS
+ void addMeshNVDecoration(spv::Id id, int member, const glslang::TQualifier & qualifier);
+#endif
+ spv::Id createSpvConstant(const glslang::TIntermTyped&);
+ spv::Id createSpvConstantFromConstUnionArray(const glslang::TType& type, const glslang::TConstUnionArray&, int& nextConst, bool specConstant);
+ bool isTrivialLeaf(const glslang::TIntermTyped* node);
+ bool isTrivial(const glslang::TIntermTyped* node);
+ spv::Id createShortCircuit(glslang::TOperator, glslang::TIntermTyped& left, glslang::TIntermTyped& right);
+#ifdef AMD_EXTENSIONS
+ spv::Id getExtBuiltins(const char* name);
+#endif
+ void addPre13Extension(const char* ext)
+ {
+ if (builder.getSpvVersion() < glslang::EShTargetSpv_1_3)
+ builder.addExtension(ext);
+ }
+
+ glslang::SpvOptions& options;
+ spv::Function* shaderEntry;
+ spv::Function* currentFunction;
+ spv::Instruction* entryPoint;
+ int sequenceDepth;
+
+ spv::SpvBuildLogger* logger;
+
+ // There is a 1:1 mapping between a spv builder and a module; this is thread safe
+ spv::Builder builder;
+ bool inEntryPoint;
+ bool entryPointTerminated;
+ bool linkageOnly; // true when visiting the set of objects in the AST present only for establishing interface, whether or not they were statically used
+ std::set<spv::Id> iOSet; // all input/output variables from either static use or declaration of interface
+ const glslang::TIntermediate* glslangIntermediate;
+ spv::Id stdBuiltins;
+ std::unordered_map<const char*, spv::Id> extBuiltinMap;
+
+ std::unordered_map<int, spv::Id> symbolValues;
+ std::unordered_set<int> rValueParameters; // set of formal function parameters passed as rValues, rather than a pointer
+ std::unordered_map<std::string, spv::Function*> functionMap;
+ std::unordered_map<const glslang::TTypeList*, spv::Id> structMap[glslang::ElpCount][glslang::ElmCount];
+ // for mapping glslang block indices to spv indices (e.g., due to hidden members):
+ std::unordered_map<const glslang::TTypeList*, std::vector<int> > memberRemapper;
+ std::stack<bool> breakForLoop; // false means break for switch
+ std::unordered_map<std::string, const glslang::TIntermSymbol*> counterOriginator;
+ // Map pointee types for EbtReference to their forward pointers
+ std::map<const glslang::TType *, spv::Id> forwardPointers;
+};
+
+//
+// Helper functions for translating glslang representations to SPIR-V enumerants.
+//
+
+// Translate glslang profile to SPIR-V source language.
+spv::SourceLanguage TranslateSourceLanguage(glslang::EShSource source, EProfile profile)
+{
+ switch (source) {
+ case glslang::EShSourceGlsl:
+ switch (profile) {
+ case ENoProfile:
+ case ECoreProfile:
+ case ECompatibilityProfile:
+ return spv::SourceLanguageGLSL;
+ case EEsProfile:
+ return spv::SourceLanguageESSL;
+ default:
+ return spv::SourceLanguageUnknown;
+ }
+ case glslang::EShSourceHlsl:
+ return spv::SourceLanguageHLSL;
+ default:
+ return spv::SourceLanguageUnknown;
+ }
+}
+
+// Translate glslang language (stage) to SPIR-V execution model.
+spv::ExecutionModel TranslateExecutionModel(EShLanguage stage)
+{
+ switch (stage) {
+ case EShLangVertex: return spv::ExecutionModelVertex;
+ case EShLangTessControl: return spv::ExecutionModelTessellationControl;
+ case EShLangTessEvaluation: return spv::ExecutionModelTessellationEvaluation;
+ case EShLangGeometry: return spv::ExecutionModelGeometry;
+ case EShLangFragment: return spv::ExecutionModelFragment;
+ case EShLangCompute: return spv::ExecutionModelGLCompute;
+#ifdef NV_EXTENSIONS
+ case EShLangRayGenNV: return spv::ExecutionModelRayGenerationNV;
+ case EShLangIntersectNV: return spv::ExecutionModelIntersectionNV;
+ case EShLangAnyHitNV: return spv::ExecutionModelAnyHitNV;
+ case EShLangClosestHitNV: return spv::ExecutionModelClosestHitNV;
+ case EShLangMissNV: return spv::ExecutionModelMissNV;
+ case EShLangCallableNV: return spv::ExecutionModelCallableNV;
+ case EShLangTaskNV: return spv::ExecutionModelTaskNV;
+ case EShLangMeshNV: return spv::ExecutionModelMeshNV;
+#endif
+ default:
+ assert(0);
+ return spv::ExecutionModelFragment;
+ }
+}
+
+// Translate glslang sampler type to SPIR-V dimensionality.
+spv::Dim TranslateDimensionality(const glslang::TSampler& sampler)
+{
+ switch (sampler.dim) {
+ case glslang::Esd1D: return spv::Dim1D;
+ case glslang::Esd2D: return spv::Dim2D;
+ case glslang::Esd3D: return spv::Dim3D;
+ case glslang::EsdCube: return spv::DimCube;
+ case glslang::EsdRect: return spv::DimRect;
+ case glslang::EsdBuffer: return spv::DimBuffer;
+ case glslang::EsdSubpass: return spv::DimSubpassData;
+ default:
+ assert(0);
+ return spv::Dim2D;
+ }
+}
+
+// Translate glslang precision to SPIR-V precision decorations.
+spv::Decoration TranslatePrecisionDecoration(glslang::TPrecisionQualifier glslangPrecision)
+{
+ switch (glslangPrecision) {
+ case glslang::EpqLow: return spv::DecorationRelaxedPrecision;
+ case glslang::EpqMedium: return spv::DecorationRelaxedPrecision;
+ default:
+ return spv::NoPrecision;
+ }
+}
+
+// Translate glslang type to SPIR-V precision decorations.
+spv::Decoration TranslatePrecisionDecoration(const glslang::TType& type)
+{
+ return TranslatePrecisionDecoration(type.getQualifier().precision);
+}
+
+// Translate glslang type to SPIR-V block decorations.
+spv::Decoration TranslateBlockDecoration(const glslang::TType& type, bool useStorageBuffer)
+{
+ if (type.getBasicType() == glslang::EbtBlock) {
+ switch (type.getQualifier().storage) {
+ case glslang::EvqUniform: return spv::DecorationBlock;
+ case glslang::EvqBuffer: return useStorageBuffer ? spv::DecorationBlock : spv::DecorationBufferBlock;
+ case glslang::EvqVaryingIn: return spv::DecorationBlock;
+ case glslang::EvqVaryingOut: return spv::DecorationBlock;
+#ifdef NV_EXTENSIONS
+ case glslang::EvqPayloadNV: return spv::DecorationBlock;
+ case glslang::EvqPayloadInNV: return spv::DecorationBlock;
+ case glslang::EvqHitAttrNV: return spv::DecorationBlock;
+ case glslang::EvqCallableDataNV: return spv::DecorationBlock;
+ case glslang::EvqCallableDataInNV: return spv::DecorationBlock;
+#endif
+ default:
+ assert(0);
+ break;
+ }
+ }
+
+ return spv::DecorationMax;
+}
+
+// Translate glslang type to SPIR-V memory decorations.
+void TranslateMemoryDecoration(const glslang::TQualifier& qualifier, std::vector<spv::Decoration>& memory, bool useVulkanMemoryModel)
+{
+ if (!useVulkanMemoryModel) {
+ if (qualifier.coherent)
+ memory.push_back(spv::DecorationCoherent);
+ if (qualifier.volatil) {
+ memory.push_back(spv::DecorationVolatile);
+ memory.push_back(spv::DecorationCoherent);
+ }
+ }
+ if (qualifier.restrict)
+ memory.push_back(spv::DecorationRestrict);
+ if (qualifier.readonly)
+ memory.push_back(spv::DecorationNonWritable);
+ if (qualifier.writeonly)
+ memory.push_back(spv::DecorationNonReadable);
+}
+
+// Translate glslang type to SPIR-V layout decorations.
+spv::Decoration TranslateLayoutDecoration(const glslang::TType& type, glslang::TLayoutMatrix matrixLayout)
+{
+ if (type.isMatrix()) {
+ switch (matrixLayout) {
+ case glslang::ElmRowMajor:
+ return spv::DecorationRowMajor;
+ case glslang::ElmColumnMajor:
+ return spv::DecorationColMajor;
+ default:
+ // opaque layouts don't need a majorness
+ return spv::DecorationMax;
+ }
+ } else {
+ switch (type.getBasicType()) {
+ default:
+ return spv::DecorationMax;
+ break;
+ case glslang::EbtBlock:
+ switch (type.getQualifier().storage) {
+ case glslang::EvqUniform:
+ case glslang::EvqBuffer:
+ switch (type.getQualifier().layoutPacking) {
+ case glslang::ElpShared: return spv::DecorationGLSLShared;
+ case glslang::ElpPacked: return spv::DecorationGLSLPacked;
+ default:
+ return spv::DecorationMax;
+ }
+ case glslang::EvqVaryingIn:
+ case glslang::EvqVaryingOut:
+ if (type.getQualifier().isTaskMemory()) {
+ switch (type.getQualifier().layoutPacking) {
+ case glslang::ElpShared: return spv::DecorationGLSLShared;
+ case glslang::ElpPacked: return spv::DecorationGLSLPacked;
+ default: break;
+ }
+ } else {
+ assert(type.getQualifier().layoutPacking == glslang::ElpNone);
+ }
+ return spv::DecorationMax;
+#ifdef NV_EXTENSIONS
+ case glslang::EvqPayloadNV:
+ case glslang::EvqPayloadInNV:
+ case glslang::EvqHitAttrNV:
+ case glslang::EvqCallableDataNV:
+ case glslang::EvqCallableDataInNV:
+ return spv::DecorationMax;
+#endif
+ default:
+ assert(0);
+ return spv::DecorationMax;
+ }
+ }
+ }
+}
+
+// Translate glslang type to SPIR-V interpolation decorations.
+// Returns spv::DecorationMax when no decoration
+// should be applied.
+spv::Decoration TGlslangToSpvTraverser::TranslateInterpolationDecoration(const glslang::TQualifier& qualifier)
+{
+ if (qualifier.smooth)
+ // Smooth decoration doesn't exist in SPIR-V 1.0
+ return spv::DecorationMax;
+ else if (qualifier.nopersp)
+ return spv::DecorationNoPerspective;
+ else if (qualifier.flat)
+ return spv::DecorationFlat;
+#ifdef AMD_EXTENSIONS
+ else if (qualifier.explicitInterp) {
+ builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter);
+ return spv::DecorationExplicitInterpAMD;
+ }
+#endif
+ else
+ return spv::DecorationMax;
+}
+
+// Translate glslang type to SPIR-V auxiliary storage decorations.
+// Returns spv::DecorationMax when no decoration
+// should be applied.
+spv::Decoration TGlslangToSpvTraverser::TranslateAuxiliaryStorageDecoration(const glslang::TQualifier& qualifier)
+{
+ if (qualifier.patch)
+ return spv::DecorationPatch;
+ else if (qualifier.centroid)
+ return spv::DecorationCentroid;
+ else if (qualifier.sample) {
+ builder.addCapability(spv::CapabilitySampleRateShading);
+ return spv::DecorationSample;
+ } else
+ return spv::DecorationMax;
+}
+
+// If glslang type is invariant, return SPIR-V invariant decoration.
+spv::Decoration TranslateInvariantDecoration(const glslang::TQualifier& qualifier)
+{
+ if (qualifier.invariant)
+ return spv::DecorationInvariant;
+ else
+ return spv::DecorationMax;
+}
+
+// If glslang type is noContraction, return SPIR-V NoContraction decoration.
+spv::Decoration TranslateNoContractionDecoration(const glslang::TQualifier& qualifier)
+{
+ if (qualifier.noContraction)
+ return spv::DecorationNoContraction;
+ else
+ return spv::DecorationMax;
+}
+
+// If glslang type is nonUniform, return SPIR-V NonUniform decoration.
+spv::Decoration TGlslangToSpvTraverser::TranslateNonUniformDecoration(const glslang::TQualifier& qualifier)
+{
+ if (qualifier.isNonUniform()) {
+ builder.addExtension("SPV_EXT_descriptor_indexing");
+ builder.addCapability(spv::CapabilityShaderNonUniformEXT);
+ return spv::DecorationNonUniformEXT;
+ } else
+ return spv::DecorationMax;
+}
+
+spv::MemoryAccessMask TGlslangToSpvTraverser::TranslateMemoryAccess(const spv::Builder::AccessChain::CoherentFlags &coherentFlags)
+{
+ if (!glslangIntermediate->usingVulkanMemoryModel() || coherentFlags.isImage) {
+ return spv::MemoryAccessMaskNone;
+ }
+ spv::MemoryAccessMask mask = spv::MemoryAccessMaskNone;
+ if (coherentFlags.volatil ||
+ coherentFlags.coherent ||
+ coherentFlags.devicecoherent ||
+ coherentFlags.queuefamilycoherent ||
+ coherentFlags.workgroupcoherent ||
+ coherentFlags.subgroupcoherent) {
+ mask = mask | spv::MemoryAccessMakePointerAvailableKHRMask |
+ spv::MemoryAccessMakePointerVisibleKHRMask;
+ }
+ if (coherentFlags.nonprivate) {
+ mask = mask | spv::MemoryAccessNonPrivatePointerKHRMask;
+ }
+ if (coherentFlags.volatil) {
+ mask = mask | spv::MemoryAccessVolatileMask;
+ }
+ if (mask != spv::MemoryAccessMaskNone) {
+ builder.addCapability(spv::CapabilityVulkanMemoryModelKHR);
+ }
+ return mask;
+}
+
+spv::ImageOperandsMask TGlslangToSpvTraverser::TranslateImageOperands(const spv::Builder::AccessChain::CoherentFlags &coherentFlags)
+{
+ if (!glslangIntermediate->usingVulkanMemoryModel()) {
+ return spv::ImageOperandsMaskNone;
+ }
+ spv::ImageOperandsMask mask = spv::ImageOperandsMaskNone;
+ if (coherentFlags.volatil ||
+ coherentFlags.coherent ||
+ coherentFlags.devicecoherent ||
+ coherentFlags.queuefamilycoherent ||
+ coherentFlags.workgroupcoherent ||
+ coherentFlags.subgroupcoherent) {
+ mask = mask | spv::ImageOperandsMakeTexelAvailableKHRMask |
+ spv::ImageOperandsMakeTexelVisibleKHRMask;
+ }
+ if (coherentFlags.nonprivate) {
+ mask = mask | spv::ImageOperandsNonPrivateTexelKHRMask;
+ }
+ if (coherentFlags.volatil) {
+ mask = mask | spv::ImageOperandsVolatileTexelKHRMask;
+ }
+ if (mask != spv::ImageOperandsMaskNone) {
+ builder.addCapability(spv::CapabilityVulkanMemoryModelKHR);
+ }
+ return mask;
+}
+
+spv::Builder::AccessChain::CoherentFlags TGlslangToSpvTraverser::TranslateCoherent(const glslang::TType& type)
+{
+ spv::Builder::AccessChain::CoherentFlags flags;
+ flags.coherent = type.getQualifier().coherent;
+ flags.devicecoherent = type.getQualifier().devicecoherent;
+ flags.queuefamilycoherent = type.getQualifier().queuefamilycoherent;
+ // shared variables are implicitly workgroupcoherent in GLSL.
+ flags.workgroupcoherent = type.getQualifier().workgroupcoherent ||
+ type.getQualifier().storage == glslang::EvqShared;
+ flags.subgroupcoherent = type.getQualifier().subgroupcoherent;
+ flags.volatil = type.getQualifier().volatil;
+ // *coherent variables are implicitly nonprivate in GLSL
+ flags.nonprivate = type.getQualifier().nonprivate ||
+ flags.subgroupcoherent ||
+ flags.workgroupcoherent ||
+ flags.queuefamilycoherent ||
+ flags.devicecoherent ||
+ flags.coherent ||
+ flags.volatil;
+ flags.isImage = type.getBasicType() == glslang::EbtSampler;
+ return flags;
+}
+
+spv::Scope TGlslangToSpvTraverser::TranslateMemoryScope(const spv::Builder::AccessChain::CoherentFlags &coherentFlags)
+{
+ spv::Scope scope;
+ if (coherentFlags.volatil || coherentFlags.coherent) {
+ // coherent defaults to Device scope in the old model, QueueFamilyKHR scope in the new model
+ scope = glslangIntermediate->usingVulkanMemoryModel() ? spv::ScopeQueueFamilyKHR : spv::ScopeDevice;
+ } else if (coherentFlags.devicecoherent) {
+ scope = spv::ScopeDevice;
+ } else if (coherentFlags.queuefamilycoherent) {
+ scope = spv::ScopeQueueFamilyKHR;
+ } else if (coherentFlags.workgroupcoherent) {
+ scope = spv::ScopeWorkgroup;
+ } else if (coherentFlags.subgroupcoherent) {
+ scope = spv::ScopeSubgroup;
+ } else {
+ scope = spv::ScopeMax;
+ }
+ if (glslangIntermediate->usingVulkanMemoryModel() && scope == spv::ScopeDevice) {
+ builder.addCapability(spv::CapabilityVulkanMemoryModelDeviceScopeKHR);
+ }
+ return scope;
+}
+
+// Translate a glslang built-in variable to a SPIR-V built in decoration. Also generate
+// associated capabilities when required. For some built-in variables, a capability
+// is generated only when using the variable in an executable instruction, but not when
+// just declaring a struct member variable with it. This is true for PointSize,
+// ClipDistance, and CullDistance.
+spv::BuiltIn TGlslangToSpvTraverser::TranslateBuiltInDecoration(glslang::TBuiltInVariable builtIn, bool memberDeclaration)
+{
+ switch (builtIn) {
+ case glslang::EbvPointSize:
+ // Defer adding the capability until the built-in is actually used.
+ if (! memberDeclaration) {
+ switch (glslangIntermediate->getStage()) {
+ case EShLangGeometry:
+ builder.addCapability(spv::CapabilityGeometryPointSize);
+ break;
+ case EShLangTessControl:
+ case EShLangTessEvaluation:
+ builder.addCapability(spv::CapabilityTessellationPointSize);
+ break;
+ default:
+ break;
+ }
+ }
+ return spv::BuiltInPointSize;
+
+ // These *Distance capabilities logically belong here, but if the member is declared and
+ // then never used, consumers of SPIR-V prefer the capability not be declared.
+ // They are now generated when used, rather than here when declared.
+ // Potentially, the specification should be more clear what the minimum
+ // use needed is to trigger the capability.
+ //
+ case glslang::EbvClipDistance:
+ if (!memberDeclaration)
+ builder.addCapability(spv::CapabilityClipDistance);
+ return spv::BuiltInClipDistance;
+
+ case glslang::EbvCullDistance:
+ if (!memberDeclaration)
+ builder.addCapability(spv::CapabilityCullDistance);
+ return spv::BuiltInCullDistance;
+
+ case glslang::EbvViewportIndex:
+ builder.addCapability(spv::CapabilityMultiViewport);
+ if (glslangIntermediate->getStage() == EShLangVertex ||
+ glslangIntermediate->getStage() == EShLangTessControl ||
+ glslangIntermediate->getStage() == EShLangTessEvaluation) {
+
+ builder.addExtension(spv::E_SPV_EXT_shader_viewport_index_layer);
+ builder.addCapability(spv::CapabilityShaderViewportIndexLayerEXT);
+ }
+ return spv::BuiltInViewportIndex;
+
+ case glslang::EbvSampleId:
+ builder.addCapability(spv::CapabilitySampleRateShading);
+ return spv::BuiltInSampleId;
+
+ case glslang::EbvSamplePosition:
+ builder.addCapability(spv::CapabilitySampleRateShading);
+ return spv::BuiltInSamplePosition;
+
+ case glslang::EbvSampleMask:
+ return spv::BuiltInSampleMask;
+
+ case glslang::EbvLayer:
+#ifdef NV_EXTENSIONS
+ if (glslangIntermediate->getStage() == EShLangMeshNV) {
+ return spv::BuiltInLayer;
+ }
+#endif
+ builder.addCapability(spv::CapabilityGeometry);
+ if (glslangIntermediate->getStage() == EShLangVertex ||
+ glslangIntermediate->getStage() == EShLangTessControl ||
+ glslangIntermediate->getStage() == EShLangTessEvaluation) {
+
+ builder.addExtension(spv::E_SPV_EXT_shader_viewport_index_layer);
+ builder.addCapability(spv::CapabilityShaderViewportIndexLayerEXT);
+ }
+ return spv::BuiltInLayer;
+
+ case glslang::EbvPosition: return spv::BuiltInPosition;
+ case glslang::EbvVertexId: return spv::BuiltInVertexId;
+ case glslang::EbvInstanceId: return spv::BuiltInInstanceId;
+ case glslang::EbvVertexIndex: return spv::BuiltInVertexIndex;
+ case glslang::EbvInstanceIndex: return spv::BuiltInInstanceIndex;
+
+ case glslang::EbvBaseVertex:
+ addPre13Extension(spv::E_SPV_KHR_shader_draw_parameters);
+ builder.addCapability(spv::CapabilityDrawParameters);
+ return spv::BuiltInBaseVertex;
+
+ case glslang::EbvBaseInstance:
+ addPre13Extension(spv::E_SPV_KHR_shader_draw_parameters);
+ builder.addCapability(spv::CapabilityDrawParameters);
+ return spv::BuiltInBaseInstance;
+
+ case glslang::EbvDrawId:
+ addPre13Extension(spv::E_SPV_KHR_shader_draw_parameters);
+ builder.addCapability(spv::CapabilityDrawParameters);
+ return spv::BuiltInDrawIndex;
+
+ case glslang::EbvPrimitiveId:
+ if (glslangIntermediate->getStage() == EShLangFragment)
+ builder.addCapability(spv::CapabilityGeometry);
+ return spv::BuiltInPrimitiveId;
+
+ case glslang::EbvFragStencilRef:
+ builder.addExtension(spv::E_SPV_EXT_shader_stencil_export);
+ builder.addCapability(spv::CapabilityStencilExportEXT);
+ return spv::BuiltInFragStencilRefEXT;
+
+ case glslang::EbvInvocationId: return spv::BuiltInInvocationId;
+ case glslang::EbvTessLevelInner: return spv::BuiltInTessLevelInner;
+ case glslang::EbvTessLevelOuter: return spv::BuiltInTessLevelOuter;
+ case glslang::EbvTessCoord: return spv::BuiltInTessCoord;
+ case glslang::EbvPatchVertices: return spv::BuiltInPatchVertices;
+ case glslang::EbvFragCoord: return spv::BuiltInFragCoord;
+ case glslang::EbvPointCoord: return spv::BuiltInPointCoord;
+ case glslang::EbvFace: return spv::BuiltInFrontFacing;
+ case glslang::EbvFragDepth: return spv::BuiltInFragDepth;
+ case glslang::EbvHelperInvocation: return spv::BuiltInHelperInvocation;
+ case glslang::EbvNumWorkGroups: return spv::BuiltInNumWorkgroups;
+ case glslang::EbvWorkGroupSize: return spv::BuiltInWorkgroupSize;
+ case glslang::EbvWorkGroupId: return spv::BuiltInWorkgroupId;
+ case glslang::EbvLocalInvocationId: return spv::BuiltInLocalInvocationId;
+ case glslang::EbvLocalInvocationIndex: return spv::BuiltInLocalInvocationIndex;
+ case glslang::EbvGlobalInvocationId: return spv::BuiltInGlobalInvocationId;
+
+ case glslang::EbvSubGroupSize:
+ builder.addExtension(spv::E_SPV_KHR_shader_ballot);
+ builder.addCapability(spv::CapabilitySubgroupBallotKHR);
+ return spv::BuiltInSubgroupSize;
+
+ case glslang::EbvSubGroupInvocation:
+ builder.addExtension(spv::E_SPV_KHR_shader_ballot);
+ builder.addCapability(spv::CapabilitySubgroupBallotKHR);
+ return spv::BuiltInSubgroupLocalInvocationId;
+
+ case glslang::EbvSubGroupEqMask:
+ builder.addExtension(spv::E_SPV_KHR_shader_ballot);
+ builder.addCapability(spv::CapabilitySubgroupBallotKHR);
+ return spv::BuiltInSubgroupEqMaskKHR;
+
+ case glslang::EbvSubGroupGeMask:
+ builder.addExtension(spv::E_SPV_KHR_shader_ballot);
+ builder.addCapability(spv::CapabilitySubgroupBallotKHR);
+ return spv::BuiltInSubgroupGeMaskKHR;
+
+ case glslang::EbvSubGroupGtMask:
+ builder.addExtension(spv::E_SPV_KHR_shader_ballot);
+ builder.addCapability(spv::CapabilitySubgroupBallotKHR);
+ return spv::BuiltInSubgroupGtMaskKHR;
+
+ case glslang::EbvSubGroupLeMask:
+ builder.addExtension(spv::E_SPV_KHR_shader_ballot);
+ builder.addCapability(spv::CapabilitySubgroupBallotKHR);
+ return spv::BuiltInSubgroupLeMaskKHR;
+
+ case glslang::EbvSubGroupLtMask:
+ builder.addExtension(spv::E_SPV_KHR_shader_ballot);
+ builder.addCapability(spv::CapabilitySubgroupBallotKHR);
+ return spv::BuiltInSubgroupLtMaskKHR;
+
+ case glslang::EbvNumSubgroups:
+ builder.addCapability(spv::CapabilityGroupNonUniform);
+ return spv::BuiltInNumSubgroups;
+
+ case glslang::EbvSubgroupID:
+ builder.addCapability(spv::CapabilityGroupNonUniform);
+ return spv::BuiltInSubgroupId;
+
+ case glslang::EbvSubgroupSize2:
+ builder.addCapability(spv::CapabilityGroupNonUniform);
+ return spv::BuiltInSubgroupSize;
+
+ case glslang::EbvSubgroupInvocation2:
+ builder.addCapability(spv::CapabilityGroupNonUniform);
+ return spv::BuiltInSubgroupLocalInvocationId;
+
+ case glslang::EbvSubgroupEqMask2:
+ builder.addCapability(spv::CapabilityGroupNonUniform);
+ builder.addCapability(spv::CapabilityGroupNonUniformBallot);
+ return spv::BuiltInSubgroupEqMask;
+
+ case glslang::EbvSubgroupGeMask2:
+ builder.addCapability(spv::CapabilityGroupNonUniform);
+ builder.addCapability(spv::CapabilityGroupNonUniformBallot);
+ return spv::BuiltInSubgroupGeMask;
+
+ case glslang::EbvSubgroupGtMask2:
+ builder.addCapability(spv::CapabilityGroupNonUniform);
+ builder.addCapability(spv::CapabilityGroupNonUniformBallot);
+ return spv::BuiltInSubgroupGtMask;
+
+ case glslang::EbvSubgroupLeMask2:
+ builder.addCapability(spv::CapabilityGroupNonUniform);
+ builder.addCapability(spv::CapabilityGroupNonUniformBallot);
+ return spv::BuiltInSubgroupLeMask;
+
+ case glslang::EbvSubgroupLtMask2:
+ builder.addCapability(spv::CapabilityGroupNonUniform);
+ builder.addCapability(spv::CapabilityGroupNonUniformBallot);
+ return spv::BuiltInSubgroupLtMask;
+#ifdef AMD_EXTENSIONS
+ case glslang::EbvBaryCoordNoPersp:
+ builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter);
+ return spv::BuiltInBaryCoordNoPerspAMD;
+
+ case glslang::EbvBaryCoordNoPerspCentroid:
+ builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter);
+ return spv::BuiltInBaryCoordNoPerspCentroidAMD;
+
+ case glslang::EbvBaryCoordNoPerspSample:
+ builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter);
+ return spv::BuiltInBaryCoordNoPerspSampleAMD;
+
+ case glslang::EbvBaryCoordSmooth:
+ builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter);
+ return spv::BuiltInBaryCoordSmoothAMD;
+
+ case glslang::EbvBaryCoordSmoothCentroid:
+ builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter);
+ return spv::BuiltInBaryCoordSmoothCentroidAMD;
+
+ case glslang::EbvBaryCoordSmoothSample:
+ builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter);
+ return spv::BuiltInBaryCoordSmoothSampleAMD;
+
+ case glslang::EbvBaryCoordPullModel:
+ builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter);
+ return spv::BuiltInBaryCoordPullModelAMD;
+#endif
+
+ case glslang::EbvDeviceIndex:
+ addPre13Extension(spv::E_SPV_KHR_device_group);
+ builder.addCapability(spv::CapabilityDeviceGroup);
+ return spv::BuiltInDeviceIndex;
+
+ case glslang::EbvViewIndex:
+ addPre13Extension(spv::E_SPV_KHR_multiview);
+ builder.addCapability(spv::CapabilityMultiView);
+ return spv::BuiltInViewIndex;
+
+ case glslang::EbvFragSizeEXT:
+ builder.addExtension(spv::E_SPV_EXT_fragment_invocation_density);
+ builder.addCapability(spv::CapabilityFragmentDensityEXT);
+ return spv::BuiltInFragSizeEXT;
+
+ case glslang::EbvFragInvocationCountEXT:
+ builder.addExtension(spv::E_SPV_EXT_fragment_invocation_density);
+ builder.addCapability(spv::CapabilityFragmentDensityEXT);
+ return spv::BuiltInFragInvocationCountEXT;
+
+#ifdef NV_EXTENSIONS
+ case glslang::EbvViewportMaskNV:
+ if (!memberDeclaration) {
+ builder.addExtension(spv::E_SPV_NV_viewport_array2);
+ builder.addCapability(spv::CapabilityShaderViewportMaskNV);
+ }
+ return spv::BuiltInViewportMaskNV;
+ case glslang::EbvSecondaryPositionNV:
+ if (!memberDeclaration) {
+ builder.addExtension(spv::E_SPV_NV_stereo_view_rendering);
+ builder.addCapability(spv::CapabilityShaderStereoViewNV);
+ }
+ return spv::BuiltInSecondaryPositionNV;
+ case glslang::EbvSecondaryViewportMaskNV:
+ if (!memberDeclaration) {
+ builder.addExtension(spv::E_SPV_NV_stereo_view_rendering);
+ builder.addCapability(spv::CapabilityShaderStereoViewNV);
+ }
+ return spv::BuiltInSecondaryViewportMaskNV;
+ case glslang::EbvPositionPerViewNV:
+ if (!memberDeclaration) {
+ builder.addExtension(spv::E_SPV_NVX_multiview_per_view_attributes);
+ builder.addCapability(spv::CapabilityPerViewAttributesNV);
+ }
+ return spv::BuiltInPositionPerViewNV;
+ case glslang::EbvViewportMaskPerViewNV:
+ if (!memberDeclaration) {
+ builder.addExtension(spv::E_SPV_NVX_multiview_per_view_attributes);
+ builder.addCapability(spv::CapabilityPerViewAttributesNV);
+ }
+ return spv::BuiltInViewportMaskPerViewNV;
+ case glslang::EbvFragFullyCoveredNV:
+ builder.addExtension(spv::E_SPV_EXT_fragment_fully_covered);
+ builder.addCapability(spv::CapabilityFragmentFullyCoveredEXT);
+ return spv::BuiltInFullyCoveredEXT;
+ case glslang::EbvFragmentSizeNV:
+ builder.addExtension(spv::E_SPV_NV_shading_rate);
+ builder.addCapability(spv::CapabilityShadingRateNV);
+ return spv::BuiltInFragmentSizeNV;
+ case glslang::EbvInvocationsPerPixelNV:
+ builder.addExtension(spv::E_SPV_NV_shading_rate);
+ builder.addCapability(spv::CapabilityShadingRateNV);
+ return spv::BuiltInInvocationsPerPixelNV;
+
+ // raytracing
+ case glslang::EbvLaunchIdNV:
+ return spv::BuiltInLaunchIdNV;
+ case glslang::EbvLaunchSizeNV:
+ return spv::BuiltInLaunchSizeNV;
+ case glslang::EbvWorldRayOriginNV:
+ return spv::BuiltInWorldRayOriginNV;
+ case glslang::EbvWorldRayDirectionNV:
+ return spv::BuiltInWorldRayDirectionNV;
+ case glslang::EbvObjectRayOriginNV:
+ return spv::BuiltInObjectRayOriginNV;
+ case glslang::EbvObjectRayDirectionNV:
+ return spv::BuiltInObjectRayDirectionNV;
+ case glslang::EbvRayTminNV:
+ return spv::BuiltInRayTminNV;
+ case glslang::EbvRayTmaxNV:
+ return spv::BuiltInRayTmaxNV;
+ case glslang::EbvInstanceCustomIndexNV:
+ return spv::BuiltInInstanceCustomIndexNV;
+ case glslang::EbvHitTNV:
+ return spv::BuiltInHitTNV;
+ case glslang::EbvHitKindNV:
+ return spv::BuiltInHitKindNV;
+ case glslang::EbvObjectToWorldNV:
+ return spv::BuiltInObjectToWorldNV;
+ case glslang::EbvWorldToObjectNV:
+ return spv::BuiltInWorldToObjectNV;
+ case glslang::EbvIncomingRayFlagsNV:
+ return spv::BuiltInIncomingRayFlagsNV;
+ case glslang::EbvBaryCoordNV:
+ builder.addExtension(spv::E_SPV_NV_fragment_shader_barycentric);
+ builder.addCapability(spv::CapabilityFragmentBarycentricNV);
+ return spv::BuiltInBaryCoordNV;
+ case glslang::EbvBaryCoordNoPerspNV:
+ builder.addExtension(spv::E_SPV_NV_fragment_shader_barycentric);
+ builder.addCapability(spv::CapabilityFragmentBarycentricNV);
+ return spv::BuiltInBaryCoordNoPerspNV;
+ case glslang::EbvTaskCountNV:
+ return spv::BuiltInTaskCountNV;
+ case glslang::EbvPrimitiveCountNV:
+ return spv::BuiltInPrimitiveCountNV;
+ case glslang::EbvPrimitiveIndicesNV:
+ return spv::BuiltInPrimitiveIndicesNV;
+ case glslang::EbvClipDistancePerViewNV:
+ return spv::BuiltInClipDistancePerViewNV;
+ case glslang::EbvCullDistancePerViewNV:
+ return spv::BuiltInCullDistancePerViewNV;
+ case glslang::EbvLayerPerViewNV:
+ return spv::BuiltInLayerPerViewNV;
+ case glslang::EbvMeshViewCountNV:
+ return spv::BuiltInMeshViewCountNV;
+ case glslang::EbvMeshViewIndicesNV:
+ return spv::BuiltInMeshViewIndicesNV;
+#endif
+ default:
+ return spv::BuiltInMax;
+ }
+}
+
+// Translate glslang image layout format to SPIR-V image format.
+spv::ImageFormat TGlslangToSpvTraverser::TranslateImageFormat(const glslang::TType& type)
+{
+ assert(type.getBasicType() == glslang::EbtSampler);
+
+ // Check for capabilities
+ switch (type.getQualifier().layoutFormat) {
+ case glslang::ElfRg32f:
+ case glslang::ElfRg16f:
+ case glslang::ElfR11fG11fB10f:
+ case glslang::ElfR16f:
+ case glslang::ElfRgba16:
+ case glslang::ElfRgb10A2:
+ case glslang::ElfRg16:
+ case glslang::ElfRg8:
+ case glslang::ElfR16:
+ case glslang::ElfR8:
+ case glslang::ElfRgba16Snorm:
+ case glslang::ElfRg16Snorm:
+ case glslang::ElfRg8Snorm:
+ case glslang::ElfR16Snorm:
+ case glslang::ElfR8Snorm:
+
+ case glslang::ElfRg32i:
+ case glslang::ElfRg16i:
+ case glslang::ElfRg8i:
+ case glslang::ElfR16i:
+ case glslang::ElfR8i:
+
+ case glslang::ElfRgb10a2ui:
+ case glslang::ElfRg32ui:
+ case glslang::ElfRg16ui:
+ case glslang::ElfRg8ui:
+ case glslang::ElfR16ui:
+ case glslang::ElfR8ui:
+ builder.addCapability(spv::CapabilityStorageImageExtendedFormats);
+ break;
+
+ default:
+ break;
+ }
+
+ // do the translation
+ switch (type.getQualifier().layoutFormat) {
+ case glslang::ElfNone: return spv::ImageFormatUnknown;
+ case glslang::ElfRgba32f: return spv::ImageFormatRgba32f;
+ case glslang::ElfRgba16f: return spv::ImageFormatRgba16f;
+ case glslang::ElfR32f: return spv::ImageFormatR32f;
+ case glslang::ElfRgba8: return spv::ImageFormatRgba8;
+ case glslang::ElfRgba8Snorm: return spv::ImageFormatRgba8Snorm;
+ case glslang::ElfRg32f: return spv::ImageFormatRg32f;
+ case glslang::ElfRg16f: return spv::ImageFormatRg16f;
+ case glslang::ElfR11fG11fB10f: return spv::ImageFormatR11fG11fB10f;
+ case glslang::ElfR16f: return spv::ImageFormatR16f;
+ case glslang::ElfRgba16: return spv::ImageFormatRgba16;
+ case glslang::ElfRgb10A2: return spv::ImageFormatRgb10A2;
+ case glslang::ElfRg16: return spv::ImageFormatRg16;
+ case glslang::ElfRg8: return spv::ImageFormatRg8;
+ case glslang::ElfR16: return spv::ImageFormatR16;
+ case glslang::ElfR8: return spv::ImageFormatR8;
+ case glslang::ElfRgba16Snorm: return spv::ImageFormatRgba16Snorm;
+ case glslang::ElfRg16Snorm: return spv::ImageFormatRg16Snorm;
+ case glslang::ElfRg8Snorm: return spv::ImageFormatRg8Snorm;
+ case glslang::ElfR16Snorm: return spv::ImageFormatR16Snorm;
+ case glslang::ElfR8Snorm: return spv::ImageFormatR8Snorm;
+ case glslang::ElfRgba32i: return spv::ImageFormatRgba32i;
+ case glslang::ElfRgba16i: return spv::ImageFormatRgba16i;
+ case glslang::ElfRgba8i: return spv::ImageFormatRgba8i;
+ case glslang::ElfR32i: return spv::ImageFormatR32i;
+ case glslang::ElfRg32i: return spv::ImageFormatRg32i;
+ case glslang::ElfRg16i: return spv::ImageFormatRg16i;
+ case glslang::ElfRg8i: return spv::ImageFormatRg8i;
+ case glslang::ElfR16i: return spv::ImageFormatR16i;
+ case glslang::ElfR8i: return spv::ImageFormatR8i;
+ case glslang::ElfRgba32ui: return spv::ImageFormatRgba32ui;
+ case glslang::ElfRgba16ui: return spv::ImageFormatRgba16ui;
+ case glslang::ElfRgba8ui: return spv::ImageFormatRgba8ui;
+ case glslang::ElfR32ui: return spv::ImageFormatR32ui;
+ case glslang::ElfRg32ui: return spv::ImageFormatRg32ui;
+ case glslang::ElfRg16ui: return spv::ImageFormatRg16ui;
+ case glslang::ElfRgb10a2ui: return spv::ImageFormatRgb10a2ui;
+ case glslang::ElfRg8ui: return spv::ImageFormatRg8ui;
+ case glslang::ElfR16ui: return spv::ImageFormatR16ui;
+ case glslang::ElfR8ui: return spv::ImageFormatR8ui;
+ default: return spv::ImageFormatMax;
+ }
+}
+
+spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSelectionControl(const glslang::TIntermSelection& selectionNode) const
+{
+ if (selectionNode.getFlatten())
+ return spv::SelectionControlFlattenMask;
+ if (selectionNode.getDontFlatten())
+ return spv::SelectionControlDontFlattenMask;
+ return spv::SelectionControlMaskNone;
+}
+
+spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSwitchControl(const glslang::TIntermSwitch& switchNode) const
+{
+ if (switchNode.getFlatten())
+ return spv::SelectionControlFlattenMask;
+ if (switchNode.getDontFlatten())
+ return spv::SelectionControlDontFlattenMask;
+ return spv::SelectionControlMaskNone;
+}
+
+// return a non-0 dependency if the dependency argument must be set
+spv::LoopControlMask TGlslangToSpvTraverser::TranslateLoopControl(const glslang::TIntermLoop& loopNode,
+ unsigned int& dependencyLength) const
+{
+ spv::LoopControlMask control = spv::LoopControlMaskNone;
+
+ if (loopNode.getDontUnroll())
+ control = control | spv::LoopControlDontUnrollMask;
+ if (loopNode.getUnroll())
+ control = control | spv::LoopControlUnrollMask;
+ if (unsigned(loopNode.getLoopDependency()) == glslang::TIntermLoop::dependencyInfinite)
+ control = control | spv::LoopControlDependencyInfiniteMask;
+ else if (loopNode.getLoopDependency() > 0) {
+ control = control | spv::LoopControlDependencyLengthMask;
+ dependencyLength = loopNode.getLoopDependency();
+ }
+
+ return control;
+}
+
+// Translate glslang type to SPIR-V storage class.
+spv::StorageClass TGlslangToSpvTraverser::TranslateStorageClass(const glslang::TType& type)
+{
+ if (type.getQualifier().isPipeInput())
+ return spv::StorageClassInput;
+ if (type.getQualifier().isPipeOutput())
+ return spv::StorageClassOutput;
+
+ if (glslangIntermediate->getSource() != glslang::EShSourceHlsl ||
+ type.getQualifier().storage == glslang::EvqUniform) {
+ if (type.getBasicType() == glslang::EbtAtomicUint)
+ return spv::StorageClassAtomicCounter;
+ if (type.containsOpaque())
+ return spv::StorageClassUniformConstant;
+ }
+
+#ifdef NV_EXTENSIONS
+ if (type.getQualifier().isUniformOrBuffer() &&
+ type.getQualifier().layoutShaderRecordNV) {
+ return spv::StorageClassShaderRecordBufferNV;
+ }
+#endif
+
+ if (glslangIntermediate->usingStorageBuffer() && type.getQualifier().storage == glslang::EvqBuffer) {
+ addPre13Extension(spv::E_SPV_KHR_storage_buffer_storage_class);
+ return spv::StorageClassStorageBuffer;
+ }
+
+ if (type.getQualifier().isUniformOrBuffer()) {
+ if (type.getQualifier().layoutPushConstant)
+ return spv::StorageClassPushConstant;
+ if (type.getBasicType() == glslang::EbtBlock)
+ return spv::StorageClassUniform;
+ return spv::StorageClassUniformConstant;
+ }
+
+ switch (type.getQualifier().storage) {
+ case glslang::EvqShared: return spv::StorageClassWorkgroup;
+ case glslang::EvqGlobal: return spv::StorageClassPrivate;
+ case glslang::EvqConstReadOnly: return spv::StorageClassFunction;
+ case glslang::EvqTemporary: return spv::StorageClassFunction;
+#ifdef NV_EXTENSIONS
+ case glslang::EvqPayloadNV: return spv::StorageClassRayPayloadNV;
+ case glslang::EvqPayloadInNV: return spv::StorageClassIncomingRayPayloadNV;
+ case glslang::EvqHitAttrNV: return spv::StorageClassHitAttributeNV;
+ case glslang::EvqCallableDataNV: return spv::StorageClassCallableDataNV;
+ case glslang::EvqCallableDataInNV: return spv::StorageClassIncomingCallableDataNV;
+#endif
+ default:
+ assert(0);
+ break;
+ }
+
+ return spv::StorageClassFunction;
+}
+
+// Add capabilities pertaining to how an array is indexed.
+void TGlslangToSpvTraverser::addIndirectionIndexCapabilities(const glslang::TType& baseType,
+ const glslang::TType& indexType)
+{
+ if (indexType.getQualifier().isNonUniform()) {
+ // deal with an asserted non-uniform index
+ // SPV_EXT_descriptor_indexing already added in TranslateNonUniformDecoration
+ if (baseType.getBasicType() == glslang::EbtSampler) {
+ if (baseType.getQualifier().hasAttachment())
+ builder.addCapability(spv::CapabilityInputAttachmentArrayNonUniformIndexingEXT);
+ else if (baseType.isImage() && baseType.getSampler().dim == glslang::EsdBuffer)
+ builder.addCapability(spv::CapabilityStorageTexelBufferArrayNonUniformIndexingEXT);
+ else if (baseType.isTexture() && baseType.getSampler().dim == glslang::EsdBuffer)
+ builder.addCapability(spv::CapabilityUniformTexelBufferArrayNonUniformIndexingEXT);
+ else if (baseType.isImage())
+ builder.addCapability(spv::CapabilityStorageImageArrayNonUniformIndexingEXT);
+ else if (baseType.isTexture())
+ builder.addCapability(spv::CapabilitySampledImageArrayNonUniformIndexingEXT);
+ } else if (baseType.getBasicType() == glslang::EbtBlock) {
+ if (baseType.getQualifier().storage == glslang::EvqBuffer)
+ builder.addCapability(spv::CapabilityStorageBufferArrayNonUniformIndexingEXT);
+ else if (baseType.getQualifier().storage == glslang::EvqUniform)
+ builder.addCapability(spv::CapabilityUniformBufferArrayNonUniformIndexingEXT);
+ }
+ } else {
+ // assume a dynamically uniform index
+ if (baseType.getBasicType() == glslang::EbtSampler) {
+ if (baseType.getQualifier().hasAttachment()) {
+ builder.addExtension("SPV_EXT_descriptor_indexing");
+ builder.addCapability(spv::CapabilityInputAttachmentArrayDynamicIndexingEXT);
+ } else if (baseType.isImage() && baseType.getSampler().dim == glslang::EsdBuffer) {
+ builder.addExtension("SPV_EXT_descriptor_indexing");
+ builder.addCapability(spv::CapabilityStorageTexelBufferArrayDynamicIndexingEXT);
+ } else if (baseType.isTexture() && baseType.getSampler().dim == glslang::EsdBuffer) {
+ builder.addExtension("SPV_EXT_descriptor_indexing");
+ builder.addCapability(spv::CapabilityUniformTexelBufferArrayDynamicIndexingEXT);
+ }
+ }
+ }
+}
+
+// Return whether or not the given type is something that should be tied to a
+// descriptor set.
+bool IsDescriptorResource(const glslang::TType& type)
+{
+ // uniform and buffer blocks are included, unless it is a push_constant
+ if (type.getBasicType() == glslang::EbtBlock)
+ return type.getQualifier().isUniformOrBuffer() &&
+#ifdef NV_EXTENSIONS
+ ! type.getQualifier().layoutShaderRecordNV &&
+#endif
+ ! type.getQualifier().layoutPushConstant;
+
+ // non block...
+ // basically samplerXXX/subpass/sampler/texture are all included
+ // if they are the global-scope-class, not the function parameter
+ // (or local, if they ever exist) class.
+ if (type.getBasicType() == glslang::EbtSampler)
+ return type.getQualifier().isUniformOrBuffer();
+
+ // None of the above.
+ return false;
+}
+
+void InheritQualifiers(glslang::TQualifier& child, const glslang::TQualifier& parent)
+{
+ if (child.layoutMatrix == glslang::ElmNone)
+ child.layoutMatrix = parent.layoutMatrix;
+
+ if (parent.invariant)
+ child.invariant = true;
+ if (parent.nopersp)
+ child.nopersp = true;
+#ifdef AMD_EXTENSIONS
+ if (parent.explicitInterp)
+ child.explicitInterp = true;
+#endif
+ if (parent.flat)
+ child.flat = true;
+ if (parent.centroid)
+ child.centroid = true;
+ if (parent.patch)
+ child.patch = true;
+ if (parent.sample)
+ child.sample = true;
+ if (parent.coherent)
+ child.coherent = true;
+ if (parent.devicecoherent)
+ child.devicecoherent = true;
+ if (parent.queuefamilycoherent)
+ child.queuefamilycoherent = true;
+ if (parent.workgroupcoherent)
+ child.workgroupcoherent = true;
+ if (parent.subgroupcoherent)
+ child.subgroupcoherent = true;
+ if (parent.nonprivate)
+ child.nonprivate = true;
+ if (parent.volatil)
+ child.volatil = true;
+ if (parent.restrict)
+ child.restrict = true;
+ if (parent.readonly)
+ child.readonly = true;
+ if (parent.writeonly)
+ child.writeonly = true;
+#ifdef NV_EXTENSIONS
+ if (parent.perPrimitiveNV)
+ child.perPrimitiveNV = true;
+ if (parent.perViewNV)
+ child.perViewNV = true;
+ if (parent.perTaskNV)
+ child.perTaskNV = true;
+#endif
+}
+
+bool HasNonLayoutQualifiers(const glslang::TType& type, const glslang::TQualifier& qualifier)
+{
+ // This should list qualifiers that simultaneous satisfy:
+ // - struct members might inherit from a struct declaration
+ // (note that non-block structs don't explicitly inherit,
+ // only implicitly, meaning no decoration involved)
+ // - affect decorations on the struct members
+ // (note smooth does not, and expecting something like volatile
+ // to effect the whole object)
+ // - are not part of the offset/st430/etc or row/column-major layout
+ return qualifier.invariant || (qualifier.hasLocation() && type.getBasicType() == glslang::EbtBlock);
+}
+
+//
+// Implement the TGlslangToSpvTraverser class.
+//
+
+TGlslangToSpvTraverser::TGlslangToSpvTraverser(unsigned int spvVersion, const glslang::TIntermediate* glslangIntermediate,
+ spv::SpvBuildLogger* buildLogger, glslang::SpvOptions& options)
+ : TIntermTraverser(true, false, true),
+ options(options),
+ shaderEntry(nullptr), currentFunction(nullptr),
+ sequenceDepth(0), logger(buildLogger),
+ builder(spvVersion, (glslang::GetKhronosToolId() << 16) | glslang::GetSpirvGeneratorVersion(), logger),
+ inEntryPoint(false), entryPointTerminated(false), linkageOnly(false),
+ glslangIntermediate(glslangIntermediate)
+{
+ spv::ExecutionModel executionModel = TranslateExecutionModel(glslangIntermediate->getStage());
+
+ builder.clearAccessChain();
+ builder.setSource(TranslateSourceLanguage(glslangIntermediate->getSource(), glslangIntermediate->getProfile()),
+ glslangIntermediate->getVersion());
+
+ if (options.generateDebugInfo) {
+ builder.setEmitOpLines();
+ builder.setSourceFile(glslangIntermediate->getSourceFile());
+
+ // Set the source shader's text. If for SPV version 1.0, include
+ // a preamble in comments stating the OpModuleProcessed instructions.
+ // Otherwise, emit those as actual instructions.
+ std::string text;
+ const std::vector<std::string>& processes = glslangIntermediate->getProcesses();
+ for (int p = 0; p < (int)processes.size(); ++p) {
+ if (glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_1) {
+ text.append("// OpModuleProcessed ");
+ text.append(processes[p]);
+ text.append("\n");
+ } else
+ builder.addModuleProcessed(processes[p]);
+ }
+ if (glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_1 && (int)processes.size() > 0)
+ text.append("#line 1\n");
+ text.append(glslangIntermediate->getSourceText());
+ builder.setSourceText(text);
+ // Pass name and text for all included files
+ const std::map<std::string, std::string>& include_txt = glslangIntermediate->getIncludeText();
+ for (auto iItr = include_txt.begin(); iItr != include_txt.end(); ++iItr)
+ builder.addInclude(iItr->first, iItr->second);
+ }
+ stdBuiltins = builder.import("GLSL.std.450");
+
+ spv::AddressingModel addressingModel = spv::AddressingModelLogical;
+ spv::MemoryModel memoryModel = spv::MemoryModelGLSL450;
+
+ if (glslangIntermediate->usingPhysicalStorageBuffer()) {
+ addressingModel = spv::AddressingModelPhysicalStorageBuffer64EXT;
+ builder.addExtension(spv::E_SPV_EXT_physical_storage_buffer);
+ builder.addCapability(spv::CapabilityPhysicalStorageBufferAddressesEXT);
+ };
+ if (glslangIntermediate->usingVulkanMemoryModel()) {
+ memoryModel = spv::MemoryModelVulkanKHR;
+ builder.addCapability(spv::CapabilityVulkanMemoryModelKHR);
+ builder.addExtension(spv::E_SPV_KHR_vulkan_memory_model);
+ }
+ builder.setMemoryModel(addressingModel, memoryModel);
+
+ if (glslangIntermediate->usingVariablePointers()) {
+ builder.addCapability(spv::CapabilityVariablePointers);
+ }
+
+ shaderEntry = builder.makeEntryPoint(glslangIntermediate->getEntryPointName().c_str());
+ entryPoint = builder.addEntryPoint(executionModel, shaderEntry, glslangIntermediate->getEntryPointName().c_str());
+
+ // Add the source extensions
+ const auto& sourceExtensions = glslangIntermediate->getRequestedExtensions();
+ for (auto it = sourceExtensions.begin(); it != sourceExtensions.end(); ++it)
+ builder.addSourceExtension(it->c_str());
+
+ // Add the top-level modes for this shader.
+
+ if (glslangIntermediate->getXfbMode()) {
+ builder.addCapability(spv::CapabilityTransformFeedback);
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModeXfb);
+ }
+
+ unsigned int mode;
+ switch (glslangIntermediate->getStage()) {
+ case EShLangVertex:
+ builder.addCapability(spv::CapabilityShader);
+ break;
+
+ case EShLangTessEvaluation:
+ case EShLangTessControl:
+ builder.addCapability(spv::CapabilityTessellation);
+
+ glslang::TLayoutGeometry primitive;
+
+ if (glslangIntermediate->getStage() == EShLangTessControl) {
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, glslangIntermediate->getVertices());
+ primitive = glslangIntermediate->getOutputPrimitive();
+ } else {
+ primitive = glslangIntermediate->getInputPrimitive();
+ }
+
+ switch (primitive) {
+ case glslang::ElgTriangles: mode = spv::ExecutionModeTriangles; break;
+ case glslang::ElgQuads: mode = spv::ExecutionModeQuads; break;
+ case glslang::ElgIsolines: mode = spv::ExecutionModeIsolines; break;
+ default: mode = spv::ExecutionModeMax; break;
+ }
+ if (mode != spv::ExecutionModeMax)
+ builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode);
+
+ switch (glslangIntermediate->getVertexSpacing()) {
+ case glslang::EvsEqual: mode = spv::ExecutionModeSpacingEqual; break;
+ case glslang::EvsFractionalEven: mode = spv::ExecutionModeSpacingFractionalEven; break;
+ case glslang::EvsFractionalOdd: mode = spv::ExecutionModeSpacingFractionalOdd; break;
+ default: mode = spv::ExecutionModeMax; break;
+ }
+ if (mode != spv::ExecutionModeMax)
+ builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode);
+
+ switch (glslangIntermediate->getVertexOrder()) {
+ case glslang::EvoCw: mode = spv::ExecutionModeVertexOrderCw; break;
+ case glslang::EvoCcw: mode = spv::ExecutionModeVertexOrderCcw; break;
+ default: mode = spv::ExecutionModeMax; break;
+ }
+ if (mode != spv::ExecutionModeMax)
+ builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode);
+
+ if (glslangIntermediate->getPointMode())
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModePointMode);
+ break;
+
+ case EShLangGeometry:
+ builder.addCapability(spv::CapabilityGeometry);
+ switch (glslangIntermediate->getInputPrimitive()) {
+ case glslang::ElgPoints: mode = spv::ExecutionModeInputPoints; break;
+ case glslang::ElgLines: mode = spv::ExecutionModeInputLines; break;
+ case glslang::ElgLinesAdjacency: mode = spv::ExecutionModeInputLinesAdjacency; break;
+ case glslang::ElgTriangles: mode = spv::ExecutionModeTriangles; break;
+ case glslang::ElgTrianglesAdjacency: mode = spv::ExecutionModeInputTrianglesAdjacency; break;
+ default: mode = spv::ExecutionModeMax; break;
+ }
+ if (mode != spv::ExecutionModeMax)
+ builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode);
+
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModeInvocations, glslangIntermediate->getInvocations());
+
+ switch (glslangIntermediate->getOutputPrimitive()) {
+ case glslang::ElgPoints: mode = spv::ExecutionModeOutputPoints; break;
+ case glslang::ElgLineStrip: mode = spv::ExecutionModeOutputLineStrip; break;
+ case glslang::ElgTriangleStrip: mode = spv::ExecutionModeOutputTriangleStrip; break;
+ default: mode = spv::ExecutionModeMax; break;
+ }
+ if (mode != spv::ExecutionModeMax)
+ builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode);
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, glslangIntermediate->getVertices());
+ break;
+
+ case EShLangFragment:
+ builder.addCapability(spv::CapabilityShader);
+ if (glslangIntermediate->getPixelCenterInteger())
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModePixelCenterInteger);
+
+ if (glslangIntermediate->getOriginUpperLeft())
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModeOriginUpperLeft);
+ else
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModeOriginLowerLeft);
+
+ if (glslangIntermediate->getEarlyFragmentTests())
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModeEarlyFragmentTests);
+
+ if (glslangIntermediate->getPostDepthCoverage()) {
+ builder.addCapability(spv::CapabilitySampleMaskPostDepthCoverage);
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModePostDepthCoverage);
+ builder.addExtension(spv::E_SPV_KHR_post_depth_coverage);
+ }
+
+ switch(glslangIntermediate->getDepth()) {
+ case glslang::EldGreater: mode = spv::ExecutionModeDepthGreater; break;
+ case glslang::EldLess: mode = spv::ExecutionModeDepthLess; break;
+ default: mode = spv::ExecutionModeMax; break;
+ }
+ if (mode != spv::ExecutionModeMax)
+ builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode);
+
+ if (glslangIntermediate->getDepth() != glslang::EldUnchanged && glslangIntermediate->isDepthReplacing())
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModeDepthReplacing);
+ break;
+
+ case EShLangCompute:
+ builder.addCapability(spv::CapabilityShader);
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModeLocalSize, glslangIntermediate->getLocalSize(0),
+ glslangIntermediate->getLocalSize(1),
+ glslangIntermediate->getLocalSize(2));
+#ifdef NV_EXTENSIONS
+ if (glslangIntermediate->getLayoutDerivativeModeNone() == glslang::LayoutDerivativeGroupQuads) {
+ builder.addCapability(spv::CapabilityComputeDerivativeGroupQuadsNV);
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModeDerivativeGroupQuadsNV);
+ builder.addExtension(spv::E_SPV_NV_compute_shader_derivatives);
+ } else if (glslangIntermediate->getLayoutDerivativeModeNone() == glslang::LayoutDerivativeGroupLinear) {
+ builder.addCapability(spv::CapabilityComputeDerivativeGroupLinearNV);
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModeDerivativeGroupLinearNV);
+ builder.addExtension(spv::E_SPV_NV_compute_shader_derivatives);
+ }
+#endif
+ break;
+
+#ifdef NV_EXTENSIONS
+ case EShLangRayGenNV:
+ case EShLangIntersectNV:
+ case EShLangAnyHitNV:
+ case EShLangClosestHitNV:
+ case EShLangMissNV:
+ case EShLangCallableNV:
+ builder.addCapability(spv::CapabilityRayTracingNV);
+ builder.addExtension("SPV_NV_ray_tracing");
+ break;
+ case EShLangTaskNV:
+ case EShLangMeshNV:
+ builder.addCapability(spv::CapabilityMeshShadingNV);
+ builder.addExtension(spv::E_SPV_NV_mesh_shader);
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModeLocalSize, glslangIntermediate->getLocalSize(0),
+ glslangIntermediate->getLocalSize(1),
+ glslangIntermediate->getLocalSize(2));
+ if (glslangIntermediate->getStage() == EShLangMeshNV) {
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, glslangIntermediate->getVertices());
+ builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputPrimitivesNV, glslangIntermediate->getPrimitives());
+
+ switch (glslangIntermediate->getOutputPrimitive()) {
+ case glslang::ElgPoints: mode = spv::ExecutionModeOutputPoints; break;
+ case glslang::ElgLines: mode = spv::ExecutionModeOutputLinesNV; break;
+ case glslang::ElgTriangles: mode = spv::ExecutionModeOutputTrianglesNV; break;
+ default: mode = spv::ExecutionModeMax; break;
+ }
+ if (mode != spv::ExecutionModeMax)
+ builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode);
+ }
+ break;
+#endif
+
+ default:
+ break;
+ }
+}
+
+// Finish creating SPV, after the traversal is complete.
+void TGlslangToSpvTraverser::finishSpv()
+{
+ // Finish the entry point function
+ if (! entryPointTerminated) {
+ builder.setBuildPoint(shaderEntry->getLastBlock());
+ builder.leaveFunction();
+ }
+
+ // finish off the entry-point SPV instruction by adding the Input/Output <id>
+ for (auto it = iOSet.cbegin(); it != iOSet.cend(); ++it)
+ entryPoint->addIdOperand(*it);
+
+ // Add capabilities, extensions, remove unneeded decorations, etc.,
+ // based on the resulting SPIR-V.
+ builder.postProcess();
+}
+
+// Write the SPV into 'out'.
+void TGlslangToSpvTraverser::dumpSpv(std::vector<unsigned int>& out)
+{
+ builder.dump(out);
+}
+
+//
+// Implement the traversal functions.
+//
+// Return true from interior nodes to have the external traversal
+// continue on to children. Return false if children were
+// already processed.
+//
+
+//
+// Symbols can turn into
+// - uniform/input reads
+// - output writes
+// - complex lvalue base setups: foo.bar[3].... , where we see foo and start up an access chain
+// - something simple that degenerates into the last bullet
+//
+void TGlslangToSpvTraverser::visitSymbol(glslang::TIntermSymbol* symbol)
+{
+ SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder);
+ if (symbol->getType().getQualifier().isSpecConstant())
+ spec_constant_op_mode_setter.turnOnSpecConstantOpMode();
+
+ // getSymbolId() will set up all the IO decorations on the first call.
+ // Formal function parameters were mapped during makeFunctions().
+ spv::Id id = getSymbolId(symbol);
+
+ // Include all "static use" and "linkage only" interface variables on the OpEntryPoint instruction
+ if (builder.isPointer(id)) {
+ spv::StorageClass sc = builder.getStorageClass(id);
+ if (sc == spv::StorageClassInput || sc == spv::StorageClassOutput) {
+ if (!symbol->getType().isStruct() || symbol->getType().getStruct()->size() > 0)
+ iOSet.insert(id);
+ }
+ }
+
+ // Only process non-linkage-only nodes for generating actual static uses
+ if (! linkageOnly || symbol->getQualifier().isSpecConstant()) {
+ // Prepare to generate code for the access
+
+ // L-value chains will be computed left to right. We're on the symbol now,
+ // which is the left-most part of the access chain, so now is "clear" time,
+ // followed by setting the base.
+ builder.clearAccessChain();
+
+ // For now, we consider all user variables as being in memory, so they are pointers,
+ // except for
+ // A) R-Value arguments to a function, which are an intermediate object.
+ // See comments in handleUserFunctionCall().
+ // B) Specialization constants (normal constants don't even come in as a variable),
+ // These are also pure R-values.
+ glslang::TQualifier qualifier = symbol->getQualifier();
+ if (qualifier.isSpecConstant() || rValueParameters.find(symbol->getId()) != rValueParameters.end())
+ builder.setAccessChainRValue(id);
+ else
+ builder.setAccessChainLValue(id);
+ }
+
+ // Process linkage-only nodes for any special additional interface work.
+ if (linkageOnly) {
+ if (glslangIntermediate->getHlslFunctionality1()) {
+ // Map implicit counter buffers to their originating buffers, which should have been
+ // seen by now, given earlier pruning of unused counters, and preservation of order
+ // of declaration.
+ if (symbol->getType().getQualifier().isUniformOrBuffer()) {
+ if (!glslangIntermediate->hasCounterBufferName(symbol->getName())) {
+ // Save possible originating buffers for counter buffers, keyed by
+ // making the potential counter-buffer name.
+ std::string keyName = symbol->getName().c_str();
+ keyName = glslangIntermediate->addCounterBufferName(keyName);
+ counterOriginator[keyName] = symbol;
+ } else {
+ // Handle a counter buffer, by finding the saved originating buffer.
+ std::string keyName = symbol->getName().c_str();
+ auto it = counterOriginator.find(keyName);
+ if (it != counterOriginator.end()) {
+ id = getSymbolId(it->second);
+ if (id != spv::NoResult) {
+ spv::Id counterId = getSymbolId(symbol);
+ if (counterId != spv::NoResult) {
+ builder.addExtension("SPV_GOOGLE_hlsl_functionality1");
+ builder.addDecorationId(id, spv::DecorationHlslCounterBufferGOOGLE, counterId);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::TIntermBinary* node)
+{
+ builder.setLine(node->getLoc().line, node->getLoc().getFilename());
+
+ SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder);
+ if (node->getType().getQualifier().isSpecConstant())
+ spec_constant_op_mode_setter.turnOnSpecConstantOpMode();
+
+ // First, handle special cases
+ switch (node->getOp()) {
+ case glslang::EOpAssign:
+ case glslang::EOpAddAssign:
+ case glslang::EOpSubAssign:
+ case glslang::EOpMulAssign:
+ case glslang::EOpVectorTimesMatrixAssign:
+ case glslang::EOpVectorTimesScalarAssign:
+ case glslang::EOpMatrixTimesScalarAssign:
+ case glslang::EOpMatrixTimesMatrixAssign:
+ case glslang::EOpDivAssign:
+ case glslang::EOpModAssign:
+ case glslang::EOpAndAssign:
+ case glslang::EOpInclusiveOrAssign:
+ case glslang::EOpExclusiveOrAssign:
+ case glslang::EOpLeftShiftAssign:
+ case glslang::EOpRightShiftAssign:
+ // A bin-op assign "a += b" means the same thing as "a = a + b"
+ // where a is evaluated before b. For a simple assignment, GLSL
+ // says to evaluate the left before the right. So, always, left
+ // node then right node.
+ {
+ // get the left l-value, save it away
+ builder.clearAccessChain();
+ node->getLeft()->traverse(this);
+ spv::Builder::AccessChain lValue = builder.getAccessChain();
+
+ // evaluate the right
+ builder.clearAccessChain();
+ node->getRight()->traverse(this);
+ spv::Id rValue = accessChainLoad(node->getRight()->getType());
+
+ if (node->getOp() != glslang::EOpAssign) {
+ // the left is also an r-value
+ builder.setAccessChain(lValue);
+ spv::Id leftRValue = accessChainLoad(node->getLeft()->getType());
+
+ // do the operation
+ OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()),
+ TranslateNoContractionDecoration(node->getType().getQualifier()),
+ TranslateNonUniformDecoration(node->getType().getQualifier()) };
+ rValue = createBinaryOperation(node->getOp(), decorations,
+ convertGlslangToSpvType(node->getType()), leftRValue, rValue,
+ node->getType().getBasicType());
+
+ // these all need their counterparts in createBinaryOperation()
+ assert(rValue != spv::NoResult);
+ }
+
+ // store the result
+ builder.setAccessChain(lValue);
+ multiTypeStore(node->getLeft()->getType(), rValue);
+
+ // assignments are expressions having an rValue after they are evaluated...
+ builder.clearAccessChain();
+ builder.setAccessChainRValue(rValue);
+ }
+ return false;
+ case glslang::EOpIndexDirect:
+ case glslang::EOpIndexDirectStruct:
+ {
+ // Get the left part of the access chain.
+ node->getLeft()->traverse(this);
+
+ // Add the next element in the chain
+
+ const int glslangIndex = node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst();
+ if (! node->getLeft()->getType().isArray() &&
+ node->getLeft()->getType().isVector() &&
+ node->getOp() == glslang::EOpIndexDirect) {
+ // This is essentially a hard-coded vector swizzle of size 1,
+ // so short circuit the access-chain stuff with a swizzle.
+ std::vector<unsigned> swizzle;
+ swizzle.push_back(glslangIndex);
+ int dummySize;
+ builder.accessChainPushSwizzle(swizzle, convertGlslangToSpvType(node->getLeft()->getType()),
+ TranslateCoherent(node->getLeft()->getType()),
+ glslangIntermediate->getBaseAlignmentScalar(node->getLeft()->getType(), dummySize));
+ } else {
+
+ // Load through a block reference is performed with a dot operator that
+ // is mapped to EOpIndexDirectStruct. When we get to the actual reference,
+ // do a load and reset the access chain.
+ if (node->getLeft()->getBasicType() == glslang::EbtReference &&
+ !node->getLeft()->getType().isArray() &&
+ node->getOp() == glslang::EOpIndexDirectStruct)
+ {
+ spv::Id left = accessChainLoad(node->getLeft()->getType());
+ builder.clearAccessChain();
+ builder.setAccessChainLValue(left);
+ }
+
+ int spvIndex = glslangIndex;
+ if (node->getLeft()->getBasicType() == glslang::EbtBlock &&
+ node->getOp() == glslang::EOpIndexDirectStruct)
+ {
+ // This may be, e.g., an anonymous block-member selection, which generally need
+ // index remapping due to hidden members in anonymous blocks.
+ std::vector<int>& remapper = memberRemapper[node->getLeft()->getType().getStruct()];
+ assert(remapper.size() > 0);
+ spvIndex = remapper[glslangIndex];
+ }
+
+ // normal case for indexing array or structure or block
+ builder.accessChainPush(builder.makeIntConstant(spvIndex), TranslateCoherent(node->getLeft()->getType()), node->getLeft()->getType().getBufferReferenceAlignment());
+
+ // Add capabilities here for accessing PointSize and clip/cull distance.
+ // We have deferred generation of associated capabilities until now.
+ if (node->getLeft()->getType().isStruct() && ! node->getLeft()->getType().isArray())
+ declareUseOfStructMember(*(node->getLeft()->getType().getStruct()), glslangIndex);
+ }
+ }
+ return false;
+ case glslang::EOpIndexIndirect:
+ {
+ // Structure or array or vector indirection.
+ // Will use native SPIR-V access-chain for struct and array indirection;
+ // matrices are arrays of vectors, so will also work for a matrix.
+ // Will use the access chain's 'component' for variable index into a vector.
+
+ // This adapter is building access chains left to right.
+ // Set up the access chain to the left.
+ node->getLeft()->traverse(this);
+
+ // save it so that computing the right side doesn't trash it
+ spv::Builder::AccessChain partial = builder.getAccessChain();
+
+ // compute the next index in the chain
+ builder.clearAccessChain();
+ node->getRight()->traverse(this);
+ spv::Id index = accessChainLoad(node->getRight()->getType());
+
+ addIndirectionIndexCapabilities(node->getLeft()->getType(), node->getRight()->getType());
+
+ // restore the saved access chain
+ builder.setAccessChain(partial);
+
+ if (! node->getLeft()->getType().isArray() && node->getLeft()->getType().isVector()) {
+ int dummySize;
+ builder.accessChainPushComponent(index, convertGlslangToSpvType(node->getLeft()->getType()),
+ TranslateCoherent(node->getLeft()->getType()),
+ glslangIntermediate->getBaseAlignmentScalar(node->getLeft()->getType(), dummySize));
+ } else
+ builder.accessChainPush(index, TranslateCoherent(node->getLeft()->getType()), node->getLeft()->getType().getBufferReferenceAlignment());
+ }
+ return false;
+ case glslang::EOpVectorSwizzle:
+ {
+ node->getLeft()->traverse(this);
+ std::vector<unsigned> swizzle;
+ convertSwizzle(*node->getRight()->getAsAggregate(), swizzle);
+ int dummySize;
+ builder.accessChainPushSwizzle(swizzle, convertGlslangToSpvType(node->getLeft()->getType()),
+ TranslateCoherent(node->getLeft()->getType()),
+ glslangIntermediate->getBaseAlignmentScalar(node->getLeft()->getType(), dummySize));
+ }
+ return false;
+ case glslang::EOpMatrixSwizzle:
+ logger->missingFunctionality("matrix swizzle");
+ return true;
+ case glslang::EOpLogicalOr:
+ case glslang::EOpLogicalAnd:
+ {
+
+ // These may require short circuiting, but can sometimes be done as straight
+ // binary operations. The right operand must be short circuited if it has
+ // side effects, and should probably be if it is complex.
+ if (isTrivial(node->getRight()->getAsTyped()))
+ break; // handle below as a normal binary operation
+ // otherwise, we need to do dynamic short circuiting on the right operand
+ spv::Id result = createShortCircuit(node->getOp(), *node->getLeft()->getAsTyped(), *node->getRight()->getAsTyped());
+ builder.clearAccessChain();
+ builder.setAccessChainRValue(result);
+ }
+ return false;
+ default:
+ break;
+ }
+
+ // Assume generic binary op...
+
+ // get right operand
+ builder.clearAccessChain();
+ node->getLeft()->traverse(this);
+ spv::Id left = accessChainLoad(node->getLeft()->getType());
+
+ // get left operand
+ builder.clearAccessChain();
+ node->getRight()->traverse(this);
+ spv::Id right = accessChainLoad(node->getRight()->getType());
+
+ // get result
+ OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()),
+ TranslateNoContractionDecoration(node->getType().getQualifier()),
+ TranslateNonUniformDecoration(node->getType().getQualifier()) };
+ spv::Id result = createBinaryOperation(node->getOp(), decorations,
+ convertGlslangToSpvType(node->getType()), left, right,
+ node->getLeft()->getType().getBasicType());
+
+ builder.clearAccessChain();
+ if (! result) {
+ logger->missingFunctionality("unknown glslang binary operation");
+ return true; // pick up a child as the place-holder result
+ } else {
+ builder.setAccessChainRValue(result);
+ return false;
+ }
+}
+
+bool TGlslangToSpvTraverser::visitUnary(glslang::TVisit /* visit */, glslang::TIntermUnary* node)
+{
+ builder.setLine(node->getLoc().line, node->getLoc().getFilename());
+
+ SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder);
+ if (node->getType().getQualifier().isSpecConstant())
+ spec_constant_op_mode_setter.turnOnSpecConstantOpMode();
+
+ spv::Id result = spv::NoResult;
+
+ // try texturing first
+ result = createImageTextureFunctionCall(node);
+ if (result != spv::NoResult) {
+ builder.clearAccessChain();
+ builder.setAccessChainRValue(result);
+
+ return false; // done with this node
+ }
+
+ // Non-texturing.
+
+ if (node->getOp() == glslang::EOpArrayLength) {
+ // Quite special; won't want to evaluate the operand.
+
+ // Currently, the front-end does not allow .length() on an array until it is sized,
+ // except for the last block membeor of an SSBO.
+ // TODO: If this changes, link-time sized arrays might show up here, and need their
+ // size extracted.
+
+ // Normal .length() would have been constant folded by the front-end.
+ // So, this has to be block.lastMember.length().
+ // SPV wants "block" and member number as the operands, go get them.
+
+ spv::Id length;
+ if (node->getOperand()->getType().isCoopMat()) {
+ spec_constant_op_mode_setter.turnOnSpecConstantOpMode();
+
+ spv::Id typeId = convertGlslangToSpvType(node->getOperand()->getType());
+ assert(builder.isCooperativeMatrixType(typeId));
+
+ length = builder.createCooperativeMatrixLength(typeId);
+ } else {
+ glslang::TIntermTyped* block = node->getOperand()->getAsBinaryNode()->getLeft();
+ block->traverse(this);
+ unsigned int member = node->getOperand()->getAsBinaryNode()->getRight()->getAsConstantUnion()->getConstArray()[0].getUConst();
+ length = builder.createArrayLength(builder.accessChainGetLValue(), member);
+ }
+
+ // GLSL semantics say the result of .length() is an int, while SPIR-V says
+ // signedness must be 0. So, convert from SPIR-V unsigned back to GLSL's
+ // AST expectation of a signed result.
+ if (glslangIntermediate->getSource() == glslang::EShSourceGlsl) {
+ if (builder.isInSpecConstCodeGenMode()) {
+ length = builder.createBinOp(spv::OpIAdd, builder.makeIntType(32), length, builder.makeIntConstant(0));
+ } else {
+ length = builder.createUnaryOp(spv::OpBitcast, builder.makeIntType(32), length);
+ }
+ }
+
+ builder.clearAccessChain();
+ builder.setAccessChainRValue(length);
+
+ return false;
+ }
+
+ // Start by evaluating the operand
+
+ // Does it need a swizzle inversion? If so, evaluation is inverted;
+ // operate first on the swizzle base, then apply the swizzle.
+ spv::Id invertedType = spv::NoType;
+ auto resultType = [&invertedType, &node, this](){ return invertedType != spv::NoType ? invertedType : convertGlslangToSpvType(node->getType()); };
+ if (node->getOp() == glslang::EOpInterpolateAtCentroid)
+ invertedType = getInvertedSwizzleType(*node->getOperand());
+
+ builder.clearAccessChain();
+ if (invertedType != spv::NoType)
+ node->getOperand()->getAsBinaryNode()->getLeft()->traverse(this);
+ else
+ node->getOperand()->traverse(this);
+
+ spv::Id operand = spv::NoResult;
+
+ if (node->getOp() == glslang::EOpAtomicCounterIncrement ||
+ node->getOp() == glslang::EOpAtomicCounterDecrement ||
+ node->getOp() == glslang::EOpAtomicCounter ||
+ node->getOp() == glslang::EOpInterpolateAtCentroid)
+ operand = builder.accessChainGetLValue(); // Special case l-value operands
+ else
+ operand = accessChainLoad(node->getOperand()->getType());
+
+ OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()),
+ TranslateNoContractionDecoration(node->getType().getQualifier()),
+ TranslateNonUniformDecoration(node->getType().getQualifier()) };
+
+ // it could be a conversion
+ if (! result)
+ result = createConversion(node->getOp(), decorations, resultType(), operand, node->getOperand()->getBasicType());
+
+ // if not, then possibly an operation
+ if (! result)
+ result = createUnaryOperation(node->getOp(), decorations, resultType(), operand, node->getOperand()->getBasicType());
+
+ if (result) {
+ if (invertedType) {
+ result = createInvertedSwizzle(decorations.precision, *node->getOperand(), result);
+ builder.addDecoration(result, decorations.nonUniform);
+ }
+
+ builder.clearAccessChain();
+ builder.setAccessChainRValue(result);
+
+ return false; // done with this node
+ }
+
+ // it must be a special case, check...
+ switch (node->getOp()) {
+ case glslang::EOpPostIncrement:
+ case glslang::EOpPostDecrement:
+ case glslang::EOpPreIncrement:
+ case glslang::EOpPreDecrement:
+ {
+ // we need the integer value "1" or the floating point "1.0" to add/subtract
+ spv::Id one = 0;
+ if (node->getBasicType() == glslang::EbtFloat)
+ one = builder.makeFloatConstant(1.0F);
+ else if (node->getBasicType() == glslang::EbtDouble)
+ one = builder.makeDoubleConstant(1.0);
+ else if (node->getBasicType() == glslang::EbtFloat16)
+ one = builder.makeFloat16Constant(1.0F);
+ else if (node->getBasicType() == glslang::EbtInt8 || node->getBasicType() == glslang::EbtUint8)
+ one = builder.makeInt8Constant(1);
+ else if (node->getBasicType() == glslang::EbtInt16 || node->getBasicType() == glslang::EbtUint16)
+ one = builder.makeInt16Constant(1);
+ else if (node->getBasicType() == glslang::EbtInt64 || node->getBasicType() == glslang::EbtUint64)
+ one = builder.makeInt64Constant(1);
+ else
+ one = builder.makeIntConstant(1);
+ glslang::TOperator op;
+ if (node->getOp() == glslang::EOpPreIncrement ||
+ node->getOp() == glslang::EOpPostIncrement)
+ op = glslang::EOpAdd;
+ else
+ op = glslang::EOpSub;
+
+ spv::Id result = createBinaryOperation(op, decorations,
+ convertGlslangToSpvType(node->getType()), operand, one,
+ node->getType().getBasicType());
+ assert(result != spv::NoResult);
+
+ // The result of operation is always stored, but conditionally the
+ // consumed result. The consumed result is always an r-value.
+ builder.accessChainStore(result);
+ builder.clearAccessChain();
+ if (node->getOp() == glslang::EOpPreIncrement ||
+ node->getOp() == glslang::EOpPreDecrement)
+ builder.setAccessChainRValue(result);
+ else
+ builder.setAccessChainRValue(operand);
+ }
+
+ return false;
+
+ case glslang::EOpEmitStreamVertex:
+ builder.createNoResultOp(spv::OpEmitStreamVertex, operand);
+ return false;
+ case glslang::EOpEndStreamPrimitive:
+ builder.createNoResultOp(spv::OpEndStreamPrimitive, operand);
+ return false;
+
+ default:
+ logger->missingFunctionality("unknown glslang unary");
+ return true; // pick up operand as placeholder result
+ }
+}
+
+bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TIntermAggregate* node)
+{
+ SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder);
+ if (node->getType().getQualifier().isSpecConstant())
+ spec_constant_op_mode_setter.turnOnSpecConstantOpMode();
+
+ spv::Id result = spv::NoResult;
+ spv::Id invertedType = spv::NoType; // to use to override the natural type of the node
+ auto resultType = [&invertedType, &node, this](){ return invertedType != spv::NoType ? invertedType : convertGlslangToSpvType(node->getType()); };
+
+ // try texturing
+ result = createImageTextureFunctionCall(node);
+ if (result != spv::NoResult) {
+ builder.clearAccessChain();
+ builder.setAccessChainRValue(result);
+
+ return false;
+ } else if (node->getOp() == glslang::EOpImageStore ||
+#ifdef AMD_EXTENSIONS
+ node->getOp() == glslang::EOpImageStoreLod ||
+#endif
+ node->getOp() == glslang::EOpImageAtomicStore) {
+ // "imageStore" is a special case, which has no result
+ return false;
+ }
+
+ glslang::TOperator binOp = glslang::EOpNull;
+ bool reduceComparison = true;
+ bool isMatrix = false;
+ bool noReturnValue = false;
+ bool atomic = false;
+
+ assert(node->getOp());
+
+ spv::Decoration precision = TranslatePrecisionDecoration(node->getOperationPrecision());
+
+ switch (node->getOp()) {
+ case glslang::EOpSequence:
+ {
+ if (preVisit)
+ ++sequenceDepth;
+ else
+ --sequenceDepth;
+
+ if (sequenceDepth == 1) {
+ // If this is the parent node of all the functions, we want to see them
+ // early, so all call points have actual SPIR-V functions to reference.
+ // In all cases, still let the traverser visit the children for us.
+ makeFunctions(node->getAsAggregate()->getSequence());
+
+ // Also, we want all globals initializers to go into the beginning of the entry point, before
+ // anything else gets there, so visit out of order, doing them all now.
+ makeGlobalInitializers(node->getAsAggregate()->getSequence());
+
+ // Initializers are done, don't want to visit again, but functions and link objects need to be processed,
+ // so do them manually.
+ visitFunctions(node->getAsAggregate()->getSequence());
+
+ return false;
+ }
+
+ return true;
+ }
+ case glslang::EOpLinkerObjects:
+ {
+ if (visit == glslang::EvPreVisit)
+ linkageOnly = true;
+ else
+ linkageOnly = false;
+
+ return true;
+ }
+ case glslang::EOpComma:
+ {
+ // processing from left to right naturally leaves the right-most
+ // lying around in the access chain
+ glslang::TIntermSequence& glslangOperands = node->getSequence();
+ for (int i = 0; i < (int)glslangOperands.size(); ++i)
+ glslangOperands[i]->traverse(this);
+
+ return false;
+ }
+ case glslang::EOpFunction:
+ if (visit == glslang::EvPreVisit) {
+ if (isShaderEntryPoint(node)) {
+ inEntryPoint = true;
+ builder.setBuildPoint(shaderEntry->getLastBlock());
+ currentFunction = shaderEntry;
+ } else {
+ handleFunctionEntry(node);
+ }
+ } else {
+ if (inEntryPoint)
+ entryPointTerminated = true;
+ builder.leaveFunction();
+ inEntryPoint = false;
+ }
+
+ return true;
+ case glslang::EOpParameters:
+ // Parameters will have been consumed by EOpFunction processing, but not
+ // the body, so we still visited the function node's children, making this
+ // child redundant.
+ return false;
+ case glslang::EOpFunctionCall:
+ {
+ builder.setLine(node->getLoc().line, node->getLoc().getFilename());
+ if (node->isUserDefined())
+ result = handleUserFunctionCall(node);
+ // assert(result); // this can happen for bad shaders because the call graph completeness checking is not yet done
+ if (result) {
+ builder.clearAccessChain();
+ builder.setAccessChainRValue(result);
+ } else
+ logger->missingFunctionality("missing user function; linker needs to catch that");
+
+ return false;
+ }
+ case glslang::EOpConstructMat2x2:
+ case glslang::EOpConstructMat2x3:
+ case glslang::EOpConstructMat2x4:
+ case glslang::EOpConstructMat3x2:
+ case glslang::EOpConstructMat3x3:
+ case glslang::EOpConstructMat3x4:
+ case glslang::EOpConstructMat4x2:
+ case glslang::EOpConstructMat4x3:
+ case glslang::EOpConstructMat4x4:
+ case glslang::EOpConstructDMat2x2:
+ case glslang::EOpConstructDMat2x3:
+ case glslang::EOpConstructDMat2x4:
+ case glslang::EOpConstructDMat3x2:
+ case glslang::EOpConstructDMat3x3:
+ case glslang::EOpConstructDMat3x4:
+ case glslang::EOpConstructDMat4x2:
+ case glslang::EOpConstructDMat4x3:
+ case glslang::EOpConstructDMat4x4:
+ case glslang::EOpConstructIMat2x2:
+ case glslang::EOpConstructIMat2x3:
+ case glslang::EOpConstructIMat2x4:
+ case glslang::EOpConstructIMat3x2:
+ case glslang::EOpConstructIMat3x3:
+ case glslang::EOpConstructIMat3x4:
+ case glslang::EOpConstructIMat4x2:
+ case glslang::EOpConstructIMat4x3:
+ case glslang::EOpConstructIMat4x4:
+ case glslang::EOpConstructUMat2x2:
+ case glslang::EOpConstructUMat2x3:
+ case glslang::EOpConstructUMat2x4:
+ case glslang::EOpConstructUMat3x2:
+ case glslang::EOpConstructUMat3x3:
+ case glslang::EOpConstructUMat3x4:
+ case glslang::EOpConstructUMat4x2:
+ case glslang::EOpConstructUMat4x3:
+ case glslang::EOpConstructUMat4x4:
+ case glslang::EOpConstructBMat2x2:
+ case glslang::EOpConstructBMat2x3:
+ case glslang::EOpConstructBMat2x4:
+ case glslang::EOpConstructBMat3x2:
+ case glslang::EOpConstructBMat3x3:
+ case glslang::EOpConstructBMat3x4:
+ case glslang::EOpConstructBMat4x2:
+ case glslang::EOpConstructBMat4x3:
+ case glslang::EOpConstructBMat4x4:
+ case glslang::EOpConstructF16Mat2x2:
+ case glslang::EOpConstructF16Mat2x3:
+ case glslang::EOpConstructF16Mat2x4:
+ case glslang::EOpConstructF16Mat3x2:
+ case glslang::EOpConstructF16Mat3x3:
+ case glslang::EOpConstructF16Mat3x4:
+ case glslang::EOpConstructF16Mat4x2:
+ case glslang::EOpConstructF16Mat4x3:
+ case glslang::EOpConstructF16Mat4x4:
+ isMatrix = true;
+ // fall through
+ case glslang::EOpConstructFloat:
+ case glslang::EOpConstructVec2:
+ case glslang::EOpConstructVec3:
+ case glslang::EOpConstructVec4:
+ case glslang::EOpConstructDouble:
+ case glslang::EOpConstructDVec2:
+ case glslang::EOpConstructDVec3:
+ case glslang::EOpConstructDVec4:
+ case glslang::EOpConstructFloat16:
+ case glslang::EOpConstructF16Vec2:
+ case glslang::EOpConstructF16Vec3:
+ case glslang::EOpConstructF16Vec4:
+ case glslang::EOpConstructBool:
+ case glslang::EOpConstructBVec2:
+ case glslang::EOpConstructBVec3:
+ case glslang::EOpConstructBVec4:
+ case glslang::EOpConstructInt8:
+ case glslang::EOpConstructI8Vec2:
+ case glslang::EOpConstructI8Vec3:
+ case glslang::EOpConstructI8Vec4:
+ case glslang::EOpConstructUint8:
+ case glslang::EOpConstructU8Vec2:
+ case glslang::EOpConstructU8Vec3:
+ case glslang::EOpConstructU8Vec4:
+ case glslang::EOpConstructInt16:
+ case glslang::EOpConstructI16Vec2:
+ case glslang::EOpConstructI16Vec3:
+ case glslang::EOpConstructI16Vec4:
+ case glslang::EOpConstructUint16:
+ case glslang::EOpConstructU16Vec2:
+ case glslang::EOpConstructU16Vec3:
+ case glslang::EOpConstructU16Vec4:
+ case glslang::EOpConstructInt:
+ case glslang::EOpConstructIVec2:
+ case glslang::EOpConstructIVec3:
+ case glslang::EOpConstructIVec4:
+ case glslang::EOpConstructUint:
+ case glslang::EOpConstructUVec2:
+ case glslang::EOpConstructUVec3:
+ case glslang::EOpConstructUVec4:
+ case glslang::EOpConstructInt64:
+ case glslang::EOpConstructI64Vec2:
+ case glslang::EOpConstructI64Vec3:
+ case glslang::EOpConstructI64Vec4:
+ case glslang::EOpConstructUint64:
+ case glslang::EOpConstructU64Vec2:
+ case glslang::EOpConstructU64Vec3:
+ case glslang::EOpConstructU64Vec4:
+ case glslang::EOpConstructStruct:
+ case glslang::EOpConstructTextureSampler:
+ case glslang::EOpConstructReference:
+ case glslang::EOpConstructCooperativeMatrix:
+ {
+ builder.setLine(node->getLoc().line, node->getLoc().getFilename());
+ std::vector<spv::Id> arguments;
+ translateArguments(*node, arguments);
+ spv::Id constructed;
+ if (node->getOp() == glslang::EOpConstructTextureSampler)
+ constructed = builder.createOp(spv::OpSampledImage, resultType(), arguments);
+ else if (node->getOp() == glslang::EOpConstructStruct ||
+ node->getOp() == glslang::EOpConstructCooperativeMatrix ||
+ node->getType().isArray()) {
+ std::vector<spv::Id> constituents;
+ for (int c = 0; c < (int)arguments.size(); ++c)
+ constituents.push_back(arguments[c]);
+ constructed = builder.createCompositeConstruct(resultType(), constituents);
+ } else if (isMatrix)
+ constructed = builder.createMatrixConstructor(precision, arguments, resultType());
+ else
+ constructed = builder.createConstructor(precision, arguments, resultType());
+
+ builder.clearAccessChain();
+ builder.setAccessChainRValue(constructed);
+
+ return false;
+ }
+
+ // These six are component-wise compares with component-wise results.
+ // Forward on to createBinaryOperation(), requesting a vector result.
+ case glslang::EOpLessThan:
+ case glslang::EOpGreaterThan:
+ case glslang::EOpLessThanEqual:
+ case glslang::EOpGreaterThanEqual:
+ case glslang::EOpVectorEqual:
+ case glslang::EOpVectorNotEqual:
+ {
+ // Map the operation to a binary
+ binOp = node->getOp();
+ reduceComparison = false;
+ switch (node->getOp()) {
+ case glslang::EOpVectorEqual: binOp = glslang::EOpVectorEqual; break;
+ case glslang::EOpVectorNotEqual: binOp = glslang::EOpVectorNotEqual; break;
+ default: binOp = node->getOp(); break;
+ }
+
+ break;
+ }
+ case glslang::EOpMul:
+ // component-wise matrix multiply
+ binOp = glslang::EOpMul;
+ break;
+ case glslang::EOpOuterProduct:
+ // two vectors multiplied to make a matrix
+ binOp = glslang::EOpOuterProduct;
+ break;
+ case glslang::EOpDot:
+ {
+ // for scalar dot product, use multiply
+ glslang::TIntermSequence& glslangOperands = node->getSequence();
+ if (glslangOperands[0]->getAsTyped()->getVectorSize() == 1)
+ binOp = glslang::EOpMul;
+ break;
+ }
+ case glslang::EOpMod:
+ // when an aggregate, this is the floating-point mod built-in function,
+ // which can be emitted by the one in createBinaryOperation()
+ binOp = glslang::EOpMod;
+ break;
+ case glslang::EOpEmitVertex:
+ case glslang::EOpEndPrimitive:
+ case glslang::EOpBarrier:
+ case glslang::EOpMemoryBarrier:
+ case glslang::EOpMemoryBarrierAtomicCounter:
+ case glslang::EOpMemoryBarrierBuffer:
+ case glslang::EOpMemoryBarrierImage:
+ case glslang::EOpMemoryBarrierShared:
+ case glslang::EOpGroupMemoryBarrier:
+ case glslang::EOpDeviceMemoryBarrier:
+ case glslang::EOpAllMemoryBarrierWithGroupSync:
+ case glslang::EOpDeviceMemoryBarrierWithGroupSync:
+ case glslang::EOpWorkgroupMemoryBarrier:
+ case glslang::EOpWorkgroupMemoryBarrierWithGroupSync:
+ case glslang::EOpSubgroupBarrier:
+ case glslang::EOpSubgroupMemoryBarrier:
+ case glslang::EOpSubgroupMemoryBarrierBuffer:
+ case glslang::EOpSubgroupMemoryBarrierImage:
+ case glslang::EOpSubgroupMemoryBarrierShared:
+ noReturnValue = true;
+ // These all have 0 operands and will naturally finish up in the code below for 0 operands
+ break;
+
+ case glslang::EOpAtomicStore:
+ noReturnValue = true;
+ // fallthrough
+ case glslang::EOpAtomicLoad:
+ case glslang::EOpAtomicAdd:
+ case glslang::EOpAtomicMin:
+ case glslang::EOpAtomicMax:
+ case glslang::EOpAtomicAnd:
+ case glslang::EOpAtomicOr:
+ case glslang::EOpAtomicXor:
+ case glslang::EOpAtomicExchange:
+ case glslang::EOpAtomicCompSwap:
+ atomic = true;
+ break;
+
+ case glslang::EOpAtomicCounterAdd:
+ case glslang::EOpAtomicCounterSubtract:
+ case glslang::EOpAtomicCounterMin:
+ case glslang::EOpAtomicCounterMax:
+ case glslang::EOpAtomicCounterAnd:
+ case glslang::EOpAtomicCounterOr:
+ case glslang::EOpAtomicCounterXor:
+ case glslang::EOpAtomicCounterExchange:
+ case glslang::EOpAtomicCounterCompSwap:
+ builder.addExtension("SPV_KHR_shader_atomic_counter_ops");
+ builder.addCapability(spv::CapabilityAtomicStorageOps);
+ atomic = true;
+ break;
+
+#ifdef NV_EXTENSIONS
+ case glslang::EOpIgnoreIntersectionNV:
+ case glslang::EOpTerminateRayNV:
+ case glslang::EOpTraceNV:
+ case glslang::EOpExecuteCallableNV:
+ case glslang::EOpWritePackedPrimitiveIndices4x8NV:
+ noReturnValue = true;
+ break;
+#endif
+ case glslang::EOpCooperativeMatrixLoad:
+ case glslang::EOpCooperativeMatrixStore:
+ noReturnValue = true;
+ break;
+
+ default:
+ break;
+ }
+
+ //
+ // See if it maps to a regular operation.
+ //
+ if (binOp != glslang::EOpNull) {
+ glslang::TIntermTyped* left = node->getSequence()[0]->getAsTyped();
+ glslang::TIntermTyped* right = node->getSequence()[1]->getAsTyped();
+ assert(left && right);
+
+ builder.clearAccessChain();
+ left->traverse(this);
+ spv::Id leftId = accessChainLoad(left->getType());
+
+ builder.clearAccessChain();
+ right->traverse(this);
+ spv::Id rightId = accessChainLoad(right->getType());
+
+ builder.setLine(node->getLoc().line, node->getLoc().getFilename());
+ OpDecorations decorations = { precision,
+ TranslateNoContractionDecoration(node->getType().getQualifier()),
+ TranslateNonUniformDecoration(node->getType().getQualifier()) };
+ result = createBinaryOperation(binOp, decorations,
+ resultType(), leftId, rightId,
+ left->getType().getBasicType(), reduceComparison);
+
+ // code above should only make binOp that exists in createBinaryOperation
+ assert(result != spv::NoResult);
+ builder.clearAccessChain();
+ builder.setAccessChainRValue(result);
+
+ return false;
+ }
+
+ //
+ // Create the list of operands.
+ //
+ glslang::TIntermSequence& glslangOperands = node->getSequence();
+ std::vector<spv::Id> operands;
+ std::vector<spv::IdImmediate> memoryAccessOperands;
+ for (int arg = 0; arg < (int)glslangOperands.size(); ++arg) {
+ // special case l-value operands; there are just a few
+ bool lvalue = false;
+ switch (node->getOp()) {
+ case glslang::EOpFrexp:
+ case glslang::EOpModf:
+ if (arg == 1)
+ lvalue = true;
+ break;
+ case glslang::EOpInterpolateAtSample:
+ case glslang::EOpInterpolateAtOffset:
+#ifdef AMD_EXTENSIONS
+ case glslang::EOpInterpolateAtVertex:
+#endif
+ if (arg == 0) {
+ lvalue = true;
+
+ // Does it need a swizzle inversion? If so, evaluation is inverted;
+ // operate first on the swizzle base, then apply the swizzle.
+ if (glslangOperands[0]->getAsOperator() &&
+ glslangOperands[0]->getAsOperator()->getOp() == glslang::EOpVectorSwizzle)
+ invertedType = convertGlslangToSpvType(glslangOperands[0]->getAsBinaryNode()->getLeft()->getType());
+ }
+ break;
+ case glslang::EOpAtomicAdd:
+ case glslang::EOpAtomicMin:
+ case glslang::EOpAtomicMax:
+ case glslang::EOpAtomicAnd:
+ case glslang::EOpAtomicOr:
+ case glslang::EOpAtomicXor:
+ case glslang::EOpAtomicExchange:
+ case glslang::EOpAtomicCompSwap:
+ case glslang::EOpAtomicLoad:
+ case glslang::EOpAtomicStore:
+ case glslang::EOpAtomicCounterAdd:
+ case glslang::EOpAtomicCounterSubtract:
+ case glslang::EOpAtomicCounterMin:
+ case glslang::EOpAtomicCounterMax:
+ case glslang::EOpAtomicCounterAnd:
+ case glslang::EOpAtomicCounterOr:
+ case glslang::EOpAtomicCounterXor:
+ case glslang::EOpAtomicCounterExchange:
+ case glslang::EOpAtomicCounterCompSwap:
+ if (arg == 0)
+ lvalue = true;
+ break;
+ case glslang::EOpAddCarry:
+ case glslang::EOpSubBorrow:
+ if (arg == 2)
+ lvalue = true;
+ break;
+ case glslang::EOpUMulExtended:
+ case glslang::EOpIMulExtended:
+ if (arg >= 2)
+ lvalue = true;
+ break;
+ case glslang::EOpCooperativeMatrixLoad:
+ if (arg == 0 || arg == 1)
+ lvalue = true;
+ break;
+ case glslang::EOpCooperativeMatrixStore:
+ if (arg == 1)
+ lvalue = true;
+ break;
+ default:
+ break;
+ }
+ builder.clearAccessChain();
+ if (invertedType != spv::NoType && arg == 0)
+ glslangOperands[0]->getAsBinaryNode()->getLeft()->traverse(this);
+ else
+ glslangOperands[arg]->traverse(this);
+
+ if (node->getOp() == glslang::EOpCooperativeMatrixLoad ||
+ node->getOp() == glslang::EOpCooperativeMatrixStore) {
+
+ if (arg == 1) {
+ // fold "element" parameter into the access chain
+ spv::Builder::AccessChain save = builder.getAccessChain();
+ builder.clearAccessChain();
+ glslangOperands[2]->traverse(this);
+
+ spv::Id elementId = accessChainLoad(glslangOperands[2]->getAsTyped()->getType());
+
+ builder.setAccessChain(save);
+
+ // Point to the first element of the array.
+ builder.accessChainPush(elementId, TranslateCoherent(glslangOperands[arg]->getAsTyped()->getType()),
+ glslangOperands[arg]->getAsTyped()->getType().getBufferReferenceAlignment());
+
+ spv::Builder::AccessChain::CoherentFlags coherentFlags = builder.getAccessChain().coherentFlags;
+ unsigned int alignment = builder.getAccessChain().alignment;
+
+ int memoryAccess = TranslateMemoryAccess(coherentFlags);
+ if (node->getOp() == glslang::EOpCooperativeMatrixLoad)
+ memoryAccess &= ~spv::MemoryAccessMakePointerAvailableKHRMask;
+ if (node->getOp() == glslang::EOpCooperativeMatrixStore)
+ memoryAccess &= ~spv::MemoryAccessMakePointerVisibleKHRMask;
+ if (builder.getStorageClass(builder.getAccessChain().base) == spv::StorageClassPhysicalStorageBufferEXT) {
+ memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask);
+ }
+
+ memoryAccessOperands.push_back(spv::IdImmediate(false, memoryAccess));
+
+ if (memoryAccess & spv::MemoryAccessAlignedMask) {
+ memoryAccessOperands.push_back(spv::IdImmediate(false, alignment));
+ }
+
+ if (memoryAccess & (spv::MemoryAccessMakePointerAvailableKHRMask | spv::MemoryAccessMakePointerVisibleKHRMask)) {
+ memoryAccessOperands.push_back(spv::IdImmediate(true, builder.makeUintConstant(TranslateMemoryScope(coherentFlags))));
+ }
+ } else if (arg == 2) {
+ continue;
+ }
+ }
+
+ if (lvalue)
+ operands.push_back(builder.accessChainGetLValue());
+ else {
+ builder.setLine(node->getLoc().line, node->getLoc().getFilename());
+ operands.push_back(accessChainLoad(glslangOperands[arg]->getAsTyped()->getType()));
+ }
+ }
+
+ builder.setLine(node->getLoc().line, node->getLoc().getFilename());
+ if (node->getOp() == glslang::EOpCooperativeMatrixLoad) {
+ std::vector<spv::IdImmediate> idImmOps;
+
+ idImmOps.push_back(spv::IdImmediate(true, operands[1])); // buf
+ idImmOps.push_back(spv::IdImmediate(true, operands[2])); // stride
+ idImmOps.push_back(spv::IdImmediate(true, operands[3])); // colMajor
+ idImmOps.insert(idImmOps.end(), memoryAccessOperands.begin(), memoryAccessOperands.end());
+ // get the pointee type
+ spv::Id typeId = builder.getContainedTypeId(builder.getTypeId(operands[0]));
+ assert(builder.isCooperativeMatrixType(typeId));
+ // do the op
+ spv::Id result = builder.createOp(spv::OpCooperativeMatrixLoadNV, typeId, idImmOps);
+ // store the result to the pointer (out param 'm')
+ builder.createStore(result, operands[0]);
+ result = 0;
+ } else if (node->getOp() == glslang::EOpCooperativeMatrixStore) {
+ std::vector<spv::IdImmediate> idImmOps;
+
+ idImmOps.push_back(spv::IdImmediate(true, operands[1])); // buf
+ idImmOps.push_back(spv::IdImmediate(true, operands[0])); // object
+ idImmOps.push_back(spv::IdImmediate(true, operands[2])); // stride
+ idImmOps.push_back(spv::IdImmediate(true, operands[3])); // colMajor
+ idImmOps.insert(idImmOps.end(), memoryAccessOperands.begin(), memoryAccessOperands.end());
+
+ builder.createNoResultOp(spv::OpCooperativeMatrixStoreNV, idImmOps);
+ result = 0;
+ } else if (atomic) {
+ // Handle all atomics
+ result = createAtomicOperation(node->getOp(), precision, resultType(), operands, node->getBasicType());
+ } else {
+ // Pass through to generic operations.
+ switch (glslangOperands.size()) {
+ case 0:
+ result = createNoArgOperation(node->getOp(), precision, resultType());
+ break;
+ case 1:
+ {
+ OpDecorations decorations = { precision,
+ TranslateNoContractionDecoration(node->getType().getQualifier()),
+ TranslateNonUniformDecoration(node->getType().getQualifier()) };
+ result = createUnaryOperation(
+ node->getOp(), decorations,
+ resultType(), operands.front(),
+ glslangOperands[0]->getAsTyped()->getBasicType());
+ }
+ break;
+ default:
+ result = createMiscOperation(node->getOp(), precision, resultType(), operands, node->getBasicType());
+ break;
+ }
+ if (invertedType)
+ result = createInvertedSwizzle(precision, *glslangOperands[0]->getAsBinaryNode(), result);
+ }
+
+ if (noReturnValue)
+ return false;
+
+ if (! result) {
+ logger->missingFunctionality("unknown glslang aggregate");
+ return true; // pick up a child as a placeholder operand
+ } else {
+ builder.clearAccessChain();
+ builder.setAccessChainRValue(result);
+ return false;
+ }
+}
+
+// This path handles both if-then-else and ?:
+// The if-then-else has a node type of void, while
+// ?: has either a void or a non-void node type
+//
+// Leaving the result, when not void:
+// GLSL only has r-values as the result of a :?, but
+// if we have an l-value, that can be more efficient if it will
+// become the base of a complex r-value expression, because the
+// next layer copies r-values into memory to use the access-chain mechanism
+bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang::TIntermSelection* node)
+{
+ // See if it simple and safe, or required, to execute both sides.
+ // Crucially, side effects must be either semantically required or avoided,
+ // and there are performance trade-offs.
+ // Return true if required or a good idea (and safe) to execute both sides,
+ // false otherwise.
+ const auto bothSidesPolicy = [&]() -> bool {
+ // do we have both sides?
+ if (node->getTrueBlock() == nullptr ||
+ node->getFalseBlock() == nullptr)
+ return false;
+
+ // required? (unless we write additional code to look for side effects
+ // and make performance trade-offs if none are present)
+ if (!node->getShortCircuit())
+ return true;
+
+ // if not required to execute both, decide based on performance/practicality...
+
+ // see if OpSelect can handle it
+ if ((!node->getType().isScalar() && !node->getType().isVector()) ||
+ node->getBasicType() == glslang::EbtVoid)
+ return false;
+
+ assert(node->getType() == node->getTrueBlock() ->getAsTyped()->getType() &&
+ node->getType() == node->getFalseBlock()->getAsTyped()->getType());
+
+ // return true if a single operand to ? : is okay for OpSelect
+ const auto operandOkay = [](glslang::TIntermTyped* node) {
+ return node->getAsSymbolNode() || node->getType().getQualifier().isConstant();
+ };
+
+ return operandOkay(node->getTrueBlock() ->getAsTyped()) &&
+ operandOkay(node->getFalseBlock()->getAsTyped());
+ };
+
+ spv::Id result = spv::NoResult; // upcoming result selecting between trueValue and falseValue
+ // emit the condition before doing anything with selection
+ node->getCondition()->traverse(this);
+ spv::Id condition = accessChainLoad(node->getCondition()->getType());
+
+ // Find a way of executing both sides and selecting the right result.
+ const auto executeBothSides = [&]() -> void {
+ // execute both sides
+ node->getTrueBlock()->traverse(this);
+ spv::Id trueValue = accessChainLoad(node->getTrueBlock()->getAsTyped()->getType());
+ node->getFalseBlock()->traverse(this);
+ spv::Id falseValue = accessChainLoad(node->getTrueBlock()->getAsTyped()->getType());
+
+ builder.setLine(node->getLoc().line, node->getLoc().getFilename());
+
+ // done if void
+ if (node->getBasicType() == glslang::EbtVoid)
+ return;
+
+ // emit code to select between trueValue and falseValue
+
+ // see if OpSelect can handle it
+ if (node->getType().isScalar() || node->getType().isVector()) {
+ // Emit OpSelect for this selection.
+
+ // smear condition to vector, if necessary (AST is always scalar)
+ if (builder.isVector(trueValue))
+ condition = builder.smearScalar(spv::NoPrecision, condition,
+ builder.makeVectorType(builder.makeBoolType(),
+ builder.getNumComponents(trueValue)));
+
+ // OpSelect
+ result = builder.createTriOp(spv::OpSelect,
+ convertGlslangToSpvType(node->getType()), condition,
+ trueValue, falseValue);
+
+ builder.clearAccessChain();
+ builder.setAccessChainRValue(result);
+ } else {
+ // We need control flow to select the result.
+ // TODO: Once SPIR-V OpSelect allows arbitrary types, eliminate this path.
+ result = builder.createVariable(spv::StorageClassFunction, convertGlslangToSpvType(node->getType()));
+
+ // Selection control:
+ const spv::SelectionControlMask control = TranslateSelectionControl(*node);
+
+ // make an "if" based on the value created by the condition
+ spv::Builder::If ifBuilder(condition, control, builder);
+
+ // emit the "then" statement
+ builder.createStore(trueValue, result);
+ ifBuilder.makeBeginElse();
+ // emit the "else" statement
+ builder.createStore(falseValue, result);
+
+ // finish off the control flow
+ ifBuilder.makeEndIf();
+
+ builder.clearAccessChain();
+ builder.setAccessChainLValue(result);
+ }
+ };
+
+ // Execute the one side needed, as per the condition
+ const auto executeOneSide = [&]() {
+ // Always emit control flow.
+ if (node->getBasicType() != glslang::EbtVoid)
+ result = builder.createVariable(spv::StorageClassFunction, convertGlslangToSpvType(node->getType()));
+
+ // Selection control:
+ const spv::SelectionControlMask control = TranslateSelectionControl(*node);
+
+ // make an "if" based on the value created by the condition
+ spv::Builder::If ifBuilder(condition, control, builder);
+
+ // emit the "then" statement
+ if (node->getTrueBlock() != nullptr) {
+ node->getTrueBlock()->traverse(this);
+ if (result != spv::NoResult)
+ builder.createStore(accessChainLoad(node->getTrueBlock()->getAsTyped()->getType()), result);
+ }
+
+ if (node->getFalseBlock() != nullptr) {
+ ifBuilder.makeBeginElse();
+ // emit the "else" statement
+ node->getFalseBlock()->traverse(this);
+ if (result != spv::NoResult)
+ builder.createStore(accessChainLoad(node->getFalseBlock()->getAsTyped()->getType()), result);
+ }
+
+ // finish off the control flow
+ ifBuilder.makeEndIf();
+
+ if (result != spv::NoResult) {
+ builder.clearAccessChain();
+ builder.setAccessChainLValue(result);
+ }
+ };
+
+ // Try for OpSelect (or a requirement to execute both sides)
+ if (bothSidesPolicy()) {
+ SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder);
+ if (node->getType().getQualifier().isSpecConstant())
+ spec_constant_op_mode_setter.turnOnSpecConstantOpMode();
+ executeBothSides();
+ } else
+ executeOneSide();
+
+ return false;
+}
+
+bool TGlslangToSpvTraverser::visitSwitch(glslang::TVisit /* visit */, glslang::TIntermSwitch* node)
+{
+ // emit and get the condition before doing anything with switch
+ node->getCondition()->traverse(this);
+ spv::Id selector = accessChainLoad(node->getCondition()->getAsTyped()->getType());
+
+ // Selection control:
+ const spv::SelectionControlMask control = TranslateSwitchControl(*node);
+
+ // browse the children to sort out code segments
+ int defaultSegment = -1;
+ std::vector<TIntermNode*> codeSegments;
+ glslang::TIntermSequence& sequence = node->getBody()->getSequence();
+ std::vector<int> caseValues;
+ std::vector<int> valueIndexToSegment(sequence.size()); // note: probably not all are used, it is an overestimate
+ for (glslang::TIntermSequence::iterator c = sequence.begin(); c != sequence.end(); ++c) {
+ TIntermNode* child = *c;
+ if (child->getAsBranchNode() && child->getAsBranchNode()->getFlowOp() == glslang::EOpDefault)
+ defaultSegment = (int)codeSegments.size();
+ else if (child->getAsBranchNode() && child->getAsBranchNode()->getFlowOp() == glslang::EOpCase) {
+ valueIndexToSegment[caseValues.size()] = (int)codeSegments.size();
+ caseValues.push_back(child->getAsBranchNode()->getExpression()->getAsConstantUnion()->getConstArray()[0].getIConst());
+ } else
+ codeSegments.push_back(child);
+ }
+
+ // handle the case where the last code segment is missing, due to no code
+ // statements between the last case and the end of the switch statement
+ if ((caseValues.size() && (int)codeSegments.size() == valueIndexToSegment[caseValues.size() - 1]) ||
+ (int)codeSegments.size() == defaultSegment)
+ codeSegments.push_back(nullptr);
+
+ // make the switch statement
+ std::vector<spv::Block*> segmentBlocks; // returned, as the blocks allocated in the call
+ builder.makeSwitch(selector, control, (int)codeSegments.size(), caseValues, valueIndexToSegment, defaultSegment, segmentBlocks);
+
+ // emit all the code in the segments
+ breakForLoop.push(false);
+ for (unsigned int s = 0; s < codeSegments.size(); ++s) {
+ builder.nextSwitchSegment(segmentBlocks, s);
+ if (codeSegments[s])
+ codeSegments[s]->traverse(this);
+ else
+ builder.addSwitchBreak();
+ }
+ breakForLoop.pop();
+
+ builder.endSwitch(segmentBlocks);
+
+ return false;
+}
+
+void TGlslangToSpvTraverser::visitConstantUnion(glslang::TIntermConstantUnion* node)
+{
+ int nextConst = 0;
+ spv::Id constant = createSpvConstantFromConstUnionArray(node->getType(), node->getConstArray(), nextConst, false);
+
+ builder.clearAccessChain();
+ builder.setAccessChainRValue(constant);
+}
+
+bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIntermLoop* node)
+{
+ auto blocks = builder.makeNewLoop();
+ builder.createBranch(&blocks.head);
+
+ // Loop control:
+ unsigned int dependencyLength = glslang::TIntermLoop::dependencyInfinite;
+ const spv::LoopControlMask control = TranslateLoopControl(*node, dependencyLength);
+
+ // Spec requires back edges to target header blocks, and every header block
+ // must dominate its merge block. Make a header block first to ensure these
+ // conditions are met. By definition, it will contain OpLoopMerge, followed
+ // by a block-ending branch. But we don't want to put any other body/test
+ // instructions in it, since the body/test may have arbitrary instructions,
+ // including merges of its own.
+ builder.setLine(node->getLoc().line, node->getLoc().getFilename());
+ builder.setBuildPoint(&blocks.head);
+ builder.createLoopMerge(&blocks.merge, &blocks.continue_target, control, dependencyLength);
+ if (node->testFirst() && node->getTest()) {
+ spv::Block& test = builder.makeNewBlock();
+ builder.createBranch(&test);
+
+ builder.setBuildPoint(&test);
+ node->getTest()->traverse(this);
+ spv::Id condition = accessChainLoad(node->getTest()->getType());
+ builder.createConditionalBranch(condition, &blocks.body, &blocks.merge);
+
+ builder.setBuildPoint(&blocks.body);
+ breakForLoop.push(true);
+ if (node->getBody())
+ node->getBody()->traverse(this);
+ builder.createBranch(&blocks.continue_target);
+ breakForLoop.pop();
+
+ builder.setBuildPoint(&blocks.continue_target);
+ if (node->getTerminal())
+ node->getTerminal()->traverse(this);
+ builder.createBranch(&blocks.head);
+ } else {
+ builder.setLine(node->getLoc().line, node->getLoc().getFilename());
+ builder.createBranch(&blocks.body);
+
+ breakForLoop.push(true);
+ builder.setBuildPoint(&blocks.body);
+ if (node->getBody())
+ node->getBody()->traverse(this);
+ builder.createBranch(&blocks.continue_target);
+ breakForLoop.pop();
+
+ builder.setBuildPoint(&blocks.continue_target);
+ if (node->getTerminal())
+ node->getTerminal()->traverse(this);
+ if (node->getTest()) {
+ node->getTest()->traverse(this);
+ spv::Id condition =
+ accessChainLoad(node->getTest()->getType());
+ builder.createConditionalBranch(condition, &blocks.head, &blocks.merge);
+ } else {
+ // TODO: unless there was a break/return/discard instruction
+ // somewhere in the body, this is an infinite loop, so we should
+ // issue a warning.
+ builder.createBranch(&blocks.head);
+ }
+ }
+ builder.setBuildPoint(&blocks.merge);
+ builder.closeLoop();
+ return false;
+}
+
+bool TGlslangToSpvTraverser::visitBranch(glslang::TVisit /* visit */, glslang::TIntermBranch* node)
+{
+ if (node->getExpression())
+ node->getExpression()->traverse(this);
+
+ builder.setLine(node->getLoc().line, node->getLoc().getFilename());
+
+ switch (node->getFlowOp()) {
+ case glslang::EOpKill:
+ builder.makeDiscard();
+ break;
+ case glslang::EOpBreak:
+ if (breakForLoop.top())
+ builder.createLoopExit();
+ else
+ builder.addSwitchBreak();
+ break;
+ case glslang::EOpContinue:
+ builder.createLoopContinue();
+ break;
+ case glslang::EOpReturn:
+ if (node->getExpression()) {
+ const glslang::TType& glslangReturnType = node->getExpression()->getType();
+ spv::Id returnId = accessChainLoad(glslangReturnType);
+ if (builder.getTypeId(returnId) != currentFunction->getReturnType()) {
+ builder.clearAccessChain();
+ spv::Id copyId = builder.createVariable(spv::StorageClassFunction, currentFunction->getReturnType());
+ builder.setAccessChainLValue(copyId);
+ multiTypeStore(glslangReturnType, returnId);
+ returnId = builder.createLoad(copyId);
+ }
+ builder.makeReturn(false, returnId);
+ } else
+ builder.makeReturn(false);
+
+ builder.clearAccessChain();
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+ return false;
+}
+
+spv::Id TGlslangToSpvTraverser::createSpvVariable(const glslang::TIntermSymbol* node)
+{
+ // First, steer off constants, which are not SPIR-V variables, but
+ // can still have a mapping to a SPIR-V Id.
+ // This includes specialization constants.
+ if (node->getQualifier().isConstant()) {
+ spv::Id result = createSpvConstant(*node);
+ if (result != spv::NoResult)
+ return result;
+ }
+
+ // Now, handle actual variables
+ spv::StorageClass storageClass = TranslateStorageClass(node->getType());
+ spv::Id spvType = convertGlslangToSpvType(node->getType());
+
+ const bool contains16BitType = node->getType().containsBasicType(glslang::EbtFloat16) ||
+ node->getType().containsBasicType(glslang::EbtInt16) ||
+ node->getType().containsBasicType(glslang::EbtUint16);
+ if (contains16BitType) {
+ switch (storageClass) {
+ case spv::StorageClassInput:
+ case spv::StorageClassOutput:
+ addPre13Extension(spv::E_SPV_KHR_16bit_storage);
+ builder.addCapability(spv::CapabilityStorageInputOutput16);
+ break;
+ case spv::StorageClassPushConstant:
+ addPre13Extension(spv::E_SPV_KHR_16bit_storage);
+ builder.addCapability(spv::CapabilityStoragePushConstant16);
+ break;
+ case spv::StorageClassUniform:
+ addPre13Extension(spv::E_SPV_KHR_16bit_storage);
+ if (node->getType().getQualifier().storage == glslang::EvqBuffer)
+ builder.addCapability(spv::CapabilityStorageUniformBufferBlock16);
+ else
+ builder.addCapability(spv::CapabilityStorageUniform16);
+ break;
+ case spv::StorageClassStorageBuffer:
+ case spv::StorageClassPhysicalStorageBufferEXT:
+ addPre13Extension(spv::E_SPV_KHR_16bit_storage);
+ builder.addCapability(spv::CapabilityStorageUniformBufferBlock16);
+ break;
+ default:
+ break;
+ }
+ }
+
+ const bool contains8BitType = node->getType().containsBasicType(glslang::EbtInt8) ||
+ node->getType().containsBasicType(glslang::EbtUint8);
+ if (contains8BitType) {
+ if (storageClass == spv::StorageClassPushConstant) {
+ builder.addExtension(spv::E_SPV_KHR_8bit_storage);
+ builder.addCapability(spv::CapabilityStoragePushConstant8);
+ } else if (storageClass == spv::StorageClassUniform) {
+ builder.addExtension(spv::E_SPV_KHR_8bit_storage);
+ builder.addCapability(spv::CapabilityUniformAndStorageBuffer8BitAccess);
+ } else if (storageClass == spv::StorageClassStorageBuffer) {
+ builder.addExtension(spv::E_SPV_KHR_8bit_storage);
+ builder.addCapability(spv::CapabilityStorageBuffer8BitAccess);
+ }
+ }
+
+ const char* name = node->getName().c_str();
+ if (glslang::IsAnonymous(name))
+ name = "";
+
+ return builder.createVariable(storageClass, spvType, name);
+}
+
+// Return type Id of the sampled type.
+spv::Id TGlslangToSpvTraverser::getSampledType(const glslang::TSampler& sampler)
+{
+ switch (sampler.type) {
+ case glslang::EbtFloat: return builder.makeFloatType(32);
+#ifdef AMD_EXTENSIONS
+ case glslang::EbtFloat16:
+ builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float_fetch);
+ builder.addCapability(spv::CapabilityFloat16ImageAMD);
+ return builder.makeFloatType(16);
+#endif
+ case glslang::EbtInt: return builder.makeIntType(32);
+ case glslang::EbtUint: return builder.makeUintType(32);
+ default:
+ assert(0);
+ return builder.makeFloatType(32);
+ }
+}
+
+// If node is a swizzle operation, return the type that should be used if
+// the swizzle base is first consumed by another operation, before the swizzle
+// is applied.
+spv::Id TGlslangToSpvTraverser::getInvertedSwizzleType(const glslang::TIntermTyped& node)
+{
+ if (node.getAsOperator() &&
+ node.getAsOperator()->getOp() == glslang::EOpVectorSwizzle)
+ return convertGlslangToSpvType(node.getAsBinaryNode()->getLeft()->getType());
+ else
+ return spv::NoType;
+}
+
+// When inverting a swizzle with a parent op, this function
+// will apply the swizzle operation to a completed parent operation.
+spv::Id TGlslangToSpvTraverser::createInvertedSwizzle(spv::Decoration precision, const glslang::TIntermTyped& node, spv::Id parentResult)
+{
+ std::vector<unsigned> swizzle;
+ convertSwizzle(*node.getAsBinaryNode()->getRight()->getAsAggregate(), swizzle);
+ return builder.createRvalueSwizzle(precision, convertGlslangToSpvType(node.getType()), parentResult, swizzle);
+}
+
+// Convert a glslang AST swizzle node to a swizzle vector for building SPIR-V.
+void TGlslangToSpvTraverser::convertSwizzle(const glslang::TIntermAggregate& node, std::vector<unsigned>& swizzle)
+{
+ const glslang::TIntermSequence& swizzleSequence = node.getSequence();
+ for (int i = 0; i < (int)swizzleSequence.size(); ++i)
+ swizzle.push_back(swizzleSequence[i]->getAsConstantUnion()->getConstArray()[0].getIConst());
+}
+
+// Convert from a glslang type to an SPV type, by calling into a
+// recursive version of this function. This establishes the inherited
+// layout state rooted from the top-level type.
+spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& type, bool forwardReferenceOnly)
+{
+ return convertGlslangToSpvType(type, getExplicitLayout(type), type.getQualifier(), false, forwardReferenceOnly);
+}
+
+// Do full recursive conversion of an arbitrary glslang type to a SPIR-V Id.
+// explicitLayout can be kept the same throughout the hierarchical recursive walk.
+// Mutually recursive with convertGlslangStructToSpvType().
+spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& type,
+ glslang::TLayoutPacking explicitLayout, const glslang::TQualifier& qualifier,
+ bool lastBufferBlockMember, bool forwardReferenceOnly)
+{
+ spv::Id spvType = spv::NoResult;
+
+ switch (type.getBasicType()) {
+ case glslang::EbtVoid:
+ spvType = builder.makeVoidType();
+ assert (! type.isArray());
+ break;
+ case glslang::EbtFloat:
+ spvType = builder.makeFloatType(32);
+ break;
+ case glslang::EbtDouble:
+ spvType = builder.makeFloatType(64);
+ break;
+ case glslang::EbtFloat16:
+ spvType = builder.makeFloatType(16);
+ break;
+ case glslang::EbtBool:
+ // "transparent" bool doesn't exist in SPIR-V. The GLSL convention is
+ // a 32-bit int where non-0 means true.
+ if (explicitLayout != glslang::ElpNone)
+ spvType = builder.makeUintType(32);
+ else
+ spvType = builder.makeBoolType();
+ break;
+ case glslang::EbtInt8:
+ spvType = builder.makeIntType(8);
+ break;
+ case glslang::EbtUint8:
+ spvType = builder.makeUintType(8);
+ break;
+ case glslang::EbtInt16:
+ spvType = builder.makeIntType(16);
+ break;
+ case glslang::EbtUint16:
+ spvType = builder.makeUintType(16);
+ break;
+ case glslang::EbtInt:
+ spvType = builder.makeIntType(32);
+ break;
+ case glslang::EbtUint:
+ spvType = builder.makeUintType(32);
+ break;
+ case glslang::EbtInt64:
+ spvType = builder.makeIntType(64);
+ break;
+ case glslang::EbtUint64:
+ spvType = builder.makeUintType(64);
+ break;
+ case glslang::EbtAtomicUint:
+ builder.addCapability(spv::CapabilityAtomicStorage);
+ spvType = builder.makeUintType(32);
+ break;
+#ifdef NV_EXTENSIONS
+ case glslang::EbtAccStructNV:
+ spvType = builder.makeAccelerationStructureNVType();
+ break;
+#endif
+ case glslang::EbtSampler:
+ {
+ const glslang::TSampler& sampler = type.getSampler();
+ if (sampler.sampler) {
+ // pure sampler
+ spvType = builder.makeSamplerType();
+ } else {
+ // an image is present, make its type
+ spvType = builder.makeImageType(getSampledType(sampler), TranslateDimensionality(sampler), sampler.shadow, sampler.arrayed, sampler.ms,
+ sampler.image ? 2 : 1, TranslateImageFormat(type));
+ if (sampler.combined) {
+ // already has both image and sampler, make the combined type
+ spvType = builder.makeSampledImageType(spvType);
+ }
+ }
+ }
+ break;
+ case glslang::EbtStruct:
+ case glslang::EbtBlock:
+ {
+ // If we've seen this struct type, return it
+ const glslang::TTypeList* glslangMembers = type.getStruct();
+
+ // Try to share structs for different layouts, but not yet for other
+ // kinds of qualification (primarily not yet including interpolant qualification).
+ if (! HasNonLayoutQualifiers(type, qualifier))
+ spvType = structMap[explicitLayout][qualifier.layoutMatrix][glslangMembers];
+ if (spvType != spv::NoResult)
+ break;
+
+ // else, we haven't seen it...
+ if (type.getBasicType() == glslang::EbtBlock)
+ memberRemapper[glslangMembers].resize(glslangMembers->size());
+ spvType = convertGlslangStructToSpvType(type, glslangMembers, explicitLayout, qualifier);
+ }
+ break;
+ case glslang::EbtReference:
+ {
+ // Make the forward pointer, then recurse to convert the structure type, then
+ // patch up the forward pointer with a real pointer type.
+ if (forwardPointers.find(type.getReferentType()) == forwardPointers.end()) {
+ spv::Id forwardId = builder.makeForwardPointer(spv::StorageClassPhysicalStorageBufferEXT);
+ forwardPointers[type.getReferentType()] = forwardId;
+ }
+ spvType = forwardPointers[type.getReferentType()];
+ if (!forwardReferenceOnly) {
+ spv::Id referentType = convertGlslangToSpvType(*type.getReferentType());
+ builder.makePointerFromForwardPointer(spv::StorageClassPhysicalStorageBufferEXT,
+ forwardPointers[type.getReferentType()],
+ referentType);
+ }
+ }
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ if (type.isMatrix())
+ spvType = builder.makeMatrixType(spvType, type.getMatrixCols(), type.getMatrixRows());
+ else {
+ // If this variable has a vector element count greater than 1, create a SPIR-V vector
+ if (type.getVectorSize() > 1)
+ spvType = builder.makeVectorType(spvType, type.getVectorSize());
+ }
+
+ if (type.isCoopMat()) {
+ builder.addCapability(spv::CapabilityCooperativeMatrixNV);
+ builder.addExtension(spv::E_SPV_NV_cooperative_matrix);
+ if (type.getBasicType() == glslang::EbtFloat16)
+ builder.addCapability(spv::CapabilityFloat16);
+
+ spv::Id scope = makeArraySizeId(*type.getTypeParameters(), 1);
+ spv::Id rows = makeArraySizeId(*type.getTypeParameters(), 2);
+ spv::Id cols = makeArraySizeId(*type.getTypeParameters(), 3);
+
+ spvType = builder.makeCooperativeMatrixType(spvType, scope, rows, cols);
+ }
+
+ if (type.isArray()) {
+ int stride = 0; // keep this 0 unless doing an explicit layout; 0 will mean no decoration, no stride
+
+ // Do all but the outer dimension
+ if (type.getArraySizes()->getNumDims() > 1) {
+ // We need to decorate array strides for types needing explicit layout, except blocks.
+ if (explicitLayout != glslang::ElpNone && type.getBasicType() != glslang::EbtBlock) {
+ // Use a dummy glslang type for querying internal strides of
+ // arrays of arrays, but using just a one-dimensional array.
+ glslang::TType simpleArrayType(type, 0); // deference type of the array
+ while (simpleArrayType.getArraySizes()->getNumDims() > 1)
+ simpleArrayType.getArraySizes()->dereference();
+
+ // Will compute the higher-order strides here, rather than making a whole
+ // pile of types and doing repetitive recursion on their contents.
+ stride = getArrayStride(simpleArrayType, explicitLayout, qualifier.layoutMatrix);
+ }
+
+ // make the arrays
+ for (int dim = type.getArraySizes()->getNumDims() - 1; dim > 0; --dim) {
+ spvType = builder.makeArrayType(spvType, makeArraySizeId(*type.getArraySizes(), dim), stride);
+ if (stride > 0)
+ builder.addDecoration(spvType, spv::DecorationArrayStride, stride);
+ stride *= type.getArraySizes()->getDimSize(dim);
+ }
+ } else {
+ // single-dimensional array, and don't yet have stride
+
+ // We need to decorate array strides for types needing explicit layout, except blocks.
+ if (explicitLayout != glslang::ElpNone && type.getBasicType() != glslang::EbtBlock)
+ stride = getArrayStride(type, explicitLayout, qualifier.layoutMatrix);
+ }
+
+ // Do the outer dimension, which might not be known for a runtime-sized array.
+ // (Unsized arrays that survive through linking will be runtime-sized arrays)
+ if (type.isSizedArray())
+ spvType = builder.makeArrayType(spvType, makeArraySizeId(*type.getArraySizes(), 0), stride);
+ else {
+ if (!lastBufferBlockMember) {
+ builder.addExtension("SPV_EXT_descriptor_indexing");
+ builder.addCapability(spv::CapabilityRuntimeDescriptorArrayEXT);
+ }
+ spvType = builder.makeRuntimeArray(spvType);
+ }
+ if (stride > 0)
+ builder.addDecoration(spvType, spv::DecorationArrayStride, stride);
+ }
+
+ return spvType;
+}
+
+// TODO: this functionality should exist at a higher level, in creating the AST
+//
+// Identify interface members that don't have their required extension turned on.
+//
+bool TGlslangToSpvTraverser::filterMember(const glslang::TType& member)
+{
+#ifdef NV_EXTENSIONS
+ auto& extensions = glslangIntermediate->getRequestedExtensions();
+
+ if (member.getFieldName() == "gl_SecondaryViewportMaskNV" &&
+ extensions.find("GL_NV_stereo_view_rendering") == extensions.end())
+ return true;
+ if (member.getFieldName() == "gl_SecondaryPositionNV" &&
+ extensions.find("GL_NV_stereo_view_rendering") == extensions.end())
+ return true;
+
+ if (glslangIntermediate->getStage() != EShLangMeshNV) {
+ if (member.getFieldName() == "gl_ViewportMask" &&
+ extensions.find("GL_NV_viewport_array2") == extensions.end())
+ return true;
+ if (member.getFieldName() == "gl_PositionPerViewNV" &&
+ extensions.find("GL_NVX_multiview_per_view_attributes") == extensions.end())
+ return true;
+ if (member.getFieldName() == "gl_ViewportMaskPerViewNV" &&
+ extensions.find("GL_NVX_multiview_per_view_attributes") == extensions.end())
+ return true;
+ }
+#endif
+
+ return false;
+};
+
+// Do full recursive conversion of a glslang structure (or block) type to a SPIR-V Id.
+// explicitLayout can be kept the same throughout the hierarchical recursive walk.
+// Mutually recursive with convertGlslangToSpvType().
+spv::Id TGlslangToSpvTraverser::convertGlslangStructToSpvType(const glslang::TType& type,
+ const glslang::TTypeList* glslangMembers,
+ glslang::TLayoutPacking explicitLayout,
+ const glslang::TQualifier& qualifier)
+{
+ // Create a vector of struct types for SPIR-V to consume
+ std::vector<spv::Id> spvMembers;
+ int memberDelta = 0; // how much the member's index changes from glslang to SPIR-V, normally 0, except sometimes for blocks
+ std::vector<std::pair<glslang::TType*, glslang::TQualifier> > deferredForwardPointers;
+ for (int i = 0; i < (int)glslangMembers->size(); i++) {
+ glslang::TType& glslangMember = *(*glslangMembers)[i].type;
+ if (glslangMember.hiddenMember()) {
+ ++memberDelta;
+ if (type.getBasicType() == glslang::EbtBlock)
+ memberRemapper[glslangMembers][i] = -1;
+ } else {
+ if (type.getBasicType() == glslang::EbtBlock) {
+ memberRemapper[glslangMembers][i] = i - memberDelta;
+ if (filterMember(glslangMember))
+ continue;
+ }
+ // modify just this child's view of the qualifier
+ glslang::TQualifier memberQualifier = glslangMember.getQualifier();
+ InheritQualifiers(memberQualifier, qualifier);
+
+ // manually inherit location
+ if (! memberQualifier.hasLocation() && qualifier.hasLocation())
+ memberQualifier.layoutLocation = qualifier.layoutLocation;
+
+ // recurse
+ bool lastBufferBlockMember = qualifier.storage == glslang::EvqBuffer &&
+ i == (int)glslangMembers->size() - 1;
+
+ // Make forward pointers for any pointer members, and create a list of members to
+ // convert to spirv types after creating the struct.
+ if (glslangMember.getBasicType() == glslang::EbtReference) {
+ if (forwardPointers.find(glslangMember.getReferentType()) == forwardPointers.end()) {
+ deferredForwardPointers.push_back(std::make_pair(&glslangMember, memberQualifier));
+ }
+ spvMembers.push_back(
+ convertGlslangToSpvType(glslangMember, explicitLayout, memberQualifier, lastBufferBlockMember, true));
+ } else {
+ spvMembers.push_back(
+ convertGlslangToSpvType(glslangMember, explicitLayout, memberQualifier, lastBufferBlockMember, false));
+ }
+ }
+ }
+
+ // Make the SPIR-V type
+ spv::Id spvType = builder.makeStructType(spvMembers, type.getTypeName().c_str());
+ if (! HasNonLayoutQualifiers(type, qualifier))
+ structMap[explicitLayout][qualifier.layoutMatrix][glslangMembers] = spvType;
+
+ // Decorate it
+ decorateStructType(type, glslangMembers, explicitLayout, qualifier, spvType);
+
+ for (int i = 0; i < (int)deferredForwardPointers.size(); ++i) {
+ auto it = deferredForwardPointers[i];
+ convertGlslangToSpvType(*it.first, explicitLayout, it.second, false);
+ }
+
+ return spvType;
+}
+
+void TGlslangToSpvTraverser::decorateStructType(const glslang::TType& type,
+ const glslang::TTypeList* glslangMembers,
+ glslang::TLayoutPacking explicitLayout,
+ const glslang::TQualifier& qualifier,
+ spv::Id spvType)
+{
+ // Name and decorate the non-hidden members
+ int offset = -1;
+ int locationOffset = 0; // for use within the members of this struct
+ for (int i = 0; i < (int)glslangMembers->size(); i++) {
+ glslang::TType& glslangMember = *(*glslangMembers)[i].type;
+ int member = i;
+ if (type.getBasicType() == glslang::EbtBlock) {
+ member = memberRemapper[glslangMembers][i];
+ if (filterMember(glslangMember))
+ continue;
+ }
+
+ // modify just this child's view of the qualifier
+ glslang::TQualifier memberQualifier = glslangMember.getQualifier();
+ InheritQualifiers(memberQualifier, qualifier);
+
+ // using -1 above to indicate a hidden member
+ if (member < 0)
+ continue;
+
+ builder.addMemberName(spvType, member, glslangMember.getFieldName().c_str());
+ builder.addMemberDecoration(spvType, member,
+ TranslateLayoutDecoration(glslangMember, memberQualifier.layoutMatrix));
+ builder.addMemberDecoration(spvType, member, TranslatePrecisionDecoration(glslangMember));
+ // Add interpolation and auxiliary storage decorations only to
+ // top-level members of Input and Output storage classes
+ if (type.getQualifier().storage == glslang::EvqVaryingIn ||
+ type.getQualifier().storage == glslang::EvqVaryingOut) {
+ if (type.getBasicType() == glslang::EbtBlock ||
+ glslangIntermediate->getSource() == glslang::EShSourceHlsl) {
+ builder.addMemberDecoration(spvType, member, TranslateInterpolationDecoration(memberQualifier));
+ builder.addMemberDecoration(spvType, member, TranslateAuxiliaryStorageDecoration(memberQualifier));
+#ifdef NV_EXTENSIONS
+ addMeshNVDecoration(spvType, member, memberQualifier);
+#endif
+ }
+ }
+ builder.addMemberDecoration(spvType, member, TranslateInvariantDecoration(memberQualifier));
+
+ if (type.getBasicType() == glslang::EbtBlock &&
+ qualifier.storage == glslang::EvqBuffer) {
+ // Add memory decorations only to top-level members of shader storage block
+ std::vector<spv::Decoration> memory;
+ TranslateMemoryDecoration(memberQualifier, memory, glslangIntermediate->usingVulkanMemoryModel());
+ for (unsigned int i = 0; i < memory.size(); ++i)
+ builder.addMemberDecoration(spvType, member, memory[i]);
+ }
+
+ // Location assignment was already completed correctly by the front end,
+ // just track whether a member needs to be decorated.
+ // Ignore member locations if the container is an array, as that's
+ // ill-specified and decisions have been made to not allow this.
+ if (! type.isArray() && memberQualifier.hasLocation())
+ builder.addMemberDecoration(spvType, member, spv::DecorationLocation, memberQualifier.layoutLocation);
+
+ if (qualifier.hasLocation()) // track for upcoming inheritance
+ locationOffset += glslangIntermediate->computeTypeLocationSize(
+ glslangMember, glslangIntermediate->getStage());
+
+ // component, XFB, others
+ if (glslangMember.getQualifier().hasComponent())
+ builder.addMemberDecoration(spvType, member, spv::DecorationComponent,
+ glslangMember.getQualifier().layoutComponent);
+ if (glslangMember.getQualifier().hasXfbOffset())
+ builder.addMemberDecoration(spvType, member, spv::DecorationOffset,
+ glslangMember.getQualifier().layoutXfbOffset);
+ else if (explicitLayout != glslang::ElpNone) {
+ // figure out what to do with offset, which is accumulating
+ int nextOffset;
+ updateMemberOffset(type, glslangMember, offset, nextOffset, explicitLayout, memberQualifier.layoutMatrix);
+ if (offset >= 0)
+ builder.addMemberDecoration(spvType, member, spv::DecorationOffset, offset);
+ offset = nextOffset;
+ }
+
+ if (glslangMember.isMatrix() && explicitLayout != glslang::ElpNone)
+ builder.addMemberDecoration(spvType, member, spv::DecorationMatrixStride,
+ getMatrixStride(glslangMember, explicitLayout, memberQualifier.layoutMatrix));
+
+ // built-in variable decorations
+ spv::BuiltIn builtIn = TranslateBuiltInDecoration(glslangMember.getQualifier().builtIn, true);
+ if (builtIn != spv::BuiltInMax)
+ builder.addMemberDecoration(spvType, member, spv::DecorationBuiltIn, (int)builtIn);
+
+ // nonuniform
+ builder.addMemberDecoration(spvType, member, TranslateNonUniformDecoration(glslangMember.getQualifier()));
+
+ if (glslangIntermediate->getHlslFunctionality1() && memberQualifier.semanticName != nullptr) {
+ builder.addExtension("SPV_GOOGLE_hlsl_functionality1");
+ builder.addMemberDecoration(spvType, member, (spv::Decoration)spv::DecorationHlslSemanticGOOGLE,
+ memberQualifier.semanticName);
+ }
+
+#ifdef NV_EXTENSIONS
+ if (builtIn == spv::BuiltInLayer) {
+ // SPV_NV_viewport_array2 extension
+ if (glslangMember.getQualifier().layoutViewportRelative){
+ builder.addMemberDecoration(spvType, member, (spv::Decoration)spv::DecorationViewportRelativeNV);
+ builder.addCapability(spv::CapabilityShaderViewportMaskNV);
+ builder.addExtension(spv::E_SPV_NV_viewport_array2);
+ }
+ if (glslangMember.getQualifier().layoutSecondaryViewportRelativeOffset != -2048){
+ builder.addMemberDecoration(spvType, member,
+ (spv::Decoration)spv::DecorationSecondaryViewportRelativeNV,
+ glslangMember.getQualifier().layoutSecondaryViewportRelativeOffset);
+ builder.addCapability(spv::CapabilityShaderStereoViewNV);
+ builder.addExtension(spv::E_SPV_NV_stereo_view_rendering);
+ }
+ }
+ if (glslangMember.getQualifier().layoutPassthrough) {
+ builder.addMemberDecoration(spvType, member, (spv::Decoration)spv::DecorationPassthroughNV);
+ builder.addCapability(spv::CapabilityGeometryShaderPassthroughNV);
+ builder.addExtension(spv::E_SPV_NV_geometry_shader_passthrough);
+ }
+#endif
+ }
+
+ // Decorate the structure
+ builder.addDecoration(spvType, TranslateLayoutDecoration(type, qualifier.layoutMatrix));
+ builder.addDecoration(spvType, TranslateBlockDecoration(type, glslangIntermediate->usingStorageBuffer()));
+}
+
+// Turn the expression forming the array size into an id.
+// This is not quite trivial, because of specialization constants.
+// Sometimes, a raw constant is turned into an Id, and sometimes
+// a specialization constant expression is.
+spv::Id TGlslangToSpvTraverser::makeArraySizeId(const glslang::TArraySizes& arraySizes, int dim)
+{
+ // First, see if this is sized with a node, meaning a specialization constant:
+ glslang::TIntermTyped* specNode = arraySizes.getDimNode(dim);
+ if (specNode != nullptr) {
+ builder.clearAccessChain();
+ specNode->traverse(this);
+ return accessChainLoad(specNode->getAsTyped()->getType());
+ }
+
+ // Otherwise, need a compile-time (front end) size, get it:
+ int size = arraySizes.getDimSize(dim);
+ assert(size > 0);
+ return builder.makeUintConstant(size);
+}
+
+// Wrap the builder's accessChainLoad to:
+// - localize handling of RelaxedPrecision
+// - use the SPIR-V inferred type instead of another conversion of the glslang type
+// (avoids unnecessary work and possible type punning for structures)
+// - do conversion of concrete to abstract type
+spv::Id TGlslangToSpvTraverser::accessChainLoad(const glslang::TType& type)
+{
+ spv::Id nominalTypeId = builder.accessChainGetInferredType();
+
+ spv::Builder::AccessChain::CoherentFlags coherentFlags = builder.getAccessChain().coherentFlags;
+ coherentFlags |= TranslateCoherent(type);
+
+ unsigned int alignment = builder.getAccessChain().alignment;
+ alignment |= type.getBufferReferenceAlignment();
+
+ spv::Id loadedId = builder.accessChainLoad(TranslatePrecisionDecoration(type),
+ TranslateNonUniformDecoration(type.getQualifier()),
+ nominalTypeId,
+ spv::MemoryAccessMask(TranslateMemoryAccess(coherentFlags) & ~spv::MemoryAccessMakePointerAvailableKHRMask),
+ TranslateMemoryScope(coherentFlags),
+ alignment);
+
+ // Need to convert to abstract types when necessary
+ if (type.getBasicType() == glslang::EbtBool) {
+ if (builder.isScalarType(nominalTypeId)) {
+ // Conversion for bool
+ spv::Id boolType = builder.makeBoolType();
+ if (nominalTypeId != boolType)
+ loadedId = builder.createBinOp(spv::OpINotEqual, boolType, loadedId, builder.makeUintConstant(0));
+ } else if (builder.isVectorType(nominalTypeId)) {
+ // Conversion for bvec
+ int vecSize = builder.getNumTypeComponents(nominalTypeId);
+ spv::Id bvecType = builder.makeVectorType(builder.makeBoolType(), vecSize);
+ if (nominalTypeId != bvecType)
+ loadedId = builder.createBinOp(spv::OpINotEqual, bvecType, loadedId, makeSmearedConstant(builder.makeUintConstant(0), vecSize));
+ }
+ }
+
+ return loadedId;
+}
+
+// Wrap the builder's accessChainStore to:
+// - do conversion of concrete to abstract type
+//
+// Implicitly uses the existing builder.accessChain as the storage target.
+void TGlslangToSpvTraverser::accessChainStore(const glslang::TType& type, spv::Id rvalue)
+{
+ // Need to convert to abstract types when necessary
+ if (type.getBasicType() == glslang::EbtBool) {
+ spv::Id nominalTypeId = builder.accessChainGetInferredType();
+
+ if (builder.isScalarType(nominalTypeId)) {
+ // Conversion for bool
+ spv::Id boolType = builder.makeBoolType();
+ if (nominalTypeId != boolType) {
+ // keep these outside arguments, for determinant order-of-evaluation
+ spv::Id one = builder.makeUintConstant(1);
+ spv::Id zero = builder.makeUintConstant(0);
+ rvalue = builder.createTriOp(spv::OpSelect, nominalTypeId, rvalue, one, zero);
+ } else if (builder.getTypeId(rvalue) != boolType)
+ rvalue = builder.createBinOp(spv::OpINotEqual, boolType, rvalue, builder.makeUintConstant(0));
+ } else if (builder.isVectorType(nominalTypeId)) {
+ // Conversion for bvec
+ int vecSize = builder.getNumTypeComponents(nominalTypeId);
+ spv::Id bvecType = builder.makeVectorType(builder.makeBoolType(), vecSize);
+ if (nominalTypeId != bvecType) {
+ // keep these outside arguments, for determinant order-of-evaluation
+ spv::Id one = makeSmearedConstant(builder.makeUintConstant(1), vecSize);
+ spv::Id zero = makeSmearedConstant(builder.makeUintConstant(0), vecSize);
+ rvalue = builder.createTriOp(spv::OpSelect, nominalTypeId, rvalue, one, zero);
+ } else if (builder.getTypeId(rvalue) != bvecType)
+ rvalue = builder.createBinOp(spv::OpINotEqual, bvecType, rvalue,
+ makeSmearedConstant(builder.makeUintConstant(0), vecSize));
+ }
+ }
+
+ spv::Builder::AccessChain::CoherentFlags coherentFlags = builder.getAccessChain().coherentFlags;
+ coherentFlags |= TranslateCoherent(type);
+
+ unsigned int alignment = builder.getAccessChain().alignment;
+ alignment |= type.getBufferReferenceAlignment();
+
+ builder.accessChainStore(rvalue,
+ spv::MemoryAccessMask(TranslateMemoryAccess(coherentFlags) & ~spv::MemoryAccessMakePointerVisibleKHRMask),
+ TranslateMemoryScope(coherentFlags), alignment);
+}
+
+// For storing when types match at the glslang level, but not might match at the
+// SPIR-V level.
+//
+// This especially happens when a single glslang type expands to multiple
+// SPIR-V types, like a struct that is used in a member-undecorated way as well
+// as in a member-decorated way.
+//
+// NOTE: This function can handle any store request; if it's not special it
+// simplifies to a simple OpStore.
+//
+// Implicitly uses the existing builder.accessChain as the storage target.
+void TGlslangToSpvTraverser::multiTypeStore(const glslang::TType& type, spv::Id rValue)
+{
+ // we only do the complex path here if it's an aggregate
+ if (! type.isStruct() && ! type.isArray()) {
+ accessChainStore(type, rValue);
+ return;
+ }
+
+ // and, it has to be a case of type aliasing
+ spv::Id rType = builder.getTypeId(rValue);
+ spv::Id lValue = builder.accessChainGetLValue();
+ spv::Id lType = builder.getContainedTypeId(builder.getTypeId(lValue));
+ if (lType == rType) {
+ accessChainStore(type, rValue);
+ return;
+ }
+
+ // Recursively (as needed) copy an aggregate type to a different aggregate type,
+ // where the two types were the same type in GLSL. This requires member
+ // by member copy, recursively.
+
+ // If an array, copy element by element.
+ if (type.isArray()) {
+ glslang::TType glslangElementType(type, 0);
+ spv::Id elementRType = builder.getContainedTypeId(rType);
+ for (int index = 0; index < type.getOuterArraySize(); ++index) {
+ // get the source member
+ spv::Id elementRValue = builder.createCompositeExtract(rValue, elementRType, index);
+
+ // set up the target storage
+ builder.clearAccessChain();
+ builder.setAccessChainLValue(lValue);
+ builder.accessChainPush(builder.makeIntConstant(index), TranslateCoherent(type), type.getBufferReferenceAlignment());
+
+ // store the member
+ multiTypeStore(glslangElementType, elementRValue);
+ }
+ } else {
+ assert(type.isStruct());
+
+ // loop over structure members
+ const glslang::TTypeList& members = *type.getStruct();
+ for (int m = 0; m < (int)members.size(); ++m) {
+ const glslang::TType& glslangMemberType = *members[m].type;
+
+ // get the source member
+ spv::Id memberRType = builder.getContainedTypeId(rType, m);
+ spv::Id memberRValue = builder.createCompositeExtract(rValue, memberRType, m);
+
+ // set up the target storage
+ builder.clearAccessChain();
+ builder.setAccessChainLValue(lValue);
+ builder.accessChainPush(builder.makeIntConstant(m), TranslateCoherent(type), type.getBufferReferenceAlignment());
+
+ // store the member
+ multiTypeStore(glslangMemberType, memberRValue);
+ }
+ }
+}
+
+// Decide whether or not this type should be
+// decorated with offsets and strides, and if so
+// whether std140 or std430 rules should be applied.
+glslang::TLayoutPacking TGlslangToSpvTraverser::getExplicitLayout(const glslang::TType& type) const
+{
+ // has to be a block
+ if (type.getBasicType() != glslang::EbtBlock)
+ return glslang::ElpNone;
+
+ // has to be a uniform or buffer block or task in/out blocks
+ if (type.getQualifier().storage != glslang::EvqUniform &&
+ type.getQualifier().storage != glslang::EvqBuffer &&
+ !type.getQualifier().isTaskMemory())
+ return glslang::ElpNone;
+
+ // return the layout to use
+ switch (type.getQualifier().layoutPacking) {
+ case glslang::ElpStd140:
+ case glslang::ElpStd430:
+ case glslang::ElpScalar:
+ return type.getQualifier().layoutPacking;
+ default:
+ return glslang::ElpNone;
+ }
+}
+
+// Given an array type, returns the integer stride required for that array
+int TGlslangToSpvTraverser::getArrayStride(const glslang::TType& arrayType, glslang::TLayoutPacking explicitLayout, glslang::TLayoutMatrix matrixLayout)
+{
+ int size;
+ int stride;
+ glslangIntermediate->getMemberAlignment(arrayType, size, stride, explicitLayout, matrixLayout == glslang::ElmRowMajor);
+
+ return stride;
+}
+
+// Given a matrix type, or array (of array) of matrixes type, returns the integer stride required for that matrix
+// when used as a member of an interface block
+int TGlslangToSpvTraverser::getMatrixStride(const glslang::TType& matrixType, glslang::TLayoutPacking explicitLayout, glslang::TLayoutMatrix matrixLayout)
+{
+ glslang::TType elementType;
+ elementType.shallowCopy(matrixType);
+ elementType.clearArraySizes();
+
+ int size;
+ int stride;
+ glslangIntermediate->getMemberAlignment(elementType, size, stride, explicitLayout, matrixLayout == glslang::ElmRowMajor);
+
+ return stride;
+}
+
+// Given a member type of a struct, realign the current offset for it, and compute
+// the next (not yet aligned) offset for the next member, which will get aligned
+// on the next call.
+// 'currentOffset' should be passed in already initialized, ready to modify, and reflecting
+// the migration of data from nextOffset -> currentOffset. It should be -1 on the first call.
+// -1 means a non-forced member offset (no decoration needed).
+void TGlslangToSpvTraverser::updateMemberOffset(const glslang::TType& structType, const glslang::TType& memberType, int& currentOffset, int& nextOffset,
+ glslang::TLayoutPacking explicitLayout, glslang::TLayoutMatrix matrixLayout)
+{
+ // this will get a positive value when deemed necessary
+ nextOffset = -1;
+
+ // override anything in currentOffset with user-set offset
+ if (memberType.getQualifier().hasOffset())
+ currentOffset = memberType.getQualifier().layoutOffset;
+
+ // It could be that current linker usage in glslang updated all the layoutOffset,
+ // in which case the following code does not matter. But, that's not quite right
+ // once cross-compilation unit GLSL validation is done, as the original user
+ // settings are needed in layoutOffset, and then the following will come into play.
+
+ if (explicitLayout == glslang::ElpNone) {
+ if (! memberType.getQualifier().hasOffset())
+ currentOffset = -1;
+
+ return;
+ }
+
+ // Getting this far means we need explicit offsets
+ if (currentOffset < 0)
+ currentOffset = 0;
+
+ // Now, currentOffset is valid (either 0, or from a previous nextOffset),
+ // but possibly not yet correctly aligned.
+
+ int memberSize;
+ int dummyStride;
+ int memberAlignment = glslangIntermediate->getMemberAlignment(memberType, memberSize, dummyStride, explicitLayout, matrixLayout == glslang::ElmRowMajor);
+
+ // Adjust alignment for HLSL rules
+ // TODO: make this consistent in early phases of code:
+ // adjusting this late means inconsistencies with earlier code, which for reflection is an issue
+ // Until reflection is brought in sync with these adjustments, don't apply to $Global,
+ // which is the most likely to rely on reflection, and least likely to rely implicit layouts
+ if (glslangIntermediate->usingHlslOffsets() &&
+ ! memberType.isArray() && memberType.isVector() && structType.getTypeName().compare("$Global") != 0) {
+ int dummySize;
+ int componentAlignment = glslangIntermediate->getBaseAlignmentScalar(memberType, dummySize);
+ if (componentAlignment <= 4)
+ memberAlignment = componentAlignment;
+ }
+
+ // Bump up to member alignment
+ glslang::RoundToPow2(currentOffset, memberAlignment);
+
+ // Bump up to vec4 if there is a bad straddle
+ if (explicitLayout != glslang::ElpScalar && glslangIntermediate->improperStraddle(memberType, memberSize, currentOffset))
+ glslang::RoundToPow2(currentOffset, 16);
+
+ nextOffset = currentOffset + memberSize;
+}
+
+void TGlslangToSpvTraverser::declareUseOfStructMember(const glslang::TTypeList& members, int glslangMember)
+{
+ const glslang::TBuiltInVariable glslangBuiltIn = members[glslangMember].type->getQualifier().builtIn;
+ switch (glslangBuiltIn)
+ {
+ case glslang::EbvClipDistance:
+ case glslang::EbvCullDistance:
+ case glslang::EbvPointSize:
+#ifdef NV_EXTENSIONS
+ case glslang::EbvViewportMaskNV:
+ case glslang::EbvSecondaryPositionNV:
+ case glslang::EbvSecondaryViewportMaskNV:
+ case glslang::EbvPositionPerViewNV:
+ case glslang::EbvViewportMaskPerViewNV:
+ case glslang::EbvTaskCountNV:
+ case glslang::EbvPrimitiveCountNV:
+ case glslang::EbvPrimitiveIndicesNV:
+ case glslang::EbvClipDistancePerViewNV:
+ case glslang::EbvCullDistancePerViewNV:
+ case glslang::EbvLayerPerViewNV:
+ case glslang::EbvMeshViewCountNV:
+ case glslang::EbvMeshViewIndicesNV:
+#endif
+ // Generate the associated capability. Delegate to TranslateBuiltInDecoration.
+ // Alternately, we could just call this for any glslang built-in, since the
+ // capability already guards against duplicates.
+ TranslateBuiltInDecoration(glslangBuiltIn, false);
+ break;
+ default:
+ // Capabilities were already generated when the struct was declared.
+ break;
+ }
+}
+
+bool TGlslangToSpvTraverser::isShaderEntryPoint(const glslang::TIntermAggregate* node)
+{
+ return node->getName().compare(glslangIntermediate->getEntryPointMangledName().c_str()) == 0;
+}
+
+// Does parameter need a place to keep writes, separate from the original?
+// Assumes called after originalParam(), which filters out block/buffer/opaque-based
+// qualifiers such that we should have only in/out/inout/constreadonly here.
+bool TGlslangToSpvTraverser::writableParam(glslang::TStorageQualifier qualifier) const
+{
+ assert(qualifier == glslang::EvqIn ||
+ qualifier == glslang::EvqOut ||
+ qualifier == glslang::EvqInOut ||
+ qualifier == glslang::EvqConstReadOnly);
+ return qualifier != glslang::EvqConstReadOnly;
+}
+
+// Is parameter pass-by-original?
+bool TGlslangToSpvTraverser::originalParam(glslang::TStorageQualifier qualifier, const glslang::TType& paramType,
+ bool implicitThisParam)
+{
+ if (implicitThisParam) // implicit this
+ return true;
+ if (glslangIntermediate->getSource() == glslang::EShSourceHlsl)
+ return paramType.getBasicType() == glslang::EbtBlock;
+ return paramType.containsOpaque() || // sampler, etc.
+ (paramType.getBasicType() == glslang::EbtBlock && qualifier == glslang::EvqBuffer); // SSBO
+}
+
+// Make all the functions, skeletally, without actually visiting their bodies.
+void TGlslangToSpvTraverser::makeFunctions(const glslang::TIntermSequence& glslFunctions)
+{
+ const auto getParamDecorations = [&](std::vector<spv::Decoration>& decorations, const glslang::TType& type, bool useVulkanMemoryModel) {
+ spv::Decoration paramPrecision = TranslatePrecisionDecoration(type);
+ if (paramPrecision != spv::NoPrecision)
+ decorations.push_back(paramPrecision);
+ TranslateMemoryDecoration(type.getQualifier(), decorations, useVulkanMemoryModel);
+ if (type.getBasicType() == glslang::EbtReference) {
+ // Original and non-writable params pass the pointer directly and
+ // use restrict/aliased, others are stored to a pointer in Function
+ // memory and use RestrictPointer/AliasedPointer.
+ if (originalParam(type.getQualifier().storage, type, false) ||
+ !writableParam(type.getQualifier().storage)) {
+ decorations.push_back(type.getQualifier().restrict ? spv::DecorationRestrict : spv::DecorationAliased);
+ } else {
+ decorations.push_back(type.getQualifier().restrict ? spv::DecorationRestrictPointerEXT : spv::DecorationAliasedPointerEXT);
+ }
+ }
+ };
+
+ for (int f = 0; f < (int)glslFunctions.size(); ++f) {
+ glslang::TIntermAggregate* glslFunction = glslFunctions[f]->getAsAggregate();
+ if (! glslFunction || glslFunction->getOp() != glslang::EOpFunction || isShaderEntryPoint(glslFunction))
+ continue;
+
+ // We're on a user function. Set up the basic interface for the function now,
+ // so that it's available to call. Translating the body will happen later.
+ //
+ // Typically (except for a "const in" parameter), an address will be passed to the
+ // function. What it is an address of varies:
+ //
+ // - "in" parameters not marked as "const" can be written to without modifying the calling
+ // argument so that write needs to be to a copy, hence the address of a copy works.
+ //
+ // - "const in" parameters can just be the r-value, as no writes need occur.
+ //
+ // - "out" and "inout" arguments can't be done as pointers to the calling argument, because
+ // GLSL has copy-in/copy-out semantics. They can be handled though with a pointer to a copy.
+
+ std::vector<spv::Id> paramTypes;
+ std::vector<std::vector<spv::Decoration>> paramDecorations; // list of decorations per parameter
+ glslang::TIntermSequence& parameters = glslFunction->getSequence()[0]->getAsAggregate()->getSequence();
+
+ bool implicitThis = (int)parameters.size() > 0 && parameters[0]->getAsSymbolNode()->getName() ==
+ glslangIntermediate->implicitThisName;
+
+ paramDecorations.resize(parameters.size());
+ for (int p = 0; p < (int)parameters.size(); ++p) {
+ const glslang::TType& paramType = parameters[p]->getAsTyped()->getType();
+ spv::Id typeId = convertGlslangToSpvType(paramType);
+ if (originalParam(paramType.getQualifier().storage, paramType, implicitThis && p == 0))
+ typeId = builder.makePointer(TranslateStorageClass(paramType), typeId);
+ else if (writableParam(paramType.getQualifier().storage))
+ typeId = builder.makePointer(spv::StorageClassFunction, typeId);
+ else
+ rValueParameters.insert(parameters[p]->getAsSymbolNode()->getId());
+ getParamDecorations(paramDecorations[p], paramType, glslangIntermediate->usingVulkanMemoryModel());
+ paramTypes.push_back(typeId);
+ }
+
+ spv::Block* functionBlock;
+ spv::Function *function = builder.makeFunctionEntry(TranslatePrecisionDecoration(glslFunction->getType()),
+ convertGlslangToSpvType(glslFunction->getType()),
+ glslFunction->getName().c_str(), paramTypes,
+ paramDecorations, &functionBlock);
+ if (implicitThis)
+ function->setImplicitThis();
+
+ // Track function to emit/call later
+ functionMap[glslFunction->getName().c_str()] = function;
+
+ // Set the parameter id's
+ for (int p = 0; p < (int)parameters.size(); ++p) {
+ symbolValues[parameters[p]->getAsSymbolNode()->getId()] = function->getParamId(p);
+ // give a name too
+ builder.addName(function->getParamId(p), parameters[p]->getAsSymbolNode()->getName().c_str());
+ }
+ }
+}
+
+// Process all the initializers, while skipping the functions and link objects
+void TGlslangToSpvTraverser::makeGlobalInitializers(const glslang::TIntermSequence& initializers)
+{
+ builder.setBuildPoint(shaderEntry->getLastBlock());
+ for (int i = 0; i < (int)initializers.size(); ++i) {
+ glslang::TIntermAggregate* initializer = initializers[i]->getAsAggregate();
+ if (initializer && initializer->getOp() != glslang::EOpFunction && initializer->getOp() != glslang::EOpLinkerObjects) {
+
+ // We're on a top-level node that's not a function. Treat as an initializer, whose
+ // code goes into the beginning of the entry point.
+ initializer->traverse(this);
+ }
+ }
+}
+
+// Process all the functions, while skipping initializers.
+void TGlslangToSpvTraverser::visitFunctions(const glslang::TIntermSequence& glslFunctions)
+{
+ for (int f = 0; f < (int)glslFunctions.size(); ++f) {
+ glslang::TIntermAggregate* node = glslFunctions[f]->getAsAggregate();
+ if (node && (node->getOp() == glslang::EOpFunction || node->getOp() == glslang::EOpLinkerObjects))
+ node->traverse(this);
+ }
+}
+
+void TGlslangToSpvTraverser::handleFunctionEntry(const glslang::TIntermAggregate* node)
+{
+ // SPIR-V functions should already be in the functionMap from the prepass
+ // that called makeFunctions().
+ currentFunction = functionMap[node->getName().c_str()];
+ spv::Block* functionBlock = currentFunction->getEntryBlock();
+ builder.setBuildPoint(functionBlock);
+}
+
+void TGlslangToSpvTraverser::translateArguments(const glslang::TIntermAggregate& node, std::vector<spv::Id>& arguments)
+{
+ const glslang::TIntermSequence& glslangArguments = node.getSequence();
+
+ glslang::TSampler sampler = {};
+ bool cubeCompare = false;
+#ifdef AMD_EXTENSIONS
+ bool f16ShadowCompare = false;
+#endif
+ if (node.isTexture() || node.isImage()) {
+ sampler = glslangArguments[0]->getAsTyped()->getType().getSampler();
+ cubeCompare = sampler.dim == glslang::EsdCube && sampler.arrayed && sampler.shadow;
+#ifdef AMD_EXTENSIONS
+ f16ShadowCompare = sampler.shadow && glslangArguments[1]->getAsTyped()->getType().getBasicType() == glslang::EbtFloat16;
+#endif
+ }
+
+ for (int i = 0; i < (int)glslangArguments.size(); ++i) {
+ builder.clearAccessChain();
+ glslangArguments[i]->traverse(this);
+
+ // Special case l-value operands
+ bool lvalue = false;
+ switch (node.getOp()) {
+ case glslang::EOpImageAtomicAdd:
+ case glslang::EOpImageAtomicMin:
+ case glslang::EOpImageAtomicMax:
+ case glslang::EOpImageAtomicAnd:
+ case glslang::EOpImageAtomicOr:
+ case glslang::EOpImageAtomicXor:
+ case glslang::EOpImageAtomicExchange:
+ case glslang::EOpImageAtomicCompSwap:
+ case glslang::EOpImageAtomicLoad:
+ case glslang::EOpImageAtomicStore:
+ if (i == 0)
+ lvalue = true;
+ break;
+ case glslang::EOpSparseImageLoad:
+ if ((sampler.ms && i == 3) || (! sampler.ms && i == 2))
+ lvalue = true;
+ break;
+#ifdef AMD_EXTENSIONS
+ case glslang::EOpSparseTexture:
+ if (((cubeCompare || f16ShadowCompare) && i == 3) || (! (cubeCompare || f16ShadowCompare) && i == 2))
+ lvalue = true;
+ break;
+ case glslang::EOpSparseTextureClamp:
+ if (((cubeCompare || f16ShadowCompare) && i == 4) || (! (cubeCompare || f16ShadowCompare) && i == 3))
+ lvalue = true;
+ break;
+ case glslang::EOpSparseTextureLod:
+ case glslang::EOpSparseTextureOffset:
+ if ((f16ShadowCompare && i == 4) || (! f16ShadowCompare && i == 3))
+ lvalue = true;
+ break;
+#else
+ case glslang::EOpSparseTexture:
+ if ((cubeCompare && i == 3) || (! cubeCompare && i == 2))
+ lvalue = true;
+ break;
+ case glslang::EOpSparseTextureClamp:
+ if ((cubeCompare && i == 4) || (! cubeCompare && i == 3))
+ lvalue = true;
+ break;
+ case glslang::EOpSparseTextureLod:
+ case glslang::EOpSparseTextureOffset:
+ if (i == 3)
+ lvalue = true;
+ break;
+#endif
+ case glslang::EOpSparseTextureFetch:
+ if ((sampler.dim != glslang::EsdRect && i == 3) || (sampler.dim == glslang::EsdRect && i == 2))
+ lvalue = true;
+ break;
+ case glslang::EOpSparseTextureFetchOffset:
+ if ((sampler.dim != glslang::EsdRect && i == 4) || (sampler.dim == glslang::EsdRect && i == 3))
+ lvalue = true;
+ break;
+#ifdef AMD_EXTENSIONS
+ case glslang::EOpSparseTextureLodOffset:
+ case glslang::EOpSparseTextureGrad:
+ case glslang::EOpSparseTextureOffsetClamp:
+ if ((f16ShadowCompare && i == 5) || (! f16ShadowCompare && i == 4))
+ lvalue = true;
+ break;
+ case glslang::EOpSparseTextureGradOffset:
+ case glslang::EOpSparseTextureGradClamp:
+ if ((f16ShadowCompare && i == 6) || (! f16ShadowCompare && i == 5))
+ lvalue = true;
+ break;
+ case glslang::EOpSparseTextureGradOffsetClamp:
+ if ((f16ShadowCompare && i == 7) || (! f16ShadowCompare && i == 6))
+ lvalue = true;
+ break;
+#else
+ case glslang::EOpSparseTextureLodOffset:
+ case glslang::EOpSparseTextureGrad:
+ case glslang::EOpSparseTextureOffsetClamp:
+ if (i == 4)
+ lvalue = true;
+ break;
+ case glslang::EOpSparseTextureGradOffset:
+ case glslang::EOpSparseTextureGradClamp:
+ if (i == 5)
+ lvalue = true;
+ break;
+ case glslang::EOpSparseTextureGradOffsetClamp:
+ if (i == 6)
+ lvalue = true;
+ break;
+#endif
+ case glslang::EOpSparseTextureGather:
+ if ((sampler.shadow && i == 3) || (! sampler.shadow && i == 2))
+ lvalue = true;
+ break;
+ case glslang::EOpSparseTextureGatherOffset:
+ case glslang::EOpSparseTextureGatherOffsets:
+ if ((sampler.shadow && i == 4) || (! sampler.shadow && i == 3))
+ lvalue = true;
+ break;
+#ifdef AMD_EXTENSIONS
+ case glslang::EOpSparseTextureGatherLod:
+ if (i == 3)
+ lvalue = true;
+ break;
+ case glslang::EOpSparseTextureGatherLodOffset:
+ case glslang::EOpSparseTextureGatherLodOffsets:
+ if (i == 4)
+ lvalue = true;
+ break;
+ case glslang::EOpSparseImageLoadLod:
+ if (i == 3)
+ lvalue = true;
+ break;
+#endif
+#ifdef NV_EXTENSIONS
+ case glslang::EOpImageSampleFootprintNV:
+ if (i == 4)
+ lvalue = true;
+ break;
+ case glslang::EOpImageSampleFootprintClampNV:
+ case glslang::EOpImageSampleFootprintLodNV:
+ if (i == 5)
+ lvalue = true;
+ break;
+ case glslang::EOpImageSampleFootprintGradNV:
+ if (i == 6)
+ lvalue = true;
+ break;
+ case glslang::EOpImageSampleFootprintGradClampNV:
+ if (i == 7)
+ lvalue = true;
+ break;
+#endif
+ default:
+ break;
+ }
+
+ if (lvalue)
+ arguments.push_back(builder.accessChainGetLValue());
+ else
+ arguments.push_back(accessChainLoad(glslangArguments[i]->getAsTyped()->getType()));
+ }
+}
+
+void TGlslangToSpvTraverser::translateArguments(glslang::TIntermUnary& node, std::vector<spv::Id>& arguments)
+{
+ builder.clearAccessChain();
+ node.getOperand()->traverse(this);
+ arguments.push_back(accessChainLoad(node.getOperand()->getType()));
+}
+
+spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermOperator* node)
+{
+ if (! node->isImage() && ! node->isTexture())
+ return spv::NoResult;
+
+ builder.setLine(node->getLoc().line, node->getLoc().getFilename());
+
+ // Process a GLSL texturing op (will be SPV image)
+
+ const glslang::TType &imageType = node->getAsAggregate() ? node->getAsAggregate()->getSequence()[0]->getAsTyped()->getType()
+ : node->getAsUnaryNode()->getOperand()->getAsTyped()->getType();
+ const glslang::TSampler sampler = imageType.getSampler();
+#ifdef AMD_EXTENSIONS
+ bool f16ShadowCompare = (sampler.shadow && node->getAsAggregate())
+ ? node->getAsAggregate()->getSequence()[1]->getAsTyped()->getType().getBasicType() == glslang::EbtFloat16
+ : false;
+#endif
+
+ std::vector<spv::Id> arguments;
+ if (node->getAsAggregate())
+ translateArguments(*node->getAsAggregate(), arguments);
+ else
+ translateArguments(*node->getAsUnaryNode(), arguments);
+ spv::Decoration precision = TranslatePrecisionDecoration(node->getOperationPrecision());
+
+ spv::Builder::TextureParameters params = { };
+ params.sampler = arguments[0];
+
+ glslang::TCrackedTextureOp cracked;
+ node->crackTexture(sampler, cracked);
+
+ const bool isUnsignedResult = node->getType().getBasicType() == glslang::EbtUint;
+
+ // Check for queries
+ if (cracked.query) {
+ // OpImageQueryLod works on a sampled image, for other queries the image has to be extracted first
+ if (node->getOp() != glslang::EOpTextureQueryLod && builder.isSampledImage(params.sampler))
+ params.sampler = builder.createUnaryOp(spv::OpImage, builder.getImageType(params.sampler), params.sampler);
+
+ switch (node->getOp()) {
+ case glslang::EOpImageQuerySize:
+ case glslang::EOpTextureQuerySize:
+ if (arguments.size() > 1) {
+ params.lod = arguments[1];
+ return builder.createTextureQueryCall(spv::OpImageQuerySizeLod, params, isUnsignedResult);
+ } else
+ return builder.createTextureQueryCall(spv::OpImageQuerySize, params, isUnsignedResult);
+ case glslang::EOpImageQuerySamples:
+ case glslang::EOpTextureQuerySamples:
+ return builder.createTextureQueryCall(spv::OpImageQuerySamples, params, isUnsignedResult);
+ case glslang::EOpTextureQueryLod:
+ params.coords = arguments[1];
+ return builder.createTextureQueryCall(spv::OpImageQueryLod, params, isUnsignedResult);
+ case glslang::EOpTextureQueryLevels:
+ return builder.createTextureQueryCall(spv::OpImageQueryLevels, params, isUnsignedResult);
+ case glslang::EOpSparseTexelsResident:
+ return builder.createUnaryOp(spv::OpImageSparseTexelsResident, builder.makeBoolType(), arguments[0]);
+ default:
+ assert(0);
+ break;
+ }
+ }
+
+ int components = node->getType().getVectorSize();
+
+ if (node->getOp() == glslang::EOpTextureFetch) {
+ // These must produce 4 components, per SPIR-V spec. We'll add a conversion constructor if needed.
+ // This will only happen through the HLSL path for operator[], so we do not have to handle e.g.
+ // the EOpTexture/Proj/Lod/etc family. It would be harmless to do so, but would need more logic
+ // here around e.g. which ones return scalars or other types.
+ components = 4;
+ }
+
+ glslang::TType returnType(node->getType().getBasicType(), glslang::EvqTemporary, components);
+
+ auto resultType = [&returnType,this]{ return convertGlslangToSpvType(returnType); };
+
+ // Check for image functions other than queries
+ if (node->isImage()) {
+ std::vector<spv::IdImmediate> operands;
+ auto opIt = arguments.begin();
+ spv::IdImmediate image = { true, *(opIt++) };
+ operands.push_back(image);
+
+ // Handle subpass operations
+ // TODO: GLSL should change to have the "MS" only on the type rather than the
+ // built-in function.
+ if (cracked.subpass) {
+ // add on the (0,0) coordinate
+ spv::Id zero = builder.makeIntConstant(0);
+ std::vector<spv::Id> comps;
+ comps.push_back(zero);
+ comps.push_back(zero);
+ spv::IdImmediate coord = { true,
+ builder.makeCompositeConstant(builder.makeVectorType(builder.makeIntType(32), 2), comps) };
+ operands.push_back(coord);
+ if (sampler.ms) {
+ spv::IdImmediate imageOperands = { false, spv::ImageOperandsSampleMask };
+ operands.push_back(imageOperands);
+ spv::IdImmediate imageOperand = { true, *(opIt++) };
+ operands.push_back(imageOperand);
+ }
+ spv::Id result = builder.createOp(spv::OpImageRead, resultType(), operands);
+ builder.setPrecision(result, precision);
+ return result;
+ }
+
+ spv::IdImmediate coord = { true, *(opIt++) };
+ operands.push_back(coord);
+#ifdef AMD_EXTENSIONS
+ if (node->getOp() == glslang::EOpImageLoad || node->getOp() == glslang::EOpImageLoadLod) {
+#else
+ if (node->getOp() == glslang::EOpImageLoad) {
+#endif
+ spv::ImageOperandsMask mask = spv::ImageOperandsMaskNone;
+ if (sampler.ms) {
+ mask = mask | spv::ImageOperandsSampleMask;
+ }
+#ifdef AMD_EXTENSIONS
+ if (cracked.lod) {
+ builder.addExtension(spv::E_SPV_AMD_shader_image_load_store_lod);
+ builder.addCapability(spv::CapabilityImageReadWriteLodAMD);
+ mask = mask | spv::ImageOperandsLodMask;
+ }
+#endif
+ mask = mask | TranslateImageOperands(TranslateCoherent(imageType));
+ mask = (spv::ImageOperandsMask)(mask & ~spv::ImageOperandsMakeTexelAvailableKHRMask);
+ if (mask) {
+ spv::IdImmediate imageOperands = { false, (unsigned int)mask };
+ operands.push_back(imageOperands);
+ }
+ if (mask & spv::ImageOperandsSampleMask) {
+ spv::IdImmediate imageOperand = { true, *opIt++ };
+ operands.push_back(imageOperand);
+ }
+#ifdef AMD_EXTENSIONS
+ if (mask & spv::ImageOperandsLodMask) {
+ spv::IdImmediate imageOperand = { true, *opIt++ };
+ operands.push_back(imageOperand);
+ }
+#endif
+ if (mask & spv::ImageOperandsMakeTexelVisibleKHRMask) {
+ spv::IdImmediate imageOperand = { true, builder.makeUintConstant(TranslateMemoryScope(TranslateCoherent(imageType))) };
+ operands.push_back(imageOperand);
+ }
+
+ if (builder.getImageTypeFormat(builder.getImageType(operands.front().word)) == spv::ImageFormatUnknown)
+ builder.addCapability(spv::CapabilityStorageImageReadWithoutFormat);
+
+ std::vector<spv::Id> result(1, builder.createOp(spv::OpImageRead, resultType(), operands));
+ builder.setPrecision(result[0], precision);
+
+ // If needed, add a conversion constructor to the proper size.
+ if (components != node->getType().getVectorSize())
+ result[0] = builder.createConstructor(precision, result, convertGlslangToSpvType(node->getType()));
+
+ return result[0];
+#ifdef AMD_EXTENSIONS
+ } else if (node->getOp() == glslang::EOpImageStore || node->getOp() == glslang::EOpImageStoreLod) {
+#else
+ } else if (node->getOp() == glslang::EOpImageStore) {
+#endif
+
+ // Push the texel value before the operands
+#ifdef AMD_EXTENSIONS
+ if (sampler.ms || cracked.lod) {
+#else
+ if (sampler.ms) {
+#endif
+ spv::IdImmediate texel = { true, *(opIt + 1) };
+ operands.push_back(texel);
+ } else {
+ spv::IdImmediate texel = { true, *opIt };
+ operands.push_back(texel);
+ }
+
+ spv::ImageOperandsMask mask = spv::ImageOperandsMaskNone;
+ if (sampler.ms) {
+ mask = mask | spv::ImageOperandsSampleMask;
+ }
+#ifdef AMD_EXTENSIONS
+ if (cracked.lod) {
+ builder.addExtension(spv::E_SPV_AMD_shader_image_load_store_lod);
+ builder.addCapability(spv::CapabilityImageReadWriteLodAMD);
+ mask = mask | spv::ImageOperandsLodMask;
+ }
+#endif
+ mask = mask | TranslateImageOperands(TranslateCoherent(imageType));
+ mask = (spv::ImageOperandsMask)(mask & ~spv::ImageOperandsMakeTexelVisibleKHRMask);
+ if (mask) {
+ spv::IdImmediate imageOperands = { false, (unsigned int)mask };
+ operands.push_back(imageOperands);
+ }
+ if (mask & spv::ImageOperandsSampleMask) {
+ spv::IdImmediate imageOperand = { true, *opIt++ };
+ operands.push_back(imageOperand);
+ }
+#ifdef AMD_EXTENSIONS
+ if (mask & spv::ImageOperandsLodMask) {
+ spv::IdImmediate imageOperand = { true, *opIt++ };
+ operands.push_back(imageOperand);
+ }
+#endif
+ if (mask & spv::ImageOperandsMakeTexelAvailableKHRMask) {
+ spv::IdImmediate imageOperand = { true, builder.makeUintConstant(TranslateMemoryScope(TranslateCoherent(imageType))) };
+ operands.push_back(imageOperand);
+ }
+
+ builder.createNoResultOp(spv::OpImageWrite, operands);
+ if (builder.getImageTypeFormat(builder.getImageType(operands.front().word)) == spv::ImageFormatUnknown)
+ builder.addCapability(spv::CapabilityStorageImageWriteWithoutFormat);
+ return spv::NoResult;
+#ifdef AMD_EXTENSIONS
+ } else if (node->getOp() == glslang::EOpSparseImageLoad || node->getOp() == glslang::EOpSparseImageLoadLod) {
+#else
+ } else if (node->getOp() == glslang::EOpSparseImageLoad) {
+#endif
+ builder.addCapability(spv::CapabilitySparseResidency);
+ if (builder.getImageTypeFormat(builder.getImageType(operands.front().word)) == spv::ImageFormatUnknown)
+ builder.addCapability(spv::CapabilityStorageImageReadWithoutFormat);
+
+ spv::ImageOperandsMask mask = spv::ImageOperandsMaskNone;
+ if (sampler.ms) {
+ mask = mask | spv::ImageOperandsSampleMask;
+ }
+#ifdef AMD_EXTENSIONS
+ if (cracked.lod) {
+ builder.addExtension(spv::E_SPV_AMD_shader_image_load_store_lod);
+ builder.addCapability(spv::CapabilityImageReadWriteLodAMD);
+
+ mask = mask | spv::ImageOperandsLodMask;
+ }
+#endif
+ mask = mask | TranslateImageOperands(TranslateCoherent(imageType));
+ mask = (spv::ImageOperandsMask)(mask & ~spv::ImageOperandsMakeTexelAvailableKHRMask);
+ if (mask) {
+ spv::IdImmediate imageOperands = { false, (unsigned int)mask };
+ operands.push_back(imageOperands);
+ }
+ if (mask & spv::ImageOperandsSampleMask) {
+ spv::IdImmediate imageOperand = { true, *opIt++ };
+ operands.push_back(imageOperand);
+ }
+#ifdef AMD_EXTENSIONS
+ if (mask & spv::ImageOperandsLodMask) {
+ spv::IdImmediate imageOperand = { true, *opIt++ };
+ operands.push_back(imageOperand);
+ }
+#endif
+ if (mask & spv::ImageOperandsMakeTexelVisibleKHRMask) {
+ spv::IdImmediate imageOperand = { true, builder.makeUintConstant(TranslateMemoryScope(TranslateCoherent(imageType))) };
+ operands.push_back(imageOperand);
+ }
+
+ // Create the return type that was a special structure
+ spv::Id texelOut = *opIt;
+ spv::Id typeId0 = resultType();
+ spv::Id typeId1 = builder.getDerefTypeId(texelOut);
+ spv::Id resultTypeId = builder.makeStructResultType(typeId0, typeId1);
+
+ spv::Id resultId = builder.createOp(spv::OpImageSparseRead, resultTypeId, operands);
+
+ // Decode the return type
+ builder.createStore(builder.createCompositeExtract(resultId, typeId1, 1), texelOut);
+ return builder.createCompositeExtract(resultId, typeId0, 0);
+ } else {
+ // Process image atomic operations
+
+ // GLSL "IMAGE_PARAMS" will involve in constructing an image texel pointer and this pointer,
+ // as the first source operand, is required by SPIR-V atomic operations.
+ // For non-MS, the sample value should be 0
+ spv::IdImmediate sample = { true, sampler.ms ? *(opIt++) : builder.makeUintConstant(0) };
+ operands.push_back(sample);
+
+ spv::Id resultTypeId;
+ // imageAtomicStore has a void return type so base the pointer type on
+ // the type of the value operand.
+ if (node->getOp() == glslang::EOpImageAtomicStore) {
+ resultTypeId = builder.makePointer(spv::StorageClassImage, builder.getTypeId(operands[2].word));
+ } else {
+ resultTypeId = builder.makePointer(spv::StorageClassImage, resultType());
+ }
+ spv::Id pointer = builder.createOp(spv::OpImageTexelPointer, resultTypeId, operands);
+
+ std::vector<spv::Id> operands;
+ operands.push_back(pointer);
+ for (; opIt != arguments.end(); ++opIt)
+ operands.push_back(*opIt);
+
+ return createAtomicOperation(node->getOp(), precision, resultType(), operands, node->getBasicType());
+ }
+ }
+
+#ifdef AMD_EXTENSIONS
+ // Check for fragment mask functions other than queries
+ if (cracked.fragMask) {
+ assert(sampler.ms);
+
+ auto opIt = arguments.begin();
+ std::vector<spv::Id> operands;
+
+ // Extract the image if necessary
+ if (builder.isSampledImage(params.sampler))
+ params.sampler = builder.createUnaryOp(spv::OpImage, builder.getImageType(params.sampler), params.sampler);
+
+ operands.push_back(params.sampler);
+ ++opIt;
+
+ if (sampler.isSubpass()) {
+ // add on the (0,0) coordinate
+ spv::Id zero = builder.makeIntConstant(0);
+ std::vector<spv::Id> comps;
+ comps.push_back(zero);
+ comps.push_back(zero);
+ operands.push_back(builder.makeCompositeConstant(builder.makeVectorType(builder.makeIntType(32), 2), comps));
+ }
+
+ for (; opIt != arguments.end(); ++opIt)
+ operands.push_back(*opIt);
+
+ spv::Op fragMaskOp = spv::OpNop;
+ if (node->getOp() == glslang::EOpFragmentMaskFetch)
+ fragMaskOp = spv::OpFragmentMaskFetchAMD;
+ else if (node->getOp() == glslang::EOpFragmentFetch)
+ fragMaskOp = spv::OpFragmentFetchAMD;
+
+ builder.addExtension(spv::E_SPV_AMD_shader_fragment_mask);
+ builder.addCapability(spv::CapabilityFragmentMaskAMD);
+ return builder.createOp(fragMaskOp, resultType(), operands);
+ }
+#endif
+
+ // Check for texture functions other than queries
+ bool sparse = node->isSparseTexture();
+#ifdef NV_EXTENSIONS
+ bool imageFootprint = node->isImageFootprint();
+#endif
+
+ bool cubeCompare = sampler.dim == glslang::EsdCube && sampler.arrayed && sampler.shadow;
+
+ // check for bias argument
+ bool bias = false;
+#ifdef AMD_EXTENSIONS
+ if (! cracked.lod && ! cracked.grad && ! cracked.fetch && ! cubeCompare) {
+#else
+ if (! cracked.lod && ! cracked.gather && ! cracked.grad && ! cracked.fetch && ! cubeCompare) {
+#endif
+ int nonBiasArgCount = 2;
+#ifdef AMD_EXTENSIONS
+ if (cracked.gather)
+ ++nonBiasArgCount; // comp argument should be present when bias argument is present
+
+ if (f16ShadowCompare)
+ ++nonBiasArgCount;
+#endif
+ if (cracked.offset)
+ ++nonBiasArgCount;
+#ifdef AMD_EXTENSIONS
+ else if (cracked.offsets)
+ ++nonBiasArgCount;
+#endif
+ if (cracked.grad)
+ nonBiasArgCount += 2;
+ if (cracked.lodClamp)
+ ++nonBiasArgCount;
+ if (sparse)
+ ++nonBiasArgCount;
+#ifdef NV_EXTENSIONS
+ if (imageFootprint)
+ //Following three extra arguments
+ // int granularity, bool coarse, out gl_TextureFootprint2DNV footprint
+ nonBiasArgCount += 3;
+#endif
+ if ((int)arguments.size() > nonBiasArgCount)
+ bias = true;
+ }
+
+ // See if the sampler param should really be just the SPV image part
+ if (cracked.fetch) {
+ // a fetch needs to have the image extracted first
+ if (builder.isSampledImage(params.sampler))
+ params.sampler = builder.createUnaryOp(spv::OpImage, builder.getImageType(params.sampler), params.sampler);
+ }
+
+#ifdef AMD_EXTENSIONS
+ if (cracked.gather) {
+ const auto& sourceExtensions = glslangIntermediate->getRequestedExtensions();
+ if (bias || cracked.lod ||
+ sourceExtensions.find(glslang::E_GL_AMD_texture_gather_bias_lod) != sourceExtensions.end()) {
+ builder.addExtension(spv::E_SPV_AMD_texture_gather_bias_lod);
+ builder.addCapability(spv::CapabilityImageGatherBiasLodAMD);
+ }
+ }
+#endif
+
+ // set the rest of the arguments
+
+ params.coords = arguments[1];
+ int extraArgs = 0;
+ bool noImplicitLod = false;
+
+ // sort out where Dref is coming from
+#ifdef AMD_EXTENSIONS
+ if (cubeCompare || f16ShadowCompare) {
+#else
+ if (cubeCompare) {
+#endif
+ params.Dref = arguments[2];
+ ++extraArgs;
+ } else if (sampler.shadow && cracked.gather) {
+ params.Dref = arguments[2];
+ ++extraArgs;
+ } else if (sampler.shadow) {
+ std::vector<spv::Id> indexes;
+ int dRefComp;
+ if (cracked.proj)
+ dRefComp = 2; // "The resulting 3rd component of P in the shadow forms is used as Dref"
+ else
+ dRefComp = builder.getNumComponents(params.coords) - 1;
+ indexes.push_back(dRefComp);
+ params.Dref = builder.createCompositeExtract(params.coords, builder.getScalarTypeId(builder.getTypeId(params.coords)), indexes);
+ }
+
+ // lod
+ if (cracked.lod) {
+ params.lod = arguments[2 + extraArgs];
+ ++extraArgs;
+ } else if (glslangIntermediate->getStage() != EShLangFragment
+#ifdef NV_EXTENSIONS
+ // NV_compute_shader_derivatives layout qualifiers allow for implicit LODs
+ && !(glslangIntermediate->getStage() == EShLangCompute &&
+ (glslangIntermediate->getLayoutDerivativeModeNone() != glslang::LayoutDerivativeNone))
+#endif
+ ) {
+ // we need to invent the default lod for an explicit lod instruction for a non-fragment stage
+ noImplicitLod = true;
+ }
+
+ // multisample
+ if (sampler.ms) {
+ params.sample = arguments[2 + extraArgs]; // For MS, "sample" should be specified
+ ++extraArgs;
+ }
+
+ // gradient
+ if (cracked.grad) {
+ params.gradX = arguments[2 + extraArgs];
+ params.gradY = arguments[3 + extraArgs];
+ extraArgs += 2;
+ }
+
+ // offset and offsets
+ if (cracked.offset) {
+ params.offset = arguments[2 + extraArgs];
+ ++extraArgs;
+ } else if (cracked.offsets) {
+ params.offsets = arguments[2 + extraArgs];
+ ++extraArgs;
+ }
+
+ // lod clamp
+ if (cracked.lodClamp) {
+ params.lodClamp = arguments[2 + extraArgs];
+ ++extraArgs;
+ }
+ // sparse
+ if (sparse) {
+ params.texelOut = arguments[2 + extraArgs];
+ ++extraArgs;
+ }
+
+ // gather component
+ if (cracked.gather && ! sampler.shadow) {
+ // default component is 0, if missing, otherwise an argument
+ if (2 + extraArgs < (int)arguments.size()) {
+ params.component = arguments[2 + extraArgs];
+ ++extraArgs;
+ } else
+ params.component = builder.makeIntConstant(0);
+ }
+#ifdef NV_EXTENSIONS
+ spv::Id resultStruct = spv::NoResult;
+ if (imageFootprint) {
+ //Following three extra arguments
+ // int granularity, bool coarse, out gl_TextureFootprint2DNV footprint
+ params.granularity = arguments[2 + extraArgs];
+ params.coarse = arguments[3 + extraArgs];
+ resultStruct = arguments[4 + extraArgs];
+ extraArgs += 3;
+ }
+#endif
+ // bias
+ if (bias) {
+ params.bias = arguments[2 + extraArgs];
+ ++extraArgs;
+ }
+
+#ifdef NV_EXTENSIONS
+ if (imageFootprint) {
+ builder.addExtension(spv::E_SPV_NV_shader_image_footprint);
+ builder.addCapability(spv::CapabilityImageFootprintNV);
+
+
+ //resultStructType(OpenGL type) contains 5 elements:
+ //struct gl_TextureFootprint2DNV {
+ // uvec2 anchor;
+ // uvec2 offset;
+ // uvec2 mask;
+ // uint lod;
+ // uint granularity;
+ //};
+ //or
+ //struct gl_TextureFootprint3DNV {
+ // uvec3 anchor;
+ // uvec3 offset;
+ // uvec2 mask;
+ // uint lod;
+ // uint granularity;
+ //};
+ spv::Id resultStructType = builder.getContainedTypeId(builder.getTypeId(resultStruct));
+ assert(builder.isStructType(resultStructType));
+
+ //resType (SPIR-V type) contains 6 elements:
+ //Member 0 must be a Boolean type scalar(LOD),
+ //Member 1 must be a vector of integer type, whose Signedness operand is 0(anchor),
+ //Member 2 must be a vector of integer type, whose Signedness operand is 0(offset),
+ //Member 3 must be a vector of integer type, whose Signedness operand is 0(mask),
+ //Member 4 must be a scalar of integer type, whose Signedness operand is 0(lod),
+ //Member 5 must be a scalar of integer type, whose Signedness operand is 0(granularity).
+ std::vector<spv::Id> members;
+ members.push_back(resultType());
+ for (int i = 0; i < 5; i++) {
+ members.push_back(builder.getContainedTypeId(resultStructType, i));
+ }
+ spv::Id resType = builder.makeStructType(members, "ResType");
+
+ //call ImageFootprintNV
+ spv::Id res = builder.createTextureCall(precision, resType, sparse, cracked.fetch, cracked.proj, cracked.gather, noImplicitLod, params);
+
+ //copy resType (SPIR-V type) to resultStructType(OpenGL type)
+ for (int i = 0; i < 5; i++) {
+ builder.clearAccessChain();
+ builder.setAccessChainLValue(resultStruct);
+
+ //Accessing to a struct we created, no coherent flag is set
+ spv::Builder::AccessChain::CoherentFlags flags;
+ flags.clear();
+
+ builder.accessChainPush(builder.makeIntConstant(i), flags, 0);
+ builder.accessChainStore(builder.createCompositeExtract(res, builder.getContainedTypeId(resType, i+1), i+1));
+ }
+ return builder.createCompositeExtract(res, resultType(), 0);
+ }
+#endif
+
+ // projective component (might not to move)
+ // GLSL: "The texture coordinates consumed from P, not including the last component of P,
+ // are divided by the last component of P."
+ // SPIR-V: "... (u [, v] [, w], q)... It may be a vector larger than needed, but all
+ // unused components will appear after all used components."
+ if (cracked.proj) {
+ int projSourceComp = builder.getNumComponents(params.coords) - 1;
+ int projTargetComp;
+ switch (sampler.dim) {
+ case glslang::Esd1D: projTargetComp = 1; break;
+ case glslang::Esd2D: projTargetComp = 2; break;
+ case glslang::EsdRect: projTargetComp = 2; break;
+ default: projTargetComp = projSourceComp; break;
+ }
+ // copy the projective coordinate if we have to
+ if (projTargetComp != projSourceComp) {
+ spv::Id projComp = builder.createCompositeExtract(params.coords,
+ builder.getScalarTypeId(builder.getTypeId(params.coords)),
+ projSourceComp);
+ params.coords = builder.createCompositeInsert(projComp, params.coords,
+ builder.getTypeId(params.coords), projTargetComp);
+ }
+ }
+
+ // nonprivate
+ if (imageType.getQualifier().nonprivate) {
+ params.nonprivate = true;
+ }
+
+ // volatile
+ if (imageType.getQualifier().volatil) {
+ params.volatil = true;
+ }
+
+ std::vector<spv::Id> result( 1,
+ builder.createTextureCall(precision, resultType(), sparse, cracked.fetch, cracked.proj, cracked.gather, noImplicitLod, params)
+ );
+
+ if (components != node->getType().getVectorSize())
+ result[0] = builder.createConstructor(precision, result, convertGlslangToSpvType(node->getType()));
+
+ return result[0];
+}
+
+spv::Id TGlslangToSpvTraverser::handleUserFunctionCall(const glslang::TIntermAggregate* node)
+{
+ // Grab the function's pointer from the previously created function
+ spv::Function* function = functionMap[node->getName().c_str()];
+ if (! function)
+ return 0;
+
+ const glslang::TIntermSequence& glslangArgs = node->getSequence();
+ const glslang::TQualifierList& qualifiers = node->getQualifierList();
+
+ // See comments in makeFunctions() for details about the semantics for parameter passing.
+ //
+ // These imply we need a four step process:
+ // 1. Evaluate the arguments
+ // 2. Allocate and make copies of in, out, and inout arguments
+ // 3. Make the call
+ // 4. Copy back the results
+
+ // 1. Evaluate the arguments and their types
+ std::vector<spv::Builder::AccessChain> lValues;
+ std::vector<spv::Id> rValues;
+ std::vector<const glslang::TType*> argTypes;
+ for (int a = 0; a < (int)glslangArgs.size(); ++a) {
+ argTypes.push_back(&glslangArgs[a]->getAsTyped()->getType());
+ // build l-value
+ builder.clearAccessChain();
+ glslangArgs[a]->traverse(this);
+ // keep outputs and pass-by-originals as l-values, evaluate others as r-values
+ if (originalParam(qualifiers[a], *argTypes[a], function->hasImplicitThis() && a == 0) ||
+ writableParam(qualifiers[a])) {
+ // save l-value
+ lValues.push_back(builder.getAccessChain());
+ } else {
+ // process r-value
+ rValues.push_back(accessChainLoad(*argTypes.back()));
+ }
+ }
+
+ // 2. Allocate space for anything needing a copy, and if it's "in" or "inout"
+ // copy the original into that space.
+ //
+ // Also, build up the list of actual arguments to pass in for the call
+ int lValueCount = 0;
+ int rValueCount = 0;
+ std::vector<spv::Id> spvArgs;
+ for (int a = 0; a < (int)glslangArgs.size(); ++a) {
+ spv::Id arg;
+ if (originalParam(qualifiers[a], *argTypes[a], function->hasImplicitThis() && a == 0)) {
+ builder.setAccessChain(lValues[lValueCount]);
+ arg = builder.accessChainGetLValue();
+ ++lValueCount;
+ } else if (writableParam(qualifiers[a])) {
+ // need space to hold the copy
+ arg = builder.createVariable(spv::StorageClassFunction, builder.getContainedTypeId(function->getParamType(a)), "param");
+ if (qualifiers[a] == glslang::EvqIn || qualifiers[a] == glslang::EvqInOut) {
+ // need to copy the input into output space
+ builder.setAccessChain(lValues[lValueCount]);
+ spv::Id copy = accessChainLoad(*argTypes[a]);
+ builder.clearAccessChain();
+ builder.setAccessChainLValue(arg);
+ multiTypeStore(*argTypes[a], copy);
+ }
+ ++lValueCount;
+ } else {
+ // process r-value, which involves a copy for a type mismatch
+ if (function->getParamType(a) != convertGlslangToSpvType(*argTypes[a])) {
+ spv::Id argCopy = builder.createVariable(spv::StorageClassFunction, function->getParamType(a), "arg");
+ builder.clearAccessChain();
+ builder.setAccessChainLValue(argCopy);
+ multiTypeStore(*argTypes[a], rValues[rValueCount]);
+ arg = builder.createLoad(argCopy);
+ } else
+ arg = rValues[rValueCount];
+ ++rValueCount;
+ }
+ spvArgs.push_back(arg);
+ }
+
+ // 3. Make the call.
+ spv::Id result = builder.createFunctionCall(function, spvArgs);
+ builder.setPrecision(result, TranslatePrecisionDecoration(node->getType()));
+
+ // 4. Copy back out an "out" arguments.
+ lValueCount = 0;
+ for (int a = 0; a < (int)glslangArgs.size(); ++a) {
+ if (originalParam(qualifiers[a], *argTypes[a], function->hasImplicitThis() && a == 0))
+ ++lValueCount;
+ else if (writableParam(qualifiers[a])) {
+ if (qualifiers[a] == glslang::EvqOut || qualifiers[a] == glslang::EvqInOut) {
+ spv::Id copy = builder.createLoad(spvArgs[a]);
+ builder.setAccessChain(lValues[lValueCount]);
+ multiTypeStore(*argTypes[a], copy);
+ }
+ ++lValueCount;
+ }
+ }
+
+ return result;
+}
+
+// Translate AST operation to SPV operation, already having SPV-based operands/types.
+spv::Id TGlslangToSpvTraverser::createBinaryOperation(glslang::TOperator op, OpDecorations& decorations,
+ spv::Id typeId, spv::Id left, spv::Id right,
+ glslang::TBasicType typeProxy, bool reduceComparison)
+{
+ bool isUnsigned = isTypeUnsignedInt(typeProxy);
+ bool isFloat = isTypeFloat(typeProxy);
+ bool isBool = typeProxy == glslang::EbtBool;
+
+ spv::Op binOp = spv::OpNop;
+ bool needMatchingVectors = true; // for non-matrix ops, would a scalar need to smear to match a vector?
+ bool comparison = false;
+
+ switch (op) {
+ case glslang::EOpAdd:
+ case glslang::EOpAddAssign:
+ if (isFloat)
+ binOp = spv::OpFAdd;
+ else
+ binOp = spv::OpIAdd;
+ break;
+ case glslang::EOpSub:
+ case glslang::EOpSubAssign:
+ if (isFloat)
+ binOp = spv::OpFSub;
+ else
+ binOp = spv::OpISub;
+ break;
+ case glslang::EOpMul:
+ case glslang::EOpMulAssign:
+ if (isFloat)
+ binOp = spv::OpFMul;
+ else
+ binOp = spv::OpIMul;
+ break;
+ case glslang::EOpVectorTimesScalar:
+ case glslang::EOpVectorTimesScalarAssign:
+ if (isFloat && (builder.isVector(left) || builder.isVector(right))) {
+ if (builder.isVector(right))
+ std::swap(left, right);
+ assert(builder.isScalar(right));
+ needMatchingVectors = false;
+ binOp = spv::OpVectorTimesScalar;
+ } else if (isFloat)
+ binOp = spv::OpFMul;
+ else
+ binOp = spv::OpIMul;
+ break;
+ case glslang::EOpVectorTimesMatrix:
+ case glslang::EOpVectorTimesMatrixAssign:
+ binOp = spv::OpVectorTimesMatrix;
+ break;
+ case glslang::EOpMatrixTimesVector:
+ binOp = spv::OpMatrixTimesVector;
+ break;
+ case glslang::EOpMatrixTimesScalar:
+ case glslang::EOpMatrixTimesScalarAssign:
+ binOp = spv::OpMatrixTimesScalar;
+ break;
+ case glslang::EOpMatrixTimesMatrix:
+ case glslang::EOpMatrixTimesMatrixAssign:
+ binOp = spv::OpMatrixTimesMatrix;
+ break;
+ case glslang::EOpOuterProduct:
+ binOp = spv::OpOuterProduct;
+ needMatchingVectors = false;
+ break;
+
+ case glslang::EOpDiv:
+ case glslang::EOpDivAssign:
+ if (isFloat)
+ binOp = spv::OpFDiv;
+ else if (isUnsigned)
+ binOp = spv::OpUDiv;
+ else
+ binOp = spv::OpSDiv;
+ break;
+ case glslang::EOpMod:
+ case glslang::EOpModAssign:
+ if (isFloat)
+ binOp = spv::OpFMod;
+ else if (isUnsigned)
+ binOp = spv::OpUMod;
+ else
+ binOp = spv::OpSMod;
+ break;
+ case glslang::EOpRightShift:
+ case glslang::EOpRightShiftAssign:
+ if (isUnsigned)
+ binOp = spv::OpShiftRightLogical;
+ else
+ binOp = spv::OpShiftRightArithmetic;
+ break;
+ case glslang::EOpLeftShift:
+ case glslang::EOpLeftShiftAssign:
+ binOp = spv::OpShiftLeftLogical;
+ break;
+ case glslang::EOpAnd:
+ case glslang::EOpAndAssign:
+ binOp = spv::OpBitwiseAnd;
+ break;
+ case glslang::EOpLogicalAnd:
+ needMatchingVectors = false;
+ binOp = spv::OpLogicalAnd;
+ break;
+ case glslang::EOpInclusiveOr:
+ case glslang::EOpInclusiveOrAssign:
+ binOp = spv::OpBitwiseOr;
+ break;
+ case glslang::EOpLogicalOr:
+ needMatchingVectors = false;
+ binOp = spv::OpLogicalOr;
+ break;
+ case glslang::EOpExclusiveOr:
+ case glslang::EOpExclusiveOrAssign:
+ binOp = spv::OpBitwiseXor;
+ break;
+ case glslang::EOpLogicalXor:
+ needMatchingVectors = false;
+ binOp = spv::OpLogicalNotEqual;
+ break;
+
+ case glslang::EOpLessThan:
+ case glslang::EOpGreaterThan:
+ case glslang::EOpLessThanEqual:
+ case glslang::EOpGreaterThanEqual:
+ case glslang::EOpEqual:
+ case glslang::EOpNotEqual:
+ case glslang::EOpVectorEqual:
+ case glslang::EOpVectorNotEqual:
+ comparison = true;
+ break;
+ default:
+ break;
+ }
+
+ // handle mapped binary operations (should be non-comparison)
+ if (binOp != spv::OpNop) {
+ assert(comparison == false);
+ if (builder.isMatrix(left) || builder.isMatrix(right) ||
+ builder.isCooperativeMatrix(left) || builder.isCooperativeMatrix(right))
+ return createBinaryMatrixOperation(binOp, decorations, typeId, left, right);
+
+ // No matrix involved; make both operands be the same number of components, if needed
+ if (needMatchingVectors)
+ builder.promoteScalar(decorations.precision, left, right);
+
+ spv::Id result = builder.createBinOp(binOp, typeId, left, right);
+ builder.addDecoration(result, decorations.noContraction);
+ builder.addDecoration(result, decorations.nonUniform);
+ return builder.setPrecision(result, decorations.precision);
+ }
+
+ if (! comparison)
+ return 0;
+
+ // Handle comparison instructions
+
+ if (reduceComparison && (op == glslang::EOpEqual || op == glslang::EOpNotEqual)
+ && (builder.isVector(left) || builder.isMatrix(left) || builder.isAggregate(left))) {
+ spv::Id result = builder.createCompositeCompare(decorations.precision, left, right, op == glslang::EOpEqual);
+ builder.addDecoration(result, decorations.nonUniform);
+ return result;
+ }
+
+ switch (op) {
+ case glslang::EOpLessThan:
+ if (isFloat)
+ binOp = spv::OpFOrdLessThan;
+ else if (isUnsigned)
+ binOp = spv::OpULessThan;
+ else
+ binOp = spv::OpSLessThan;
+ break;
+ case glslang::EOpGreaterThan:
+ if (isFloat)
+ binOp = spv::OpFOrdGreaterThan;
+ else if (isUnsigned)
+ binOp = spv::OpUGreaterThan;
+ else
+ binOp = spv::OpSGreaterThan;
+ break;
+ case glslang::EOpLessThanEqual:
+ if (isFloat)
+ binOp = spv::OpFOrdLessThanEqual;
+ else if (isUnsigned)
+ binOp = spv::OpULessThanEqual;
+ else
+ binOp = spv::OpSLessThanEqual;
+ break;
+ case glslang::EOpGreaterThanEqual:
+ if (isFloat)
+ binOp = spv::OpFOrdGreaterThanEqual;
+ else if (isUnsigned)
+ binOp = spv::OpUGreaterThanEqual;
+ else
+ binOp = spv::OpSGreaterThanEqual;
+ break;
+ case glslang::EOpEqual:
+ case glslang::EOpVectorEqual:
+ if (isFloat)
+ binOp = spv::OpFOrdEqual;
+ else if (isBool)
+ binOp = spv::OpLogicalEqual;
+ else
+ binOp = spv::OpIEqual;
+ break;
+ case glslang::EOpNotEqual:
+ case glslang::EOpVectorNotEqual:
+ if (isFloat)
+ binOp = spv::OpFOrdNotEqual;
+ else if (isBool)
+ binOp = spv::OpLogicalNotEqual;
+ else
+ binOp = spv::OpINotEqual;
+ break;
+ default:
+ break;
+ }
+
+ if (binOp != spv::OpNop) {
+ spv::Id result = builder.createBinOp(binOp, typeId, left, right);
+ builder.addDecoration(result, decorations.noContraction);
+ builder.addDecoration(result, decorations.nonUniform);
+ return builder.setPrecision(result, decorations.precision);
+ }
+
+ return 0;
+}
+
+//
+// Translate AST matrix operation to SPV operation, already having SPV-based operands/types.
+// These can be any of:
+//
+// matrix * scalar
+// scalar * matrix
+// matrix * matrix linear algebraic
+// matrix * vector
+// vector * matrix
+// matrix * matrix componentwise
+// matrix op matrix op in {+, -, /}
+// matrix op scalar op in {+, -, /}
+// scalar op matrix op in {+, -, /}
+//
+spv::Id TGlslangToSpvTraverser::createBinaryMatrixOperation(spv::Op op, OpDecorations& decorations, spv::Id typeId,
+ spv::Id left, spv::Id right)
+{
+ bool firstClass = true;
+
+ // First, handle first-class matrix operations (* and matrix/scalar)
+ switch (op) {
+ case spv::OpFDiv:
+ if (builder.isMatrix(left) && builder.isScalar(right)) {
+ // turn matrix / scalar into a multiply...
+ spv::Id resultType = builder.getTypeId(right);
+ right = builder.createBinOp(spv::OpFDiv, resultType, builder.makeFpConstant(resultType, 1.0), right);
+ op = spv::OpMatrixTimesScalar;
+ } else
+ firstClass = false;
+ break;
+ case spv::OpMatrixTimesScalar:
+ if (builder.isMatrix(right) || builder.isCooperativeMatrix(right))
+ std::swap(left, right);
+ assert(builder.isScalar(right));
+ break;
+ case spv::OpVectorTimesMatrix:
+ assert(builder.isVector(left));
+ assert(builder.isMatrix(right));
+ break;
+ case spv::OpMatrixTimesVector:
+ assert(builder.isMatrix(left));
+ assert(builder.isVector(right));
+ break;
+ case spv::OpMatrixTimesMatrix:
+ assert(builder.isMatrix(left));
+ assert(builder.isMatrix(right));
+ break;
+ default:
+ firstClass = false;
+ break;
+ }
+
+ if (builder.isCooperativeMatrix(left) || builder.isCooperativeMatrix(right))
+ firstClass = true;
+
+ if (firstClass) {
+ spv::Id result = builder.createBinOp(op, typeId, left, right);
+ builder.addDecoration(result, decorations.noContraction);
+ builder.addDecoration(result, decorations.nonUniform);
+ return builder.setPrecision(result, decorations.precision);
+ }
+
+ // Handle component-wise +, -, *, %, and / for all combinations of type.
+ // The result type of all of them is the same type as the (a) matrix operand.
+ // The algorithm is to:
+ // - break the matrix(es) into vectors
+ // - smear any scalar to a vector
+ // - do vector operations
+ // - make a matrix out the vector results
+ switch (op) {
+ case spv::OpFAdd:
+ case spv::OpFSub:
+ case spv::OpFDiv:
+ case spv::OpFMod:
+ case spv::OpFMul:
+ {
+ // one time set up...
+ bool leftMat = builder.isMatrix(left);
+ bool rightMat = builder.isMatrix(right);
+ unsigned int numCols = leftMat ? builder.getNumColumns(left) : builder.getNumColumns(right);
+ int numRows = leftMat ? builder.getNumRows(left) : builder.getNumRows(right);
+ spv::Id scalarType = builder.getScalarTypeId(typeId);
+ spv::Id vecType = builder.makeVectorType(scalarType, numRows);
+ std::vector<spv::Id> results;
+ spv::Id smearVec = spv::NoResult;
+ if (builder.isScalar(left))
+ smearVec = builder.smearScalar(decorations.precision, left, vecType);
+ else if (builder.isScalar(right))
+ smearVec = builder.smearScalar(decorations.precision, right, vecType);
+
+ // do each vector op
+ for (unsigned int c = 0; c < numCols; ++c) {
+ std::vector<unsigned int> indexes;
+ indexes.push_back(c);
+ spv::Id leftVec = leftMat ? builder.createCompositeExtract( left, vecType, indexes) : smearVec;
+ spv::Id rightVec = rightMat ? builder.createCompositeExtract(right, vecType, indexes) : smearVec;
+ spv::Id result = builder.createBinOp(op, vecType, leftVec, rightVec);
+ builder.addDecoration(result, decorations.noContraction);
+ builder.addDecoration(result, decorations.nonUniform);
+ results.push_back(builder.setPrecision(result, decorations.precision));
+ }
+
+ // put the pieces together
+ spv::Id result = builder.setPrecision(builder.createCompositeConstruct(typeId, results), decorations.precision);
+ builder.addDecoration(result, decorations.nonUniform);
+ return result;
+ }
+ default:
+ assert(0);
+ return spv::NoResult;
+ }
+}
+
+spv::Id TGlslangToSpvTraverser::createUnaryOperation(glslang::TOperator op, OpDecorations& decorations, spv::Id typeId,
+ spv::Id operand, glslang::TBasicType typeProxy)
+{
+ spv::Op unaryOp = spv::OpNop;
+ int extBuiltins = -1;
+ int libCall = -1;
+ bool isUnsigned = isTypeUnsignedInt(typeProxy);
+ bool isFloat = isTypeFloat(typeProxy);
+
+ switch (op) {
+ case glslang::EOpNegative:
+ if (isFloat) {
+ unaryOp = spv::OpFNegate;
+ if (builder.isMatrixType(typeId))
+ return createUnaryMatrixOperation(unaryOp, decorations, typeId, operand, typeProxy);
+ } else
+ unaryOp = spv::OpSNegate;
+ break;
+
+ case glslang::EOpLogicalNot:
+ case glslang::EOpVectorLogicalNot:
+ unaryOp = spv::OpLogicalNot;
+ break;
+ case glslang::EOpBitwiseNot:
+ unaryOp = spv::OpNot;
+ break;
+
+ case glslang::EOpDeterminant:
+ libCall = spv::GLSLstd450Determinant;
+ break;
+ case glslang::EOpMatrixInverse:
+ libCall = spv::GLSLstd450MatrixInverse;
+ break;
+ case glslang::EOpTranspose:
+ unaryOp = spv::OpTranspose;
+ break;
+
+ case glslang::EOpRadians:
+ libCall = spv::GLSLstd450Radians;
+ break;
+ case glslang::EOpDegrees:
+ libCall = spv::GLSLstd450Degrees;
+ break;
+ case glslang::EOpSin:
+ libCall = spv::GLSLstd450Sin;
+ break;
+ case glslang::EOpCos:
+ libCall = spv::GLSLstd450Cos;
+ break;
+ case glslang::EOpTan:
+ libCall = spv::GLSLstd450Tan;
+ break;
+ case glslang::EOpAcos:
+ libCall = spv::GLSLstd450Acos;
+ break;
+ case glslang::EOpAsin:
+ libCall = spv::GLSLstd450Asin;
+ break;
+ case glslang::EOpAtan:
+ libCall = spv::GLSLstd450Atan;
+ break;
+
+ case glslang::EOpAcosh:
+ libCall = spv::GLSLstd450Acosh;
+ break;
+ case glslang::EOpAsinh:
+ libCall = spv::GLSLstd450Asinh;
+ break;
+ case glslang::EOpAtanh:
+ libCall = spv::GLSLstd450Atanh;
+ break;
+ case glslang::EOpTanh:
+ libCall = spv::GLSLstd450Tanh;
+ break;
+ case glslang::EOpCosh:
+ libCall = spv::GLSLstd450Cosh;
+ break;
+ case glslang::EOpSinh:
+ libCall = spv::GLSLstd450Sinh;
+ break;
+
+ case glslang::EOpLength:
+ libCall = spv::GLSLstd450Length;
+ break;
+ case glslang::EOpNormalize:
+ libCall = spv::GLSLstd450Normalize;
+ break;
+
+ case glslang::EOpExp:
+ libCall = spv::GLSLstd450Exp;
+ break;
+ case glslang::EOpLog:
+ libCall = spv::GLSLstd450Log;
+ break;
+ case glslang::EOpExp2:
+ libCall = spv::GLSLstd450Exp2;
+ break;
+ case glslang::EOpLog2:
+ libCall = spv::GLSLstd450Log2;
+ break;
+ case glslang::EOpSqrt:
+ libCall = spv::GLSLstd450Sqrt;
+ break;
+ case glslang::EOpInverseSqrt:
+ libCall = spv::GLSLstd450InverseSqrt;
+ break;
+
+ case glslang::EOpFloor:
+ libCall = spv::GLSLstd450Floor;
+ break;
+ case glslang::EOpTrunc:
+ libCall = spv::GLSLstd450Trunc;
+ break;
+ case glslang::EOpRound:
+ libCall = spv::GLSLstd450Round;
+ break;
+ case glslang::EOpRoundEven:
+ libCall = spv::GLSLstd450RoundEven;
+ break;
+ case glslang::EOpCeil:
+ libCall = spv::GLSLstd450Ceil;
+ break;
+ case glslang::EOpFract:
+ libCall = spv::GLSLstd450Fract;
+ break;
+
+ case glslang::EOpIsNan:
+ unaryOp = spv::OpIsNan;
+ break;
+ case glslang::EOpIsInf:
+ unaryOp = spv::OpIsInf;
+ break;
+ case glslang::EOpIsFinite:
+ unaryOp = spv::OpIsFinite;
+ break;
+
+ case glslang::EOpFloatBitsToInt:
+ case glslang::EOpFloatBitsToUint:
+ case glslang::EOpIntBitsToFloat:
+ case glslang::EOpUintBitsToFloat:
+ case glslang::EOpDoubleBitsToInt64:
+ case glslang::EOpDoubleBitsToUint64:
+ case glslang::EOpInt64BitsToDouble:
+ case glslang::EOpUint64BitsToDouble:
+ case glslang::EOpFloat16BitsToInt16:
+ case glslang::EOpFloat16BitsToUint16:
+ case glslang::EOpInt16BitsToFloat16:
+ case glslang::EOpUint16BitsToFloat16:
+ unaryOp = spv::OpBitcast;
+ break;
+
+ case glslang::EOpPackSnorm2x16:
+ libCall = spv::GLSLstd450PackSnorm2x16;
+ break;
+ case glslang::EOpUnpackSnorm2x16:
+ libCall = spv::GLSLstd450UnpackSnorm2x16;
+ break;
+ case glslang::EOpPackUnorm2x16:
+ libCall = spv::GLSLstd450PackUnorm2x16;
+ break;
+ case glslang::EOpUnpackUnorm2x16:
+ libCall = spv::GLSLstd450UnpackUnorm2x16;
+ break;
+ case glslang::EOpPackHalf2x16:
+ libCall = spv::GLSLstd450PackHalf2x16;
+ break;
+ case glslang::EOpUnpackHalf2x16:
+ libCall = spv::GLSLstd450UnpackHalf2x16;
+ break;
+ case glslang::EOpPackSnorm4x8:
+ libCall = spv::GLSLstd450PackSnorm4x8;
+ break;
+ case glslang::EOpUnpackSnorm4x8:
+ libCall = spv::GLSLstd450UnpackSnorm4x8;
+ break;
+ case glslang::EOpPackUnorm4x8:
+ libCall = spv::GLSLstd450PackUnorm4x8;
+ break;
+ case glslang::EOpUnpackUnorm4x8:
+ libCall = spv::GLSLstd450UnpackUnorm4x8;
+ break;
+ case glslang::EOpPackDouble2x32:
+ libCall = spv::GLSLstd450PackDouble2x32;
+ break;
+ case glslang::EOpUnpackDouble2x32:
+ libCall = spv::GLSLstd450UnpackDouble2x32;
+ break;
+
+ case glslang::EOpPackInt2x32:
+ case glslang::EOpUnpackInt2x32:
+ case glslang::EOpPackUint2x32:
+ case glslang::EOpUnpackUint2x32:
+ case glslang::EOpPack16:
+ case glslang::EOpPack32:
+ case glslang::EOpPack64:
+ case glslang::EOpUnpack32:
+ case glslang::EOpUnpack16:
+ case glslang::EOpUnpack8:
+ case glslang::EOpPackInt2x16:
+ case glslang::EOpUnpackInt2x16:
+ case glslang::EOpPackUint2x16:
+ case glslang::EOpUnpackUint2x16:
+ case glslang::EOpPackInt4x16:
+ case glslang::EOpUnpackInt4x16:
+ case glslang::EOpPackUint4x16:
+ case glslang::EOpUnpackUint4x16:
+ case glslang::EOpPackFloat2x16:
+ case glslang::EOpUnpackFloat2x16:
+ unaryOp = spv::OpBitcast;
+ break;
+
+ case glslang::EOpDPdx:
+ unaryOp = spv::OpDPdx;
+ break;
+ case glslang::EOpDPdy:
+ unaryOp = spv::OpDPdy;
+ break;
+ case glslang::EOpFwidth:
+ unaryOp = spv::OpFwidth;
+ break;
+ case glslang::EOpDPdxFine:
+ unaryOp = spv::OpDPdxFine;
+ break;
+ case glslang::EOpDPdyFine:
+ unaryOp = spv::OpDPdyFine;
+ break;
+ case glslang::EOpFwidthFine:
+ unaryOp = spv::OpFwidthFine;
+ break;
+ case glslang::EOpDPdxCoarse:
+ unaryOp = spv::OpDPdxCoarse;
+ break;
+ case glslang::EOpDPdyCoarse:
+ unaryOp = spv::OpDPdyCoarse;
+ break;
+ case glslang::EOpFwidthCoarse:
+ unaryOp = spv::OpFwidthCoarse;
+ break;
+ case glslang::EOpInterpolateAtCentroid:
+#ifdef AMD_EXTENSIONS
+ if (typeProxy == glslang::EbtFloat16)
+ builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float);
+#endif
+ libCall = spv::GLSLstd450InterpolateAtCentroid;
+ break;
+ case glslang::EOpAny:
+ unaryOp = spv::OpAny;
+ break;
+ case glslang::EOpAll:
+ unaryOp = spv::OpAll;
+ break;
+
+ case glslang::EOpAbs:
+ if (isFloat)
+ libCall = spv::GLSLstd450FAbs;
+ else
+ libCall = spv::GLSLstd450SAbs;
+ break;
+ case glslang::EOpSign:
+ if (isFloat)
+ libCall = spv::GLSLstd450FSign;
+ else
+ libCall = spv::GLSLstd450SSign;
+ break;
+
+ case glslang::EOpAtomicCounterIncrement:
+ case glslang::EOpAtomicCounterDecrement:
+ case glslang::EOpAtomicCounter:
+ {
+ // Handle all of the atomics in one place, in createAtomicOperation()
+ std::vector<spv::Id> operands;
+ operands.push_back(operand);
+ return createAtomicOperation(op, decorations.precision, typeId, operands, typeProxy);
+ }
+
+ case glslang::EOpBitFieldReverse:
+ unaryOp = spv::OpBitReverse;
+ break;
+ case glslang::EOpBitCount:
+ unaryOp = spv::OpBitCount;
+ break;
+ case glslang::EOpFindLSB:
+ libCall = spv::GLSLstd450FindILsb;
+ break;
+ case glslang::EOpFindMSB:
+ if (isUnsigned)
+ libCall = spv::GLSLstd450FindUMsb;
+ else
+ libCall = spv::GLSLstd450FindSMsb;
+ break;
+
+ case glslang::EOpBallot:
+ case glslang::EOpReadFirstInvocation:
+ case glslang::EOpAnyInvocation:
+ case glslang::EOpAllInvocations:
+ case glslang::EOpAllInvocationsEqual:
+#ifdef AMD_EXTENSIONS
+ case glslang::EOpMinInvocations:
+ case glslang::EOpMaxInvocations:
+ case glslang::EOpAddInvocations:
+ case glslang::EOpMinInvocationsNonUniform:
+ case glslang::EOpMaxInvocationsNonUniform:
+ case glslang::EOpAddInvocationsNonUniform:
+ case glslang::EOpMinInvocationsInclusiveScan:
+ case glslang::EOpMaxInvocationsInclusiveScan:
+ case glslang::EOpAddInvocationsInclusiveScan:
+ case glslang::EOpMinInvocationsInclusiveScanNonUniform:
+ case glslang::EOpMaxInvocationsInclusiveScanNonUniform:
+ case glslang::EOpAddInvocationsInclusiveScanNonUniform:
+ case glslang::EOpMinInvocationsExclusiveScan:
+ case glslang::EOpMaxInvocationsExclusiveScan:
+ case glslang::EOpAddInvocationsExclusiveScan:
+ case glslang::EOpMinInvocationsExclusiveScanNonUniform:
+ case glslang::EOpMaxInvocationsExclusiveScanNonUniform:
+ case glslang::EOpAddInvocationsExclusiveScanNonUniform:
+#endif
+ {
+ std::vector<spv::Id> operands;
+ operands.push_back(operand);
+ return createInvocationsOperation(op, typeId, operands, typeProxy);
+ }
+ case glslang::EOpSubgroupAll:
+ case glslang::EOpSubgroupAny:
+ case glslang::EOpSubgroupAllEqual:
+ case glslang::EOpSubgroupBroadcastFirst:
+ case glslang::EOpSubgroupBallot:
+ case glslang::EOpSubgroupInverseBallot:
+ case glslang::EOpSubgroupBallotBitCount:
+ case glslang::EOpSubgroupBallotInclusiveBitCount:
+ case glslang::EOpSubgroupBallotExclusiveBitCount:
+ case glslang::EOpSubgroupBallotFindLSB:
+ case glslang::EOpSubgroupBallotFindMSB:
+ case glslang::EOpSubgroupAdd:
+ case glslang::EOpSubgroupMul:
+ case glslang::EOpSubgroupMin:
+ case glslang::EOpSubgroupMax:
+ case glslang::EOpSubgroupAnd:
+ case glslang::EOpSubgroupOr:
+ case glslang::EOpSubgroupXor:
+ case glslang::EOpSubgroupInclusiveAdd:
+ case glslang::EOpSubgroupInclusiveMul:
+ case glslang::EOpSubgroupInclusiveMin:
+ case glslang::EOpSubgroupInclusiveMax:
+ case glslang::EOpSubgroupInclusiveAnd:
+ case glslang::EOpSubgroupInclusiveOr:
+ case glslang::EOpSubgroupInclusiveXor:
+ case glslang::EOpSubgroupExclusiveAdd:
+ case glslang::EOpSubgroupExclusiveMul:
+ case glslang::EOpSubgroupExclusiveMin:
+ case glslang::EOpSubgroupExclusiveMax:
+ case glslang::EOpSubgroupExclusiveAnd:
+ case glslang::EOpSubgroupExclusiveOr:
+ case glslang::EOpSubgroupExclusiveXor:
+ case glslang::EOpSubgroupQuadSwapHorizontal:
+ case glslang::EOpSubgroupQuadSwapVertical:
+ case glslang::EOpSubgroupQuadSwapDiagonal: {
+ std::vector<spv::Id> operands;
+ operands.push_back(operand);
+ return createSubgroupOperation(op, typeId, operands, typeProxy);
+ }
+#ifdef AMD_EXTENSIONS
+ case glslang::EOpMbcnt:
+ extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot);
+ libCall = spv::MbcntAMD;
+ break;
+
+ case glslang::EOpCubeFaceIndex:
+ extBuiltins = getExtBuiltins(spv::E_SPV_AMD_gcn_shader);
+ libCall = spv::CubeFaceIndexAMD;
+ break;
+
+ case glslang::EOpCubeFaceCoord:
+ extBuiltins = getExtBuiltins(spv::E_SPV_AMD_gcn_shader);
+ libCall = spv::CubeFaceCoordAMD;
+ break;
+#endif
+#ifdef NV_EXTENSIONS
+ case glslang::EOpSubgroupPartition:
+ unaryOp = spv::OpGroupNonUniformPartitionNV;
+ break;
+#endif
+ case glslang::EOpConstructReference:
+ unaryOp = spv::OpBitcast;
+ break;
+ default:
+ return 0;
+ }
+
+ spv::Id id;
+ if (libCall >= 0) {
+ std::vector<spv::Id> args;
+ args.push_back(operand);
+ id = builder.createBuiltinCall(typeId, extBuiltins >= 0 ? extBuiltins : stdBuiltins, libCall, args);
+ } else {
+ id = builder.createUnaryOp(unaryOp, typeId, operand);
+ }
+
+ builder.addDecoration(id, decorations.noContraction);
+ builder.addDecoration(id, decorations.nonUniform);
+ return builder.setPrecision(id, decorations.precision);
+}
+
+// Create a unary operation on a matrix
+spv::Id TGlslangToSpvTraverser::createUnaryMatrixOperation(spv::Op op, OpDecorations& decorations, spv::Id typeId,
+ spv::Id operand, glslang::TBasicType /* typeProxy */)
+{
+ // Handle unary operations vector by vector.
+ // The result type is the same type as the original type.
+ // The algorithm is to:
+ // - break the matrix into vectors
+ // - apply the operation to each vector
+ // - make a matrix out the vector results
+
+ // get the types sorted out
+ int numCols = builder.getNumColumns(operand);
+ int numRows = builder.getNumRows(operand);
+ spv::Id srcVecType = builder.makeVectorType(builder.getScalarTypeId(builder.getTypeId(operand)), numRows);
+ spv::Id destVecType = builder.makeVectorType(builder.getScalarTypeId(typeId), numRows);
+ std::vector<spv::Id> results;
+
+ // do each vector op
+ for (int c = 0; c < numCols; ++c) {
+ std::vector<unsigned int> indexes;
+ indexes.push_back(c);
+ spv::Id srcVec = builder.createCompositeExtract(operand, srcVecType, indexes);
+ spv::Id destVec = builder.createUnaryOp(op, destVecType, srcVec);
+ builder.addDecoration(destVec, decorations.noContraction);
+ builder.addDecoration(destVec, decorations.nonUniform);
+ results.push_back(builder.setPrecision(destVec, decorations.precision));
+ }
+
+ // put the pieces together
+ spv::Id result = builder.setPrecision(builder.createCompositeConstruct(typeId, results), decorations.precision);
+ builder.addDecoration(result, decorations.nonUniform);
+ return result;
+}
+
+// For converting integers where both the bitwidth and the signedness could
+// change, but only do the width change here. The caller is still responsible
+// for the signedness conversion.
+spv::Id TGlslangToSpvTraverser::createIntWidthConversion(glslang::TOperator op, spv::Id operand, int vectorSize)
+{
+ // Get the result type width, based on the type to convert to.
+ int width = 32;
+ switch(op) {
+ case glslang::EOpConvInt16ToUint8:
+ case glslang::EOpConvIntToUint8:
+ case glslang::EOpConvInt64ToUint8:
+ case glslang::EOpConvUint16ToInt8:
+ case glslang::EOpConvUintToInt8:
+ case glslang::EOpConvUint64ToInt8:
+ width = 8;
+ break;
+ case glslang::EOpConvInt8ToUint16:
+ case glslang::EOpConvIntToUint16:
+ case glslang::EOpConvInt64ToUint16:
+ case glslang::EOpConvUint8ToInt16:
+ case glslang::EOpConvUintToInt16:
+ case glslang::EOpConvUint64ToInt16:
+ width = 16;
+ break;
+ case glslang::EOpConvInt8ToUint:
+ case glslang::EOpConvInt16ToUint:
+ case glslang::EOpConvInt64ToUint:
+ case glslang::EOpConvUint8ToInt:
+ case glslang::EOpConvUint16ToInt:
+ case glslang::EOpConvUint64ToInt:
+ width = 32;
+ break;
+ case glslang::EOpConvInt8ToUint64:
+ case glslang::EOpConvInt16ToUint64:
+ case glslang::EOpConvIntToUint64:
+ case glslang::EOpConvUint8ToInt64:
+ case glslang::EOpConvUint16ToInt64:
+ case glslang::EOpConvUintToInt64:
+ width = 64;
+ break;
+
+ default:
+ assert(false && "Default missing");
+ break;
+ }
+
+ // Get the conversion operation and result type,
+ // based on the target width, but the source type.
+ spv::Id type = spv::NoType;
+ spv::Op convOp = spv::OpNop;
+ switch(op) {
+ case glslang::EOpConvInt8ToUint16:
+ case glslang::EOpConvInt8ToUint:
+ case glslang::EOpConvInt8ToUint64:
+ case glslang::EOpConvInt16ToUint8:
+ case glslang::EOpConvInt16ToUint:
+ case glslang::EOpConvInt16ToUint64:
+ case glslang::EOpConvIntToUint8:
+ case glslang::EOpConvIntToUint16:
+ case glslang::EOpConvIntToUint64:
+ case glslang::EOpConvInt64ToUint8:
+ case glslang::EOpConvInt64ToUint16:
+ case glslang::EOpConvInt64ToUint:
+ convOp = spv::OpSConvert;
+ type = builder.makeIntType(width);
+ break;
+ default:
+ convOp = spv::OpUConvert;
+ type = builder.makeUintType(width);
+ break;
+ }
+
+ if (vectorSize > 0)
+ type = builder.makeVectorType(type, vectorSize);
+
+ return builder.createUnaryOp(convOp, type, operand);
+}
+
+spv::Id TGlslangToSpvTraverser::createConversion(glslang::TOperator op, OpDecorations& decorations, spv::Id destType,
+ spv::Id operand, glslang::TBasicType typeProxy)
+{
+ spv::Op convOp = spv::OpNop;
+ spv::Id zero = 0;
+ spv::Id one = 0;
+
+ int vectorSize = builder.isVectorType(destType) ? builder.getNumTypeComponents(destType) : 0;
+
+ switch (op) {
+ case glslang::EOpConvInt8ToBool:
+ case glslang::EOpConvUint8ToBool:
+ zero = builder.makeUint8Constant(0);
+ zero = makeSmearedConstant(zero, vectorSize);
+ return builder.createBinOp(spv::OpINotEqual, destType, operand, zero);
+ case glslang::EOpConvInt16ToBool:
+ case glslang::EOpConvUint16ToBool:
+ zero = builder.makeUint16Constant(0);
+ zero = makeSmearedConstant(zero, vectorSize);
+ return builder.createBinOp(spv::OpINotEqual, destType, operand, zero);
+ case glslang::EOpConvIntToBool:
+ case glslang::EOpConvUintToBool:
+ zero = builder.makeUintConstant(0);
+ zero = makeSmearedConstant(zero, vectorSize);
+ return builder.createBinOp(spv::OpINotEqual, destType, operand, zero);
+ case glslang::EOpConvInt64ToBool:
+ case glslang::EOpConvUint64ToBool:
+ zero = builder.makeUint64Constant(0);
+ zero = makeSmearedConstant(zero, vectorSize);
+ return builder.createBinOp(spv::OpINotEqual, destType, operand, zero);
+
+ case glslang::EOpConvFloatToBool:
+ zero = builder.makeFloatConstant(0.0F);
+ zero = makeSmearedConstant(zero, vectorSize);
+ return builder.createBinOp(spv::OpFOrdNotEqual, destType, operand, zero);
+
+ case glslang::EOpConvDoubleToBool:
+ zero = builder.makeDoubleConstant(0.0);
+ zero = makeSmearedConstant(zero, vectorSize);
+ return builder.createBinOp(spv::OpFOrdNotEqual, destType, operand, zero);
+
+ case glslang::EOpConvFloat16ToBool:
+ zero = builder.makeFloat16Constant(0.0F);
+ zero = makeSmearedConstant(zero, vectorSize);
+ return builder.createBinOp(spv::OpFOrdNotEqual, destType, operand, zero);
+
+ case glslang::EOpConvBoolToFloat:
+ convOp = spv::OpSelect;
+ zero = builder.makeFloatConstant(0.0F);
+ one = builder.makeFloatConstant(1.0F);
+ break;
+
+ case glslang::EOpConvBoolToDouble:
+ convOp = spv::OpSelect;
+ zero = builder.makeDoubleConstant(0.0);
+ one = builder.makeDoubleConstant(1.0);
+ break;
+
+ case glslang::EOpConvBoolToFloat16:
+ convOp = spv::OpSelect;
+ zero = builder.makeFloat16Constant(0.0F);
+ one = builder.makeFloat16Constant(1.0F);
+ break;
+
+ case glslang::EOpConvBoolToInt8:
+ zero = builder.makeInt8Constant(0);
+ one = builder.makeInt8Constant(1);
+ convOp = spv::OpSelect;
+ break;
+
+ case glslang::EOpConvBoolToUint8:
+ zero = builder.makeUint8Constant(0);
+ one = builder.makeUint8Constant(1);
+ convOp = spv::OpSelect;
+ break;
+
+ case glslang::EOpConvBoolToInt16:
+ zero = builder.makeInt16Constant(0);
+ one = builder.makeInt16Constant(1);
+ convOp = spv::OpSelect;
+ break;
+
+ case glslang::EOpConvBoolToUint16:
+ zero = builder.makeUint16Constant(0);
+ one = builder.makeUint16Constant(1);
+ convOp = spv::OpSelect;
+ break;
+
+ case glslang::EOpConvBoolToInt:
+ case glslang::EOpConvBoolToInt64:
+ if (op == glslang::EOpConvBoolToInt64)
+ zero = builder.makeInt64Constant(0);
+ else
+ zero = builder.makeIntConstant(0);
+
+ if (op == glslang::EOpConvBoolToInt64)
+ one = builder.makeInt64Constant(1);
+ else
+ one = builder.makeIntConstant(1);
+
+ convOp = spv::OpSelect;
+ break;
+
+ case glslang::EOpConvBoolToUint:
+ case glslang::EOpConvBoolToUint64:
+ if (op == glslang::EOpConvBoolToUint64)
+ zero = builder.makeUint64Constant(0);
+ else
+ zero = builder.makeUintConstant(0);
+
+ if (op == glslang::EOpConvBoolToUint64)
+ one = builder.makeUint64Constant(1);
+ else
+ one = builder.makeUintConstant(1);
+
+ convOp = spv::OpSelect;
+ break;
+
+ case glslang::EOpConvInt8ToFloat16:
+ case glslang::EOpConvInt8ToFloat:
+ case glslang::EOpConvInt8ToDouble:
+ case glslang::EOpConvInt16ToFloat16:
+ case glslang::EOpConvInt16ToFloat:
+ case glslang::EOpConvInt16ToDouble:
+ case glslang::EOpConvIntToFloat16:
+ case glslang::EOpConvIntToFloat:
+ case glslang::EOpConvIntToDouble:
+ case glslang::EOpConvInt64ToFloat:
+ case glslang::EOpConvInt64ToDouble:
+ case glslang::EOpConvInt64ToFloat16:
+ convOp = spv::OpConvertSToF;
+ break;
+
+ case glslang::EOpConvUint8ToFloat16:
+ case glslang::EOpConvUint8ToFloat:
+ case glslang::EOpConvUint8ToDouble:
+ case glslang::EOpConvUint16ToFloat16:
+ case glslang::EOpConvUint16ToFloat:
+ case glslang::EOpConvUint16ToDouble:
+ case glslang::EOpConvUintToFloat16:
+ case glslang::EOpConvUintToFloat:
+ case glslang::EOpConvUintToDouble:
+ case glslang::EOpConvUint64ToFloat:
+ case glslang::EOpConvUint64ToDouble:
+ case glslang::EOpConvUint64ToFloat16:
+ convOp = spv::OpConvertUToF;
+ break;
+
+ case glslang::EOpConvDoubleToFloat:
+ case glslang::EOpConvFloatToDouble:
+ case glslang::EOpConvDoubleToFloat16:
+ case glslang::EOpConvFloat16ToDouble:
+ case glslang::EOpConvFloatToFloat16:
+ case glslang::EOpConvFloat16ToFloat:
+ convOp = spv::OpFConvert;
+ if (builder.isMatrixType(destType))
+ return createUnaryMatrixOperation(convOp, decorations, destType, operand, typeProxy);
+ break;
+
+ case glslang::EOpConvFloat16ToInt8:
+ case glslang::EOpConvFloatToInt8:
+ case glslang::EOpConvDoubleToInt8:
+ case glslang::EOpConvFloat16ToInt16:
+ case glslang::EOpConvFloatToInt16:
+ case glslang::EOpConvDoubleToInt16:
+ case glslang::EOpConvFloat16ToInt:
+ case glslang::EOpConvFloatToInt:
+ case glslang::EOpConvDoubleToInt:
+ case glslang::EOpConvFloat16ToInt64:
+ case glslang::EOpConvFloatToInt64:
+ case glslang::EOpConvDoubleToInt64:
+ convOp = spv::OpConvertFToS;
+ break;
+
+ case glslang::EOpConvUint8ToInt8:
+ case glslang::EOpConvInt8ToUint8:
+ case glslang::EOpConvUint16ToInt16:
+ case glslang::EOpConvInt16ToUint16:
+ case glslang::EOpConvUintToInt:
+ case glslang::EOpConvIntToUint:
+ case glslang::EOpConvUint64ToInt64:
+ case glslang::EOpConvInt64ToUint64:
+ if (builder.isInSpecConstCodeGenMode()) {
+ // Build zero scalar or vector for OpIAdd.
+ if(op == glslang::EOpConvUint8ToInt8 || op == glslang::EOpConvInt8ToUint8) {
+ zero = builder.makeUint8Constant(0);
+ } else if (op == glslang::EOpConvUint16ToInt16 || op == glslang::EOpConvInt16ToUint16) {
+ zero = builder.makeUint16Constant(0);
+ } else if (op == glslang::EOpConvUint64ToInt64 || op == glslang::EOpConvInt64ToUint64) {
+ zero = builder.makeUint64Constant(0);
+ } else {
+ zero = builder.makeUintConstant(0);
+ }
+ zero = makeSmearedConstant(zero, vectorSize);
+ // Use OpIAdd, instead of OpBitcast to do the conversion when
+ // generating for OpSpecConstantOp instruction.
+ return builder.createBinOp(spv::OpIAdd, destType, operand, zero);
+ }
+ // For normal run-time conversion instruction, use OpBitcast.
+ convOp = spv::OpBitcast;
+ break;
+
+ case glslang::EOpConvFloat16ToUint8:
+ case glslang::EOpConvFloatToUint8:
+ case glslang::EOpConvDoubleToUint8:
+ case glslang::EOpConvFloat16ToUint16:
+ case glslang::EOpConvFloatToUint16:
+ case glslang::EOpConvDoubleToUint16:
+ case glslang::EOpConvFloat16ToUint:
+ case glslang::EOpConvFloatToUint:
+ case glslang::EOpConvDoubleToUint:
+ case glslang::EOpConvFloatToUint64:
+ case glslang::EOpConvDoubleToUint64:
+ case glslang::EOpConvFloat16ToUint64:
+ convOp = spv::OpConvertFToU;
+ break;
+
+ case glslang::EOpConvInt8ToInt16:
+ case glslang::EOpConvInt8ToInt:
+ case glslang::EOpConvInt8ToInt64:
+ case glslang::EOpConvInt16ToInt8:
+ case glslang::EOpConvInt16ToInt:
+ case glslang::EOpConvInt16ToInt64:
+ case glslang::EOpConvIntToInt8:
+ case glslang::EOpConvIntToInt16:
+ case glslang::EOpConvIntToInt64:
+ case glslang::EOpConvInt64ToInt8:
+ case glslang::EOpConvInt64ToInt16:
+ case glslang::EOpConvInt64ToInt:
+ convOp = spv::OpSConvert;
+ break;
+
+ case glslang::EOpConvUint8ToUint16:
+ case glslang::EOpConvUint8ToUint:
+ case glslang::EOpConvUint8ToUint64:
+ case glslang::EOpConvUint16ToUint8:
+ case glslang::EOpConvUint16ToUint:
+ case glslang::EOpConvUint16ToUint64:
+ case glslang::EOpConvUintToUint8:
+ case glslang::EOpConvUintToUint16:
+ case glslang::EOpConvUintToUint64:
+ case glslang::EOpConvUint64ToUint8:
+ case glslang::EOpConvUint64ToUint16:
+ case glslang::EOpConvUint64ToUint:
+ convOp = spv::OpUConvert;
+ break;
+
+ case glslang::EOpConvInt8ToUint16:
+ case glslang::EOpConvInt8ToUint:
+ case glslang::EOpConvInt8ToUint64:
+ case glslang::EOpConvInt16ToUint8:
+ case glslang::EOpConvInt16ToUint:
+ case glslang::EOpConvInt16ToUint64:
+ case glslang::EOpConvIntToUint8:
+ case glslang::EOpConvIntToUint16:
+ case glslang::EOpConvIntToUint64:
+ case glslang::EOpConvInt64ToUint8:
+ case glslang::EOpConvInt64ToUint16:
+ case glslang::EOpConvInt64ToUint:
+ case glslang::EOpConvUint8ToInt16:
+ case glslang::EOpConvUint8ToInt:
+ case glslang::EOpConvUint8ToInt64:
+ case glslang::EOpConvUint16ToInt8:
+ case glslang::EOpConvUint16ToInt:
+ case glslang::EOpConvUint16ToInt64:
+ case glslang::EOpConvUintToInt8:
+ case glslang::EOpConvUintToInt16:
+ case glslang::EOpConvUintToInt64:
+ case glslang::EOpConvUint64ToInt8:
+ case glslang::EOpConvUint64ToInt16:
+ case glslang::EOpConvUint64ToInt:
+ // OpSConvert/OpUConvert + OpBitCast
+ operand = createIntWidthConversion(op, operand, vectorSize);
+
+ if (builder.isInSpecConstCodeGenMode()) {
+ // Build zero scalar or vector for OpIAdd.
+ switch(op) {
+ case glslang::EOpConvInt16ToUint8:
+ case glslang::EOpConvIntToUint8:
+ case glslang::EOpConvInt64ToUint8:
+ case glslang::EOpConvUint16ToInt8:
+ case glslang::EOpConvUintToInt8:
+ case glslang::EOpConvUint64ToInt8:
+ zero = builder.makeUint8Constant(0);
+ break;
+ case glslang::EOpConvInt8ToUint16:
+ case glslang::EOpConvIntToUint16:
+ case glslang::EOpConvInt64ToUint16:
+ case glslang::EOpConvUint8ToInt16:
+ case glslang::EOpConvUintToInt16:
+ case glslang::EOpConvUint64ToInt16:
+ zero = builder.makeUint16Constant(0);
+ break;
+ case glslang::EOpConvInt8ToUint:
+ case glslang::EOpConvInt16ToUint:
+ case glslang::EOpConvInt64ToUint:
+ case glslang::EOpConvUint8ToInt:
+ case glslang::EOpConvUint16ToInt:
+ case glslang::EOpConvUint64ToInt:
+ zero = builder.makeUintConstant(0);
+ break;
+ case glslang::EOpConvInt8ToUint64:
+ case glslang::EOpConvInt16ToUint64:
+ case glslang::EOpConvIntToUint64:
+ case glslang::EOpConvUint8ToInt64:
+ case glslang::EOpConvUint16ToInt64:
+ case glslang::EOpConvUintToInt64:
+ zero = builder.makeUint64Constant(0);
+ break;
+ default:
+ assert(false && "Default missing");
+ break;
+ }
+ zero = makeSmearedConstant(zero, vectorSize);
+ // Use OpIAdd, instead of OpBitcast to do the conversion when
+ // generating for OpSpecConstantOp instruction.
+ return builder.createBinOp(spv::OpIAdd, destType, operand, zero);
+ }
+ // For normal run-time conversion instruction, use OpBitcast.
+ convOp = spv::OpBitcast;
+ break;
+ case glslang::EOpConvUint64ToPtr:
+ convOp = spv::OpConvertUToPtr;
+ break;
+ case glslang::EOpConvPtrToUint64:
+ convOp = spv::OpConvertPtrToU;
+ break;
+ default:
+ break;
+ }
+
+ spv::Id result = 0;
+ if (convOp == spv::OpNop)
+ return result;
+
+ if (convOp == spv::OpSelect) {
+ zero = makeSmearedConstant(zero, vectorSize);
+ one = makeSmearedConstant(one, vectorSize);
+ result = builder.createTriOp(convOp, destType, operand, one, zero);
+ } else
+ result = builder.createUnaryOp(convOp, destType, operand);
+
+ result = builder.setPrecision(result, decorations.precision);
+ builder.addDecoration(result, decorations.nonUniform);
+ return result;
+}
+
+spv::Id TGlslangToSpvTraverser::makeSmearedConstant(spv::Id constant, int vectorSize)
+{
+ if (vectorSize == 0)
+ return constant;
+
+ spv::Id vectorTypeId = builder.makeVectorType(builder.getTypeId(constant), vectorSize);
+ std::vector<spv::Id> components;
+ for (int c = 0; c < vectorSize; ++c)
+ components.push_back(constant);
+ return builder.makeCompositeConstant(vectorTypeId, components);
+}
+
+// For glslang ops that map to SPV atomic opCodes
+spv::Id TGlslangToSpvTraverser::createAtomicOperation(glslang::TOperator op, spv::Decoration /*precision*/, spv::Id typeId, std::vector<spv::Id>& operands, glslang::TBasicType typeProxy)
+{
+ spv::Op opCode = spv::OpNop;
+
+ switch (op) {
+ case glslang::EOpAtomicAdd:
+ case glslang::EOpImageAtomicAdd:
+ case glslang::EOpAtomicCounterAdd:
+ opCode = spv::OpAtomicIAdd;
+ break;
+ case glslang::EOpAtomicCounterSubtract:
+ opCode = spv::OpAtomicISub;
+ break;
+ case glslang::EOpAtomicMin:
+ case glslang::EOpImageAtomicMin:
+ case glslang::EOpAtomicCounterMin:
+ opCode = (typeProxy == glslang::EbtUint || typeProxy == glslang::EbtUint64) ? spv::OpAtomicUMin : spv::OpAtomicSMin;
+ break;
+ case glslang::EOpAtomicMax:
+ case glslang::EOpImageAtomicMax:
+ case glslang::EOpAtomicCounterMax:
+ opCode = (typeProxy == glslang::EbtUint || typeProxy == glslang::EbtUint64) ? spv::OpAtomicUMax : spv::OpAtomicSMax;
+ break;
+ case glslang::EOpAtomicAnd:
+ case glslang::EOpImageAtomicAnd:
+ case glslang::EOpAtomicCounterAnd:
+ opCode = spv::OpAtomicAnd;
+ break;
+ case glslang::EOpAtomicOr:
+ case glslang::EOpImageAtomicOr:
+ case glslang::EOpAtomicCounterOr:
+ opCode = spv::OpAtomicOr;
+ break;
+ case glslang::EOpAtomicXor:
+ case glslang::EOpImageAtomicXor:
+ case glslang::EOpAtomicCounterXor:
+ opCode = spv::OpAtomicXor;
+ break;
+ case glslang::EOpAtomicExchange:
+ case glslang::EOpImageAtomicExchange:
+ case glslang::EOpAtomicCounterExchange:
+ opCode = spv::OpAtomicExchange;
+ break;
+ case glslang::EOpAtomicCompSwap:
+ case glslang::EOpImageAtomicCompSwap:
+ case glslang::EOpAtomicCounterCompSwap:
+ opCode = spv::OpAtomicCompareExchange;
+ break;
+ case glslang::EOpAtomicCounterIncrement:
+ opCode = spv::OpAtomicIIncrement;
+ break;
+ case glslang::EOpAtomicCounterDecrement:
+ opCode = spv::OpAtomicIDecrement;
+ break;
+ case glslang::EOpAtomicCounter:
+ case glslang::EOpImageAtomicLoad:
+ case glslang::EOpAtomicLoad:
+ opCode = spv::OpAtomicLoad;
+ break;
+ case glslang::EOpAtomicStore:
+ case glslang::EOpImageAtomicStore:
+ opCode = spv::OpAtomicStore;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ if (typeProxy == glslang::EbtInt64 || typeProxy == glslang::EbtUint64)
+ builder.addCapability(spv::CapabilityInt64Atomics);
+
+ // Sort out the operands
+ // - mapping from glslang -> SPV
+ // - there are extra SPV operands that are optional in glslang
+ // - compare-exchange swaps the value and comparator
+ // - compare-exchange has an extra memory semantics
+ // - EOpAtomicCounterDecrement needs a post decrement
+ spv::Id pointerId = 0, compareId = 0, valueId = 0;
+ // scope defaults to Device in the old model, QueueFamilyKHR in the new model
+ spv::Id scopeId;
+ if (glslangIntermediate->usingVulkanMemoryModel()) {
+ scopeId = builder.makeUintConstant(spv::ScopeQueueFamilyKHR);
+ } else {
+ scopeId = builder.makeUintConstant(spv::ScopeDevice);
+ }
+ // semantics default to relaxed
+ spv::Id semanticsId = builder.makeUintConstant(spv::MemorySemanticsMaskNone);
+ spv::Id semanticsId2 = semanticsId;
+
+ pointerId = operands[0];
+ if (opCode == spv::OpAtomicIIncrement || opCode == spv::OpAtomicIDecrement) {
+ // no additional operands
+ } else if (opCode == spv::OpAtomicCompareExchange) {
+ compareId = operands[1];
+ valueId = operands[2];
+ if (operands.size() > 3) {
+ scopeId = operands[3];
+ semanticsId = builder.makeUintConstant(builder.getConstantScalar(operands[4]) | builder.getConstantScalar(operands[5]));
+ semanticsId2 = builder.makeUintConstant(builder.getConstantScalar(operands[6]) | builder.getConstantScalar(operands[7]));
+ }
+ } else if (opCode == spv::OpAtomicLoad) {
+ if (operands.size() > 1) {
+ scopeId = operands[1];
+ semanticsId = builder.makeUintConstant(builder.getConstantScalar(operands[2]) | builder.getConstantScalar(operands[3]));
+ }
+ } else {
+ // atomic store or RMW
+ valueId = operands[1];
+ if (operands.size() > 2) {
+ scopeId = operands[2];
+ semanticsId = builder.makeUintConstant(builder.getConstantScalar(operands[3]) | builder.getConstantScalar(operands[4]));
+ }
+ }
+
+ // Check for capabilities
+ unsigned semanticsImmediate = builder.getConstantScalar(semanticsId) | builder.getConstantScalar(semanticsId2);
+ if (semanticsImmediate & (spv::MemorySemanticsMakeAvailableKHRMask | spv::MemorySemanticsMakeVisibleKHRMask | spv::MemorySemanticsOutputMemoryKHRMask)) {
+ builder.addCapability(spv::CapabilityVulkanMemoryModelKHR);
+ }
+
+ if (glslangIntermediate->usingVulkanMemoryModel() && builder.getConstantScalar(scopeId) == spv::ScopeDevice) {
+ builder.addCapability(spv::CapabilityVulkanMemoryModelDeviceScopeKHR);
+ }
+
+ std::vector<spv::Id> spvAtomicOperands; // hold the spv operands
+ spvAtomicOperands.push_back(pointerId);
+ spvAtomicOperands.push_back(scopeId);
+ spvAtomicOperands.push_back(semanticsId);
+ if (opCode == spv::OpAtomicCompareExchange) {
+ spvAtomicOperands.push_back(semanticsId2);
+ spvAtomicOperands.push_back(valueId);
+ spvAtomicOperands.push_back(compareId);
+ } else if (opCode != spv::OpAtomicLoad && opCode != spv::OpAtomicIIncrement && opCode != spv::OpAtomicIDecrement) {
+ spvAtomicOperands.push_back(valueId);
+ }
+
+ if (opCode == spv::OpAtomicStore) {
+ builder.createNoResultOp(opCode, spvAtomicOperands);
+ return 0;
+ } else {
+ spv::Id resultId = builder.createOp(opCode, typeId, spvAtomicOperands);
+
+ // GLSL and HLSL atomic-counter decrement return post-decrement value,
+ // while SPIR-V returns pre-decrement value. Translate between these semantics.
+ if (op == glslang::EOpAtomicCounterDecrement)
+ resultId = builder.createBinOp(spv::OpISub, typeId, resultId, builder.makeIntConstant(1));
+
+ return resultId;
+ }
+}
+
+// Create group invocation operations.
+spv::Id TGlslangToSpvTraverser::createInvocationsOperation(glslang::TOperator op, spv::Id typeId, std::vector<spv::Id>& operands, glslang::TBasicType typeProxy)
+{
+#ifdef AMD_EXTENSIONS
+ bool isUnsigned = isTypeUnsignedInt(typeProxy);
+ bool isFloat = isTypeFloat(typeProxy);
+#endif
+
+ spv::Op opCode = spv::OpNop;
+ std::vector<spv::IdImmediate> spvGroupOperands;
+ spv::GroupOperation groupOperation = spv::GroupOperationMax;
+
+ if (op == glslang::EOpBallot || op == glslang::EOpReadFirstInvocation ||
+ op == glslang::EOpReadInvocation) {
+ builder.addExtension(spv::E_SPV_KHR_shader_ballot);
+ builder.addCapability(spv::CapabilitySubgroupBallotKHR);
+ } else if (op == glslang::EOpAnyInvocation ||
+ op == glslang::EOpAllInvocations ||
+ op == glslang::EOpAllInvocationsEqual) {
+ builder.addExtension(spv::E_SPV_KHR_subgroup_vote);
+ builder.addCapability(spv::CapabilitySubgroupVoteKHR);
+ } else {
+ builder.addCapability(spv::CapabilityGroups);
+#ifdef AMD_EXTENSIONS
+ if (op == glslang::EOpMinInvocationsNonUniform ||
+ op == glslang::EOpMaxInvocationsNonUniform ||
+ op == glslang::EOpAddInvocationsNonUniform ||
+ op == glslang::EOpMinInvocationsInclusiveScanNonUniform ||
+ op == glslang::EOpMaxInvocationsInclusiveScanNonUniform ||
+ op == glslang::EOpAddInvocationsInclusiveScanNonUniform ||
+ op == glslang::EOpMinInvocationsExclusiveScanNonUniform ||
+ op == glslang::EOpMaxInvocationsExclusiveScanNonUniform ||
+ op == glslang::EOpAddInvocationsExclusiveScanNonUniform)
+ builder.addExtension(spv::E_SPV_AMD_shader_ballot);
+#endif
+
+#ifdef AMD_EXTENSIONS
+ switch (op) {
+ case glslang::EOpMinInvocations:
+ case glslang::EOpMaxInvocations:
+ case glslang::EOpAddInvocations:
+ case glslang::EOpMinInvocationsNonUniform:
+ case glslang::EOpMaxInvocationsNonUniform:
+ case glslang::EOpAddInvocationsNonUniform:
+ groupOperation = spv::GroupOperationReduce;
+ break;
+ case glslang::EOpMinInvocationsInclusiveScan:
+ case glslang::EOpMaxInvocationsInclusiveScan:
+ case glslang::EOpAddInvocationsInclusiveScan:
+ case glslang::EOpMinInvocationsInclusiveScanNonUniform:
+ case glslang::EOpMaxInvocationsInclusiveScanNonUniform:
+ case glslang::EOpAddInvocationsInclusiveScanNonUniform:
+ groupOperation = spv::GroupOperationInclusiveScan;
+ break;
+ case glslang::EOpMinInvocationsExclusiveScan:
+ case glslang::EOpMaxInvocationsExclusiveScan:
+ case glslang::EOpAddInvocationsExclusiveScan:
+ case glslang::EOpMinInvocationsExclusiveScanNonUniform:
+ case glslang::EOpMaxInvocationsExclusiveScanNonUniform:
+ case glslang::EOpAddInvocationsExclusiveScanNonUniform:
+ groupOperation = spv::GroupOperationExclusiveScan;
+ break;
+ default:
+ break;
+ }
+ spv::IdImmediate scope = { true, builder.makeUintConstant(spv::ScopeSubgroup) };
+ spvGroupOperands.push_back(scope);
+ if (groupOperation != spv::GroupOperationMax) {
+ spv::IdImmediate groupOp = { false, (unsigned)groupOperation };
+ spvGroupOperands.push_back(groupOp);
+ }
+#endif
+ }
+
+ for (auto opIt = operands.begin(); opIt != operands.end(); ++opIt) {
+ spv::IdImmediate op = { true, *opIt };
+ spvGroupOperands.push_back(op);
+ }
+
+ switch (op) {
+ case glslang::EOpAnyInvocation:
+ opCode = spv::OpSubgroupAnyKHR;
+ break;
+ case glslang::EOpAllInvocations:
+ opCode = spv::OpSubgroupAllKHR;
+ break;
+ case glslang::EOpAllInvocationsEqual:
+ opCode = spv::OpSubgroupAllEqualKHR;
+ break;
+ case glslang::EOpReadInvocation:
+ opCode = spv::OpSubgroupReadInvocationKHR;
+ if (builder.isVectorType(typeId))
+ return CreateInvocationsVectorOperation(opCode, groupOperation, typeId, operands);
+ break;
+ case glslang::EOpReadFirstInvocation:
+ opCode = spv::OpSubgroupFirstInvocationKHR;
+ break;
+ case glslang::EOpBallot:
+ {
+ // NOTE: According to the spec, the result type of "OpSubgroupBallotKHR" must be a 4 component vector of 32
+ // bit integer types. The GLSL built-in function "ballotARB()" assumes the maximum number of invocations in
+ // a subgroup is 64. Thus, we have to convert uvec4.xy to uint64_t as follow:
+ //
+ // result = Bitcast(SubgroupBallotKHR(Predicate).xy)
+ //
+ spv::Id uintType = builder.makeUintType(32);
+ spv::Id uvec4Type = builder.makeVectorType(uintType, 4);
+ spv::Id result = builder.createOp(spv::OpSubgroupBallotKHR, uvec4Type, spvGroupOperands);
+
+ std::vector<spv::Id> components;
+ components.push_back(builder.createCompositeExtract(result, uintType, 0));
+ components.push_back(builder.createCompositeExtract(result, uintType, 1));
+
+ spv::Id uvec2Type = builder.makeVectorType(uintType, 2);
+ return builder.createUnaryOp(spv::OpBitcast, typeId,
+ builder.createCompositeConstruct(uvec2Type, components));
+ }
+
+#ifdef AMD_EXTENSIONS
+ case glslang::EOpMinInvocations:
+ case glslang::EOpMaxInvocations:
+ case glslang::EOpAddInvocations:
+ case glslang::EOpMinInvocationsInclusiveScan:
+ case glslang::EOpMaxInvocationsInclusiveScan:
+ case glslang::EOpAddInvocationsInclusiveScan:
+ case glslang::EOpMinInvocationsExclusiveScan:
+ case glslang::EOpMaxInvocationsExclusiveScan:
+ case glslang::EOpAddInvocationsExclusiveScan:
+ if (op == glslang::EOpMinInvocations ||
+ op == glslang::EOpMinInvocationsInclusiveScan ||
+ op == glslang::EOpMinInvocationsExclusiveScan) {
+ if (isFloat)
+ opCode = spv::OpGroupFMin;
+ else {
+ if (isUnsigned)
+ opCode = spv::OpGroupUMin;
+ else
+ opCode = spv::OpGroupSMin;
+ }
+ } else if (op == glslang::EOpMaxInvocations ||
+ op == glslang::EOpMaxInvocationsInclusiveScan ||
+ op == glslang::EOpMaxInvocationsExclusiveScan) {
+ if (isFloat)
+ opCode = spv::OpGroupFMax;
+ else {
+ if (isUnsigned)
+ opCode = spv::OpGroupUMax;
+ else
+ opCode = spv::OpGroupSMax;
+ }
+ } else {
+ if (isFloat)
+ opCode = spv::OpGroupFAdd;
+ else
+ opCode = spv::OpGroupIAdd;
+ }
+
+ if (builder.isVectorType(typeId))
+ return CreateInvocationsVectorOperation(opCode, groupOperation, typeId, operands);
+
+ break;
+ case glslang::EOpMinInvocationsNonUniform:
+ case glslang::EOpMaxInvocationsNonUniform:
+ case glslang::EOpAddInvocationsNonUniform:
+ case glslang::EOpMinInvocationsInclusiveScanNonUniform:
+ case glslang::EOpMaxInvocationsInclusiveScanNonUniform:
+ case glslang::EOpAddInvocationsInclusiveScanNonUniform:
+ case glslang::EOpMinInvocationsExclusiveScanNonUniform:
+ case glslang::EOpMaxInvocationsExclusiveScanNonUniform:
+ case glslang::EOpAddInvocationsExclusiveScanNonUniform:
+ if (op == glslang::EOpMinInvocationsNonUniform ||
+ op == glslang::EOpMinInvocationsInclusiveScanNonUniform ||
+ op == glslang::EOpMinInvocationsExclusiveScanNonUniform) {
+ if (isFloat)
+ opCode = spv::OpGroupFMinNonUniformAMD;
+ else {
+ if (isUnsigned)
+ opCode = spv::OpGroupUMinNonUniformAMD;
+ else
+ opCode = spv::OpGroupSMinNonUniformAMD;
+ }
+ }
+ else if (op == glslang::EOpMaxInvocationsNonUniform ||
+ op == glslang::EOpMaxInvocationsInclusiveScanNonUniform ||
+ op == glslang::EOpMaxInvocationsExclusiveScanNonUniform) {
+ if (isFloat)
+ opCode = spv::OpGroupFMaxNonUniformAMD;
+ else {
+ if (isUnsigned)
+ opCode = spv::OpGroupUMaxNonUniformAMD;
+ else
+ opCode = spv::OpGroupSMaxNonUniformAMD;
+ }
+ }
+ else {
+ if (isFloat)
+ opCode = spv::OpGroupFAddNonUniformAMD;
+ else
+ opCode = spv::OpGroupIAddNonUniformAMD;
+ }
+
+ if (builder.isVectorType(typeId))
+ return CreateInvocationsVectorOperation(opCode, groupOperation, typeId, operands);
+
+ break;
+#endif
+ default:
+ logger->missingFunctionality("invocation operation");
+ return spv::NoResult;
+ }
+
+ assert(opCode != spv::OpNop);
+ return builder.createOp(opCode, typeId, spvGroupOperands);
+}
+
+// Create group invocation operations on a vector
+spv::Id TGlslangToSpvTraverser::CreateInvocationsVectorOperation(spv::Op op, spv::GroupOperation groupOperation,
+ spv::Id typeId, std::vector<spv::Id>& operands)
+{
+#ifdef AMD_EXTENSIONS
+ assert(op == spv::OpGroupFMin || op == spv::OpGroupUMin || op == spv::OpGroupSMin ||
+ op == spv::OpGroupFMax || op == spv::OpGroupUMax || op == spv::OpGroupSMax ||
+ op == spv::OpGroupFAdd || op == spv::OpGroupIAdd || op == spv::OpGroupBroadcast ||
+ op == spv::OpSubgroupReadInvocationKHR ||
+ op == spv::OpGroupFMinNonUniformAMD || op == spv::OpGroupUMinNonUniformAMD || op == spv::OpGroupSMinNonUniformAMD ||
+ op == spv::OpGroupFMaxNonUniformAMD || op == spv::OpGroupUMaxNonUniformAMD || op == spv::OpGroupSMaxNonUniformAMD ||
+ op == spv::OpGroupFAddNonUniformAMD || op == spv::OpGroupIAddNonUniformAMD);
+#else
+ assert(op == spv::OpGroupFMin || op == spv::OpGroupUMin || op == spv::OpGroupSMin ||
+ op == spv::OpGroupFMax || op == spv::OpGroupUMax || op == spv::OpGroupSMax ||
+ op == spv::OpGroupFAdd || op == spv::OpGroupIAdd || op == spv::OpGroupBroadcast ||
+ op == spv::OpSubgroupReadInvocationKHR);
+#endif
+
+ // Handle group invocation operations scalar by scalar.
+ // The result type is the same type as the original type.
+ // The algorithm is to:
+ // - break the vector into scalars
+ // - apply the operation to each scalar
+ // - make a vector out the scalar results
+
+ // get the types sorted out
+ int numComponents = builder.getNumComponents(operands[0]);
+ spv::Id scalarType = builder.getScalarTypeId(builder.getTypeId(operands[0]));
+ std::vector<spv::Id> results;
+
+ // do each scalar op
+ for (int comp = 0; comp < numComponents; ++comp) {
+ std::vector<unsigned int> indexes;
+ indexes.push_back(comp);
+ spv::IdImmediate scalar = { true, builder.createCompositeExtract(operands[0], scalarType, indexes) };
+ std::vector<spv::IdImmediate> spvGroupOperands;
+ if (op == spv::OpSubgroupReadInvocationKHR) {
+ spvGroupOperands.push_back(scalar);
+ spv::IdImmediate operand = { true, operands[1] };
+ spvGroupOperands.push_back(operand);
+ } else if (op == spv::OpGroupBroadcast) {
+ spv::IdImmediate scope = { true, builder.makeUintConstant(spv::ScopeSubgroup) };
+ spvGroupOperands.push_back(scope);
+ spvGroupOperands.push_back(scalar);
+ spv::IdImmediate operand = { true, operands[1] };
+ spvGroupOperands.push_back(operand);
+ } else {
+ spv::IdImmediate scope = { true, builder.makeUintConstant(spv::ScopeSubgroup) };
+ spvGroupOperands.push_back(scope);
+ spv::IdImmediate groupOp = { false, (unsigned)groupOperation };
+ spvGroupOperands.push_back(groupOp);
+ spvGroupOperands.push_back(scalar);
+ }
+
+ results.push_back(builder.createOp(op, scalarType, spvGroupOperands));
+ }
+
+ // put the pieces together
+ return builder.createCompositeConstruct(typeId, results);
+}
+
+// Create subgroup invocation operations.
+spv::Id TGlslangToSpvTraverser::createSubgroupOperation(glslang::TOperator op, spv::Id typeId,
+ std::vector<spv::Id>& operands, glslang::TBasicType typeProxy)
+{
+ // Add the required capabilities.
+ switch (op) {
+ case glslang::EOpSubgroupElect:
+ builder.addCapability(spv::CapabilityGroupNonUniform);
+ break;
+ case glslang::EOpSubgroupAll:
+ case glslang::EOpSubgroupAny:
+ case glslang::EOpSubgroupAllEqual:
+ builder.addCapability(spv::CapabilityGroupNonUniform);
+ builder.addCapability(spv::CapabilityGroupNonUniformVote);
+ break;
+ case glslang::EOpSubgroupBroadcast:
+ case glslang::EOpSubgroupBroadcastFirst:
+ case glslang::EOpSubgroupBallot:
+ case glslang::EOpSubgroupInverseBallot:
+ case glslang::EOpSubgroupBallotBitExtract:
+ case glslang::EOpSubgroupBallotBitCount:
+ case glslang::EOpSubgroupBallotInclusiveBitCount:
+ case glslang::EOpSubgroupBallotExclusiveBitCount:
+ case glslang::EOpSubgroupBallotFindLSB:
+ case glslang::EOpSubgroupBallotFindMSB:
+ builder.addCapability(spv::CapabilityGroupNonUniform);
+ builder.addCapability(spv::CapabilityGroupNonUniformBallot);
+ break;
+ case glslang::EOpSubgroupShuffle:
+ case glslang::EOpSubgroupShuffleXor:
+ builder.addCapability(spv::CapabilityGroupNonUniform);
+ builder.addCapability(spv::CapabilityGroupNonUniformShuffle);
+ break;
+ case glslang::EOpSubgroupShuffleUp:
+ case glslang::EOpSubgroupShuffleDown:
+ builder.addCapability(spv::CapabilityGroupNonUniform);
+ builder.addCapability(spv::CapabilityGroupNonUniformShuffleRelative);
+ break;
+ case glslang::EOpSubgroupAdd:
+ case glslang::EOpSubgroupMul:
+ case glslang::EOpSubgroupMin:
+ case glslang::EOpSubgroupMax:
+ case glslang::EOpSubgroupAnd:
+ case glslang::EOpSubgroupOr:
+ case glslang::EOpSubgroupXor:
+ case glslang::EOpSubgroupInclusiveAdd:
+ case glslang::EOpSubgroupInclusiveMul:
+ case glslang::EOpSubgroupInclusiveMin:
+ case glslang::EOpSubgroupInclusiveMax:
+ case glslang::EOpSubgroupInclusiveAnd:
+ case glslang::EOpSubgroupInclusiveOr:
+ case glslang::EOpSubgroupInclusiveXor:
+ case glslang::EOpSubgroupExclusiveAdd:
+ case glslang::EOpSubgroupExclusiveMul:
+ case glslang::EOpSubgroupExclusiveMin:
+ case glslang::EOpSubgroupExclusiveMax:
+ case glslang::EOpSubgroupExclusiveAnd:
+ case glslang::EOpSubgroupExclusiveOr:
+ case glslang::EOpSubgroupExclusiveXor:
+ builder.addCapability(spv::CapabilityGroupNonUniform);
+ builder.addCapability(spv::CapabilityGroupNonUniformArithmetic);
+ break;
+ case glslang::EOpSubgroupClusteredAdd:
+ case glslang::EOpSubgroupClusteredMul:
+ case glslang::EOpSubgroupClusteredMin:
+ case glslang::EOpSubgroupClusteredMax:
+ case glslang::EOpSubgroupClusteredAnd:
+ case glslang::EOpSubgroupClusteredOr:
+ case glslang::EOpSubgroupClusteredXor:
+ builder.addCapability(spv::CapabilityGroupNonUniform);
+ builder.addCapability(spv::CapabilityGroupNonUniformClustered);
+ break;
+ case glslang::EOpSubgroupQuadBroadcast:
+ case glslang::EOpSubgroupQuadSwapHorizontal:
+ case glslang::EOpSubgroupQuadSwapVertical:
+ case glslang::EOpSubgroupQuadSwapDiagonal:
+ builder.addCapability(spv::CapabilityGroupNonUniform);
+ builder.addCapability(spv::CapabilityGroupNonUniformQuad);
+ break;
+#ifdef NV_EXTENSIONS
+ case glslang::EOpSubgroupPartitionedAdd:
+ case glslang::EOpSubgroupPartitionedMul:
+ case glslang::EOpSubgroupPartitionedMin:
+ case glslang::EOpSubgroupPartitionedMax:
+ case glslang::EOpSubgroupPartitionedAnd:
+ case glslang::EOpSubgroupPartitionedOr:
+ case glslang::EOpSubgroupPartitionedXor:
+ case glslang::EOpSubgroupPartitionedInclusiveAdd:
+ case glslang::EOpSubgroupPartitionedInclusiveMul:
+ case glslang::EOpSubgroupPartitionedInclusiveMin:
+ case glslang::EOpSubgroupPartitionedInclusiveMax:
+ case glslang::EOpSubgroupPartitionedInclusiveAnd:
+ case glslang::EOpSubgroupPartitionedInclusiveOr:
+ case glslang::EOpSubgroupPartitionedInclusiveXor:
+ case glslang::EOpSubgroupPartitionedExclusiveAdd:
+ case glslang::EOpSubgroupPartitionedExclusiveMul:
+ case glslang::EOpSubgroupPartitionedExclusiveMin:
+ case glslang::EOpSubgroupPartitionedExclusiveMax:
+ case glslang::EOpSubgroupPartitionedExclusiveAnd:
+ case glslang::EOpSubgroupPartitionedExclusiveOr:
+ case glslang::EOpSubgroupPartitionedExclusiveXor:
+ builder.addExtension(spv::E_SPV_NV_shader_subgroup_partitioned);
+ builder.addCapability(spv::CapabilityGroupNonUniformPartitionedNV);
+ break;
+#endif
+ default: assert(0 && "Unhandled subgroup operation!");
+ }
+
+ const bool isUnsigned = typeProxy == glslang::EbtUint || typeProxy == glslang::EbtUint64;
+ const bool isFloat = typeProxy == glslang::EbtFloat || typeProxy == glslang::EbtDouble;
+ const bool isBool = typeProxy == glslang::EbtBool;
+
+ spv::Op opCode = spv::OpNop;
+
+ // Figure out which opcode to use.
+ switch (op) {
+ case glslang::EOpSubgroupElect: opCode = spv::OpGroupNonUniformElect; break;
+ case glslang::EOpSubgroupAll: opCode = spv::OpGroupNonUniformAll; break;
+ case glslang::EOpSubgroupAny: opCode = spv::OpGroupNonUniformAny; break;
+ case glslang::EOpSubgroupAllEqual: opCode = spv::OpGroupNonUniformAllEqual; break;
+ case glslang::EOpSubgroupBroadcast: opCode = spv::OpGroupNonUniformBroadcast; break;
+ case glslang::EOpSubgroupBroadcastFirst: opCode = spv::OpGroupNonUniformBroadcastFirst; break;
+ case glslang::EOpSubgroupBallot: opCode = spv::OpGroupNonUniformBallot; break;
+ case glslang::EOpSubgroupInverseBallot: opCode = spv::OpGroupNonUniformInverseBallot; break;
+ case glslang::EOpSubgroupBallotBitExtract: opCode = spv::OpGroupNonUniformBallotBitExtract; break;
+ case glslang::EOpSubgroupBallotBitCount:
+ case glslang::EOpSubgroupBallotInclusiveBitCount:
+ case glslang::EOpSubgroupBallotExclusiveBitCount: opCode = spv::OpGroupNonUniformBallotBitCount; break;
+ case glslang::EOpSubgroupBallotFindLSB: opCode = spv::OpGroupNonUniformBallotFindLSB; break;
+ case glslang::EOpSubgroupBallotFindMSB: opCode = spv::OpGroupNonUniformBallotFindMSB; break;
+ case glslang::EOpSubgroupShuffle: opCode = spv::OpGroupNonUniformShuffle; break;
+ case glslang::EOpSubgroupShuffleXor: opCode = spv::OpGroupNonUniformShuffleXor; break;
+ case glslang::EOpSubgroupShuffleUp: opCode = spv::OpGroupNonUniformShuffleUp; break;
+ case glslang::EOpSubgroupShuffleDown: opCode = spv::OpGroupNonUniformShuffleDown; break;
+ case glslang::EOpSubgroupAdd:
+ case glslang::EOpSubgroupInclusiveAdd:
+ case glslang::EOpSubgroupExclusiveAdd:
+ case glslang::EOpSubgroupClusteredAdd:
+#ifdef NV_EXTENSIONS
+ case glslang::EOpSubgroupPartitionedAdd:
+ case glslang::EOpSubgroupPartitionedInclusiveAdd:
+ case glslang::EOpSubgroupPartitionedExclusiveAdd:
+#endif
+ if (isFloat) {
+ opCode = spv::OpGroupNonUniformFAdd;
+ } else {
+ opCode = spv::OpGroupNonUniformIAdd;
+ }
+ break;
+ case glslang::EOpSubgroupMul:
+ case glslang::EOpSubgroupInclusiveMul:
+ case glslang::EOpSubgroupExclusiveMul:
+ case glslang::EOpSubgroupClusteredMul:
+#ifdef NV_EXTENSIONS
+ case glslang::EOpSubgroupPartitionedMul:
+ case glslang::EOpSubgroupPartitionedInclusiveMul:
+ case glslang::EOpSubgroupPartitionedExclusiveMul:
+#endif
+ if (isFloat) {
+ opCode = spv::OpGroupNonUniformFMul;
+ } else {
+ opCode = spv::OpGroupNonUniformIMul;
+ }
+ break;
+ case glslang::EOpSubgroupMin:
+ case glslang::EOpSubgroupInclusiveMin:
+ case glslang::EOpSubgroupExclusiveMin:
+ case glslang::EOpSubgroupClusteredMin:
+#ifdef NV_EXTENSIONS
+ case glslang::EOpSubgroupPartitionedMin:
+ case glslang::EOpSubgroupPartitionedInclusiveMin:
+ case glslang::EOpSubgroupPartitionedExclusiveMin:
+#endif
+ if (isFloat) {
+ opCode = spv::OpGroupNonUniformFMin;
+ } else if (isUnsigned) {
+ opCode = spv::OpGroupNonUniformUMin;
+ } else {
+ opCode = spv::OpGroupNonUniformSMin;
+ }
+ break;
+ case glslang::EOpSubgroupMax:
+ case glslang::EOpSubgroupInclusiveMax:
+ case glslang::EOpSubgroupExclusiveMax:
+ case glslang::EOpSubgroupClusteredMax:
+#ifdef NV_EXTENSIONS
+ case glslang::EOpSubgroupPartitionedMax:
+ case glslang::EOpSubgroupPartitionedInclusiveMax:
+ case glslang::EOpSubgroupPartitionedExclusiveMax:
+#endif
+ if (isFloat) {
+ opCode = spv::OpGroupNonUniformFMax;
+ } else if (isUnsigned) {
+ opCode = spv::OpGroupNonUniformUMax;
+ } else {
+ opCode = spv::OpGroupNonUniformSMax;
+ }
+ break;
+ case glslang::EOpSubgroupAnd:
+ case glslang::EOpSubgroupInclusiveAnd:
+ case glslang::EOpSubgroupExclusiveAnd:
+ case glslang::EOpSubgroupClusteredAnd:
+#ifdef NV_EXTENSIONS
+ case glslang::EOpSubgroupPartitionedAnd:
+ case glslang::EOpSubgroupPartitionedInclusiveAnd:
+ case glslang::EOpSubgroupPartitionedExclusiveAnd:
+#endif
+ if (isBool) {
+ opCode = spv::OpGroupNonUniformLogicalAnd;
+ } else {
+ opCode = spv::OpGroupNonUniformBitwiseAnd;
+ }
+ break;
+ case glslang::EOpSubgroupOr:
+ case glslang::EOpSubgroupInclusiveOr:
+ case glslang::EOpSubgroupExclusiveOr:
+ case glslang::EOpSubgroupClusteredOr:
+#ifdef NV_EXTENSIONS
+ case glslang::EOpSubgroupPartitionedOr:
+ case glslang::EOpSubgroupPartitionedInclusiveOr:
+ case glslang::EOpSubgroupPartitionedExclusiveOr:
+#endif
+ if (isBool) {
+ opCode = spv::OpGroupNonUniformLogicalOr;
+ } else {
+ opCode = spv::OpGroupNonUniformBitwiseOr;
+ }
+ break;
+ case glslang::EOpSubgroupXor:
+ case glslang::EOpSubgroupInclusiveXor:
+ case glslang::EOpSubgroupExclusiveXor:
+ case glslang::EOpSubgroupClusteredXor:
+#ifdef NV_EXTENSIONS
+ case glslang::EOpSubgroupPartitionedXor:
+ case glslang::EOpSubgroupPartitionedInclusiveXor:
+ case glslang::EOpSubgroupPartitionedExclusiveXor:
+#endif
+ if (isBool) {
+ opCode = spv::OpGroupNonUniformLogicalXor;
+ } else {
+ opCode = spv::OpGroupNonUniformBitwiseXor;
+ }
+ break;
+ case glslang::EOpSubgroupQuadBroadcast: opCode = spv::OpGroupNonUniformQuadBroadcast; break;
+ case glslang::EOpSubgroupQuadSwapHorizontal:
+ case glslang::EOpSubgroupQuadSwapVertical:
+ case glslang::EOpSubgroupQuadSwapDiagonal: opCode = spv::OpGroupNonUniformQuadSwap; break;
+ default: assert(0 && "Unhandled subgroup operation!");
+ }
+
+ // get the right Group Operation
+ spv::GroupOperation groupOperation = spv::GroupOperationMax;
+ switch (op) {
+ default:
+ break;
+ case glslang::EOpSubgroupBallotBitCount:
+ case glslang::EOpSubgroupAdd:
+ case glslang::EOpSubgroupMul:
+ case glslang::EOpSubgroupMin:
+ case glslang::EOpSubgroupMax:
+ case glslang::EOpSubgroupAnd:
+ case glslang::EOpSubgroupOr:
+ case glslang::EOpSubgroupXor:
+ groupOperation = spv::GroupOperationReduce;
+ break;
+ case glslang::EOpSubgroupBallotInclusiveBitCount:
+ case glslang::EOpSubgroupInclusiveAdd:
+ case glslang::EOpSubgroupInclusiveMul:
+ case glslang::EOpSubgroupInclusiveMin:
+ case glslang::EOpSubgroupInclusiveMax:
+ case glslang::EOpSubgroupInclusiveAnd:
+ case glslang::EOpSubgroupInclusiveOr:
+ case glslang::EOpSubgroupInclusiveXor:
+ groupOperation = spv::GroupOperationInclusiveScan;
+ break;
+ case glslang::EOpSubgroupBallotExclusiveBitCount:
+ case glslang::EOpSubgroupExclusiveAdd:
+ case glslang::EOpSubgroupExclusiveMul:
+ case glslang::EOpSubgroupExclusiveMin:
+ case glslang::EOpSubgroupExclusiveMax:
+ case glslang::EOpSubgroupExclusiveAnd:
+ case glslang::EOpSubgroupExclusiveOr:
+ case glslang::EOpSubgroupExclusiveXor:
+ groupOperation = spv::GroupOperationExclusiveScan;
+ break;
+ case glslang::EOpSubgroupClusteredAdd:
+ case glslang::EOpSubgroupClusteredMul:
+ case glslang::EOpSubgroupClusteredMin:
+ case glslang::EOpSubgroupClusteredMax:
+ case glslang::EOpSubgroupClusteredAnd:
+ case glslang::EOpSubgroupClusteredOr:
+ case glslang::EOpSubgroupClusteredXor:
+ groupOperation = spv::GroupOperationClusteredReduce;
+ break;
+#ifdef NV_EXTENSIONS
+ case glslang::EOpSubgroupPartitionedAdd:
+ case glslang::EOpSubgroupPartitionedMul:
+ case glslang::EOpSubgroupPartitionedMin:
+ case glslang::EOpSubgroupPartitionedMax:
+ case glslang::EOpSubgroupPartitionedAnd:
+ case glslang::EOpSubgroupPartitionedOr:
+ case glslang::EOpSubgroupPartitionedXor:
+ groupOperation = spv::GroupOperationPartitionedReduceNV;
+ break;
+ case glslang::EOpSubgroupPartitionedInclusiveAdd:
+ case glslang::EOpSubgroupPartitionedInclusiveMul:
+ case glslang::EOpSubgroupPartitionedInclusiveMin:
+ case glslang::EOpSubgroupPartitionedInclusiveMax:
+ case glslang::EOpSubgroupPartitionedInclusiveAnd:
+ case glslang::EOpSubgroupPartitionedInclusiveOr:
+ case glslang::EOpSubgroupPartitionedInclusiveXor:
+ groupOperation = spv::GroupOperationPartitionedInclusiveScanNV;
+ break;
+ case glslang::EOpSubgroupPartitionedExclusiveAdd:
+ case glslang::EOpSubgroupPartitionedExclusiveMul:
+ case glslang::EOpSubgroupPartitionedExclusiveMin:
+ case glslang::EOpSubgroupPartitionedExclusiveMax:
+ case glslang::EOpSubgroupPartitionedExclusiveAnd:
+ case glslang::EOpSubgroupPartitionedExclusiveOr:
+ case glslang::EOpSubgroupPartitionedExclusiveXor:
+ groupOperation = spv::GroupOperationPartitionedExclusiveScanNV;
+ break;
+#endif
+ }
+
+ // build the instruction
+ std::vector<spv::IdImmediate> spvGroupOperands;
+
+ // Every operation begins with the Execution Scope operand.
+ spv::IdImmediate executionScope = { true, builder.makeUintConstant(spv::ScopeSubgroup) };
+ spvGroupOperands.push_back(executionScope);
+
+ // Next, for all operations that use a Group Operation, push that as an operand.
+ if (groupOperation != spv::GroupOperationMax) {
+ spv::IdImmediate groupOperand = { false, (unsigned)groupOperation };
+ spvGroupOperands.push_back(groupOperand);
+ }
+
+ // Push back the operands next.
+ for (auto opIt = operands.cbegin(); opIt != operands.cend(); ++opIt) {
+ spv::IdImmediate operand = { true, *opIt };
+ spvGroupOperands.push_back(operand);
+ }
+
+ // Some opcodes have additional operands.
+ spv::Id directionId = spv::NoResult;
+ switch (op) {
+ default: break;
+ case glslang::EOpSubgroupQuadSwapHorizontal: directionId = builder.makeUintConstant(0); break;
+ case glslang::EOpSubgroupQuadSwapVertical: directionId = builder.makeUintConstant(1); break;
+ case glslang::EOpSubgroupQuadSwapDiagonal: directionId = builder.makeUintConstant(2); break;
+ }
+ if (directionId != spv::NoResult) {
+ spv::IdImmediate direction = { true, directionId };
+ spvGroupOperands.push_back(direction);
+ }
+
+ return builder.createOp(opCode, typeId, spvGroupOperands);
+}
+
+spv::Id TGlslangToSpvTraverser::createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector<spv::Id>& operands, glslang::TBasicType typeProxy)
+{
+ bool isUnsigned = isTypeUnsignedInt(typeProxy);
+ bool isFloat = isTypeFloat(typeProxy);
+
+ spv::Op opCode = spv::OpNop;
+ int extBuiltins = -1;
+ int libCall = -1;
+ size_t consumedOperands = operands.size();
+ spv::Id typeId0 = 0;
+ if (consumedOperands > 0)
+ typeId0 = builder.getTypeId(operands[0]);
+ spv::Id typeId1 = 0;
+ if (consumedOperands > 1)
+ typeId1 = builder.getTypeId(operands[1]);
+ spv::Id frexpIntType = 0;
+
+ switch (op) {
+ case glslang::EOpMin:
+ if (isFloat)
+ libCall = spv::GLSLstd450FMin;
+ else if (isUnsigned)
+ libCall = spv::GLSLstd450UMin;
+ else
+ libCall = spv::GLSLstd450SMin;
+ builder.promoteScalar(precision, operands.front(), operands.back());
+ break;
+ case glslang::EOpModf:
+ libCall = spv::GLSLstd450Modf;
+ break;
+ case glslang::EOpMax:
+ if (isFloat)
+ libCall = spv::GLSLstd450FMax;
+ else if (isUnsigned)
+ libCall = spv::GLSLstd450UMax;
+ else
+ libCall = spv::GLSLstd450SMax;
+ builder.promoteScalar(precision, operands.front(), operands.back());
+ break;
+ case glslang::EOpPow:
+ libCall = spv::GLSLstd450Pow;
+ break;
+ case glslang::EOpDot:
+ opCode = spv::OpDot;
+ break;
+ case glslang::EOpAtan:
+ libCall = spv::GLSLstd450Atan2;
+ break;
+
+ case glslang::EOpClamp:
+ if (isFloat)
+ libCall = spv::GLSLstd450FClamp;
+ else if (isUnsigned)
+ libCall = spv::GLSLstd450UClamp;
+ else
+ libCall = spv::GLSLstd450SClamp;
+ builder.promoteScalar(precision, operands.front(), operands[1]);
+ builder.promoteScalar(precision, operands.front(), operands[2]);
+ break;
+ case glslang::EOpMix:
+ if (! builder.isBoolType(builder.getScalarTypeId(builder.getTypeId(operands.back())))) {
+ assert(isFloat);
+ libCall = spv::GLSLstd450FMix;
+ } else {
+ opCode = spv::OpSelect;
+ std::swap(operands.front(), operands.back());
+ }
+ builder.promoteScalar(precision, operands.front(), operands.back());
+ break;
+ case glslang::EOpStep:
+ libCall = spv::GLSLstd450Step;
+ builder.promoteScalar(precision, operands.front(), operands.back());
+ break;
+ case glslang::EOpSmoothStep:
+ libCall = spv::GLSLstd450SmoothStep;
+ builder.promoteScalar(precision, operands[0], operands[2]);
+ builder.promoteScalar(precision, operands[1], operands[2]);
+ break;
+
+ case glslang::EOpDistance:
+ libCall = spv::GLSLstd450Distance;
+ break;
+ case glslang::EOpCross:
+ libCall = spv::GLSLstd450Cross;
+ break;
+ case glslang::EOpFaceForward:
+ libCall = spv::GLSLstd450FaceForward;
+ break;
+ case glslang::EOpReflect:
+ libCall = spv::GLSLstd450Reflect;
+ break;
+ case glslang::EOpRefract:
+ libCall = spv::GLSLstd450Refract;
+ break;
+ case glslang::EOpInterpolateAtSample:
+#ifdef AMD_EXTENSIONS
+ if (typeProxy == glslang::EbtFloat16)
+ builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float);
+#endif
+ libCall = spv::GLSLstd450InterpolateAtSample;
+ break;
+ case glslang::EOpInterpolateAtOffset:
+#ifdef AMD_EXTENSIONS
+ if (typeProxy == glslang::EbtFloat16)
+ builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float);
+#endif
+ libCall = spv::GLSLstd450InterpolateAtOffset;
+ break;
+ case glslang::EOpAddCarry:
+ opCode = spv::OpIAddCarry;
+ typeId = builder.makeStructResultType(typeId0, typeId0);
+ consumedOperands = 2;
+ break;
+ case glslang::EOpSubBorrow:
+ opCode = spv::OpISubBorrow;
+ typeId = builder.makeStructResultType(typeId0, typeId0);
+ consumedOperands = 2;
+ break;
+ case glslang::EOpUMulExtended:
+ opCode = spv::OpUMulExtended;
+ typeId = builder.makeStructResultType(typeId0, typeId0);
+ consumedOperands = 2;
+ break;
+ case glslang::EOpIMulExtended:
+ opCode = spv::OpSMulExtended;
+ typeId = builder.makeStructResultType(typeId0, typeId0);
+ consumedOperands = 2;
+ break;
+ case glslang::EOpBitfieldExtract:
+ if (isUnsigned)
+ opCode = spv::OpBitFieldUExtract;
+ else
+ opCode = spv::OpBitFieldSExtract;
+ break;
+ case glslang::EOpBitfieldInsert:
+ opCode = spv::OpBitFieldInsert;
+ break;
+
+ case glslang::EOpFma:
+ libCall = spv::GLSLstd450Fma;
+ break;
+ case glslang::EOpFrexp:
+ {
+ libCall = spv::GLSLstd450FrexpStruct;
+ assert(builder.isPointerType(typeId1));
+ typeId1 = builder.getContainedTypeId(typeId1);
+ int width = builder.getScalarTypeWidth(typeId1);
+#ifdef AMD_EXTENSIONS
+ if (width == 16)
+ // Using 16-bit exp operand, enable extension SPV_AMD_gpu_shader_int16
+ builder.addExtension(spv::E_SPV_AMD_gpu_shader_int16);
+#endif
+ if (builder.getNumComponents(operands[0]) == 1)
+ frexpIntType = builder.makeIntegerType(width, true);
+ else
+ frexpIntType = builder.makeVectorType(builder.makeIntegerType(width, true), builder.getNumComponents(operands[0]));
+ typeId = builder.makeStructResultType(typeId0, frexpIntType);
+ consumedOperands = 1;
+ }
+ break;
+ case glslang::EOpLdexp:
+ libCall = spv::GLSLstd450Ldexp;
+ break;
+
+ case glslang::EOpReadInvocation:
+ return createInvocationsOperation(op, typeId, operands, typeProxy);
+
+ case glslang::EOpSubgroupBroadcast:
+ case glslang::EOpSubgroupBallotBitExtract:
+ case glslang::EOpSubgroupShuffle:
+ case glslang::EOpSubgroupShuffleXor:
+ case glslang::EOpSubgroupShuffleUp:
+ case glslang::EOpSubgroupShuffleDown:
+ case glslang::EOpSubgroupClusteredAdd:
+ case glslang::EOpSubgroupClusteredMul:
+ case glslang::EOpSubgroupClusteredMin:
+ case glslang::EOpSubgroupClusteredMax:
+ case glslang::EOpSubgroupClusteredAnd:
+ case glslang::EOpSubgroupClusteredOr:
+ case glslang::EOpSubgroupClusteredXor:
+ case glslang::EOpSubgroupQuadBroadcast:
+#ifdef NV_EXTENSIONS
+ case glslang::EOpSubgroupPartitionedAdd:
+ case glslang::EOpSubgroupPartitionedMul:
+ case glslang::EOpSubgroupPartitionedMin:
+ case glslang::EOpSubgroupPartitionedMax:
+ case glslang::EOpSubgroupPartitionedAnd:
+ case glslang::EOpSubgroupPartitionedOr:
+ case glslang::EOpSubgroupPartitionedXor:
+ case glslang::EOpSubgroupPartitionedInclusiveAdd:
+ case glslang::EOpSubgroupPartitionedInclusiveMul:
+ case glslang::EOpSubgroupPartitionedInclusiveMin:
+ case glslang::EOpSubgroupPartitionedInclusiveMax:
+ case glslang::EOpSubgroupPartitionedInclusiveAnd:
+ case glslang::EOpSubgroupPartitionedInclusiveOr:
+ case glslang::EOpSubgroupPartitionedInclusiveXor:
+ case glslang::EOpSubgroupPartitionedExclusiveAdd:
+ case glslang::EOpSubgroupPartitionedExclusiveMul:
+ case glslang::EOpSubgroupPartitionedExclusiveMin:
+ case glslang::EOpSubgroupPartitionedExclusiveMax:
+ case glslang::EOpSubgroupPartitionedExclusiveAnd:
+ case glslang::EOpSubgroupPartitionedExclusiveOr:
+ case glslang::EOpSubgroupPartitionedExclusiveXor:
+#endif
+ return createSubgroupOperation(op, typeId, operands, typeProxy);
+
+#ifdef AMD_EXTENSIONS
+ case glslang::EOpSwizzleInvocations:
+ extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot);
+ libCall = spv::SwizzleInvocationsAMD;
+ break;
+ case glslang::EOpSwizzleInvocationsMasked:
+ extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot);
+ libCall = spv::SwizzleInvocationsMaskedAMD;
+ break;
+ case glslang::EOpWriteInvocation:
+ extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot);
+ libCall = spv::WriteInvocationAMD;
+ break;
+
+ case glslang::EOpMin3:
+ extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_trinary_minmax);
+ if (isFloat)
+ libCall = spv::FMin3AMD;
+ else {
+ if (isUnsigned)
+ libCall = spv::UMin3AMD;
+ else
+ libCall = spv::SMin3AMD;
+ }
+ break;
+ case glslang::EOpMax3:
+ extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_trinary_minmax);
+ if (isFloat)
+ libCall = spv::FMax3AMD;
+ else {
+ if (isUnsigned)
+ libCall = spv::UMax3AMD;
+ else
+ libCall = spv::SMax3AMD;
+ }
+ break;
+ case glslang::EOpMid3:
+ extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_trinary_minmax);
+ if (isFloat)
+ libCall = spv::FMid3AMD;
+ else {
+ if (isUnsigned)
+ libCall = spv::UMid3AMD;
+ else
+ libCall = spv::SMid3AMD;
+ }
+ break;
+
+ case glslang::EOpInterpolateAtVertex:
+ if (typeProxy == glslang::EbtFloat16)
+ builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float);
+ extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_explicit_vertex_parameter);
+ libCall = spv::InterpolateAtVertexAMD;
+ break;
+#endif
+ case glslang::EOpBarrier:
+ {
+ // This is for the extended controlBarrier function, with four operands.
+ // The unextended barrier() goes through createNoArgOperation.
+ assert(operands.size() == 4);
+ unsigned int executionScope = builder.getConstantScalar(operands[0]);
+ unsigned int memoryScope = builder.getConstantScalar(operands[1]);
+ unsigned int semantics = builder.getConstantScalar(operands[2]) | builder.getConstantScalar(operands[3]);
+ builder.createControlBarrier((spv::Scope)executionScope, (spv::Scope)memoryScope, (spv::MemorySemanticsMask)semantics);
+ if (semantics & (spv::MemorySemanticsMakeAvailableKHRMask | spv::MemorySemanticsMakeVisibleKHRMask | spv::MemorySemanticsOutputMemoryKHRMask)) {
+ builder.addCapability(spv::CapabilityVulkanMemoryModelKHR);
+ }
+ if (glslangIntermediate->usingVulkanMemoryModel() && (executionScope == spv::ScopeDevice || memoryScope == spv::ScopeDevice)) {
+ builder.addCapability(spv::CapabilityVulkanMemoryModelDeviceScopeKHR);
+ }
+ return 0;
+ }
+ break;
+ case glslang::EOpMemoryBarrier:
+ {
+ // This is for the extended memoryBarrier function, with three operands.
+ // The unextended memoryBarrier() goes through createNoArgOperation.
+ assert(operands.size() == 3);
+ unsigned int memoryScope = builder.getConstantScalar(operands[0]);
+ unsigned int semantics = builder.getConstantScalar(operands[1]) | builder.getConstantScalar(operands[2]);
+ builder.createMemoryBarrier((spv::Scope)memoryScope, (spv::MemorySemanticsMask)semantics);
+ if (semantics & (spv::MemorySemanticsMakeAvailableKHRMask | spv::MemorySemanticsMakeVisibleKHRMask | spv::MemorySemanticsOutputMemoryKHRMask)) {
+ builder.addCapability(spv::CapabilityVulkanMemoryModelKHR);
+ }
+ if (glslangIntermediate->usingVulkanMemoryModel() && memoryScope == spv::ScopeDevice) {
+ builder.addCapability(spv::CapabilityVulkanMemoryModelDeviceScopeKHR);
+ }
+ return 0;
+ }
+ break;
+
+#ifdef NV_EXTENSIONS
+ case glslang::EOpReportIntersectionNV:
+ {
+ typeId = builder.makeBoolType();
+ opCode = spv::OpReportIntersectionNV;
+ }
+ break;
+ case glslang::EOpTraceNV:
+ {
+ builder.createNoResultOp(spv::OpTraceNV, operands);
+ return 0;
+ }
+ break;
+ case glslang::EOpExecuteCallableNV:
+ {
+ builder.createNoResultOp(spv::OpExecuteCallableNV, operands);
+ return 0;
+ }
+ break;
+ case glslang::EOpWritePackedPrimitiveIndices4x8NV:
+ builder.createNoResultOp(spv::OpWritePackedPrimitiveIndices4x8NV, operands);
+ return 0;
+#endif
+ case glslang::EOpCooperativeMatrixMulAdd:
+ opCode = spv::OpCooperativeMatrixMulAddNV;
+ break;
+
+ default:
+ return 0;
+ }
+
+ spv::Id id = 0;
+ if (libCall >= 0) {
+ // Use an extended instruction from the standard library.
+ // Construct the call arguments, without modifying the original operands vector.
+ // We might need the remaining arguments, e.g. in the EOpFrexp case.
+ std::vector<spv::Id> callArguments(operands.begin(), operands.begin() + consumedOperands);
+ id = builder.createBuiltinCall(typeId, extBuiltins >= 0 ? extBuiltins : stdBuiltins, libCall, callArguments);
+ } else if (opCode == spv::OpDot && !isFloat) {
+ // int dot(int, int)
+ // NOTE: never called for scalar/vector1, this is turned into simple mul before this can be reached
+ const int componentCount = builder.getNumComponents(operands[0]);
+ spv::Id mulOp = builder.createBinOp(spv::OpIMul, builder.getTypeId(operands[0]), operands[0], operands[1]);
+ builder.setPrecision(mulOp, precision);
+ id = builder.createCompositeExtract(mulOp, typeId, 0);
+ for (int i = 1; i < componentCount; ++i) {
+ builder.setPrecision(id, precision);
+ id = builder.createBinOp(spv::OpIAdd, typeId, id, builder.createCompositeExtract(operands[0], typeId, i));
+ }
+ } else {
+ switch (consumedOperands) {
+ case 0:
+ // should all be handled by visitAggregate and createNoArgOperation
+ assert(0);
+ return 0;
+ case 1:
+ // should all be handled by createUnaryOperation
+ assert(0);
+ return 0;
+ case 2:
+ id = builder.createBinOp(opCode, typeId, operands[0], operands[1]);
+ break;
+ default:
+ // anything 3 or over doesn't have l-value operands, so all should be consumed
+ assert(consumedOperands == operands.size());
+ id = builder.createOp(opCode, typeId, operands);
+ break;
+ }
+ }
+
+ // Decode the return types that were structures
+ switch (op) {
+ case glslang::EOpAddCarry:
+ case glslang::EOpSubBorrow:
+ builder.createStore(builder.createCompositeExtract(id, typeId0, 1), operands[2]);
+ id = builder.createCompositeExtract(id, typeId0, 0);
+ break;
+ case glslang::EOpUMulExtended:
+ case glslang::EOpIMulExtended:
+ builder.createStore(builder.createCompositeExtract(id, typeId0, 0), operands[3]);
+ builder.createStore(builder.createCompositeExtract(id, typeId0, 1), operands[2]);
+ break;
+ case glslang::EOpFrexp:
+ {
+ assert(operands.size() == 2);
+ if (builder.isFloatType(builder.getScalarTypeId(typeId1))) {
+ // "exp" is floating-point type (from HLSL intrinsic)
+ spv::Id member1 = builder.createCompositeExtract(id, frexpIntType, 1);
+ member1 = builder.createUnaryOp(spv::OpConvertSToF, typeId1, member1);
+ builder.createStore(member1, operands[1]);
+ } else
+ // "exp" is integer type (from GLSL built-in function)
+ builder.createStore(builder.createCompositeExtract(id, frexpIntType, 1), operands[1]);
+ id = builder.createCompositeExtract(id, typeId0, 0);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return builder.setPrecision(id, precision);
+}
+
+// Intrinsics with no arguments (or no return value, and no precision).
+spv::Id TGlslangToSpvTraverser::createNoArgOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId)
+{
+ // GLSL memory barriers use queuefamily scope in new model, device scope in old model
+ spv::Scope memoryBarrierScope = glslangIntermediate->usingVulkanMemoryModel() ? spv::ScopeQueueFamilyKHR : spv::ScopeDevice;
+
+ switch (op) {
+ case glslang::EOpEmitVertex:
+ builder.createNoResultOp(spv::OpEmitVertex);
+ return 0;
+ case glslang::EOpEndPrimitive:
+ builder.createNoResultOp(spv::OpEndPrimitive);
+ return 0;
+ case glslang::EOpBarrier:
+ if (glslangIntermediate->getStage() == EShLangTessControl) {
+ if (glslangIntermediate->usingVulkanMemoryModel()) {
+ builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeWorkgroup,
+ spv::MemorySemanticsOutputMemoryKHRMask |
+ spv::MemorySemanticsAcquireReleaseMask);
+ builder.addCapability(spv::CapabilityVulkanMemoryModelKHR);
+ } else {
+ builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeInvocation, spv::MemorySemanticsMaskNone);
+ }
+ } else {
+ builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeWorkgroup,
+ spv::MemorySemanticsWorkgroupMemoryMask |
+ spv::MemorySemanticsAcquireReleaseMask);
+ }
+ return 0;
+ case glslang::EOpMemoryBarrier:
+ builder.createMemoryBarrier(memoryBarrierScope, spv::MemorySemanticsAllMemory |
+ spv::MemorySemanticsAcquireReleaseMask);
+ return 0;
+ case glslang::EOpMemoryBarrierAtomicCounter:
+ builder.createMemoryBarrier(memoryBarrierScope, spv::MemorySemanticsAtomicCounterMemoryMask |
+ spv::MemorySemanticsAcquireReleaseMask);
+ return 0;
+ case glslang::EOpMemoryBarrierBuffer:
+ builder.createMemoryBarrier(memoryBarrierScope, spv::MemorySemanticsUniformMemoryMask |
+ spv::MemorySemanticsAcquireReleaseMask);
+ return 0;
+ case glslang::EOpMemoryBarrierImage:
+ builder.createMemoryBarrier(memoryBarrierScope, spv::MemorySemanticsImageMemoryMask |
+ spv::MemorySemanticsAcquireReleaseMask);
+ return 0;
+ case glslang::EOpMemoryBarrierShared:
+ builder.createMemoryBarrier(memoryBarrierScope, spv::MemorySemanticsWorkgroupMemoryMask |
+ spv::MemorySemanticsAcquireReleaseMask);
+ return 0;
+ case glslang::EOpGroupMemoryBarrier:
+ builder.createMemoryBarrier(spv::ScopeWorkgroup, spv::MemorySemanticsAllMemory |
+ spv::MemorySemanticsAcquireReleaseMask);
+ return 0;
+ case glslang::EOpAllMemoryBarrierWithGroupSync:
+ builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeDevice,
+ spv::MemorySemanticsAllMemory |
+ spv::MemorySemanticsAcquireReleaseMask);
+ return 0;
+ case glslang::EOpDeviceMemoryBarrier:
+ builder.createMemoryBarrier(spv::ScopeDevice, spv::MemorySemanticsUniformMemoryMask |
+ spv::MemorySemanticsImageMemoryMask |
+ spv::MemorySemanticsAcquireReleaseMask);
+ return 0;
+ case glslang::EOpDeviceMemoryBarrierWithGroupSync:
+ builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeDevice, spv::MemorySemanticsUniformMemoryMask |
+ spv::MemorySemanticsImageMemoryMask |
+ spv::MemorySemanticsAcquireReleaseMask);
+ return 0;
+ case glslang::EOpWorkgroupMemoryBarrier:
+ builder.createMemoryBarrier(spv::ScopeWorkgroup, spv::MemorySemanticsWorkgroupMemoryMask |
+ spv::MemorySemanticsAcquireReleaseMask);
+ return 0;
+ case glslang::EOpWorkgroupMemoryBarrierWithGroupSync:
+ builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeWorkgroup,
+ spv::MemorySemanticsWorkgroupMemoryMask |
+ spv::MemorySemanticsAcquireReleaseMask);
+ return 0;
+ case glslang::EOpSubgroupBarrier:
+ builder.createControlBarrier(spv::ScopeSubgroup, spv::ScopeSubgroup, spv::MemorySemanticsAllMemory |
+ spv::MemorySemanticsAcquireReleaseMask);
+ return spv::NoResult;
+ case glslang::EOpSubgroupMemoryBarrier:
+ builder.createMemoryBarrier(spv::ScopeSubgroup, spv::MemorySemanticsAllMemory |
+ spv::MemorySemanticsAcquireReleaseMask);
+ return spv::NoResult;
+ case glslang::EOpSubgroupMemoryBarrierBuffer:
+ builder.createMemoryBarrier(spv::ScopeSubgroup, spv::MemorySemanticsUniformMemoryMask |
+ spv::MemorySemanticsAcquireReleaseMask);
+ return spv::NoResult;
+ case glslang::EOpSubgroupMemoryBarrierImage:
+ builder.createMemoryBarrier(spv::ScopeSubgroup, spv::MemorySemanticsImageMemoryMask |
+ spv::MemorySemanticsAcquireReleaseMask);
+ return spv::NoResult;
+ case glslang::EOpSubgroupMemoryBarrierShared:
+ builder.createMemoryBarrier(spv::ScopeSubgroup, spv::MemorySemanticsWorkgroupMemoryMask |
+ spv::MemorySemanticsAcquireReleaseMask);
+ return spv::NoResult;
+ case glslang::EOpSubgroupElect: {
+ std::vector<spv::Id> operands;
+ return createSubgroupOperation(op, typeId, operands, glslang::EbtVoid);
+ }
+#ifdef AMD_EXTENSIONS
+ case glslang::EOpTime:
+ {
+ std::vector<spv::Id> args; // Dummy arguments
+ spv::Id id = builder.createBuiltinCall(typeId, getExtBuiltins(spv::E_SPV_AMD_gcn_shader), spv::TimeAMD, args);
+ return builder.setPrecision(id, precision);
+ }
+#endif
+#ifdef NV_EXTENSIONS
+ case glslang::EOpIgnoreIntersectionNV:
+ builder.createNoResultOp(spv::OpIgnoreIntersectionNV);
+ return 0;
+ case glslang::EOpTerminateRayNV:
+ builder.createNoResultOp(spv::OpTerminateRayNV);
+ return 0;
+#endif
+ default:
+ logger->missingFunctionality("unknown operation with no arguments");
+ return 0;
+ }
+}
+
+spv::Id TGlslangToSpvTraverser::getSymbolId(const glslang::TIntermSymbol* symbol)
+{
+ auto iter = symbolValues.find(symbol->getId());
+ spv::Id id;
+ if (symbolValues.end() != iter) {
+ id = iter->second;
+ return id;
+ }
+
+ // it was not found, create it
+ id = createSpvVariable(symbol);
+ symbolValues[symbol->getId()] = id;
+
+ if (symbol->getBasicType() != glslang::EbtBlock) {
+ builder.addDecoration(id, TranslatePrecisionDecoration(symbol->getType()));
+ builder.addDecoration(id, TranslateInterpolationDecoration(symbol->getType().getQualifier()));
+ builder.addDecoration(id, TranslateAuxiliaryStorageDecoration(symbol->getType().getQualifier()));
+#ifdef NV_EXTENSIONS
+ addMeshNVDecoration(id, /*member*/ -1, symbol->getType().getQualifier());
+#endif
+ if (symbol->getType().getQualifier().hasSpecConstantId())
+ builder.addDecoration(id, spv::DecorationSpecId, symbol->getType().getQualifier().layoutSpecConstantId);
+ if (symbol->getQualifier().hasIndex())
+ builder.addDecoration(id, spv::DecorationIndex, symbol->getQualifier().layoutIndex);
+ if (symbol->getQualifier().hasComponent())
+ builder.addDecoration(id, spv::DecorationComponent, symbol->getQualifier().layoutComponent);
+ // atomic counters use this:
+ if (symbol->getQualifier().hasOffset())
+ builder.addDecoration(id, spv::DecorationOffset, symbol->getQualifier().layoutOffset);
+ }
+
+ if (symbol->getQualifier().hasLocation())
+ builder.addDecoration(id, spv::DecorationLocation, symbol->getQualifier().layoutLocation);
+ builder.addDecoration(id, TranslateInvariantDecoration(symbol->getType().getQualifier()));
+ if (symbol->getQualifier().hasStream() && glslangIntermediate->isMultiStream()) {
+ builder.addCapability(spv::CapabilityGeometryStreams);
+ builder.addDecoration(id, spv::DecorationStream, symbol->getQualifier().layoutStream);
+ }
+ if (symbol->getQualifier().hasSet())
+ builder.addDecoration(id, spv::DecorationDescriptorSet, symbol->getQualifier().layoutSet);
+ else if (IsDescriptorResource(symbol->getType())) {
+ // default to 0
+ builder.addDecoration(id, spv::DecorationDescriptorSet, 0);
+ }
+ if (symbol->getQualifier().hasBinding())
+ builder.addDecoration(id, spv::DecorationBinding, symbol->getQualifier().layoutBinding);
+ else if (IsDescriptorResource(symbol->getType())) {
+ // default to 0
+ builder.addDecoration(id, spv::DecorationBinding, 0);
+ }
+ if (symbol->getQualifier().hasAttachment())
+ builder.addDecoration(id, spv::DecorationInputAttachmentIndex, symbol->getQualifier().layoutAttachment);
+ if (glslangIntermediate->getXfbMode()) {
+ builder.addCapability(spv::CapabilityTransformFeedback);
+ if (symbol->getQualifier().hasXfbBuffer()) {
+ builder.addDecoration(id, spv::DecorationXfbBuffer, symbol->getQualifier().layoutXfbBuffer);
+ unsigned stride = glslangIntermediate->getXfbStride(symbol->getQualifier().layoutXfbBuffer);
+ if (stride != glslang::TQualifier::layoutXfbStrideEnd)
+ builder.addDecoration(id, spv::DecorationXfbStride, stride);
+ }
+ if (symbol->getQualifier().hasXfbOffset())
+ builder.addDecoration(id, spv::DecorationOffset, symbol->getQualifier().layoutXfbOffset);
+ }
+
+ if (symbol->getType().isImage()) {
+ std::vector<spv::Decoration> memory;
+ TranslateMemoryDecoration(symbol->getType().getQualifier(), memory, glslangIntermediate->usingVulkanMemoryModel());
+ for (unsigned int i = 0; i < memory.size(); ++i)
+ builder.addDecoration(id, memory[i]);
+ }
+
+ // built-in variable decorations
+ spv::BuiltIn builtIn = TranslateBuiltInDecoration(symbol->getQualifier().builtIn, false);
+ if (builtIn != spv::BuiltInMax)
+ builder.addDecoration(id, spv::DecorationBuiltIn, (int)builtIn);
+
+ // nonuniform
+ builder.addDecoration(id, TranslateNonUniformDecoration(symbol->getType().getQualifier()));
+
+#ifdef NV_EXTENSIONS
+ if (builtIn == spv::BuiltInSampleMask) {
+ spv::Decoration decoration;
+ // GL_NV_sample_mask_override_coverage extension
+ if (glslangIntermediate->getLayoutOverrideCoverage())
+ decoration = (spv::Decoration)spv::DecorationOverrideCoverageNV;
+ else
+ decoration = (spv::Decoration)spv::DecorationMax;
+ builder.addDecoration(id, decoration);
+ if (decoration != spv::DecorationMax) {
+ builder.addExtension(spv::E_SPV_NV_sample_mask_override_coverage);
+ }
+ }
+ else if (builtIn == spv::BuiltInLayer) {
+ // SPV_NV_viewport_array2 extension
+ if (symbol->getQualifier().layoutViewportRelative) {
+ builder.addDecoration(id, (spv::Decoration)spv::DecorationViewportRelativeNV);
+ builder.addCapability(spv::CapabilityShaderViewportMaskNV);
+ builder.addExtension(spv::E_SPV_NV_viewport_array2);
+ }
+ if (symbol->getQualifier().layoutSecondaryViewportRelativeOffset != -2048) {
+ builder.addDecoration(id, (spv::Decoration)spv::DecorationSecondaryViewportRelativeNV,
+ symbol->getQualifier().layoutSecondaryViewportRelativeOffset);
+ builder.addCapability(spv::CapabilityShaderStereoViewNV);
+ builder.addExtension(spv::E_SPV_NV_stereo_view_rendering);
+ }
+ }
+
+ if (symbol->getQualifier().layoutPassthrough) {
+ builder.addDecoration(id, spv::DecorationPassthroughNV);
+ builder.addCapability(spv::CapabilityGeometryShaderPassthroughNV);
+ builder.addExtension(spv::E_SPV_NV_geometry_shader_passthrough);
+ }
+ if (symbol->getQualifier().pervertexNV) {
+ builder.addDecoration(id, spv::DecorationPerVertexNV);
+ builder.addCapability(spv::CapabilityFragmentBarycentricNV);
+ builder.addExtension(spv::E_SPV_NV_fragment_shader_barycentric);
+ }
+#endif
+
+ if (glslangIntermediate->getHlslFunctionality1() && symbol->getType().getQualifier().semanticName != nullptr) {
+ builder.addExtension("SPV_GOOGLE_hlsl_functionality1");
+ builder.addDecoration(id, (spv::Decoration)spv::DecorationHlslSemanticGOOGLE,
+ symbol->getType().getQualifier().semanticName);
+ }
+
+ if (symbol->getBasicType() == glslang::EbtReference) {
+ builder.addDecoration(id, symbol->getType().getQualifier().restrict ? spv::DecorationRestrictPointerEXT : spv::DecorationAliasedPointerEXT);
+ }
+
+ return id;
+}
+
+#ifdef NV_EXTENSIONS
+// add per-primitive, per-view. per-task decorations to a struct member (member >= 0) or an object
+void TGlslangToSpvTraverser::addMeshNVDecoration(spv::Id id, int member, const glslang::TQualifier& qualifier)
+{
+ if (member >= 0) {
+ if (qualifier.perPrimitiveNV) {
+ // Need to add capability/extension for fragment shader.
+ // Mesh shader already adds this by default.
+ if (glslangIntermediate->getStage() == EShLangFragment) {
+ builder.addCapability(spv::CapabilityMeshShadingNV);
+ builder.addExtension(spv::E_SPV_NV_mesh_shader);
+ }
+ builder.addMemberDecoration(id, (unsigned)member, spv::DecorationPerPrimitiveNV);
+ }
+ if (qualifier.perViewNV)
+ builder.addMemberDecoration(id, (unsigned)member, spv::DecorationPerViewNV);
+ if (qualifier.perTaskNV)
+ builder.addMemberDecoration(id, (unsigned)member, spv::DecorationPerTaskNV);
+ } else {
+ if (qualifier.perPrimitiveNV) {
+ // Need to add capability/extension for fragment shader.
+ // Mesh shader already adds this by default.
+ if (glslangIntermediate->getStage() == EShLangFragment) {
+ builder.addCapability(spv::CapabilityMeshShadingNV);
+ builder.addExtension(spv::E_SPV_NV_mesh_shader);
+ }
+ builder.addDecoration(id, spv::DecorationPerPrimitiveNV);
+ }
+ if (qualifier.perViewNV)
+ builder.addDecoration(id, spv::DecorationPerViewNV);
+ if (qualifier.perTaskNV)
+ builder.addDecoration(id, spv::DecorationPerTaskNV);
+ }
+}
+#endif
+
+// Make a full tree of instructions to build a SPIR-V specialization constant,
+// or regular constant if possible.
+//
+// TBD: this is not yet done, nor verified to be the best design, it does do the leaf symbols though
+//
+// Recursively walk the nodes. The nodes form a tree whose leaves are
+// regular constants, which themselves are trees that createSpvConstant()
+// recursively walks. So, this function walks the "top" of the tree:
+// - emit specialization constant-building instructions for specConstant
+// - when running into a non-spec-constant, switch to createSpvConstant()
+spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TIntermTyped& node)
+{
+ assert(node.getQualifier().isConstant());
+
+ // Handle front-end constants first (non-specialization constants).
+ if (! node.getQualifier().specConstant) {
+ // hand off to the non-spec-constant path
+ assert(node.getAsConstantUnion() != nullptr || node.getAsSymbolNode() != nullptr);
+ int nextConst = 0;
+ return createSpvConstantFromConstUnionArray(node.getType(), node.getAsConstantUnion() ? node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(),
+ nextConst, false);
+ }
+
+ // We now know we have a specialization constant to build
+
+ // gl_WorkGroupSize is a special case until the front-end handles hierarchical specialization constants,
+ // even then, it's specialization ids are handled by special case syntax in GLSL: layout(local_size_x = ...
+ if (node.getType().getQualifier().builtIn == glslang::EbvWorkGroupSize) {
+ std::vector<spv::Id> dimConstId;
+ for (int dim = 0; dim < 3; ++dim) {
+ bool specConst = (glslangIntermediate->getLocalSizeSpecId(dim) != glslang::TQualifier::layoutNotSet);
+ dimConstId.push_back(builder.makeUintConstant(glslangIntermediate->getLocalSize(dim), specConst));
+ if (specConst) {
+ builder.addDecoration(dimConstId.back(), spv::DecorationSpecId,
+ glslangIntermediate->getLocalSizeSpecId(dim));
+ }
+ }
+ return builder.makeCompositeConstant(builder.makeVectorType(builder.makeUintType(32), 3), dimConstId, true);
+ }
+
+ // An AST node labelled as specialization constant should be a symbol node.
+ // Its initializer should either be a sub tree with constant nodes, or a constant union array.
+ if (auto* sn = node.getAsSymbolNode()) {
+ spv::Id result;
+ if (auto* sub_tree = sn->getConstSubtree()) {
+ // Traverse the constant constructor sub tree like generating normal run-time instructions.
+ // During the AST traversal, if the node is marked as 'specConstant', SpecConstantOpModeGuard
+ // will set the builder into spec constant op instruction generating mode.
+ sub_tree->traverse(this);
+ result = accessChainLoad(sub_tree->getType());
+ } else if (auto* const_union_array = &sn->getConstArray()) {
+ int nextConst = 0;
+ result = createSpvConstantFromConstUnionArray(sn->getType(), *const_union_array, nextConst, true);
+ } else {
+ logger->missingFunctionality("Invalid initializer for spec onstant.");
+ return spv::NoResult;
+ }
+ builder.addName(result, sn->getName().c_str());
+ return result;
+ }
+
+ // Neither a front-end constant node, nor a specialization constant node with constant union array or
+ // constant sub tree as initializer.
+ logger->missingFunctionality("Neither a front-end constant nor a spec constant.");
+ return spv::NoResult;
+}
+
+// Use 'consts' as the flattened glslang source of scalar constants to recursively
+// build the aggregate SPIR-V constant.
+//
+// If there are not enough elements present in 'consts', 0 will be substituted;
+// an empty 'consts' can be used to create a fully zeroed SPIR-V constant.
+//
+spv::Id TGlslangToSpvTraverser::createSpvConstantFromConstUnionArray(const glslang::TType& glslangType, const glslang::TConstUnionArray& consts, int& nextConst, bool specConstant)
+{
+ // vector of constants for SPIR-V
+ std::vector<spv::Id> spvConsts;
+
+ // Type is used for struct and array constants
+ spv::Id typeId = convertGlslangToSpvType(glslangType);
+
+ if (glslangType.isArray()) {
+ glslang::TType elementType(glslangType, 0);
+ for (int i = 0; i < glslangType.getOuterArraySize(); ++i)
+ spvConsts.push_back(createSpvConstantFromConstUnionArray(elementType, consts, nextConst, false));
+ } else if (glslangType.isMatrix()) {
+ glslang::TType vectorType(glslangType, 0);
+ for (int col = 0; col < glslangType.getMatrixCols(); ++col)
+ spvConsts.push_back(createSpvConstantFromConstUnionArray(vectorType, consts, nextConst, false));
+ } else if (glslangType.isCoopMat()) {
+ glslang::TType componentType(glslangType.getBasicType());
+ spvConsts.push_back(createSpvConstantFromConstUnionArray(componentType, consts, nextConst, false));
+ } else if (glslangType.isStruct()) {
+ glslang::TVector<glslang::TTypeLoc>::const_iterator iter;
+ for (iter = glslangType.getStruct()->begin(); iter != glslangType.getStruct()->end(); ++iter)
+ spvConsts.push_back(createSpvConstantFromConstUnionArray(*iter->type, consts, nextConst, false));
+ } else if (glslangType.getVectorSize() > 1) {
+ for (unsigned int i = 0; i < (unsigned int)glslangType.getVectorSize(); ++i) {
+ bool zero = nextConst >= consts.size();
+ switch (glslangType.getBasicType()) {
+ case glslang::EbtInt8:
+ spvConsts.push_back(builder.makeInt8Constant(zero ? 0 : consts[nextConst].getI8Const()));
+ break;
+ case glslang::EbtUint8:
+ spvConsts.push_back(builder.makeUint8Constant(zero ? 0 : consts[nextConst].getU8Const()));
+ break;
+ case glslang::EbtInt16:
+ spvConsts.push_back(builder.makeInt16Constant(zero ? 0 : consts[nextConst].getI16Const()));
+ break;
+ case glslang::EbtUint16:
+ spvConsts.push_back(builder.makeUint16Constant(zero ? 0 : consts[nextConst].getU16Const()));
+ break;
+ case glslang::EbtInt:
+ spvConsts.push_back(builder.makeIntConstant(zero ? 0 : consts[nextConst].getIConst()));
+ break;
+ case glslang::EbtUint:
+ spvConsts.push_back(builder.makeUintConstant(zero ? 0 : consts[nextConst].getUConst()));
+ break;
+ case glslang::EbtInt64:
+ spvConsts.push_back(builder.makeInt64Constant(zero ? 0 : consts[nextConst].getI64Const()));
+ break;
+ case glslang::EbtUint64:
+ spvConsts.push_back(builder.makeUint64Constant(zero ? 0 : consts[nextConst].getU64Const()));
+ break;
+ case glslang::EbtFloat:
+ spvConsts.push_back(builder.makeFloatConstant(zero ? 0.0F : (float)consts[nextConst].getDConst()));
+ break;
+ case glslang::EbtDouble:
+ spvConsts.push_back(builder.makeDoubleConstant(zero ? 0.0 : consts[nextConst].getDConst()));
+ break;
+ case glslang::EbtFloat16:
+ spvConsts.push_back(builder.makeFloat16Constant(zero ? 0.0F : (float)consts[nextConst].getDConst()));
+ break;
+ case glslang::EbtBool:
+ spvConsts.push_back(builder.makeBoolConstant(zero ? false : consts[nextConst].getBConst()));
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ ++nextConst;
+ }
+ } else {
+ // we have a non-aggregate (scalar) constant
+ bool zero = nextConst >= consts.size();
+ spv::Id scalar = 0;
+ switch (glslangType.getBasicType()) {
+ case glslang::EbtInt8:
+ scalar = builder.makeInt8Constant(zero ? 0 : consts[nextConst].getI8Const(), specConstant);
+ break;
+ case glslang::EbtUint8:
+ scalar = builder.makeUint8Constant(zero ? 0 : consts[nextConst].getU8Const(), specConstant);
+ break;
+ case glslang::EbtInt16:
+ scalar = builder.makeInt16Constant(zero ? 0 : consts[nextConst].getI16Const(), specConstant);
+ break;
+ case glslang::EbtUint16:
+ scalar = builder.makeUint16Constant(zero ? 0 : consts[nextConst].getU16Const(), specConstant);
+ break;
+ case glslang::EbtInt:
+ scalar = builder.makeIntConstant(zero ? 0 : consts[nextConst].getIConst(), specConstant);
+ break;
+ case glslang::EbtUint:
+ scalar = builder.makeUintConstant(zero ? 0 : consts[nextConst].getUConst(), specConstant);
+ break;
+ case glslang::EbtInt64:
+ scalar = builder.makeInt64Constant(zero ? 0 : consts[nextConst].getI64Const(), specConstant);
+ break;
+ case glslang::EbtUint64:
+ scalar = builder.makeUint64Constant(zero ? 0 : consts[nextConst].getU64Const(), specConstant);
+ break;
+ case glslang::EbtFloat:
+ scalar = builder.makeFloatConstant(zero ? 0.0F : (float)consts[nextConst].getDConst(), specConstant);
+ break;
+ case glslang::EbtDouble:
+ scalar = builder.makeDoubleConstant(zero ? 0.0 : consts[nextConst].getDConst(), specConstant);
+ break;
+ case glslang::EbtFloat16:
+ scalar = builder.makeFloat16Constant(zero ? 0.0F : (float)consts[nextConst].getDConst(), specConstant);
+ break;
+ case glslang::EbtBool:
+ scalar = builder.makeBoolConstant(zero ? false : consts[nextConst].getBConst(), specConstant);
+ break;
+ case glslang::EbtReference:
+ scalar = builder.makeUint64Constant(zero ? 0 : consts[nextConst].getU64Const(), specConstant);
+ scalar = builder.createUnaryOp(spv::OpBitcast, typeId, scalar);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ ++nextConst;
+ return scalar;
+ }
+
+ return builder.makeCompositeConstant(typeId, spvConsts);
+}
+
+// Return true if the node is a constant or symbol whose reading has no
+// non-trivial observable cost or effect.
+bool TGlslangToSpvTraverser::isTrivialLeaf(const glslang::TIntermTyped* node)
+{
+ // don't know what this is
+ if (node == nullptr)
+ return false;
+
+ // a constant is safe
+ if (node->getAsConstantUnion() != nullptr)
+ return true;
+
+ // not a symbol means non-trivial
+ if (node->getAsSymbolNode() == nullptr)
+ return false;
+
+ // a symbol, depends on what's being read
+ switch (node->getType().getQualifier().storage) {
+ case glslang::EvqTemporary:
+ case glslang::EvqGlobal:
+ case glslang::EvqIn:
+ case glslang::EvqInOut:
+ case glslang::EvqConst:
+ case glslang::EvqConstReadOnly:
+ case glslang::EvqUniform:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// A node is trivial if it is a single operation with no side effects.
+// HLSL (and/or vectors) are always trivial, as it does not short circuit.
+// Otherwise, error on the side of saying non-trivial.
+// Return true if trivial.
+bool TGlslangToSpvTraverser::isTrivial(const glslang::TIntermTyped* node)
+{
+ if (node == nullptr)
+ return false;
+
+ // count non scalars as trivial, as well as anything coming from HLSL
+ if (! node->getType().isScalarOrVec1() || glslangIntermediate->getSource() == glslang::EShSourceHlsl)
+ return true;
+
+ // symbols and constants are trivial
+ if (isTrivialLeaf(node))
+ return true;
+
+ // otherwise, it needs to be a simple operation or one or two leaf nodes
+
+ // not a simple operation
+ const glslang::TIntermBinary* binaryNode = node->getAsBinaryNode();
+ const glslang::TIntermUnary* unaryNode = node->getAsUnaryNode();
+ if (binaryNode == nullptr && unaryNode == nullptr)
+ return false;
+
+ // not on leaf nodes
+ if (binaryNode && (! isTrivialLeaf(binaryNode->getLeft()) || ! isTrivialLeaf(binaryNode->getRight())))
+ return false;
+
+ if (unaryNode && ! isTrivialLeaf(unaryNode->getOperand())) {
+ return false;
+ }
+
+ switch (node->getAsOperator()->getOp()) {
+ case glslang::EOpLogicalNot:
+ case glslang::EOpConvIntToBool:
+ case glslang::EOpConvUintToBool:
+ case glslang::EOpConvFloatToBool:
+ case glslang::EOpConvDoubleToBool:
+ case glslang::EOpEqual:
+ case glslang::EOpNotEqual:
+ case glslang::EOpLessThan:
+ case glslang::EOpGreaterThan:
+ case glslang::EOpLessThanEqual:
+ case glslang::EOpGreaterThanEqual:
+ case glslang::EOpIndexDirect:
+ case glslang::EOpIndexDirectStruct:
+ case glslang::EOpLogicalXor:
+ case glslang::EOpAny:
+ case glslang::EOpAll:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Emit short-circuiting code, where 'right' is never evaluated unless
+// the left side is true (for &&) or false (for ||).
+spv::Id TGlslangToSpvTraverser::createShortCircuit(glslang::TOperator op, glslang::TIntermTyped& left, glslang::TIntermTyped& right)
+{
+ spv::Id boolTypeId = builder.makeBoolType();
+
+ // emit left operand
+ builder.clearAccessChain();
+ left.traverse(this);
+ spv::Id leftId = accessChainLoad(left.getType());
+
+ // Operands to accumulate OpPhi operands
+ std::vector<spv::Id> phiOperands;
+ // accumulate left operand's phi information
+ phiOperands.push_back(leftId);
+ phiOperands.push_back(builder.getBuildPoint()->getId());
+
+ // Make the two kinds of operation symmetric with a "!"
+ // || => emit "if (! left) result = right"
+ // && => emit "if ( left) result = right"
+ //
+ // TODO: this runtime "not" for || could be avoided by adding functionality
+ // to 'builder' to have an "else" without an "then"
+ if (op == glslang::EOpLogicalOr)
+ leftId = builder.createUnaryOp(spv::OpLogicalNot, boolTypeId, leftId);
+
+ // make an "if" based on the left value
+ spv::Builder::If ifBuilder(leftId, spv::SelectionControlMaskNone, builder);
+
+ // emit right operand as the "then" part of the "if"
+ builder.clearAccessChain();
+ right.traverse(this);
+ spv::Id rightId = accessChainLoad(right.getType());
+
+ // accumulate left operand's phi information
+ phiOperands.push_back(rightId);
+ phiOperands.push_back(builder.getBuildPoint()->getId());
+
+ // finish the "if"
+ ifBuilder.makeEndIf();
+
+ // phi together the two results
+ return builder.createOp(spv::OpPhi, boolTypeId, phiOperands);
+}
+
+#ifdef AMD_EXTENSIONS
+// Return type Id of the imported set of extended instructions corresponds to the name.
+// Import this set if it has not been imported yet.
+spv::Id TGlslangToSpvTraverser::getExtBuiltins(const char* name)
+{
+ if (extBuiltinMap.find(name) != extBuiltinMap.end())
+ return extBuiltinMap[name];
+ else {
+ builder.addExtension(name);
+ spv::Id extBuiltins = builder.import(name);
+ extBuiltinMap[name] = extBuiltins;
+ return extBuiltins;
+ }
+}
+#endif
+
+}; // end anonymous namespace
+
+namespace glslang {
+
+void GetSpirvVersion(std::string& version)
+{
+ const int bufSize = 100;
+ char buf[bufSize];
+ snprintf(buf, bufSize, "0x%08x, Revision %d", spv::Version, spv::Revision);
+ version = buf;
+}
+
+// For low-order part of the generator's magic number. Bump up
+// when there is a change in the style (e.g., if SSA form changes,
+// or a different instruction sequence to do something gets used).
+int GetSpirvGeneratorVersion()
+{
+ // return 1; // start
+ // return 2; // EOpAtomicCounterDecrement gets a post decrement, to map between GLSL -> SPIR-V
+ // return 3; // change/correct barrier-instruction operands, to match memory model group decisions
+ // return 4; // some deeper access chains: for dynamic vector component, and local Boolean component
+ // return 5; // make OpArrayLength result type be an int with signedness of 0
+ // return 6; // revert version 5 change, which makes a different (new) kind of incorrect code,
+ // versions 4 and 6 each generate OpArrayLength as it has long been done
+ return 7; // GLSL volatile keyword maps to both SPIR-V decorations Volatile and Coherent
+}
+
+// Write SPIR-V out to a binary file
+void OutputSpvBin(const std::vector<unsigned int>& spirv, const char* baseName)
+{
+ std::ofstream out;
+ out.open(baseName, std::ios::binary | std::ios::out);
+ if (out.fail())
+ printf("ERROR: Failed to open file: %s\n", baseName);
+ for (int i = 0; i < (int)spirv.size(); ++i) {
+ unsigned int word = spirv[i];
+ out.write((const char*)&word, 4);
+ }
+ out.close();
+}
+
+// Write SPIR-V out to a text file with 32-bit hexadecimal words
+void OutputSpvHex(const std::vector<unsigned int>& spirv, const char* baseName, const char* varName)
+{
+ std::ofstream out;
+ out.open(baseName, std::ios::binary | std::ios::out);
+ if (out.fail())
+ printf("ERROR: Failed to open file: %s\n", baseName);
+ out << "\t// " <<
+ GetSpirvGeneratorVersion() << "." << GLSLANG_MINOR_VERSION << "." << GLSLANG_PATCH_LEVEL <<
+ std::endl;
+ if (varName != nullptr) {
+ out << "\t #pragma once" << std::endl;
+ out << "const uint32_t " << varName << "[] = {" << std::endl;
+ }
+ const int WORDS_PER_LINE = 8;
+ for (int i = 0; i < (int)spirv.size(); i += WORDS_PER_LINE) {
+ out << "\t";
+ for (int j = 0; j < WORDS_PER_LINE && i + j < (int)spirv.size(); ++j) {
+ const unsigned int word = spirv[i + j];
+ out << "0x" << std::hex << std::setw(8) << std::setfill('0') << word;
+ if (i + j + 1 < (int)spirv.size()) {
+ out << ",";
+ }
+ }
+ out << std::endl;
+ }
+ if (varName != nullptr) {
+ out << "};";
+ }
+ out.close();
+}
+
+//
+// Set up the glslang traversal
+//
+void GlslangToSpv(const TIntermediate& intermediate, std::vector<unsigned int>& spirv, SpvOptions* options)
+{
+ spv::SpvBuildLogger logger;
+ GlslangToSpv(intermediate, spirv, &logger, options);
+}
+
+void GlslangToSpv(const TIntermediate& intermediate, std::vector<unsigned int>& spirv,
+ spv::SpvBuildLogger* logger, SpvOptions* options)
+{
+ TIntermNode* root = intermediate.getTreeRoot();
+
+ if (root == 0)
+ return;
+
+ SpvOptions defaultOptions;
+ if (options == nullptr)
+ options = &defaultOptions;
+
+ GetThreadPoolAllocator().push();
+
+ TGlslangToSpvTraverser it(intermediate.getSpv().spv, &intermediate, logger, *options);
+ root->traverse(&it);
+ it.finishSpv();
+ it.dumpSpv(spirv);
+
+#if ENABLE_OPT
+ // If from HLSL, run spirv-opt to "legalize" the SPIR-V for Vulkan
+ // eg. forward and remove memory writes of opaque types.
+ if ((intermediate.getSource() == EShSourceHlsl || options->optimizeSize) && !options->disableOptimizer)
+ SpirvToolsLegalize(intermediate, spirv, logger, options);
+
+ if (options->validate)
+ SpirvToolsValidate(intermediate, spirv, logger);
+
+ if (options->disassemble)
+ SpirvToolsDisassemble(std::cout, spirv);
+
+#endif
+
+ GetThreadPoolAllocator().pop();
+}
+
+}; // end namespace glslang
diff --git a/src/3rdparty/glslang/SPIRV/GlslangToSpv.h b/src/3rdparty/glslang/SPIRV/GlslangToSpv.h
new file mode 100644
index 0000000..86e1c23
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/GlslangToSpv.h
@@ -0,0 +1,61 @@
+//
+// Copyright (C) 2014 LunarG, Inc.
+// Copyright (C) 2015-2018 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#if defined(_MSC_VER) && _MSC_VER >= 1900
+ #pragma warning(disable : 4464) // relative include path contains '..'
+#endif
+
+#include "SpvTools.h"
+#include "../glslang/Include/intermediate.h"
+
+#include <string>
+#include <vector>
+
+#include "Logger.h"
+
+namespace glslang {
+
+void GetSpirvVersion(std::string&);
+int GetSpirvGeneratorVersion();
+void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv,
+ SpvOptions* options = nullptr);
+void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv,
+ spv::SpvBuildLogger* logger, SpvOptions* options = nullptr);
+void OutputSpvBin(const std::vector<unsigned int>& spirv, const char* baseName);
+void OutputSpvHex(const std::vector<unsigned int>& spirv, const char* baseName, const char* varName);
+
+}
diff --git a/src/3rdparty/glslang/SPIRV/InReadableOrder.cpp b/src/3rdparty/glslang/SPIRV/InReadableOrder.cpp
new file mode 100644
index 0000000..52b2961
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/InReadableOrder.cpp
@@ -0,0 +1,113 @@
+//
+// Copyright (C) 2016 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+// The SPIR-V spec requires code blocks to appear in an order satisfying the
+// dominator-tree direction (ie, dominator before the dominated). This is,
+// actually, easy to achieve: any pre-order CFG traversal algorithm will do it.
+// Because such algorithms visit a block only after traversing some path to it
+// from the root, they necessarily visit the block's idom first.
+//
+// But not every graph-traversal algorithm outputs blocks in an order that
+// appears logical to human readers. The problem is that unrelated branches may
+// be interspersed with each other, and merge blocks may come before some of the
+// branches being merged.
+//
+// A good, human-readable order of blocks may be achieved by performing
+// depth-first search but delaying merge nodes until after all their branches
+// have been visited. This is implemented below by the inReadableOrder()
+// function.
+
+#include "spvIR.h"
+
+#include <cassert>
+#include <unordered_set>
+
+using spv::Block;
+using spv::Id;
+
+namespace {
+// Traverses CFG in a readable order, invoking a pre-set callback on each block.
+// Use by calling visit() on the root block.
+class ReadableOrderTraverser {
+public:
+ explicit ReadableOrderTraverser(std::function<void(Block*)> callback) : callback_(callback) {}
+ // Visits the block if it hasn't been visited already and isn't currently
+ // being delayed. Invokes callback(block), then descends into its
+ // successors. Delays merge-block and continue-block processing until all
+ // the branches have been completed.
+ void visit(Block* block)
+ {
+ assert(block);
+ if (visited_.count(block) || delayed_.count(block))
+ return;
+ callback_(block);
+ visited_.insert(block);
+ Block* mergeBlock = nullptr;
+ Block* continueBlock = nullptr;
+ auto mergeInst = block->getMergeInstruction();
+ if (mergeInst) {
+ Id mergeId = mergeInst->getIdOperand(0);
+ mergeBlock = block->getParent().getParent().getInstruction(mergeId)->getBlock();
+ delayed_.insert(mergeBlock);
+ if (mergeInst->getOpCode() == spv::OpLoopMerge) {
+ Id continueId = mergeInst->getIdOperand(1);
+ continueBlock =
+ block->getParent().getParent().getInstruction(continueId)->getBlock();
+ delayed_.insert(continueBlock);
+ }
+ }
+ const auto successors = block->getSuccessors();
+ for (auto it = successors.cbegin(); it != successors.cend(); ++it)
+ visit(*it);
+ if (continueBlock) {
+ delayed_.erase(continueBlock);
+ visit(continueBlock);
+ }
+ if (mergeBlock) {
+ delayed_.erase(mergeBlock);
+ visit(mergeBlock);
+ }
+ }
+
+private:
+ std::function<void(Block*)> callback_;
+ // Whether a block has already been visited or is being delayed.
+ std::unordered_set<Block *> visited_, delayed_;
+};
+}
+
+void spv::inReadableOrder(Block* root, std::function<void(Block*)> callback)
+{
+ ReadableOrderTraverser(callback).visit(root);
+}
diff --git a/src/3rdparty/glslang/SPIRV/Logger.cpp b/src/3rdparty/glslang/SPIRV/Logger.cpp
new file mode 100644
index 0000000..48bd4e3
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/Logger.cpp
@@ -0,0 +1,68 @@
+//
+// Copyright (C) 2016 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#include "Logger.h"
+
+#include <algorithm>
+#include <iterator>
+#include <sstream>
+
+namespace spv {
+
+void SpvBuildLogger::tbdFunctionality(const std::string& f)
+{
+ if (std::find(std::begin(tbdFeatures), std::end(tbdFeatures), f) == std::end(tbdFeatures))
+ tbdFeatures.push_back(f);
+}
+
+void SpvBuildLogger::missingFunctionality(const std::string& f)
+{
+ if (std::find(std::begin(missingFeatures), std::end(missingFeatures), f) == std::end(missingFeatures))
+ missingFeatures.push_back(f);
+}
+
+std::string SpvBuildLogger::getAllMessages() const {
+ std::ostringstream messages;
+ for (auto it = tbdFeatures.cbegin(); it != tbdFeatures.cend(); ++it)
+ messages << "TBD functionality: " << *it << "\n";
+ for (auto it = missingFeatures.cbegin(); it != missingFeatures.cend(); ++it)
+ messages << "Missing functionality: " << *it << "\n";
+ for (auto it = warnings.cbegin(); it != warnings.cend(); ++it)
+ messages << "warning: " << *it << "\n";
+ for (auto it = errors.cbegin(); it != errors.cend(); ++it)
+ messages << "error: " << *it << "\n";
+ return messages.str();
+}
+
+} // end spv namespace
diff --git a/src/3rdparty/glslang/SPIRV/Logger.h b/src/3rdparty/glslang/SPIRV/Logger.h
new file mode 100644
index 0000000..2e4ddaf
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/Logger.h
@@ -0,0 +1,74 @@
+//
+// Copyright (C) 2016 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GLSLANG_SPIRV_LOGGER_H
+#define GLSLANG_SPIRV_LOGGER_H
+
+#include <string>
+#include <vector>
+
+namespace spv {
+
+// A class for holding all SPIR-V build status messages, including
+// missing/TBD functionalities, warnings, and errors.
+class SpvBuildLogger {
+public:
+ SpvBuildLogger() {}
+
+ // Registers a TBD functionality.
+ void tbdFunctionality(const std::string& f);
+ // Registers a missing functionality.
+ void missingFunctionality(const std::string& f);
+
+ // Logs a warning.
+ void warning(const std::string& w) { warnings.push_back(w); }
+ // Logs an error.
+ void error(const std::string& e) { errors.push_back(e); }
+
+ // Returns all messages accumulated in the order of:
+ // TBD functionalities, missing functionalities, warnings, errors.
+ std::string getAllMessages() const;
+
+private:
+ SpvBuildLogger(const SpvBuildLogger&);
+
+ std::vector<std::string> tbdFeatures;
+ std::vector<std::string> missingFeatures;
+ std::vector<std::string> warnings;
+ std::vector<std::string> errors;
+};
+
+} // end spv namespace
+
+#endif // GLSLANG_SPIRV_LOGGER_H
diff --git a/src/3rdparty/glslang/SPIRV/SPVRemapper.cpp b/src/3rdparty/glslang/SPIRV/SPVRemapper.cpp
new file mode 100644
index 0000000..fd0bb89
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/SPVRemapper.cpp
@@ -0,0 +1,1487 @@
+//
+// Copyright (C) 2015 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "SPVRemapper.h"
+#include "doc.h"
+
+#if !defined (use_cpp11)
+// ... not supported before C++11
+#else // defined (use_cpp11)
+
+#include <algorithm>
+#include <cassert>
+#include "../glslang/Include/Common.h"
+
+namespace spv {
+
+ // By default, just abort on error. Can be overridden via RegisterErrorHandler
+ spirvbin_t::errorfn_t spirvbin_t::errorHandler = [](const std::string&) { exit(5); };
+ // By default, eat log messages. Can be overridden via RegisterLogHandler
+ spirvbin_t::logfn_t spirvbin_t::logHandler = [](const std::string&) { };
+
+ // This can be overridden to provide other message behavior if needed
+ void spirvbin_t::msg(int minVerbosity, int indent, const std::string& txt) const
+ {
+ if (verbose >= minVerbosity)
+ logHandler(std::string(indent, ' ') + txt);
+ }
+
+ // hash opcode, with special handling for OpExtInst
+ std::uint32_t spirvbin_t::asOpCodeHash(unsigned word)
+ {
+ const spv::Op opCode = asOpCode(word);
+
+ std::uint32_t offset = 0;
+
+ switch (opCode) {
+ case spv::OpExtInst:
+ offset += asId(word + 4); break;
+ default:
+ break;
+ }
+
+ return opCode * 19 + offset; // 19 = small prime
+ }
+
+ spirvbin_t::range_t spirvbin_t::literalRange(spv::Op opCode) const
+ {
+ static const int maxCount = 1<<30;
+
+ switch (opCode) {
+ case spv::OpTypeFloat: // fall through...
+ case spv::OpTypePointer: return range_t(2, 3);
+ case spv::OpTypeInt: return range_t(2, 4);
+ // TODO: case spv::OpTypeImage:
+ // TODO: case spv::OpTypeSampledImage:
+ case spv::OpTypeSampler: return range_t(3, 8);
+ case spv::OpTypeVector: // fall through
+ case spv::OpTypeMatrix: // ...
+ case spv::OpTypePipe: return range_t(3, 4);
+ case spv::OpConstant: return range_t(3, maxCount);
+ default: return range_t(0, 0);
+ }
+ }
+
+ spirvbin_t::range_t spirvbin_t::typeRange(spv::Op opCode) const
+ {
+ static const int maxCount = 1<<30;
+
+ if (isConstOp(opCode))
+ return range_t(1, 2);
+
+ switch (opCode) {
+ case spv::OpTypeVector: // fall through
+ case spv::OpTypeMatrix: // ...
+ case spv::OpTypeSampler: // ...
+ case spv::OpTypeArray: // ...
+ case spv::OpTypeRuntimeArray: // ...
+ case spv::OpTypePipe: return range_t(2, 3);
+ case spv::OpTypeStruct: // fall through
+ case spv::OpTypeFunction: return range_t(2, maxCount);
+ case spv::OpTypePointer: return range_t(3, 4);
+ default: return range_t(0, 0);
+ }
+ }
+
+ spirvbin_t::range_t spirvbin_t::constRange(spv::Op opCode) const
+ {
+ static const int maxCount = 1<<30;
+
+ switch (opCode) {
+ case spv::OpTypeArray: // fall through...
+ case spv::OpTypeRuntimeArray: return range_t(3, 4);
+ case spv::OpConstantComposite: return range_t(3, maxCount);
+ default: return range_t(0, 0);
+ }
+ }
+
+ // Return the size of a type in 32-bit words. This currently only
+ // handles ints and floats, and is only invoked by queries which must be
+ // integer types. If ever needed, it can be generalized.
+ unsigned spirvbin_t::typeSizeInWords(spv::Id id) const
+ {
+ const unsigned typeStart = idPos(id);
+ const spv::Op opCode = asOpCode(typeStart);
+
+ if (errorLatch)
+ return 0;
+
+ switch (opCode) {
+ case spv::OpTypeInt: // fall through...
+ case spv::OpTypeFloat: return (spv[typeStart+2]+31)/32;
+ default:
+ return 0;
+ }
+ }
+
+ // Looks up the type of a given const or variable ID, and
+ // returns its size in 32-bit words.
+ unsigned spirvbin_t::idTypeSizeInWords(spv::Id id) const
+ {
+ const auto tid_it = idTypeSizeMap.find(id);
+ if (tid_it == idTypeSizeMap.end()) {
+ error("type size for ID not found");
+ return 0;
+ }
+
+ return tid_it->second;
+ }
+
+ // Is this an opcode we should remove when using --strip?
+ bool spirvbin_t::isStripOp(spv::Op opCode) const
+ {
+ switch (opCode) {
+ case spv::OpSource:
+ case spv::OpSourceExtension:
+ case spv::OpName:
+ case spv::OpMemberName:
+ case spv::OpLine: return true;
+ default: return false;
+ }
+ }
+
+ // Return true if this opcode is flow control
+ bool spirvbin_t::isFlowCtrl(spv::Op opCode) const
+ {
+ switch (opCode) {
+ case spv::OpBranchConditional:
+ case spv::OpBranch:
+ case spv::OpSwitch:
+ case spv::OpLoopMerge:
+ case spv::OpSelectionMerge:
+ case spv::OpLabel:
+ case spv::OpFunction:
+ case spv::OpFunctionEnd: return true;
+ default: return false;
+ }
+ }
+
+ // Return true if this opcode defines a type
+ bool spirvbin_t::isTypeOp(spv::Op opCode) const
+ {
+ switch (opCode) {
+ case spv::OpTypeVoid:
+ case spv::OpTypeBool:
+ case spv::OpTypeInt:
+ case spv::OpTypeFloat:
+ case spv::OpTypeVector:
+ case spv::OpTypeMatrix:
+ case spv::OpTypeImage:
+ case spv::OpTypeSampler:
+ case spv::OpTypeArray:
+ case spv::OpTypeRuntimeArray:
+ case spv::OpTypeStruct:
+ case spv::OpTypeOpaque:
+ case spv::OpTypePointer:
+ case spv::OpTypeFunction:
+ case spv::OpTypeEvent:
+ case spv::OpTypeDeviceEvent:
+ case spv::OpTypeReserveId:
+ case spv::OpTypeQueue:
+ case spv::OpTypeSampledImage:
+ case spv::OpTypePipe: return true;
+ default: return false;
+ }
+ }
+
+ // Return true if this opcode defines a constant
+ bool spirvbin_t::isConstOp(spv::Op opCode) const
+ {
+ switch (opCode) {
+ case spv::OpConstantSampler:
+ error("unimplemented constant type");
+ return true;
+
+ case spv::OpConstantNull:
+ case spv::OpConstantTrue:
+ case spv::OpConstantFalse:
+ case spv::OpConstantComposite:
+ case spv::OpConstant:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ const auto inst_fn_nop = [](spv::Op, unsigned) { return false; };
+ const auto op_fn_nop = [](spv::Id&) { };
+
+ // g++ doesn't like these defined in the class proper in an anonymous namespace.
+ // Dunno why. Also MSVC doesn't like the constexpr keyword. Also dunno why.
+ // Defining them externally seems to please both compilers, so, here they are.
+ const spv::Id spirvbin_t::unmapped = spv::Id(-10000);
+ const spv::Id spirvbin_t::unused = spv::Id(-10001);
+ const int spirvbin_t::header_size = 5;
+
+ spv::Id spirvbin_t::nextUnusedId(spv::Id id)
+ {
+ while (isNewIdMapped(id)) // search for an unused ID
+ ++id;
+
+ return id;
+ }
+
+ spv::Id spirvbin_t::localId(spv::Id id, spv::Id newId)
+ {
+ //assert(id != spv::NoResult && newId != spv::NoResult);
+
+ if (id > bound()) {
+ error(std::string("ID out of range: ") + std::to_string(id));
+ return spirvbin_t::unused;
+ }
+
+ if (id >= idMapL.size())
+ idMapL.resize(id+1, unused);
+
+ if (newId != unmapped && newId != unused) {
+ if (isOldIdUnused(id)) {
+ error(std::string("ID unused in module: ") + std::to_string(id));
+ return spirvbin_t::unused;
+ }
+
+ if (!isOldIdUnmapped(id)) {
+ error(std::string("ID already mapped: ") + std::to_string(id) + " -> "
+ + std::to_string(localId(id)));
+
+ return spirvbin_t::unused;
+ }
+
+ if (isNewIdMapped(newId)) {
+ error(std::string("ID already used in module: ") + std::to_string(newId));
+ return spirvbin_t::unused;
+ }
+
+ msg(4, 4, std::string("map: ") + std::to_string(id) + " -> " + std::to_string(newId));
+ setMapped(newId);
+ largestNewId = std::max(largestNewId, newId);
+ }
+
+ return idMapL[id] = newId;
+ }
+
+ // Parse a literal string from the SPIR binary and return it as an std::string
+ // Due to C++11 RValue references, this doesn't copy the result string.
+ std::string spirvbin_t::literalString(unsigned word) const
+ {
+ std::string literal;
+
+ literal.reserve(16);
+
+ const char* bytes = reinterpret_cast<const char*>(spv.data() + word);
+
+ while (bytes && *bytes)
+ literal += *bytes++;
+
+ return literal;
+ }
+
+ void spirvbin_t::applyMap()
+ {
+ msg(3, 2, std::string("Applying map: "));
+
+ // Map local IDs through the ID map
+ process(inst_fn_nop, // ignore instructions
+ [this](spv::Id& id) {
+ id = localId(id);
+
+ if (errorLatch)
+ return;
+
+ assert(id != unused && id != unmapped);
+ }
+ );
+ }
+
+ // Find free IDs for anything we haven't mapped
+ void spirvbin_t::mapRemainder()
+ {
+ msg(3, 2, std::string("Remapping remainder: "));
+
+ spv::Id unusedId = 1; // can't use 0: that's NoResult
+ spirword_t maxBound = 0;
+
+ for (spv::Id id = 0; id < idMapL.size(); ++id) {
+ if (isOldIdUnused(id))
+ continue;
+
+ // Find a new mapping for any used but unmapped IDs
+ if (isOldIdUnmapped(id)) {
+ localId(id, unusedId = nextUnusedId(unusedId));
+ if (errorLatch)
+ return;
+ }
+
+ if (isOldIdUnmapped(id)) {
+ error(std::string("old ID not mapped: ") + std::to_string(id));
+ return;
+ }
+
+ // Track max bound
+ maxBound = std::max(maxBound, localId(id) + 1);
+
+ if (errorLatch)
+ return;
+ }
+
+ bound(maxBound); // reset header ID bound to as big as it now needs to be
+ }
+
+ // Mark debug instructions for stripping
+ void spirvbin_t::stripDebug()
+ {
+ // Strip instructions in the stripOp set: debug info.
+ process(
+ [&](spv::Op opCode, unsigned start) {
+ // remember opcodes we want to strip later
+ if (isStripOp(opCode))
+ stripInst(start);
+ return true;
+ },
+ op_fn_nop);
+ }
+
+ // Mark instructions that refer to now-removed IDs for stripping
+ void spirvbin_t::stripDeadRefs()
+ {
+ process(
+ [&](spv::Op opCode, unsigned start) {
+ // strip opcodes pointing to removed data
+ switch (opCode) {
+ case spv::OpName:
+ case spv::OpMemberName:
+ case spv::OpDecorate:
+ case spv::OpMemberDecorate:
+ if (idPosR.find(asId(start+1)) == idPosR.end())
+ stripInst(start);
+ break;
+ default:
+ break; // leave it alone
+ }
+
+ return true;
+ },
+ op_fn_nop);
+
+ strip();
+ }
+
+ // Update local maps of ID, type, etc positions
+ void spirvbin_t::buildLocalMaps()
+ {
+ msg(2, 2, std::string("build local maps: "));
+
+ mapped.clear();
+ idMapL.clear();
+// preserve nameMap, so we don't clear that.
+ fnPos.clear();
+ fnCalls.clear();
+ typeConstPos.clear();
+ idPosR.clear();
+ entryPoint = spv::NoResult;
+ largestNewId = 0;
+
+ idMapL.resize(bound(), unused);
+
+ int fnStart = 0;
+ spv::Id fnRes = spv::NoResult;
+
+ // build local Id and name maps
+ process(
+ [&](spv::Op opCode, unsigned start) {
+ unsigned word = start+1;
+ spv::Id typeId = spv::NoResult;
+
+ if (spv::InstructionDesc[opCode].hasType())
+ typeId = asId(word++);
+
+ // If there's a result ID, remember the size of its type
+ if (spv::InstructionDesc[opCode].hasResult()) {
+ const spv::Id resultId = asId(word++);
+ idPosR[resultId] = start;
+
+ if (typeId != spv::NoResult) {
+ const unsigned idTypeSize = typeSizeInWords(typeId);
+
+ if (errorLatch)
+ return false;
+
+ if (idTypeSize != 0)
+ idTypeSizeMap[resultId] = idTypeSize;
+ }
+ }
+
+ if (opCode == spv::Op::OpName) {
+ const spv::Id target = asId(start+1);
+ const std::string name = literalString(start+2);
+ nameMap[name] = target;
+
+ } else if (opCode == spv::Op::OpFunctionCall) {
+ ++fnCalls[asId(start + 3)];
+ } else if (opCode == spv::Op::OpEntryPoint) {
+ entryPoint = asId(start + 2);
+ } else if (opCode == spv::Op::OpFunction) {
+ if (fnStart != 0) {
+ error("nested function found");
+ return false;
+ }
+
+ fnStart = start;
+ fnRes = asId(start + 2);
+ } else if (opCode == spv::Op::OpFunctionEnd) {
+ assert(fnRes != spv::NoResult);
+ if (fnStart == 0) {
+ error("function end without function start");
+ return false;
+ }
+
+ fnPos[fnRes] = range_t(fnStart, start + asWordCount(start));
+ fnStart = 0;
+ } else if (isConstOp(opCode)) {
+ if (errorLatch)
+ return false;
+
+ assert(asId(start + 2) != spv::NoResult);
+ typeConstPos.insert(start);
+ } else if (isTypeOp(opCode)) {
+ assert(asId(start + 1) != spv::NoResult);
+ typeConstPos.insert(start);
+ }
+
+ return false;
+ },
+
+ [this](spv::Id& id) { localId(id, unmapped); }
+ );
+ }
+
+ // Validate the SPIR header
+ void spirvbin_t::validate() const
+ {
+ msg(2, 2, std::string("validating: "));
+
+ if (spv.size() < header_size) {
+ error("file too short: ");
+ return;
+ }
+
+ if (magic() != spv::MagicNumber) {
+ error("bad magic number");
+ return;
+ }
+
+ // field 1 = version
+ // field 2 = generator magic
+ // field 3 = result <id> bound
+
+ if (schemaNum() != 0) {
+ error("bad schema, must be 0");
+ return;
+ }
+ }
+
+ int spirvbin_t::processInstruction(unsigned word, instfn_t instFn, idfn_t idFn)
+ {
+ const auto instructionStart = word;
+ const unsigned wordCount = asWordCount(instructionStart);
+ const int nextInst = word++ + wordCount;
+ spv::Op opCode = asOpCode(instructionStart);
+
+ if (nextInst > int(spv.size())) {
+ error("spir instruction terminated too early");
+ return -1;
+ }
+
+ // Base for computing number of operands; will be updated as more is learned
+ unsigned numOperands = wordCount - 1;
+
+ if (instFn(opCode, instructionStart))
+ return nextInst;
+
+ // Read type and result ID from instruction desc table
+ if (spv::InstructionDesc[opCode].hasType()) {
+ idFn(asId(word++));
+ --numOperands;
+ }
+
+ if (spv::InstructionDesc[opCode].hasResult()) {
+ idFn(asId(word++));
+ --numOperands;
+ }
+
+ // Extended instructions: currently, assume everything is an ID.
+ // TODO: add whatever data we need for exceptions to that
+ if (opCode == spv::OpExtInst) {
+ word += 2; // instruction set, and instruction from set
+ numOperands -= 2;
+
+ for (unsigned op=0; op < numOperands; ++op)
+ idFn(asId(word++)); // ID
+
+ return nextInst;
+ }
+
+ // Circular buffer so we can look back at previous unmapped values during the mapping pass.
+ static const unsigned idBufferSize = 4;
+ spv::Id idBuffer[idBufferSize];
+ unsigned idBufferPos = 0;
+
+ // Store IDs from instruction in our map
+ for (int op = 0; numOperands > 0; ++op, --numOperands) {
+ // SpecConstantOp is special: it includes the operands of another opcode which is
+ // given as a literal in the 3rd word. We will switch over to pretending that the
+ // opcode being processed is the literal opcode value of the SpecConstantOp. See the
+ // SPIRV spec for details. This way we will handle IDs and literals as appropriate for
+ // the embedded op.
+ if (opCode == spv::OpSpecConstantOp) {
+ if (op == 0) {
+ opCode = asOpCode(word++); // this is the opcode embedded in the SpecConstantOp.
+ --numOperands;
+ }
+ }
+
+ switch (spv::InstructionDesc[opCode].operands.getClass(op)) {
+ case spv::OperandId:
+ case spv::OperandScope:
+ case spv::OperandMemorySemantics:
+ idBuffer[idBufferPos] = asId(word);
+ idBufferPos = (idBufferPos + 1) % idBufferSize;
+ idFn(asId(word++));
+ break;
+
+ case spv::OperandVariableIds:
+ for (unsigned i = 0; i < numOperands; ++i)
+ idFn(asId(word++));
+ return nextInst;
+
+ case spv::OperandVariableLiterals:
+ // for clarity
+ // if (opCode == spv::OpDecorate && asDecoration(word - 1) == spv::DecorationBuiltIn) {
+ // ++word;
+ // --numOperands;
+ // }
+ // word += numOperands;
+ return nextInst;
+
+ case spv::OperandVariableLiteralId: {
+ if (opCode == OpSwitch) {
+ // word-2 is the position of the selector ID. OpSwitch Literals match its type.
+ // In case the IDs are currently being remapped, we get the word[-2] ID from
+ // the circular idBuffer.
+ const unsigned literalSizePos = (idBufferPos+idBufferSize-2) % idBufferSize;
+ const unsigned literalSize = idTypeSizeInWords(idBuffer[literalSizePos]);
+ const unsigned numLiteralIdPairs = (nextInst-word) / (1+literalSize);
+
+ if (errorLatch)
+ return -1;
+
+ for (unsigned arg=0; arg<numLiteralIdPairs; ++arg) {
+ word += literalSize; // literal
+ idFn(asId(word++)); // label
+ }
+ } else {
+ assert(0); // currentely, only OpSwitch uses OperandVariableLiteralId
+ }
+
+ return nextInst;
+ }
+
+ case spv::OperandLiteralString: {
+ const int stringWordCount = literalStringWords(literalString(word));
+ word += stringWordCount;
+ numOperands -= (stringWordCount-1); // -1 because for() header post-decrements
+ break;
+ }
+
+ // Execution mode might have extra literal operands. Skip them.
+ case spv::OperandExecutionMode:
+ return nextInst;
+
+ // Single word operands we simply ignore, as they hold no IDs
+ case spv::OperandLiteralNumber:
+ case spv::OperandSource:
+ case spv::OperandExecutionModel:
+ case spv::OperandAddressing:
+ case spv::OperandMemory:
+ case spv::OperandStorage:
+ case spv::OperandDimensionality:
+ case spv::OperandSamplerAddressingMode:
+ case spv::OperandSamplerFilterMode:
+ case spv::OperandSamplerImageFormat:
+ case spv::OperandImageChannelOrder:
+ case spv::OperandImageChannelDataType:
+ case spv::OperandImageOperands:
+ case spv::OperandFPFastMath:
+ case spv::OperandFPRoundingMode:
+ case spv::OperandLinkageType:
+ case spv::OperandAccessQualifier:
+ case spv::OperandFuncParamAttr:
+ case spv::OperandDecoration:
+ case spv::OperandBuiltIn:
+ case spv::OperandSelect:
+ case spv::OperandLoop:
+ case spv::OperandFunction:
+ case spv::OperandMemoryAccess:
+ case spv::OperandGroupOperation:
+ case spv::OperandKernelEnqueueFlags:
+ case spv::OperandKernelProfilingInfo:
+ case spv::OperandCapability:
+ ++word;
+ break;
+
+ default:
+ assert(0 && "Unhandled Operand Class");
+ break;
+ }
+ }
+
+ return nextInst;
+ }
+
+ // Make a pass over all the instructions and process them given appropriate functions
+ spirvbin_t& spirvbin_t::process(instfn_t instFn, idfn_t idFn, unsigned begin, unsigned end)
+ {
+ // For efficiency, reserve name map space. It can grow if needed.
+ nameMap.reserve(32);
+
+ // If begin or end == 0, use defaults
+ begin = (begin == 0 ? header_size : begin);
+ end = (end == 0 ? unsigned(spv.size()) : end);
+
+ // basic parsing and InstructionDesc table borrowed from SpvDisassemble.cpp...
+ unsigned nextInst = unsigned(spv.size());
+
+ for (unsigned word = begin; word < end; word = nextInst) {
+ nextInst = processInstruction(word, instFn, idFn);
+
+ if (errorLatch)
+ return *this;
+ }
+
+ return *this;
+ }
+
+ // Apply global name mapping to a single module
+ void spirvbin_t::mapNames()
+ {
+ static const std::uint32_t softTypeIdLimit = 3011; // small prime. TODO: get from options
+ static const std::uint32_t firstMappedID = 3019; // offset into ID space
+
+ for (const auto& name : nameMap) {
+ std::uint32_t hashval = 1911;
+ for (const char c : name.first)
+ hashval = hashval * 1009 + c;
+
+ if (isOldIdUnmapped(name.second)) {
+ localId(name.second, nextUnusedId(hashval % softTypeIdLimit + firstMappedID));
+ if (errorLatch)
+ return;
+ }
+ }
+ }
+
+ // Map fn contents to IDs of similar functions in other modules
+ void spirvbin_t::mapFnBodies()
+ {
+ static const std::uint32_t softTypeIdLimit = 19071; // small prime. TODO: get from options
+ static const std::uint32_t firstMappedID = 6203; // offset into ID space
+
+ // Initial approach: go through some high priority opcodes first and assign them
+ // hash values.
+
+ spv::Id fnId = spv::NoResult;
+ std::vector<unsigned> instPos;
+ instPos.reserve(unsigned(spv.size()) / 16); // initial estimate; can grow if needed.
+
+ // Build local table of instruction start positions
+ process(
+ [&](spv::Op, unsigned start) { instPos.push_back(start); return true; },
+ op_fn_nop);
+
+ if (errorLatch)
+ return;
+
+ // Window size for context-sensitive canonicalization values
+ // Empirical best size from a single data set. TODO: Would be a good tunable.
+ // We essentially perform a little convolution around each instruction,
+ // to capture the flavor of nearby code, to hopefully match to similar
+ // code in other modules.
+ static const unsigned windowSize = 2;
+
+ for (unsigned entry = 0; entry < unsigned(instPos.size()); ++entry) {
+ const unsigned start = instPos[entry];
+ const spv::Op opCode = asOpCode(start);
+
+ if (opCode == spv::OpFunction)
+ fnId = asId(start + 2);
+
+ if (opCode == spv::OpFunctionEnd)
+ fnId = spv::NoResult;
+
+ if (fnId != spv::NoResult) { // if inside a function
+ if (spv::InstructionDesc[opCode].hasResult()) {
+ const unsigned word = start + (spv::InstructionDesc[opCode].hasType() ? 2 : 1);
+ const spv::Id resId = asId(word);
+ std::uint32_t hashval = fnId * 17; // small prime
+
+ for (unsigned i = entry-1; i >= entry-windowSize; --i) {
+ if (asOpCode(instPos[i]) == spv::OpFunction)
+ break;
+ hashval = hashval * 30103 + asOpCodeHash(instPos[i]); // 30103 = semiarbitrary prime
+ }
+
+ for (unsigned i = entry; i <= entry + windowSize; ++i) {
+ if (asOpCode(instPos[i]) == spv::OpFunctionEnd)
+ break;
+ hashval = hashval * 30103 + asOpCodeHash(instPos[i]); // 30103 = semiarbitrary prime
+ }
+
+ if (isOldIdUnmapped(resId)) {
+ localId(resId, nextUnusedId(hashval % softTypeIdLimit + firstMappedID));
+ if (errorLatch)
+ return;
+ }
+
+ }
+ }
+ }
+
+ spv::Op thisOpCode(spv::OpNop);
+ std::unordered_map<int, int> opCounter;
+ int idCounter(0);
+ fnId = spv::NoResult;
+
+ process(
+ [&](spv::Op opCode, unsigned start) {
+ switch (opCode) {
+ case spv::OpFunction:
+ // Reset counters at each function
+ idCounter = 0;
+ opCounter.clear();
+ fnId = asId(start + 2);
+ break;
+
+ case spv::OpImageSampleImplicitLod:
+ case spv::OpImageSampleExplicitLod:
+ case spv::OpImageSampleDrefImplicitLod:
+ case spv::OpImageSampleDrefExplicitLod:
+ case spv::OpImageSampleProjImplicitLod:
+ case spv::OpImageSampleProjExplicitLod:
+ case spv::OpImageSampleProjDrefImplicitLod:
+ case spv::OpImageSampleProjDrefExplicitLod:
+ case spv::OpDot:
+ case spv::OpCompositeExtract:
+ case spv::OpCompositeInsert:
+ case spv::OpVectorShuffle:
+ case spv::OpLabel:
+ case spv::OpVariable:
+
+ case spv::OpAccessChain:
+ case spv::OpLoad:
+ case spv::OpStore:
+ case spv::OpCompositeConstruct:
+ case spv::OpFunctionCall:
+ ++opCounter[opCode];
+ idCounter = 0;
+ thisOpCode = opCode;
+ break;
+ default:
+ thisOpCode = spv::OpNop;
+ }
+
+ return false;
+ },
+
+ [&](spv::Id& id) {
+ if (thisOpCode != spv::OpNop) {
+ ++idCounter;
+ const std::uint32_t hashval = opCounter[thisOpCode] * thisOpCode * 50047 + idCounter + fnId * 117;
+
+ if (isOldIdUnmapped(id))
+ localId(id, nextUnusedId(hashval % softTypeIdLimit + firstMappedID));
+ }
+ });
+ }
+
+ // EXPERIMENTAL: forward IO and uniform load/stores into operands
+ // This produces invalid Schema-0 SPIRV
+ void spirvbin_t::forwardLoadStores()
+ {
+ idset_t fnLocalVars; // set of function local vars
+ idmap_t idMap; // Map of load result IDs to what they load
+
+ // EXPERIMENTAL: Forward input and access chain loads into consumptions
+ process(
+ [&](spv::Op opCode, unsigned start) {
+ // Add inputs and uniforms to the map
+ if ((opCode == spv::OpVariable && asWordCount(start) == 4) &&
+ (spv[start+3] == spv::StorageClassUniform ||
+ spv[start+3] == spv::StorageClassUniformConstant ||
+ spv[start+3] == spv::StorageClassInput))
+ fnLocalVars.insert(asId(start+2));
+
+ if (opCode == spv::OpAccessChain && fnLocalVars.count(asId(start+3)) > 0)
+ fnLocalVars.insert(asId(start+2));
+
+ if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) {
+ idMap[asId(start+2)] = asId(start+3);
+ stripInst(start);
+ }
+
+ return false;
+ },
+
+ [&](spv::Id& id) { if (idMap.find(id) != idMap.end()) id = idMap[id]; }
+ );
+
+ if (errorLatch)
+ return;
+
+ // EXPERIMENTAL: Implicit output stores
+ fnLocalVars.clear();
+ idMap.clear();
+
+ process(
+ [&](spv::Op opCode, unsigned start) {
+ // Add inputs and uniforms to the map
+ if ((opCode == spv::OpVariable && asWordCount(start) == 4) &&
+ (spv[start+3] == spv::StorageClassOutput))
+ fnLocalVars.insert(asId(start+2));
+
+ if (opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) {
+ idMap[asId(start+2)] = asId(start+1);
+ stripInst(start);
+ }
+
+ return false;
+ },
+ op_fn_nop);
+
+ if (errorLatch)
+ return;
+
+ process(
+ inst_fn_nop,
+ [&](spv::Id& id) { if (idMap.find(id) != idMap.end()) id = idMap[id]; }
+ );
+
+ if (errorLatch)
+ return;
+
+ strip(); // strip out data we decided to eliminate
+ }
+
+ // optimize loads and stores
+ void spirvbin_t::optLoadStore()
+ {
+ idset_t fnLocalVars; // candidates for removal (only locals)
+ idmap_t idMap; // Map of load result IDs to what they load
+ blockmap_t blockMap; // Map of IDs to blocks they first appear in
+ int blockNum = 0; // block count, to avoid crossing flow control
+
+ // Find all the function local pointers stored at most once, and not via access chains
+ process(
+ [&](spv::Op opCode, unsigned start) {
+ const int wordCount = asWordCount(start);
+
+ // Count blocks, so we can avoid crossing flow control
+ if (isFlowCtrl(opCode))
+ ++blockNum;
+
+ // Add local variables to the map
+ if ((opCode == spv::OpVariable && spv[start+3] == spv::StorageClassFunction && asWordCount(start) == 4)) {
+ fnLocalVars.insert(asId(start+2));
+ return true;
+ }
+
+ // Ignore process vars referenced via access chain
+ if ((opCode == spv::OpAccessChain || opCode == spv::OpInBoundsAccessChain) && fnLocalVars.count(asId(start+3)) > 0) {
+ fnLocalVars.erase(asId(start+3));
+ idMap.erase(asId(start+3));
+ return true;
+ }
+
+ if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) {
+ const spv::Id varId = asId(start+3);
+
+ // Avoid loads before stores
+ if (idMap.find(varId) == idMap.end()) {
+ fnLocalVars.erase(varId);
+ idMap.erase(varId);
+ }
+
+ // don't do for volatile references
+ if (wordCount > 4 && (spv[start+4] & spv::MemoryAccessVolatileMask)) {
+ fnLocalVars.erase(varId);
+ idMap.erase(varId);
+ }
+
+ // Handle flow control
+ if (blockMap.find(varId) == blockMap.end()) {
+ blockMap[varId] = blockNum; // track block we found it in.
+ } else if (blockMap[varId] != blockNum) {
+ fnLocalVars.erase(varId); // Ignore if crosses flow control
+ idMap.erase(varId);
+ }
+
+ return true;
+ }
+
+ if (opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) {
+ const spv::Id varId = asId(start+1);
+
+ if (idMap.find(varId) == idMap.end()) {
+ idMap[varId] = asId(start+2);
+ } else {
+ // Remove if it has more than one store to the same pointer
+ fnLocalVars.erase(varId);
+ idMap.erase(varId);
+ }
+
+ // don't do for volatile references
+ if (wordCount > 3 && (spv[start+3] & spv::MemoryAccessVolatileMask)) {
+ fnLocalVars.erase(asId(start+3));
+ idMap.erase(asId(start+3));
+ }
+
+ // Handle flow control
+ if (blockMap.find(varId) == blockMap.end()) {
+ blockMap[varId] = blockNum; // track block we found it in.
+ } else if (blockMap[varId] != blockNum) {
+ fnLocalVars.erase(varId); // Ignore if crosses flow control
+ idMap.erase(varId);
+ }
+
+ return true;
+ }
+
+ return false;
+ },
+
+ // If local var id used anywhere else, don't eliminate
+ [&](spv::Id& id) {
+ if (fnLocalVars.count(id) > 0) {
+ fnLocalVars.erase(id);
+ idMap.erase(id);
+ }
+ }
+ );
+
+ if (errorLatch)
+ return;
+
+ process(
+ [&](spv::Op opCode, unsigned start) {
+ if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0)
+ idMap[asId(start+2)] = idMap[asId(start+3)];
+ return false;
+ },
+ op_fn_nop);
+
+ if (errorLatch)
+ return;
+
+ // Chase replacements to their origins, in case there is a chain such as:
+ // 2 = store 1
+ // 3 = load 2
+ // 4 = store 3
+ // 5 = load 4
+ // We want to replace uses of 5 with 1.
+ for (const auto& idPair : idMap) {
+ spv::Id id = idPair.first;
+ while (idMap.find(id) != idMap.end()) // Chase to end of chain
+ id = idMap[id];
+
+ idMap[idPair.first] = id; // replace with final result
+ }
+
+ // Remove the load/store/variables for the ones we've discovered
+ process(
+ [&](spv::Op opCode, unsigned start) {
+ if ((opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) ||
+ (opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) ||
+ (opCode == spv::OpVariable && fnLocalVars.count(asId(start+2)) > 0)) {
+
+ stripInst(start);
+ return true;
+ }
+
+ return false;
+ },
+
+ [&](spv::Id& id) {
+ if (idMap.find(id) != idMap.end()) id = idMap[id];
+ }
+ );
+
+ if (errorLatch)
+ return;
+
+ strip(); // strip out data we decided to eliminate
+ }
+
+ // remove bodies of uncalled functions
+ void spirvbin_t::dceFuncs()
+ {
+ msg(3, 2, std::string("Removing Dead Functions: "));
+
+ // TODO: There are more efficient ways to do this.
+ bool changed = true;
+
+ while (changed) {
+ changed = false;
+
+ for (auto fn = fnPos.begin(); fn != fnPos.end(); ) {
+ if (fn->first == entryPoint) { // don't DCE away the entry point!
+ ++fn;
+ continue;
+ }
+
+ const auto call_it = fnCalls.find(fn->first);
+
+ if (call_it == fnCalls.end() || call_it->second == 0) {
+ changed = true;
+ stripRange.push_back(fn->second);
+
+ // decrease counts of called functions
+ process(
+ [&](spv::Op opCode, unsigned start) {
+ if (opCode == spv::Op::OpFunctionCall) {
+ const auto call_it = fnCalls.find(asId(start + 3));
+ if (call_it != fnCalls.end()) {
+ if (--call_it->second <= 0)
+ fnCalls.erase(call_it);
+ }
+ }
+
+ return true;
+ },
+ op_fn_nop,
+ fn->second.first,
+ fn->second.second);
+
+ if (errorLatch)
+ return;
+
+ fn = fnPos.erase(fn);
+ } else ++fn;
+ }
+ }
+ }
+
+ // remove unused function variables + decorations
+ void spirvbin_t::dceVars()
+ {
+ msg(3, 2, std::string("DCE Vars: "));
+
+ std::unordered_map<spv::Id, int> varUseCount;
+
+ // Count function variable use
+ process(
+ [&](spv::Op opCode, unsigned start) {
+ if (opCode == spv::OpVariable) {
+ ++varUseCount[asId(start+2)];
+ return true;
+ } else if (opCode == spv::OpEntryPoint) {
+ const int wordCount = asWordCount(start);
+ for (int i = 4; i < wordCount; i++) {
+ ++varUseCount[asId(start+i)];
+ }
+ return true;
+ } else
+ return false;
+ },
+
+ [&](spv::Id& id) { if (varUseCount[id]) ++varUseCount[id]; }
+ );
+
+ if (errorLatch)
+ return;
+
+ // Remove single-use function variables + associated decorations and names
+ process(
+ [&](spv::Op opCode, unsigned start) {
+ spv::Id id = spv::NoResult;
+ if (opCode == spv::OpVariable)
+ id = asId(start+2);
+ if (opCode == spv::OpDecorate || opCode == spv::OpName)
+ id = asId(start+1);
+
+ if (id != spv::NoResult && varUseCount[id] == 1)
+ stripInst(start);
+
+ return true;
+ },
+ op_fn_nop);
+ }
+
+ // remove unused types
+ void spirvbin_t::dceTypes()
+ {
+ std::vector<bool> isType(bound(), false);
+
+ // for speed, make O(1) way to get to type query (map is log(n))
+ for (const auto typeStart : typeConstPos)
+ isType[asTypeConstId(typeStart)] = true;
+
+ std::unordered_map<spv::Id, int> typeUseCount;
+
+ // This is not the most efficient algorithm, but this is an offline tool, and
+ // it's easy to write this way. Can be improved opportunistically if needed.
+ bool changed = true;
+ while (changed) {
+ changed = false;
+ strip();
+ typeUseCount.clear();
+
+ // Count total type usage
+ process(inst_fn_nop,
+ [&](spv::Id& id) { if (isType[id]) ++typeUseCount[id]; }
+ );
+
+ if (errorLatch)
+ return;
+
+ // Remove single reference types
+ for (const auto typeStart : typeConstPos) {
+ const spv::Id typeId = asTypeConstId(typeStart);
+ if (typeUseCount[typeId] == 1) {
+ changed = true;
+ --typeUseCount[typeId];
+ stripInst(typeStart);
+ }
+ }
+
+ if (errorLatch)
+ return;
+ }
+ }
+
+#ifdef NOTDEF
+ bool spirvbin_t::matchType(const spirvbin_t::globaltypes_t& globalTypes, spv::Id lt, spv::Id gt) const
+ {
+ // Find the local type id "lt" and global type id "gt"
+ const auto lt_it = typeConstPosR.find(lt);
+ if (lt_it == typeConstPosR.end())
+ return false;
+
+ const auto typeStart = lt_it->second;
+
+ // Search for entry in global table
+ const auto gtype = globalTypes.find(gt);
+ if (gtype == globalTypes.end())
+ return false;
+
+ const auto& gdata = gtype->second;
+
+ // local wordcount and opcode
+ const int wordCount = asWordCount(typeStart);
+ const spv::Op opCode = asOpCode(typeStart);
+
+ // no type match if opcodes don't match, or operand count doesn't match
+ if (opCode != opOpCode(gdata[0]) || wordCount != opWordCount(gdata[0]))
+ return false;
+
+ const unsigned numOperands = wordCount - 2; // all types have a result
+
+ const auto cmpIdRange = [&](range_t range) {
+ for (int x=range.first; x<std::min(range.second, wordCount); ++x)
+ if (!matchType(globalTypes, asId(typeStart+x), gdata[x]))
+ return false;
+ return true;
+ };
+
+ const auto cmpConst = [&]() { return cmpIdRange(constRange(opCode)); };
+ const auto cmpSubType = [&]() { return cmpIdRange(typeRange(opCode)); };
+
+ // Compare literals in range [start,end)
+ const auto cmpLiteral = [&]() {
+ const auto range = literalRange(opCode);
+ return std::equal(spir.begin() + typeStart + range.first,
+ spir.begin() + typeStart + std::min(range.second, wordCount),
+ gdata.begin() + range.first);
+ };
+
+ assert(isTypeOp(opCode) || isConstOp(opCode));
+
+ switch (opCode) {
+ case spv::OpTypeOpaque: // TODO: disable until we compare the literal strings.
+ case spv::OpTypeQueue: return false;
+ case spv::OpTypeEvent: // fall through...
+ case spv::OpTypeDeviceEvent: // ...
+ case spv::OpTypeReserveId: return false;
+ // for samplers, we don't handle the optional parameters yet
+ case spv::OpTypeSampler: return cmpLiteral() && cmpConst() && cmpSubType() && wordCount == 8;
+ default: return cmpLiteral() && cmpConst() && cmpSubType();
+ }
+ }
+
+ // Look for an equivalent type in the globalTypes map
+ spv::Id spirvbin_t::findType(const spirvbin_t::globaltypes_t& globalTypes, spv::Id lt) const
+ {
+ // Try a recursive type match on each in turn, and return a match if we find one
+ for (const auto& gt : globalTypes)
+ if (matchType(globalTypes, lt, gt.first))
+ return gt.first;
+
+ return spv::NoType;
+ }
+#endif // NOTDEF
+
+ // Return start position in SPV of given Id. error if not found.
+ unsigned spirvbin_t::idPos(spv::Id id) const
+ {
+ const auto tid_it = idPosR.find(id);
+ if (tid_it == idPosR.end()) {
+ error("ID not found");
+ return 0;
+ }
+
+ return tid_it->second;
+ }
+
+ // Hash types to canonical values. This can return ID collisions (it's a bit
+ // inevitable): it's up to the caller to handle that gracefully.
+ std::uint32_t spirvbin_t::hashType(unsigned typeStart) const
+ {
+ const unsigned wordCount = asWordCount(typeStart);
+ const spv::Op opCode = asOpCode(typeStart);
+
+ switch (opCode) {
+ case spv::OpTypeVoid: return 0;
+ case spv::OpTypeBool: return 1;
+ case spv::OpTypeInt: return 3 + (spv[typeStart+3]);
+ case spv::OpTypeFloat: return 5;
+ case spv::OpTypeVector:
+ return 6 + hashType(idPos(spv[typeStart+2])) * (spv[typeStart+3] - 1);
+ case spv::OpTypeMatrix:
+ return 30 + hashType(idPos(spv[typeStart+2])) * (spv[typeStart+3] - 1);
+ case spv::OpTypeImage:
+ return 120 + hashType(idPos(spv[typeStart+2])) +
+ spv[typeStart+3] + // dimensionality
+ spv[typeStart+4] * 8 * 16 + // depth
+ spv[typeStart+5] * 4 * 16 + // arrayed
+ spv[typeStart+6] * 2 * 16 + // multisampled
+ spv[typeStart+7] * 1 * 16; // format
+ case spv::OpTypeSampler:
+ return 500;
+ case spv::OpTypeSampledImage:
+ return 502;
+ case spv::OpTypeArray:
+ return 501 + hashType(idPos(spv[typeStart+2])) * spv[typeStart+3];
+ case spv::OpTypeRuntimeArray:
+ return 5000 + hashType(idPos(spv[typeStart+2]));
+ case spv::OpTypeStruct:
+ {
+ std::uint32_t hash = 10000;
+ for (unsigned w=2; w < wordCount; ++w)
+ hash += w * hashType(idPos(spv[typeStart+w]));
+ return hash;
+ }
+
+ case spv::OpTypeOpaque: return 6000 + spv[typeStart+2];
+ case spv::OpTypePointer: return 100000 + hashType(idPos(spv[typeStart+3]));
+ case spv::OpTypeFunction:
+ {
+ std::uint32_t hash = 200000;
+ for (unsigned w=2; w < wordCount; ++w)
+ hash += w * hashType(idPos(spv[typeStart+w]));
+ return hash;
+ }
+
+ case spv::OpTypeEvent: return 300000;
+ case spv::OpTypeDeviceEvent: return 300001;
+ case spv::OpTypeReserveId: return 300002;
+ case spv::OpTypeQueue: return 300003;
+ case spv::OpTypePipe: return 300004;
+ case spv::OpConstantTrue: return 300007;
+ case spv::OpConstantFalse: return 300008;
+ case spv::OpConstantComposite:
+ {
+ std::uint32_t hash = 300011 + hashType(idPos(spv[typeStart+1]));
+ for (unsigned w=3; w < wordCount; ++w)
+ hash += w * hashType(idPos(spv[typeStart+w]));
+ return hash;
+ }
+ case spv::OpConstant:
+ {
+ std::uint32_t hash = 400011 + hashType(idPos(spv[typeStart+1]));
+ for (unsigned w=3; w < wordCount; ++w)
+ hash += w * spv[typeStart+w];
+ return hash;
+ }
+ case spv::OpConstantNull:
+ {
+ std::uint32_t hash = 500009 + hashType(idPos(spv[typeStart+1]));
+ return hash;
+ }
+ case spv::OpConstantSampler:
+ {
+ std::uint32_t hash = 600011 + hashType(idPos(spv[typeStart+1]));
+ for (unsigned w=3; w < wordCount; ++w)
+ hash += w * spv[typeStart+w];
+ return hash;
+ }
+
+ default:
+ error("unknown type opcode");
+ return 0;
+ }
+ }
+
+ void spirvbin_t::mapTypeConst()
+ {
+ globaltypes_t globalTypeMap;
+
+ msg(3, 2, std::string("Remapping Consts & Types: "));
+
+ static const std::uint32_t softTypeIdLimit = 3011; // small prime. TODO: get from options
+ static const std::uint32_t firstMappedID = 8; // offset into ID space
+
+ for (auto& typeStart : typeConstPos) {
+ const spv::Id resId = asTypeConstId(typeStart);
+ const std::uint32_t hashval = hashType(typeStart);
+
+ if (errorLatch)
+ return;
+
+ if (isOldIdUnmapped(resId)) {
+ localId(resId, nextUnusedId(hashval % softTypeIdLimit + firstMappedID));
+ if (errorLatch)
+ return;
+ }
+ }
+ }
+
+ // Strip a single binary by removing ranges given in stripRange
+ void spirvbin_t::strip()
+ {
+ if (stripRange.empty()) // nothing to do
+ return;
+
+ // Sort strip ranges in order of traversal
+ std::sort(stripRange.begin(), stripRange.end());
+
+ // Allocate a new binary big enough to hold old binary
+ // We'll step this iterator through the strip ranges as we go through the binary
+ auto strip_it = stripRange.begin();
+
+ int strippedPos = 0;
+ for (unsigned word = 0; word < unsigned(spv.size()); ++word) {
+ while (strip_it != stripRange.end() && word >= strip_it->second)
+ ++strip_it;
+
+ if (strip_it == stripRange.end() || word < strip_it->first || word >= strip_it->second)
+ spv[strippedPos++] = spv[word];
+ }
+
+ spv.resize(strippedPos);
+ stripRange.clear();
+
+ buildLocalMaps();
+ }
+
+ // Strip a single binary by removing ranges given in stripRange
+ void spirvbin_t::remap(std::uint32_t opts)
+ {
+ options = opts;
+
+ // Set up opcode tables from SpvDoc
+ spv::Parameterize();
+
+ validate(); // validate header
+ buildLocalMaps(); // build ID maps
+
+ msg(3, 4, std::string("ID bound: ") + std::to_string(bound()));
+
+ if (options & STRIP) stripDebug();
+ if (errorLatch) return;
+
+ strip(); // strip out data we decided to eliminate
+ if (errorLatch) return;
+
+ if (options & OPT_LOADSTORE) optLoadStore();
+ if (errorLatch) return;
+
+ if (options & OPT_FWD_LS) forwardLoadStores();
+ if (errorLatch) return;
+
+ if (options & DCE_FUNCS) dceFuncs();
+ if (errorLatch) return;
+
+ if (options & DCE_VARS) dceVars();
+ if (errorLatch) return;
+
+ if (options & DCE_TYPES) dceTypes();
+ if (errorLatch) return;
+
+ strip(); // strip out data we decided to eliminate
+ if (errorLatch) return;
+
+ stripDeadRefs(); // remove references to things we DCEed
+ if (errorLatch) return;
+
+ // after the last strip, we must clean any debug info referring to now-deleted data
+
+ if (options & MAP_TYPES) mapTypeConst();
+ if (errorLatch) return;
+
+ if (options & MAP_NAMES) mapNames();
+ if (errorLatch) return;
+
+ if (options & MAP_FUNCS) mapFnBodies();
+ if (errorLatch) return;
+
+ if (options & MAP_ALL) {
+ mapRemainder(); // map any unmapped IDs
+ if (errorLatch) return;
+
+ applyMap(); // Now remap each shader to the new IDs we've come up with
+ if (errorLatch) return;
+ }
+ }
+
+ // remap from a memory image
+ void spirvbin_t::remap(std::vector<std::uint32_t>& in_spv, std::uint32_t opts)
+ {
+ spv.swap(in_spv);
+ remap(opts);
+ spv.swap(in_spv);
+ }
+
+} // namespace SPV
+
+#endif // defined (use_cpp11)
+
diff --git a/src/3rdparty/glslang/SPIRV/SPVRemapper.h b/src/3rdparty/glslang/SPIRV/SPVRemapper.h
new file mode 100644
index 0000000..97e3f31
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/SPVRemapper.h
@@ -0,0 +1,304 @@
+//
+// Copyright (C) 2015 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef SPIRVREMAPPER_H
+#define SPIRVREMAPPER_H
+
+#include <string>
+#include <vector>
+#include <cstdlib>
+#include <exception>
+
+namespace spv {
+
+// MSVC defines __cplusplus as an older value, even when it supports almost all of 11.
+// We handle that here by making our own symbol.
+#if __cplusplus >= 201103L || _MSC_VER >= 1700
+# define use_cpp11 1
+#endif
+
+class spirvbin_base_t
+{
+public:
+ enum Options {
+ NONE = 0,
+ STRIP = (1<<0),
+ MAP_TYPES = (1<<1),
+ MAP_NAMES = (1<<2),
+ MAP_FUNCS = (1<<3),
+ DCE_FUNCS = (1<<4),
+ DCE_VARS = (1<<5),
+ DCE_TYPES = (1<<6),
+ OPT_LOADSTORE = (1<<7),
+ OPT_FWD_LS = (1<<8), // EXPERIMENTAL: PRODUCES INVALID SCHEMA-0 SPIRV
+ MAP_ALL = (MAP_TYPES | MAP_NAMES | MAP_FUNCS),
+ DCE_ALL = (DCE_FUNCS | DCE_VARS | DCE_TYPES),
+ OPT_ALL = (OPT_LOADSTORE),
+
+ ALL_BUT_STRIP = (MAP_ALL | DCE_ALL | OPT_ALL),
+ DO_EVERYTHING = (STRIP | ALL_BUT_STRIP)
+ };
+};
+
+} // namespace SPV
+
+#if !defined (use_cpp11)
+#include <cstdio>
+#include <cstdint>
+
+namespace spv {
+class spirvbin_t : public spirvbin_base_t
+{
+public:
+ spirvbin_t(int /*verbose = 0*/) { }
+
+ void remap(std::vector<std::uint32_t>& /*spv*/, unsigned int /*opts = 0*/)
+ {
+ printf("Tool not compiled for C++11, which is required for SPIR-V remapping.\n");
+ exit(5);
+ }
+};
+
+} // namespace SPV
+
+#else // defined (use_cpp11)
+
+#include <functional>
+#include <cstdint>
+#include <unordered_map>
+#include <unordered_set>
+#include <map>
+#include <set>
+#include <cassert>
+
+#include "spirv.hpp"
+#include "spvIR.h"
+
+namespace spv {
+
+// class to hold SPIR-V binary data for remapping, DCE, and debug stripping
+class spirvbin_t : public spirvbin_base_t
+{
+public:
+ spirvbin_t(int verbose = 0) : entryPoint(spv::NoResult), largestNewId(0), verbose(verbose), errorLatch(false)
+ { }
+
+ virtual ~spirvbin_t() { }
+
+ // remap on an existing binary in memory
+ void remap(std::vector<std::uint32_t>& spv, std::uint32_t opts = DO_EVERYTHING);
+
+ // Type for error/log handler functions
+ typedef std::function<void(const std::string&)> errorfn_t;
+ typedef std::function<void(const std::string&)> logfn_t;
+
+ // Register error/log handling functions (can be lambda fn / functor / etc)
+ static void registerErrorHandler(errorfn_t handler) { errorHandler = handler; }
+ static void registerLogHandler(logfn_t handler) { logHandler = handler; }
+
+protected:
+ // This can be overridden to provide other message behavior if needed
+ virtual void msg(int minVerbosity, int indent, const std::string& txt) const;
+
+private:
+ // Local to global, or global to local ID map
+ typedef std::unordered_map<spv::Id, spv::Id> idmap_t;
+ typedef std::unordered_set<spv::Id> idset_t;
+ typedef std::unordered_map<spv::Id, int> blockmap_t;
+
+ void remap(std::uint32_t opts = DO_EVERYTHING);
+
+ // Map of names to IDs
+ typedef std::unordered_map<std::string, spv::Id> namemap_t;
+
+ typedef std::uint32_t spirword_t;
+
+ typedef std::pair<unsigned, unsigned> range_t;
+ typedef std::function<void(spv::Id&)> idfn_t;
+ typedef std::function<bool(spv::Op, unsigned start)> instfn_t;
+
+ // Special Values for ID map:
+ static const spv::Id unmapped; // unchanged from default value
+ static const spv::Id unused; // unused ID
+ static const int header_size; // SPIR header = 5 words
+
+ class id_iterator_t;
+
+ // For mapping type entries between different shaders
+ typedef std::vector<spirword_t> typeentry_t;
+ typedef std::map<spv::Id, typeentry_t> globaltypes_t;
+
+ // A set that preserves position order, and a reverse map
+ typedef std::set<int> posmap_t;
+ typedef std::unordered_map<spv::Id, int> posmap_rev_t;
+
+ // Maps and ID to the size of its base type, if known.
+ typedef std::unordered_map<spv::Id, unsigned> typesize_map_t;
+
+ // handle error
+ void error(const std::string& txt) const { errorLatch = true; errorHandler(txt); }
+
+ bool isConstOp(spv::Op opCode) const;
+ bool isTypeOp(spv::Op opCode) const;
+ bool isStripOp(spv::Op opCode) const;
+ bool isFlowCtrl(spv::Op opCode) const;
+ range_t literalRange(spv::Op opCode) const;
+ range_t typeRange(spv::Op opCode) const;
+ range_t constRange(spv::Op opCode) const;
+ unsigned typeSizeInWords(spv::Id id) const;
+ unsigned idTypeSizeInWords(spv::Id id) const;
+
+ spv::Id& asId(unsigned word) { return spv[word]; }
+ const spv::Id& asId(unsigned word) const { return spv[word]; }
+ spv::Op asOpCode(unsigned word) const { return opOpCode(spv[word]); }
+ std::uint32_t asOpCodeHash(unsigned word);
+ spv::Decoration asDecoration(unsigned word) const { return spv::Decoration(spv[word]); }
+ unsigned asWordCount(unsigned word) const { return opWordCount(spv[word]); }
+ spv::Id asTypeConstId(unsigned word) const { return asId(word + (isTypeOp(asOpCode(word)) ? 1 : 2)); }
+ unsigned idPos(spv::Id id) const;
+
+ static unsigned opWordCount(spirword_t data) { return data >> spv::WordCountShift; }
+ static spv::Op opOpCode(spirword_t data) { return spv::Op(data & spv::OpCodeMask); }
+
+ // Header access & set methods
+ spirword_t magic() const { return spv[0]; } // return magic number
+ spirword_t bound() const { return spv[3]; } // return Id bound from header
+ spirword_t bound(spirword_t b) { return spv[3] = b; };
+ spirword_t genmagic() const { return spv[2]; } // generator magic
+ spirword_t genmagic(spirword_t m) { return spv[2] = m; }
+ spirword_t schemaNum() const { return spv[4]; } // schema number from header
+
+ // Mapping fns: get
+ spv::Id localId(spv::Id id) const { return idMapL[id]; }
+
+ // Mapping fns: set
+ inline spv::Id localId(spv::Id id, spv::Id newId);
+ void countIds(spv::Id id);
+
+ // Return next unused new local ID.
+ // NOTE: boost::dynamic_bitset would be more efficient due to find_next(),
+ // which std::vector<bool> doens't have.
+ inline spv::Id nextUnusedId(spv::Id id);
+
+ void buildLocalMaps();
+ std::string literalString(unsigned word) const; // Return literal as a std::string
+ int literalStringWords(const std::string& str) const { return (int(str.size())+4)/4; }
+
+ bool isNewIdMapped(spv::Id newId) const { return isMapped(newId); }
+ bool isOldIdUnmapped(spv::Id oldId) const { return localId(oldId) == unmapped; }
+ bool isOldIdUnused(spv::Id oldId) const { return localId(oldId) == unused; }
+ bool isOldIdMapped(spv::Id oldId) const { return !isOldIdUnused(oldId) && !isOldIdUnmapped(oldId); }
+ bool isFunction(spv::Id oldId) const { return fnPos.find(oldId) != fnPos.end(); }
+
+ // bool matchType(const globaltypes_t& globalTypes, spv::Id lt, spv::Id gt) const;
+ // spv::Id findType(const globaltypes_t& globalTypes, spv::Id lt) const;
+ std::uint32_t hashType(unsigned typeStart) const;
+
+ spirvbin_t& process(instfn_t, idfn_t, unsigned begin = 0, unsigned end = 0);
+ int processInstruction(unsigned word, instfn_t, idfn_t);
+
+ void validate() const;
+ void mapTypeConst();
+ void mapFnBodies();
+ void optLoadStore();
+ void dceFuncs();
+ void dceVars();
+ void dceTypes();
+ void mapNames();
+ void foldIds(); // fold IDs to smallest space
+ void forwardLoadStores(); // load store forwarding (EXPERIMENTAL)
+ void offsetIds(); // create relative offset IDs
+
+ void applyMap(); // remap per local name map
+ void mapRemainder(); // map any IDs we haven't touched yet
+ void stripDebug(); // strip all debug info
+ void stripDeadRefs(); // strips debug info for now-dead references after DCE
+ void strip(); // remove debug symbols
+
+ std::vector<spirword_t> spv; // SPIR words
+
+ namemap_t nameMap; // ID names from OpName
+
+ // Since we want to also do binary ops, we can't use std::vector<bool>. we could use
+ // boost::dynamic_bitset, but we're trying to avoid a boost dependency.
+ typedef std::uint64_t bits_t;
+ std::vector<bits_t> mapped; // which new IDs have been mapped
+ static const int mBits = sizeof(bits_t) * 4;
+
+ bool isMapped(spv::Id id) const { return id < maxMappedId() && ((mapped[id/mBits] & (1LL<<(id%mBits))) != 0); }
+ void setMapped(spv::Id id) { resizeMapped(id); mapped[id/mBits] |= (1LL<<(id%mBits)); }
+ void resizeMapped(spv::Id id) { if (id >= maxMappedId()) mapped.resize(id/mBits+1, 0); }
+ size_t maxMappedId() const { return mapped.size() * mBits; }
+
+ // Add a strip range for a given instruction starting at 'start'
+ // Note: avoiding brace initializers to please older versions os MSVC.
+ void stripInst(unsigned start) { stripRange.push_back(range_t(start, start + asWordCount(start))); }
+
+ // Function start and end. use unordered_map because we'll have
+ // many fewer functions than IDs.
+ std::unordered_map<spv::Id, range_t> fnPos;
+
+ // Which functions are called, anywhere in the module, with a call count
+ std::unordered_map<spv::Id, int> fnCalls;
+
+ posmap_t typeConstPos; // word positions that define types & consts (ordered)
+ posmap_rev_t idPosR; // reverse map from IDs to positions
+ typesize_map_t idTypeSizeMap; // maps each ID to its type size, if known.
+
+ std::vector<spv::Id> idMapL; // ID {M}ap from {L}ocal to {G}lobal IDs
+
+ spv::Id entryPoint; // module entry point
+ spv::Id largestNewId; // biggest new ID we have mapped anything to
+
+ // Sections of the binary to strip, given as [begin,end)
+ std::vector<range_t> stripRange;
+
+ // processing options:
+ std::uint32_t options;
+ int verbose; // verbosity level
+
+ // Error latch: this is set if the error handler is ever executed. It would be better to
+ // use a try/catch block and throw, but that's not desired for certain environments, so
+ // this is the alternative.
+ mutable bool errorLatch;
+
+ static errorfn_t errorHandler;
+ static logfn_t logHandler;
+};
+
+} // namespace SPV
+
+#endif // defined (use_cpp11)
+#endif // SPIRVREMAPPER_H
diff --git a/src/3rdparty/glslang/SPIRV/SpvBuilder.cpp b/src/3rdparty/glslang/SPIRV/SpvBuilder.cpp
new file mode 100644
index 0000000..138c41c
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/SpvBuilder.cpp
@@ -0,0 +1,3048 @@
+//
+// Copyright (C) 2014-2015 LunarG, Inc.
+// Copyright (C) 2015-2018 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+//
+// Helper for making SPIR-V IR. Generally, this is documented in the header
+// SpvBuilder.h.
+//
+
+#include <cassert>
+#include <cstdlib>
+
+#include <unordered_set>
+#include <algorithm>
+
+#include "SpvBuilder.h"
+
+#include "hex_float.h"
+
+#ifndef _WIN32
+ #include <cstdio>
+#endif
+
+namespace spv {
+
+Builder::Builder(unsigned int spvVersion, unsigned int magicNumber, SpvBuildLogger* buildLogger) :
+ spvVersion(spvVersion),
+ source(SourceLanguageUnknown),
+ sourceVersion(0),
+ sourceFileStringId(NoResult),
+ currentLine(0),
+ currentFile(nullptr),
+ emitOpLines(false),
+ addressModel(AddressingModelLogical),
+ memoryModel(MemoryModelGLSL450),
+ builderNumber(magicNumber),
+ buildPoint(0),
+ uniqueId(0),
+ entryPointFunction(0),
+ generatingOpCodeForSpecConst(false),
+ logger(buildLogger)
+{
+ clearAccessChain();
+}
+
+Builder::~Builder()
+{
+}
+
+Id Builder::import(const char* name)
+{
+ Instruction* import = new Instruction(getUniqueId(), NoType, OpExtInstImport);
+ import->addStringOperand(name);
+ module.mapInstruction(import);
+
+ imports.push_back(std::unique_ptr<Instruction>(import));
+ return import->getResultId();
+}
+
+// Emit instruction for non-filename-based #line directives (ie. no filename
+// seen yet): emit an OpLine if we've been asked to emit OpLines and the line
+// number has changed since the last time, and is a valid line number.
+void Builder::setLine(int lineNum)
+{
+ if (lineNum != 0 && lineNum != currentLine) {
+ currentLine = lineNum;
+ if (emitOpLines)
+ addLine(sourceFileStringId, currentLine, 0);
+ }
+}
+
+// If no filename, do non-filename-based #line emit. Else do filename-based emit.
+// Emit OpLine if we've been asked to emit OpLines and the line number or filename
+// has changed since the last time, and line number is valid.
+void Builder::setLine(int lineNum, const char* filename)
+{
+ if (filename == nullptr) {
+ setLine(lineNum);
+ return;
+ }
+ if ((lineNum != 0 && lineNum != currentLine) || currentFile == nullptr ||
+ strncmp(filename, currentFile, strlen(currentFile) + 1) != 0) {
+ currentLine = lineNum;
+ currentFile = filename;
+ if (emitOpLines) {
+ spv::Id strId = getStringId(filename);
+ addLine(strId, currentLine, 0);
+ }
+ }
+}
+
+void Builder::addLine(Id fileName, int lineNum, int column)
+{
+ Instruction* line = new Instruction(OpLine);
+ line->addIdOperand(fileName);
+ line->addImmediateOperand(lineNum);
+ line->addImmediateOperand(column);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(line));
+}
+
+// For creating new groupedTypes (will return old type if the requested one was already made).
+Id Builder::makeVoidType()
+{
+ Instruction* type;
+ if (groupedTypes[OpTypeVoid].size() == 0) {
+ type = new Instruction(getUniqueId(), NoType, OpTypeVoid);
+ groupedTypes[OpTypeVoid].push_back(type);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
+ module.mapInstruction(type);
+ } else
+ type = groupedTypes[OpTypeVoid].back();
+
+ return type->getResultId();
+}
+
+Id Builder::makeBoolType()
+{
+ Instruction* type;
+ if (groupedTypes[OpTypeBool].size() == 0) {
+ type = new Instruction(getUniqueId(), NoType, OpTypeBool);
+ groupedTypes[OpTypeBool].push_back(type);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
+ module.mapInstruction(type);
+ } else
+ type = groupedTypes[OpTypeBool].back();
+
+ return type->getResultId();
+}
+
+Id Builder::makeSamplerType()
+{
+ Instruction* type;
+ if (groupedTypes[OpTypeSampler].size() == 0) {
+ type = new Instruction(getUniqueId(), NoType, OpTypeSampler);
+ groupedTypes[OpTypeSampler].push_back(type);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
+ module.mapInstruction(type);
+ } else
+ type = groupedTypes[OpTypeSampler].back();
+
+ return type->getResultId();
+}
+
+Id Builder::makePointer(StorageClass storageClass, Id pointee)
+{
+ // try to find it
+ Instruction* type;
+ for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) {
+ type = groupedTypes[OpTypePointer][t];
+ if (type->getImmediateOperand(0) == (unsigned)storageClass &&
+ type->getIdOperand(1) == pointee)
+ return type->getResultId();
+ }
+
+ // not found, make it
+ type = new Instruction(getUniqueId(), NoType, OpTypePointer);
+ type->addImmediateOperand(storageClass);
+ type->addIdOperand(pointee);
+ groupedTypes[OpTypePointer].push_back(type);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
+ module.mapInstruction(type);
+
+ return type->getResultId();
+}
+
+Id Builder::makeForwardPointer(StorageClass storageClass)
+{
+ // Caching/uniquifying doesn't work here, because we don't know the
+ // pointee type and there can be multiple forward pointers of the same
+ // storage type. Somebody higher up in the stack must keep track.
+ Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeForwardPointer);
+ type->addImmediateOperand(storageClass);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
+ module.mapInstruction(type);
+
+ return type->getResultId();
+}
+
+Id Builder::makePointerFromForwardPointer(StorageClass storageClass, Id forwardPointerType, Id pointee)
+{
+ // try to find it
+ Instruction* type;
+ for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) {
+ type = groupedTypes[OpTypePointer][t];
+ if (type->getImmediateOperand(0) == (unsigned)storageClass &&
+ type->getIdOperand(1) == pointee)
+ return type->getResultId();
+ }
+
+ type = new Instruction(forwardPointerType, NoType, OpTypePointer);
+ type->addImmediateOperand(storageClass);
+ type->addIdOperand(pointee);
+ groupedTypes[OpTypePointer].push_back(type);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
+ module.mapInstruction(type);
+
+ return type->getResultId();
+}
+
+Id Builder::makeIntegerType(int width, bool hasSign)
+{
+ // try to find it
+ Instruction* type;
+ for (int t = 0; t < (int)groupedTypes[OpTypeInt].size(); ++t) {
+ type = groupedTypes[OpTypeInt][t];
+ if (type->getImmediateOperand(0) == (unsigned)width &&
+ type->getImmediateOperand(1) == (hasSign ? 1u : 0u))
+ return type->getResultId();
+ }
+
+ // not found, make it
+ type = new Instruction(getUniqueId(), NoType, OpTypeInt);
+ type->addImmediateOperand(width);
+ type->addImmediateOperand(hasSign ? 1 : 0);
+ groupedTypes[OpTypeInt].push_back(type);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
+ module.mapInstruction(type);
+
+ // deal with capabilities
+ switch (width) {
+ case 8:
+ case 16:
+ // these are currently handled by storage-type declarations and post processing
+ break;
+ case 64:
+ addCapability(CapabilityInt64);
+ break;
+ default:
+ break;
+ }
+
+ return type->getResultId();
+}
+
+Id Builder::makeFloatType(int width)
+{
+ // try to find it
+ Instruction* type;
+ for (int t = 0; t < (int)groupedTypes[OpTypeFloat].size(); ++t) {
+ type = groupedTypes[OpTypeFloat][t];
+ if (type->getImmediateOperand(0) == (unsigned)width)
+ return type->getResultId();
+ }
+
+ // not found, make it
+ type = new Instruction(getUniqueId(), NoType, OpTypeFloat);
+ type->addImmediateOperand(width);
+ groupedTypes[OpTypeFloat].push_back(type);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
+ module.mapInstruction(type);
+
+ // deal with capabilities
+ switch (width) {
+ case 16:
+ // currently handled by storage-type declarations and post processing
+ break;
+ case 64:
+ addCapability(CapabilityFloat64);
+ break;
+ default:
+ break;
+ }
+
+ return type->getResultId();
+}
+
+// Make a struct without checking for duplication.
+// See makeStructResultType() for non-decorated structs
+// needed as the result of some instructions, which does
+// check for duplicates.
+Id Builder::makeStructType(const std::vector<Id>& members, const char* name)
+{
+ // Don't look for previous one, because in the general case,
+ // structs can be duplicated except for decorations.
+
+ // not found, make it
+ Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeStruct);
+ for (int op = 0; op < (int)members.size(); ++op)
+ type->addIdOperand(members[op]);
+ groupedTypes[OpTypeStruct].push_back(type);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
+ module.mapInstruction(type);
+ addName(type->getResultId(), name);
+
+ return type->getResultId();
+}
+
+// Make a struct for the simple results of several instructions,
+// checking for duplication.
+Id Builder::makeStructResultType(Id type0, Id type1)
+{
+ // try to find it
+ Instruction* type;
+ for (int t = 0; t < (int)groupedTypes[OpTypeStruct].size(); ++t) {
+ type = groupedTypes[OpTypeStruct][t];
+ if (type->getNumOperands() != 2)
+ continue;
+ if (type->getIdOperand(0) != type0 ||
+ type->getIdOperand(1) != type1)
+ continue;
+ return type->getResultId();
+ }
+
+ // not found, make it
+ std::vector<spv::Id> members;
+ members.push_back(type0);
+ members.push_back(type1);
+
+ return makeStructType(members, "ResType");
+}
+
+Id Builder::makeVectorType(Id component, int size)
+{
+ // try to find it
+ Instruction* type;
+ for (int t = 0; t < (int)groupedTypes[OpTypeVector].size(); ++t) {
+ type = groupedTypes[OpTypeVector][t];
+ if (type->getIdOperand(0) == component &&
+ type->getImmediateOperand(1) == (unsigned)size)
+ return type->getResultId();
+ }
+
+ // not found, make it
+ type = new Instruction(getUniqueId(), NoType, OpTypeVector);
+ type->addIdOperand(component);
+ type->addImmediateOperand(size);
+ groupedTypes[OpTypeVector].push_back(type);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
+ module.mapInstruction(type);
+
+ return type->getResultId();
+}
+
+Id Builder::makeMatrixType(Id component, int cols, int rows)
+{
+ assert(cols <= maxMatrixSize && rows <= maxMatrixSize);
+
+ Id column = makeVectorType(component, rows);
+
+ // try to find it
+ Instruction* type;
+ for (int t = 0; t < (int)groupedTypes[OpTypeMatrix].size(); ++t) {
+ type = groupedTypes[OpTypeMatrix][t];
+ if (type->getIdOperand(0) == column &&
+ type->getImmediateOperand(1) == (unsigned)cols)
+ return type->getResultId();
+ }
+
+ // not found, make it
+ type = new Instruction(getUniqueId(), NoType, OpTypeMatrix);
+ type->addIdOperand(column);
+ type->addImmediateOperand(cols);
+ groupedTypes[OpTypeMatrix].push_back(type);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
+ module.mapInstruction(type);
+
+ return type->getResultId();
+}
+
+Id Builder::makeCooperativeMatrixType(Id component, Id scope, Id rows, Id cols)
+{
+ // try to find it
+ Instruction* type;
+ for (int t = 0; t < (int)groupedTypes[OpTypeCooperativeMatrixNV].size(); ++t) {
+ type = groupedTypes[OpTypeCooperativeMatrixNV][t];
+ if (type->getIdOperand(0) == component &&
+ type->getIdOperand(1) == scope &&
+ type->getIdOperand(2) == rows &&
+ type->getIdOperand(3) == cols)
+ return type->getResultId();
+ }
+
+ // not found, make it
+ type = new Instruction(getUniqueId(), NoType, OpTypeCooperativeMatrixNV);
+ type->addIdOperand(component);
+ type->addIdOperand(scope);
+ type->addIdOperand(rows);
+ type->addIdOperand(cols);
+ groupedTypes[OpTypeCooperativeMatrixNV].push_back(type);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
+ module.mapInstruction(type);
+
+ return type->getResultId();
+}
+
+
+// TODO: performance: track arrays per stride
+// If a stride is supplied (non-zero) make an array.
+// If no stride (0), reuse previous array types.
+// 'size' is an Id of a constant or specialization constant of the array size
+Id Builder::makeArrayType(Id element, Id sizeId, int stride)
+{
+ Instruction* type;
+ if (stride == 0) {
+ // try to find existing type
+ for (int t = 0; t < (int)groupedTypes[OpTypeArray].size(); ++t) {
+ type = groupedTypes[OpTypeArray][t];
+ if (type->getIdOperand(0) == element &&
+ type->getIdOperand(1) == sizeId)
+ return type->getResultId();
+ }
+ }
+
+ // not found, make it
+ type = new Instruction(getUniqueId(), NoType, OpTypeArray);
+ type->addIdOperand(element);
+ type->addIdOperand(sizeId);
+ groupedTypes[OpTypeArray].push_back(type);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
+ module.mapInstruction(type);
+
+ return type->getResultId();
+}
+
+Id Builder::makeRuntimeArray(Id element)
+{
+ Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeRuntimeArray);
+ type->addIdOperand(element);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
+ module.mapInstruction(type);
+
+ return type->getResultId();
+}
+
+Id Builder::makeFunctionType(Id returnType, const std::vector<Id>& paramTypes)
+{
+ // try to find it
+ Instruction* type;
+ for (int t = 0; t < (int)groupedTypes[OpTypeFunction].size(); ++t) {
+ type = groupedTypes[OpTypeFunction][t];
+ if (type->getIdOperand(0) != returnType || (int)paramTypes.size() != type->getNumOperands() - 1)
+ continue;
+ bool mismatch = false;
+ for (int p = 0; p < (int)paramTypes.size(); ++p) {
+ if (paramTypes[p] != type->getIdOperand(p + 1)) {
+ mismatch = true;
+ break;
+ }
+ }
+ if (! mismatch)
+ return type->getResultId();
+ }
+
+ // not found, make it
+ type = new Instruction(getUniqueId(), NoType, OpTypeFunction);
+ type->addIdOperand(returnType);
+ for (int p = 0; p < (int)paramTypes.size(); ++p)
+ type->addIdOperand(paramTypes[p]);
+ groupedTypes[OpTypeFunction].push_back(type);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
+ module.mapInstruction(type);
+
+ return type->getResultId();
+}
+
+Id Builder::makeImageType(Id sampledType, Dim dim, bool depth, bool arrayed, bool ms, unsigned sampled, ImageFormat format)
+{
+ assert(sampled == 1 || sampled == 2);
+
+ // try to find it
+ Instruction* type;
+ for (int t = 0; t < (int)groupedTypes[OpTypeImage].size(); ++t) {
+ type = groupedTypes[OpTypeImage][t];
+ if (type->getIdOperand(0) == sampledType &&
+ type->getImmediateOperand(1) == (unsigned int)dim &&
+ type->getImmediateOperand(2) == ( depth ? 1u : 0u) &&
+ type->getImmediateOperand(3) == (arrayed ? 1u : 0u) &&
+ type->getImmediateOperand(4) == ( ms ? 1u : 0u) &&
+ type->getImmediateOperand(5) == sampled &&
+ type->getImmediateOperand(6) == (unsigned int)format)
+ return type->getResultId();
+ }
+
+ // not found, make it
+ type = new Instruction(getUniqueId(), NoType, OpTypeImage);
+ type->addIdOperand(sampledType);
+ type->addImmediateOperand( dim);
+ type->addImmediateOperand( depth ? 1 : 0);
+ type->addImmediateOperand(arrayed ? 1 : 0);
+ type->addImmediateOperand( ms ? 1 : 0);
+ type->addImmediateOperand(sampled);
+ type->addImmediateOperand((unsigned int)format);
+
+ groupedTypes[OpTypeImage].push_back(type);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
+ module.mapInstruction(type);
+
+ // deal with capabilities
+ switch (dim) {
+ case DimBuffer:
+ if (sampled == 1)
+ addCapability(CapabilitySampledBuffer);
+ else
+ addCapability(CapabilityImageBuffer);
+ break;
+ case Dim1D:
+ if (sampled == 1)
+ addCapability(CapabilitySampled1D);
+ else
+ addCapability(CapabilityImage1D);
+ break;
+ case DimCube:
+ if (arrayed) {
+ if (sampled == 1)
+ addCapability(CapabilitySampledCubeArray);
+ else
+ addCapability(CapabilityImageCubeArray);
+ }
+ break;
+ case DimRect:
+ if (sampled == 1)
+ addCapability(CapabilitySampledRect);
+ else
+ addCapability(CapabilityImageRect);
+ break;
+ case DimSubpassData:
+ addCapability(CapabilityInputAttachment);
+ break;
+ default:
+ break;
+ }
+
+ if (ms) {
+ if (sampled == 2) {
+ // Images used with subpass data are not storage
+ // images, so don't require the capability for them.
+ if (dim != Dim::DimSubpassData)
+ addCapability(CapabilityStorageImageMultisample);
+ if (arrayed)
+ addCapability(CapabilityImageMSArray);
+ }
+ }
+
+ return type->getResultId();
+}
+
+Id Builder::makeSampledImageType(Id imageType)
+{
+ // try to find it
+ Instruction* type;
+ for (int t = 0; t < (int)groupedTypes[OpTypeSampledImage].size(); ++t) {
+ type = groupedTypes[OpTypeSampledImage][t];
+ if (type->getIdOperand(0) == imageType)
+ return type->getResultId();
+ }
+
+ // not found, make it
+ type = new Instruction(getUniqueId(), NoType, OpTypeSampledImage);
+ type->addIdOperand(imageType);
+
+ groupedTypes[OpTypeSampledImage].push_back(type);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
+ module.mapInstruction(type);
+
+ return type->getResultId();
+}
+
+#ifdef NV_EXTENSIONS
+Id Builder::makeAccelerationStructureNVType()
+{
+ Instruction *type;
+ if (groupedTypes[OpTypeAccelerationStructureNV].size() == 0) {
+ type = new Instruction(getUniqueId(), NoType, OpTypeAccelerationStructureNV);
+ groupedTypes[OpTypeAccelerationStructureNV].push_back(type);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(type));
+ module.mapInstruction(type);
+ } else {
+ type = groupedTypes[OpTypeAccelerationStructureNV].back();
+ }
+
+ return type->getResultId();
+}
+#endif
+Id Builder::getDerefTypeId(Id resultId) const
+{
+ Id typeId = getTypeId(resultId);
+ assert(isPointerType(typeId));
+
+ return module.getInstruction(typeId)->getIdOperand(1);
+}
+
+Op Builder::getMostBasicTypeClass(Id typeId) const
+{
+ Instruction* instr = module.getInstruction(typeId);
+
+ Op typeClass = instr->getOpCode();
+ switch (typeClass)
+ {
+ case OpTypeVector:
+ case OpTypeMatrix:
+ case OpTypeArray:
+ case OpTypeRuntimeArray:
+ return getMostBasicTypeClass(instr->getIdOperand(0));
+ case OpTypePointer:
+ return getMostBasicTypeClass(instr->getIdOperand(1));
+ default:
+ return typeClass;
+ }
+}
+
+int Builder::getNumTypeConstituents(Id typeId) const
+{
+ Instruction* instr = module.getInstruction(typeId);
+
+ switch (instr->getOpCode())
+ {
+ case OpTypeBool:
+ case OpTypeInt:
+ case OpTypeFloat:
+ case OpTypePointer:
+ return 1;
+ case OpTypeVector:
+ case OpTypeMatrix:
+ return instr->getImmediateOperand(1);
+ case OpTypeArray:
+ {
+ Id lengthId = instr->getIdOperand(1);
+ return module.getInstruction(lengthId)->getImmediateOperand(0);
+ }
+ case OpTypeStruct:
+ return instr->getNumOperands();
+ case OpTypeCooperativeMatrixNV:
+ // has only one constituent when used with OpCompositeConstruct.
+ return 1;
+ default:
+ assert(0);
+ return 1;
+ }
+}
+
+// Return the lowest-level type of scalar that an homogeneous composite is made out of.
+// Typically, this is just to find out if something is made out of ints or floats.
+// However, it includes returning a structure, if say, it is an array of structure.
+Id Builder::getScalarTypeId(Id typeId) const
+{
+ Instruction* instr = module.getInstruction(typeId);
+
+ Op typeClass = instr->getOpCode();
+ switch (typeClass)
+ {
+ case OpTypeVoid:
+ case OpTypeBool:
+ case OpTypeInt:
+ case OpTypeFloat:
+ case OpTypeStruct:
+ return instr->getResultId();
+ case OpTypeVector:
+ case OpTypeMatrix:
+ case OpTypeArray:
+ case OpTypeRuntimeArray:
+ case OpTypePointer:
+ return getScalarTypeId(getContainedTypeId(typeId));
+ default:
+ assert(0);
+ return NoResult;
+ }
+}
+
+// Return the type of 'member' of a composite.
+Id Builder::getContainedTypeId(Id typeId, int member) const
+{
+ Instruction* instr = module.getInstruction(typeId);
+
+ Op typeClass = instr->getOpCode();
+ switch (typeClass)
+ {
+ case OpTypeVector:
+ case OpTypeMatrix:
+ case OpTypeArray:
+ case OpTypeRuntimeArray:
+ case OpTypeCooperativeMatrixNV:
+ return instr->getIdOperand(0);
+ case OpTypePointer:
+ return instr->getIdOperand(1);
+ case OpTypeStruct:
+ return instr->getIdOperand(member);
+ default:
+ assert(0);
+ return NoResult;
+ }
+}
+
+// Return the immediately contained type of a given composite type.
+Id Builder::getContainedTypeId(Id typeId) const
+{
+ return getContainedTypeId(typeId, 0);
+}
+
+// Returns true if 'typeId' is or contains a scalar type declared with 'typeOp'
+// of width 'width'. The 'width' is only consumed for int and float types.
+// Returns false otherwise.
+bool Builder::containsType(Id typeId, spv::Op typeOp, unsigned int width) const
+{
+ const Instruction& instr = *module.getInstruction(typeId);
+
+ Op typeClass = instr.getOpCode();
+ switch (typeClass)
+ {
+ case OpTypeInt:
+ case OpTypeFloat:
+ return typeClass == typeOp && instr.getImmediateOperand(0) == width;
+ case OpTypeStruct:
+ for (int m = 0; m < instr.getNumOperands(); ++m) {
+ if (containsType(instr.getIdOperand(m), typeOp, width))
+ return true;
+ }
+ return false;
+ case OpTypePointer:
+ return false;
+ case OpTypeVector:
+ case OpTypeMatrix:
+ case OpTypeArray:
+ case OpTypeRuntimeArray:
+ return containsType(getContainedTypeId(typeId), typeOp, width);
+ default:
+ return typeClass == typeOp;
+ }
+}
+
+// return true if the type is a pointer to PhysicalStorageBufferEXT or an
+// array of such pointers. These require restrict/aliased decorations.
+bool Builder::containsPhysicalStorageBufferOrArray(Id typeId) const
+{
+ const Instruction& instr = *module.getInstruction(typeId);
+
+ Op typeClass = instr.getOpCode();
+ switch (typeClass)
+ {
+ case OpTypePointer:
+ return getTypeStorageClass(typeId) == StorageClassPhysicalStorageBufferEXT;
+ case OpTypeArray:
+ return containsPhysicalStorageBufferOrArray(getContainedTypeId(typeId));
+ default:
+ return false;
+ }
+}
+
+// See if a scalar constant of this type has already been created, so it
+// can be reused rather than duplicated. (Required by the specification).
+Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value)
+{
+ Instruction* constant;
+ for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
+ constant = groupedConstants[typeClass][i];
+ if (constant->getOpCode() == opcode &&
+ constant->getTypeId() == typeId &&
+ constant->getImmediateOperand(0) == value)
+ return constant->getResultId();
+ }
+
+ return 0;
+}
+
+// Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double' or 'int64').
+Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2)
+{
+ Instruction* constant;
+ for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
+ constant = groupedConstants[typeClass][i];
+ if (constant->getOpCode() == opcode &&
+ constant->getTypeId() == typeId &&
+ constant->getImmediateOperand(0) == v1 &&
+ constant->getImmediateOperand(1) == v2)
+ return constant->getResultId();
+ }
+
+ return 0;
+}
+
+// Return true if consuming 'opcode' means consuming a constant.
+// "constant" here means after final transform to executable code,
+// the value consumed will be a constant, so includes specialization.
+bool Builder::isConstantOpCode(Op opcode) const
+{
+ switch (opcode) {
+ case OpUndef:
+ case OpConstantTrue:
+ case OpConstantFalse:
+ case OpConstant:
+ case OpConstantComposite:
+ case OpConstantSampler:
+ case OpConstantNull:
+ case OpSpecConstantTrue:
+ case OpSpecConstantFalse:
+ case OpSpecConstant:
+ case OpSpecConstantComposite:
+ case OpSpecConstantOp:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Return true if consuming 'opcode' means consuming a specialization constant.
+bool Builder::isSpecConstantOpCode(Op opcode) const
+{
+ switch (opcode) {
+ case OpSpecConstantTrue:
+ case OpSpecConstantFalse:
+ case OpSpecConstant:
+ case OpSpecConstantComposite:
+ case OpSpecConstantOp:
+ return true;
+ default:
+ return false;
+ }
+}
+
+Id Builder::makeBoolConstant(bool b, bool specConstant)
+{
+ Id typeId = makeBoolType();
+ Instruction* constant;
+ Op opcode = specConstant ? (b ? OpSpecConstantTrue : OpSpecConstantFalse) : (b ? OpConstantTrue : OpConstantFalse);
+
+ // See if we already made it. Applies only to regular constants, because specialization constants
+ // must remain distinct for the purpose of applying a SpecId decoration.
+ if (! specConstant) {
+ Id existing = 0;
+ for (int i = 0; i < (int)groupedConstants[OpTypeBool].size(); ++i) {
+ constant = groupedConstants[OpTypeBool][i];
+ if (constant->getTypeId() == typeId && constant->getOpCode() == opcode)
+ existing = constant->getResultId();
+ }
+
+ if (existing)
+ return existing;
+ }
+
+ // Make it
+ Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
+ groupedConstants[OpTypeBool].push_back(c);
+ module.mapInstruction(c);
+
+ return c->getResultId();
+}
+
+Id Builder::makeIntConstant(Id typeId, unsigned value, bool specConstant)
+{
+ Op opcode = specConstant ? OpSpecConstant : OpConstant;
+
+ // See if we already made it. Applies only to regular constants, because specialization constants
+ // must remain distinct for the purpose of applying a SpecId decoration.
+ if (! specConstant) {
+ Id existing = findScalarConstant(OpTypeInt, opcode, typeId, value);
+ if (existing)
+ return existing;
+ }
+
+ Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
+ c->addImmediateOperand(value);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
+ groupedConstants[OpTypeInt].push_back(c);
+ module.mapInstruction(c);
+
+ return c->getResultId();
+}
+
+Id Builder::makeInt64Constant(Id typeId, unsigned long long value, bool specConstant)
+{
+ Op opcode = specConstant ? OpSpecConstant : OpConstant;
+
+ unsigned op1 = value & 0xFFFFFFFF;
+ unsigned op2 = value >> 32;
+
+ // See if we already made it. Applies only to regular constants, because specialization constants
+ // must remain distinct for the purpose of applying a SpecId decoration.
+ if (! specConstant) {
+ Id existing = findScalarConstant(OpTypeInt, opcode, typeId, op1, op2);
+ if (existing)
+ return existing;
+ }
+
+ Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
+ c->addImmediateOperand(op1);
+ c->addImmediateOperand(op2);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
+ groupedConstants[OpTypeInt].push_back(c);
+ module.mapInstruction(c);
+
+ return c->getResultId();
+}
+
+Id Builder::makeFloatConstant(float f, bool specConstant)
+{
+ Op opcode = specConstant ? OpSpecConstant : OpConstant;
+ Id typeId = makeFloatType(32);
+ union { float fl; unsigned int ui; } u;
+ u.fl = f;
+ unsigned value = u.ui;
+
+ // See if we already made it. Applies only to regular constants, because specialization constants
+ // must remain distinct for the purpose of applying a SpecId decoration.
+ if (! specConstant) {
+ Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value);
+ if (existing)
+ return existing;
+ }
+
+ Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
+ c->addImmediateOperand(value);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
+ groupedConstants[OpTypeFloat].push_back(c);
+ module.mapInstruction(c);
+
+ return c->getResultId();
+}
+
+Id Builder::makeDoubleConstant(double d, bool specConstant)
+{
+ Op opcode = specConstant ? OpSpecConstant : OpConstant;
+ Id typeId = makeFloatType(64);
+ union { double db; unsigned long long ull; } u;
+ u.db = d;
+ unsigned long long value = u.ull;
+ unsigned op1 = value & 0xFFFFFFFF;
+ unsigned op2 = value >> 32;
+
+ // See if we already made it. Applies only to regular constants, because specialization constants
+ // must remain distinct for the purpose of applying a SpecId decoration.
+ if (! specConstant) {
+ Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, op1, op2);
+ if (existing)
+ return existing;
+ }
+
+ Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
+ c->addImmediateOperand(op1);
+ c->addImmediateOperand(op2);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
+ groupedConstants[OpTypeFloat].push_back(c);
+ module.mapInstruction(c);
+
+ return c->getResultId();
+}
+
+Id Builder::makeFloat16Constant(float f16, bool specConstant)
+{
+ Op opcode = specConstant ? OpSpecConstant : OpConstant;
+ Id typeId = makeFloatType(16);
+
+ spvutils::HexFloat<spvutils::FloatProxy<float>> fVal(f16);
+ spvutils::HexFloat<spvutils::FloatProxy<spvutils::Float16>> f16Val(0);
+ fVal.castTo(f16Val, spvutils::kRoundToZero);
+
+ unsigned value = f16Val.value().getAsFloat().get_value();
+
+ // See if we already made it. Applies only to regular constants, because specialization constants
+ // must remain distinct for the purpose of applying a SpecId decoration.
+ if (!specConstant) {
+ Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value);
+ if (existing)
+ return existing;
+ }
+
+ Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
+ c->addImmediateOperand(value);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
+ groupedConstants[OpTypeFloat].push_back(c);
+ module.mapInstruction(c);
+
+ return c->getResultId();
+}
+
+Id Builder::makeFpConstant(Id type, double d, bool specConstant)
+{
+ assert(isFloatType(type));
+
+ switch (getScalarTypeWidth(type)) {
+ case 16:
+ return makeFloat16Constant((float)d, specConstant);
+ case 32:
+ return makeFloatConstant((float)d, specConstant);
+ case 64:
+ return makeDoubleConstant(d, specConstant);
+ default:
+ break;
+ }
+
+ assert(false);
+ return NoResult;
+}
+
+Id Builder::findCompositeConstant(Op typeClass, Id typeId, const std::vector<Id>& comps)
+{
+ Instruction* constant = 0;
+ bool found = false;
+ for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) {
+ constant = groupedConstants[typeClass][i];
+
+ if (constant->getTypeId() != typeId)
+ continue;
+
+ // same contents?
+ bool mismatch = false;
+ for (int op = 0; op < constant->getNumOperands(); ++op) {
+ if (constant->getIdOperand(op) != comps[op]) {
+ mismatch = true;
+ break;
+ }
+ }
+ if (! mismatch) {
+ found = true;
+ break;
+ }
+ }
+
+ return found ? constant->getResultId() : NoResult;
+}
+
+Id Builder::findStructConstant(Id typeId, const std::vector<Id>& comps)
+{
+ Instruction* constant = 0;
+ bool found = false;
+ for (int i = 0; i < (int)groupedStructConstants[typeId].size(); ++i) {
+ constant = groupedStructConstants[typeId][i];
+
+ // same contents?
+ bool mismatch = false;
+ for (int op = 0; op < constant->getNumOperands(); ++op) {
+ if (constant->getIdOperand(op) != comps[op]) {
+ mismatch = true;
+ break;
+ }
+ }
+ if (! mismatch) {
+ found = true;
+ break;
+ }
+ }
+
+ return found ? constant->getResultId() : NoResult;
+}
+
+// Comments in header
+Id Builder::makeCompositeConstant(Id typeId, const std::vector<Id>& members, bool specConstant)
+{
+ Op opcode = specConstant ? OpSpecConstantComposite : OpConstantComposite;
+ assert(typeId);
+ Op typeClass = getTypeClass(typeId);
+
+ switch (typeClass) {
+ case OpTypeVector:
+ case OpTypeArray:
+ case OpTypeMatrix:
+ case OpTypeCooperativeMatrixNV:
+ if (! specConstant) {
+ Id existing = findCompositeConstant(typeClass, typeId, members);
+ if (existing)
+ return existing;
+ }
+ break;
+ case OpTypeStruct:
+ if (! specConstant) {
+ Id existing = findStructConstant(typeId, members);
+ if (existing)
+ return existing;
+ }
+ break;
+ default:
+ assert(0);
+ return makeFloatConstant(0.0);
+ }
+
+ Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
+ for (int op = 0; op < (int)members.size(); ++op)
+ c->addIdOperand(members[op]);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
+ if (typeClass == OpTypeStruct)
+ groupedStructConstants[typeId].push_back(c);
+ else
+ groupedConstants[typeClass].push_back(c);
+ module.mapInstruction(c);
+
+ return c->getResultId();
+}
+
+Instruction* Builder::addEntryPoint(ExecutionModel model, Function* function, const char* name)
+{
+ Instruction* entryPoint = new Instruction(OpEntryPoint);
+ entryPoint->addImmediateOperand(model);
+ entryPoint->addIdOperand(function->getId());
+ entryPoint->addStringOperand(name);
+
+ entryPoints.push_back(std::unique_ptr<Instruction>(entryPoint));
+
+ return entryPoint;
+}
+
+// Currently relying on the fact that all 'value' of interest are small non-negative values.
+void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int value1, int value2, int value3)
+{
+ Instruction* instr = new Instruction(OpExecutionMode);
+ instr->addIdOperand(entryPoint->getId());
+ instr->addImmediateOperand(mode);
+ if (value1 >= 0)
+ instr->addImmediateOperand(value1);
+ if (value2 >= 0)
+ instr->addImmediateOperand(value2);
+ if (value3 >= 0)
+ instr->addImmediateOperand(value3);
+
+ executionModes.push_back(std::unique_ptr<Instruction>(instr));
+}
+
+void Builder::addName(Id id, const char* string)
+{
+ Instruction* name = new Instruction(OpName);
+ name->addIdOperand(id);
+ name->addStringOperand(string);
+
+ names.push_back(std::unique_ptr<Instruction>(name));
+}
+
+void Builder::addMemberName(Id id, int memberNumber, const char* string)
+{
+ Instruction* name = new Instruction(OpMemberName);
+ name->addIdOperand(id);
+ name->addImmediateOperand(memberNumber);
+ name->addStringOperand(string);
+
+ names.push_back(std::unique_ptr<Instruction>(name));
+}
+
+void Builder::addDecoration(Id id, Decoration decoration, int num)
+{
+ if (decoration == spv::DecorationMax)
+ return;
+
+ Instruction* dec = new Instruction(OpDecorate);
+ dec->addIdOperand(id);
+ dec->addImmediateOperand(decoration);
+ if (num >= 0)
+ dec->addImmediateOperand(num);
+
+ decorations.push_back(std::unique_ptr<Instruction>(dec));
+}
+
+void Builder::addDecoration(Id id, Decoration decoration, const char* s)
+{
+ if (decoration == spv::DecorationMax)
+ return;
+
+ Instruction* dec = new Instruction(OpDecorateStringGOOGLE);
+ dec->addIdOperand(id);
+ dec->addImmediateOperand(decoration);
+ dec->addStringOperand(s);
+
+ decorations.push_back(std::unique_ptr<Instruction>(dec));
+}
+
+void Builder::addDecorationId(Id id, Decoration decoration, Id idDecoration)
+{
+ if (decoration == spv::DecorationMax)
+ return;
+
+ Instruction* dec = new Instruction(OpDecorateId);
+ dec->addIdOperand(id);
+ dec->addImmediateOperand(decoration);
+ dec->addIdOperand(idDecoration);
+
+ decorations.push_back(std::unique_ptr<Instruction>(dec));
+}
+
+void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, int num)
+{
+ if (decoration == spv::DecorationMax)
+ return;
+
+ Instruction* dec = new Instruction(OpMemberDecorate);
+ dec->addIdOperand(id);
+ dec->addImmediateOperand(member);
+ dec->addImmediateOperand(decoration);
+ if (num >= 0)
+ dec->addImmediateOperand(num);
+
+ decorations.push_back(std::unique_ptr<Instruction>(dec));
+}
+
+void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const char *s)
+{
+ if (decoration == spv::DecorationMax)
+ return;
+
+ Instruction* dec = new Instruction(OpMemberDecorateStringGOOGLE);
+ dec->addIdOperand(id);
+ dec->addImmediateOperand(member);
+ dec->addImmediateOperand(decoration);
+ dec->addStringOperand(s);
+
+ decorations.push_back(std::unique_ptr<Instruction>(dec));
+}
+
+// Comments in header
+Function* Builder::makeEntryPoint(const char* entryPoint)
+{
+ assert(! entryPointFunction);
+
+ Block* entry;
+ std::vector<Id> params;
+ std::vector<std::vector<Decoration>> decorations;
+
+ entryPointFunction = makeFunctionEntry(NoPrecision, makeVoidType(), entryPoint, params, decorations, &entry);
+
+ return entryPointFunction;
+}
+
+// Comments in header
+Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name,
+ const std::vector<Id>& paramTypes, const std::vector<std::vector<Decoration>>& decorations, Block **entry)
+{
+ // Make the function and initial instructions in it
+ Id typeId = makeFunctionType(returnType, paramTypes);
+ Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds((int)paramTypes.size());
+ Function* function = new Function(getUniqueId(), returnType, typeId, firstParamId, module);
+
+ // Set up the precisions
+ setPrecision(function->getId(), precision);
+ for (unsigned p = 0; p < (unsigned)decorations.size(); ++p) {
+ for (int d = 0; d < (int)decorations[p].size(); ++d)
+ addDecoration(firstParamId + p, decorations[p][d]);
+ }
+
+ // CFG
+ if (entry) {
+ *entry = new Block(getUniqueId(), *function);
+ function->addBlock(*entry);
+ setBuildPoint(*entry);
+ }
+
+ if (name)
+ addName(function->getId(), name);
+
+ functions.push_back(std::unique_ptr<Function>(function));
+
+ return function;
+}
+
+// Comments in header
+void Builder::makeReturn(bool implicit, Id retVal)
+{
+ if (retVal) {
+ Instruction* inst = new Instruction(NoResult, NoType, OpReturnValue);
+ inst->addIdOperand(retVal);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(inst));
+ } else
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(new Instruction(NoResult, NoType, OpReturn)));
+
+ if (! implicit)
+ createAndSetNoPredecessorBlock("post-return");
+}
+
+// Comments in header
+void Builder::leaveFunction()
+{
+ Block* block = buildPoint;
+ Function& function = buildPoint->getParent();
+ assert(block);
+
+ // If our function did not contain a return, add a return void now.
+ if (! block->isTerminated()) {
+ if (function.getReturnType() == makeVoidType())
+ makeReturn(true);
+ else {
+ makeReturn(true, createUndefined(function.getReturnType()));
+ }
+ }
+}
+
+// Comments in header
+void Builder::makeDiscard()
+{
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(new Instruction(OpKill)));
+ createAndSetNoPredecessorBlock("post-discard");
+}
+
+// Comments in header
+Id Builder::createVariable(StorageClass storageClass, Id type, const char* name)
+{
+ Id pointerType = makePointer(storageClass, type);
+ Instruction* inst = new Instruction(getUniqueId(), pointerType, OpVariable);
+ inst->addImmediateOperand(storageClass);
+
+ switch (storageClass) {
+ case StorageClassFunction:
+ // Validation rules require the declaration in the entry block
+ buildPoint->getParent().addLocalVariable(std::unique_ptr<Instruction>(inst));
+ break;
+
+ default:
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(inst));
+ module.mapInstruction(inst);
+ break;
+ }
+
+ if (name)
+ addName(inst->getResultId(), name);
+
+ return inst->getResultId();
+}
+
+// Comments in header
+Id Builder::createUndefined(Id type)
+{
+ Instruction* inst = new Instruction(getUniqueId(), type, OpUndef);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(inst));
+ return inst->getResultId();
+}
+
+// av/vis/nonprivate are unnecessary and illegal for some storage classes.
+spv::MemoryAccessMask Builder::sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc) const
+{
+ switch (sc) {
+ case spv::StorageClassUniform:
+ case spv::StorageClassWorkgroup:
+ case spv::StorageClassStorageBuffer:
+ case spv::StorageClassPhysicalStorageBufferEXT:
+ break;
+ default:
+ memoryAccess = spv::MemoryAccessMask(memoryAccess &
+ ~(spv::MemoryAccessMakePointerAvailableKHRMask |
+ spv::MemoryAccessMakePointerVisibleKHRMask |
+ spv::MemoryAccessNonPrivatePointerKHRMask));
+ break;
+ }
+ return memoryAccess;
+}
+
+// Comments in header
+void Builder::createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment)
+{
+ Instruction* store = new Instruction(OpStore);
+ store->addIdOperand(lValue);
+ store->addIdOperand(rValue);
+
+ memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue));
+
+ if (memoryAccess != MemoryAccessMaskNone) {
+ store->addImmediateOperand(memoryAccess);
+ if (memoryAccess & spv::MemoryAccessAlignedMask) {
+ store->addImmediateOperand(alignment);
+ }
+ if (memoryAccess & spv::MemoryAccessMakePointerAvailableKHRMask) {
+ store->addIdOperand(makeUintConstant(scope));
+ }
+ }
+
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(store));
+}
+
+// Comments in header
+Id Builder::createLoad(Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment)
+{
+ Instruction* load = new Instruction(getUniqueId(), getDerefTypeId(lValue), OpLoad);
+ load->addIdOperand(lValue);
+
+ memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue));
+
+ if (memoryAccess != MemoryAccessMaskNone) {
+ load->addImmediateOperand(memoryAccess);
+ if (memoryAccess & spv::MemoryAccessAlignedMask) {
+ load->addImmediateOperand(alignment);
+ }
+ if (memoryAccess & spv::MemoryAccessMakePointerVisibleKHRMask) {
+ load->addIdOperand(makeUintConstant(scope));
+ }
+ }
+
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(load));
+
+ return load->getResultId();
+}
+
+// Comments in header
+Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector<Id>& offsets)
+{
+ // Figure out the final resulting type.
+ spv::Id typeId = getTypeId(base);
+ assert(isPointerType(typeId) && offsets.size() > 0);
+ typeId = getContainedTypeId(typeId);
+ for (int i = 0; i < (int)offsets.size(); ++i) {
+ if (isStructType(typeId)) {
+ assert(isConstantScalar(offsets[i]));
+ typeId = getContainedTypeId(typeId, getConstantScalar(offsets[i]));
+ } else
+ typeId = getContainedTypeId(typeId, offsets[i]);
+ }
+ typeId = makePointer(storageClass, typeId);
+
+ // Make the instruction
+ Instruction* chain = new Instruction(getUniqueId(), typeId, OpAccessChain);
+ chain->addIdOperand(base);
+ for (int i = 0; i < (int)offsets.size(); ++i)
+ chain->addIdOperand(offsets[i]);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(chain));
+
+ return chain->getResultId();
+}
+
+Id Builder::createArrayLength(Id base, unsigned int member)
+{
+ spv::Id intType = makeUintType(32);
+ Instruction* length = new Instruction(getUniqueId(), intType, OpArrayLength);
+ length->addIdOperand(base);
+ length->addImmediateOperand(member);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(length));
+
+ return length->getResultId();
+}
+
+Id Builder::createCooperativeMatrixLength(Id type)
+{
+ spv::Id intType = makeUintType(32);
+
+ // Generate code for spec constants if in spec constant operation
+ // generation mode.
+ if (generatingOpCodeForSpecConst) {
+ return createSpecConstantOp(OpCooperativeMatrixLengthNV, intType, std::vector<Id>(1, type), std::vector<Id>());
+ }
+
+ Instruction* length = new Instruction(getUniqueId(), intType, OpCooperativeMatrixLengthNV);
+ length->addIdOperand(type);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(length));
+
+ return length->getResultId();
+}
+
+Id Builder::createCompositeExtract(Id composite, Id typeId, unsigned index)
+{
+ // Generate code for spec constants if in spec constant operation
+ // generation mode.
+ if (generatingOpCodeForSpecConst) {
+ return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite), std::vector<Id>(1, index));
+ }
+ Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract);
+ extract->addIdOperand(composite);
+ extract->addImmediateOperand(index);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(extract));
+
+ return extract->getResultId();
+}
+
+Id Builder::createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes)
+{
+ // Generate code for spec constants if in spec constant operation
+ // generation mode.
+ if (generatingOpCodeForSpecConst) {
+ return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(1, composite), indexes);
+ }
+ Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract);
+ extract->addIdOperand(composite);
+ for (int i = 0; i < (int)indexes.size(); ++i)
+ extract->addImmediateOperand(indexes[i]);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(extract));
+
+ return extract->getResultId();
+}
+
+Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, unsigned index)
+{
+ Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert);
+ insert->addIdOperand(object);
+ insert->addIdOperand(composite);
+ insert->addImmediateOperand(index);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(insert));
+
+ return insert->getResultId();
+}
+
+Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes)
+{
+ Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert);
+ insert->addIdOperand(object);
+ insert->addIdOperand(composite);
+ for (int i = 0; i < (int)indexes.size(); ++i)
+ insert->addImmediateOperand(indexes[i]);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(insert));
+
+ return insert->getResultId();
+}
+
+Id Builder::createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex)
+{
+ Instruction* extract = new Instruction(getUniqueId(), typeId, OpVectorExtractDynamic);
+ extract->addIdOperand(vector);
+ extract->addIdOperand(componentIndex);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(extract));
+
+ return extract->getResultId();
+}
+
+Id Builder::createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex)
+{
+ Instruction* insert = new Instruction(getUniqueId(), typeId, OpVectorInsertDynamic);
+ insert->addIdOperand(vector);
+ insert->addIdOperand(component);
+ insert->addIdOperand(componentIndex);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(insert));
+
+ return insert->getResultId();
+}
+
+// An opcode that has no operands, no result id, and no type
+void Builder::createNoResultOp(Op opCode)
+{
+ Instruction* op = new Instruction(opCode);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
+}
+
+// An opcode that has one id operand, no result id, and no type
+void Builder::createNoResultOp(Op opCode, Id operand)
+{
+ Instruction* op = new Instruction(opCode);
+ op->addIdOperand(operand);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
+}
+
+// An opcode that has one or more operands, no result id, and no type
+void Builder::createNoResultOp(Op opCode, const std::vector<Id>& operands)
+{
+ Instruction* op = new Instruction(opCode);
+ for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
+ op->addIdOperand(*it);
+ }
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
+}
+
+// An opcode that has multiple operands, no result id, and no type
+void Builder::createNoResultOp(Op opCode, const std::vector<IdImmediate>& operands)
+{
+ Instruction* op = new Instruction(opCode);
+ for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
+ if (it->isId)
+ op->addIdOperand(it->word);
+ else
+ op->addImmediateOperand(it->word);
+ }
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
+}
+
+void Builder::createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask semantics)
+{
+ Instruction* op = new Instruction(OpControlBarrier);
+ op->addIdOperand(makeUintConstant(execution));
+ op->addIdOperand(makeUintConstant(memory));
+ op->addIdOperand(makeUintConstant(semantics));
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
+}
+
+void Builder::createMemoryBarrier(unsigned executionScope, unsigned memorySemantics)
+{
+ Instruction* op = new Instruction(OpMemoryBarrier);
+ op->addIdOperand(makeUintConstant(executionScope));
+ op->addIdOperand(makeUintConstant(memorySemantics));
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
+}
+
+// An opcode that has one operands, a result id, and a type
+Id Builder::createUnaryOp(Op opCode, Id typeId, Id operand)
+{
+ // Generate code for spec constants if in spec constant operation
+ // generation mode.
+ if (generatingOpCodeForSpecConst) {
+ return createSpecConstantOp(opCode, typeId, std::vector<Id>(1, operand), std::vector<Id>());
+ }
+ Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
+ op->addIdOperand(operand);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
+
+ return op->getResultId();
+}
+
+Id Builder::createBinOp(Op opCode, Id typeId, Id left, Id right)
+{
+ // Generate code for spec constants if in spec constant operation
+ // generation mode.
+ if (generatingOpCodeForSpecConst) {
+ std::vector<Id> operands(2);
+ operands[0] = left; operands[1] = right;
+ return createSpecConstantOp(opCode, typeId, operands, std::vector<Id>());
+ }
+ Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
+ op->addIdOperand(left);
+ op->addIdOperand(right);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
+
+ return op->getResultId();
+}
+
+Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3)
+{
+ // Generate code for spec constants if in spec constant operation
+ // generation mode.
+ if (generatingOpCodeForSpecConst) {
+ std::vector<Id> operands(3);
+ operands[0] = op1;
+ operands[1] = op2;
+ operands[2] = op3;
+ return createSpecConstantOp(
+ opCode, typeId, operands, std::vector<Id>());
+ }
+ Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
+ op->addIdOperand(op1);
+ op->addIdOperand(op2);
+ op->addIdOperand(op3);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
+
+ return op->getResultId();
+}
+
+Id Builder::createOp(Op opCode, Id typeId, const std::vector<Id>& operands)
+{
+ Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
+ for (auto it = operands.cbegin(); it != operands.cend(); ++it)
+ op->addIdOperand(*it);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
+
+ return op->getResultId();
+}
+
+Id Builder::createOp(Op opCode, Id typeId, const std::vector<IdImmediate>& operands)
+{
+ Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
+ for (auto it = operands.cbegin(); it != operands.cend(); ++it) {
+ if (it->isId)
+ op->addIdOperand(it->word);
+ else
+ op->addImmediateOperand(it->word);
+ }
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
+
+ return op->getResultId();
+}
+
+Id Builder::createSpecConstantOp(Op opCode, Id typeId, const std::vector<Id>& operands, const std::vector<unsigned>& literals)
+{
+ Instruction* op = new Instruction(getUniqueId(), typeId, OpSpecConstantOp);
+ op->addImmediateOperand((unsigned) opCode);
+ for (auto it = operands.cbegin(); it != operands.cend(); ++it)
+ op->addIdOperand(*it);
+ for (auto it = literals.cbegin(); it != literals.cend(); ++it)
+ op->addImmediateOperand(*it);
+ module.mapInstruction(op);
+ constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(op));
+
+ return op->getResultId();
+}
+
+Id Builder::createFunctionCall(spv::Function* function, const std::vector<spv::Id>& args)
+{
+ Instruction* op = new Instruction(getUniqueId(), function->getReturnType(), OpFunctionCall);
+ op->addIdOperand(function->getId());
+ for (int a = 0; a < (int)args.size(); ++a)
+ op->addIdOperand(args[a]);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
+
+ return op->getResultId();
+}
+
+// Comments in header
+Id Builder::createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels)
+{
+ if (channels.size() == 1)
+ return setPrecision(createCompositeExtract(source, typeId, channels.front()), precision);
+
+ if (generatingOpCodeForSpecConst) {
+ std::vector<Id> operands(2);
+ operands[0] = operands[1] = source;
+ return setPrecision(createSpecConstantOp(OpVectorShuffle, typeId, operands, channels), precision);
+ }
+ Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle);
+ assert(isVector(source));
+ swizzle->addIdOperand(source);
+ swizzle->addIdOperand(source);
+ for (int i = 0; i < (int)channels.size(); ++i)
+ swizzle->addImmediateOperand(channels[i]);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(swizzle));
+
+ return setPrecision(swizzle->getResultId(), precision);
+}
+
+// Comments in header
+Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels)
+{
+ if (channels.size() == 1 && getNumComponents(source) == 1)
+ return createCompositeInsert(source, target, typeId, channels.front());
+
+ Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle);
+
+ assert(isVector(target));
+ swizzle->addIdOperand(target);
+
+ assert(getNumComponents(source) == (int)channels.size());
+ assert(isVector(source));
+ swizzle->addIdOperand(source);
+
+ // Set up an identity shuffle from the base value to the result value
+ unsigned int components[4];
+ int numTargetComponents = getNumComponents(target);
+ for (int i = 0; i < numTargetComponents; ++i)
+ components[i] = i;
+
+ // Punch in the l-value swizzle
+ for (int i = 0; i < (int)channels.size(); ++i)
+ components[channels[i]] = numTargetComponents + i;
+
+ // finish the instruction with these components selectors
+ for (int i = 0; i < numTargetComponents; ++i)
+ swizzle->addImmediateOperand(components[i]);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(swizzle));
+
+ return swizzle->getResultId();
+}
+
+// Comments in header
+void Builder::promoteScalar(Decoration precision, Id& left, Id& right)
+{
+ int direction = getNumComponents(right) - getNumComponents(left);
+
+ if (direction > 0)
+ left = smearScalar(precision, left, makeVectorType(getTypeId(left), getNumComponents(right)));
+ else if (direction < 0)
+ right = smearScalar(precision, right, makeVectorType(getTypeId(right), getNumComponents(left)));
+
+ return;
+}
+
+// Comments in header
+Id Builder::smearScalar(Decoration precision, Id scalar, Id vectorType)
+{
+ assert(getNumComponents(scalar) == 1);
+ assert(getTypeId(scalar) == getScalarTypeId(vectorType));
+
+ int numComponents = getNumTypeComponents(vectorType);
+ if (numComponents == 1)
+ return scalar;
+
+ Instruction* smear = nullptr;
+ if (generatingOpCodeForSpecConst) {
+ auto members = std::vector<spv::Id>(numComponents, scalar);
+ // Sometime even in spec-constant-op mode, the temporary vector created by
+ // promoting a scalar might not be a spec constant. This should depend on
+ // the scalar.
+ // e.g.:
+ // const vec2 spec_const_result = a_spec_const_vec2 + a_front_end_const_scalar;
+ // In such cases, the temporary vector created from a_front_end_const_scalar
+ // is not a spec constant vector, even though the binary operation node is marked
+ // as 'specConstant' and we are in spec-constant-op mode.
+ auto result_id = makeCompositeConstant(vectorType, members, isSpecConstant(scalar));
+ smear = module.getInstruction(result_id);
+ } else {
+ smear = new Instruction(getUniqueId(), vectorType, OpCompositeConstruct);
+ for (int c = 0; c < numComponents; ++c)
+ smear->addIdOperand(scalar);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(smear));
+ }
+
+ return setPrecision(smear->getResultId(), precision);
+}
+
+// Comments in header
+Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args)
+{
+ Instruction* inst = new Instruction(getUniqueId(), resultType, OpExtInst);
+ inst->addIdOperand(builtins);
+ inst->addImmediateOperand(entryPoint);
+ for (int arg = 0; arg < (int)args.size(); ++arg)
+ inst->addIdOperand(args[arg]);
+
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(inst));
+
+ return inst->getResultId();
+}
+
+// Accept all parameters needed to create a texture instruction.
+// Create the correct instruction based on the inputs, and make the call.
+Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather,
+ bool noImplicitLod, const TextureParameters& parameters)
+{
+ static const int maxTextureArgs = 10;
+ Id texArgs[maxTextureArgs] = {};
+
+ //
+ // Set up the fixed arguments
+ //
+ int numArgs = 0;
+ bool explicitLod = false;
+ texArgs[numArgs++] = parameters.sampler;
+ texArgs[numArgs++] = parameters.coords;
+ if (parameters.Dref != NoResult)
+ texArgs[numArgs++] = parameters.Dref;
+ if (parameters.component != NoResult)
+ texArgs[numArgs++] = parameters.component;
+
+#ifdef NV_EXTENSIONS
+ if (parameters.granularity != NoResult)
+ texArgs[numArgs++] = parameters.granularity;
+ if (parameters.coarse != NoResult)
+ texArgs[numArgs++] = parameters.coarse;
+#endif
+
+ //
+ // Set up the optional arguments
+ //
+ int optArgNum = numArgs; // track which operand, if it exists, is the mask of optional arguments
+ ++numArgs; // speculatively make room for the mask operand
+ ImageOperandsMask mask = ImageOperandsMaskNone; // the mask operand
+ if (parameters.bias) {
+ mask = (ImageOperandsMask)(mask | ImageOperandsBiasMask);
+ texArgs[numArgs++] = parameters.bias;
+ }
+ if (parameters.lod) {
+ mask = (ImageOperandsMask)(mask | ImageOperandsLodMask);
+ texArgs[numArgs++] = parameters.lod;
+ explicitLod = true;
+ } else if (parameters.gradX) {
+ mask = (ImageOperandsMask)(mask | ImageOperandsGradMask);
+ texArgs[numArgs++] = parameters.gradX;
+ texArgs[numArgs++] = parameters.gradY;
+ explicitLod = true;
+ } else if (noImplicitLod && ! fetch && ! gather) {
+ // have to explicitly use lod of 0 if not allowed to have them be implicit, and
+ // we would otherwise be about to issue an implicit instruction
+ mask = (ImageOperandsMask)(mask | ImageOperandsLodMask);
+ texArgs[numArgs++] = makeFloatConstant(0.0);
+ explicitLod = true;
+ }
+ if (parameters.offset) {
+ if (isConstant(parameters.offset))
+ mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetMask);
+ else {
+ addCapability(CapabilityImageGatherExtended);
+ mask = (ImageOperandsMask)(mask | ImageOperandsOffsetMask);
+ }
+ texArgs[numArgs++] = parameters.offset;
+ }
+ if (parameters.offsets) {
+ addCapability(CapabilityImageGatherExtended);
+ mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetsMask);
+ texArgs[numArgs++] = parameters.offsets;
+ }
+ if (parameters.sample) {
+ mask = (ImageOperandsMask)(mask | ImageOperandsSampleMask);
+ texArgs[numArgs++] = parameters.sample;
+ }
+ if (parameters.lodClamp) {
+ // capability if this bit is used
+ addCapability(CapabilityMinLod);
+
+ mask = (ImageOperandsMask)(mask | ImageOperandsMinLodMask);
+ texArgs[numArgs++] = parameters.lodClamp;
+ }
+ if (parameters.nonprivate) {
+ mask = mask | ImageOperandsNonPrivateTexelKHRMask;
+ }
+ if (parameters.volatil) {
+ mask = mask | ImageOperandsVolatileTexelKHRMask;
+ }
+ if (mask == ImageOperandsMaskNone)
+ --numArgs; // undo speculative reservation for the mask argument
+ else
+ texArgs[optArgNum] = mask;
+
+ //
+ // Set up the instruction
+ //
+ Op opCode = OpNop; // All paths below need to set this
+ if (fetch) {
+ if (sparse)
+ opCode = OpImageSparseFetch;
+ else
+ opCode = OpImageFetch;
+#ifdef NV_EXTENSIONS
+ } else if (parameters.granularity && parameters.coarse) {
+ opCode = OpImageSampleFootprintNV;
+#endif
+ } else if (gather) {
+ if (parameters.Dref)
+ if (sparse)
+ opCode = OpImageSparseDrefGather;
+ else
+ opCode = OpImageDrefGather;
+ else
+ if (sparse)
+ opCode = OpImageSparseGather;
+ else
+ opCode = OpImageGather;
+ } else if (explicitLod) {
+ if (parameters.Dref) {
+ if (proj)
+ if (sparse)
+ opCode = OpImageSparseSampleProjDrefExplicitLod;
+ else
+ opCode = OpImageSampleProjDrefExplicitLod;
+ else
+ if (sparse)
+ opCode = OpImageSparseSampleDrefExplicitLod;
+ else
+ opCode = OpImageSampleDrefExplicitLod;
+ } else {
+ if (proj)
+ if (sparse)
+ opCode = OpImageSparseSampleProjExplicitLod;
+ else
+ opCode = OpImageSampleProjExplicitLod;
+ else
+ if (sparse)
+ opCode = OpImageSparseSampleExplicitLod;
+ else
+ opCode = OpImageSampleExplicitLod;
+ }
+ } else {
+ if (parameters.Dref) {
+ if (proj)
+ if (sparse)
+ opCode = OpImageSparseSampleProjDrefImplicitLod;
+ else
+ opCode = OpImageSampleProjDrefImplicitLod;
+ else
+ if (sparse)
+ opCode = OpImageSparseSampleDrefImplicitLod;
+ else
+ opCode = OpImageSampleDrefImplicitLod;
+ } else {
+ if (proj)
+ if (sparse)
+ opCode = OpImageSparseSampleProjImplicitLod;
+ else
+ opCode = OpImageSampleProjImplicitLod;
+ else
+ if (sparse)
+ opCode = OpImageSparseSampleImplicitLod;
+ else
+ opCode = OpImageSampleImplicitLod;
+ }
+ }
+
+ // See if the result type is expecting a smeared result.
+ // This happens when a legacy shadow*() call is made, which
+ // gets a vec4 back instead of a float.
+ Id smearedType = resultType;
+ if (! isScalarType(resultType)) {
+ switch (opCode) {
+ case OpImageSampleDrefImplicitLod:
+ case OpImageSampleDrefExplicitLod:
+ case OpImageSampleProjDrefImplicitLod:
+ case OpImageSampleProjDrefExplicitLod:
+ resultType = getScalarTypeId(resultType);
+ break;
+ default:
+ break;
+ }
+ }
+
+ Id typeId0 = 0;
+ Id typeId1 = 0;
+
+ if (sparse) {
+ typeId0 = resultType;
+ typeId1 = getDerefTypeId(parameters.texelOut);
+ resultType = makeStructResultType(typeId0, typeId1);
+ }
+
+ // Build the SPIR-V instruction
+ Instruction* textureInst = new Instruction(getUniqueId(), resultType, opCode);
+ for (int op = 0; op < optArgNum; ++op)
+ textureInst->addIdOperand(texArgs[op]);
+ if (optArgNum < numArgs)
+ textureInst->addImmediateOperand(texArgs[optArgNum]);
+ for (int op = optArgNum + 1; op < numArgs; ++op)
+ textureInst->addIdOperand(texArgs[op]);
+ setPrecision(textureInst->getResultId(), precision);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(textureInst));
+
+ Id resultId = textureInst->getResultId();
+
+ if (sparse) {
+ // set capability
+ addCapability(CapabilitySparseResidency);
+
+ // Decode the return type that was a special structure
+ createStore(createCompositeExtract(resultId, typeId1, 1), parameters.texelOut);
+ resultId = createCompositeExtract(resultId, typeId0, 0);
+ setPrecision(resultId, precision);
+ } else {
+ // When a smear is needed, do it, as per what was computed
+ // above when resultType was changed to a scalar type.
+ if (resultType != smearedType)
+ resultId = smearScalar(precision, resultId, smearedType);
+ }
+
+ return resultId;
+}
+
+// Comments in header
+Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters, bool isUnsignedResult)
+{
+ // Figure out the result type
+ Id resultType = 0;
+ switch (opCode) {
+ case OpImageQuerySize:
+ case OpImageQuerySizeLod:
+ {
+ int numComponents = 0;
+ switch (getTypeDimensionality(getImageType(parameters.sampler))) {
+ case Dim1D:
+ case DimBuffer:
+ numComponents = 1;
+ break;
+ case Dim2D:
+ case DimCube:
+ case DimRect:
+ case DimSubpassData:
+ numComponents = 2;
+ break;
+ case Dim3D:
+ numComponents = 3;
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+ if (isArrayedImageType(getImageType(parameters.sampler)))
+ ++numComponents;
+
+ Id intType = isUnsignedResult ? makeUintType(32) : makeIntType(32);
+ if (numComponents == 1)
+ resultType = intType;
+ else
+ resultType = makeVectorType(intType, numComponents);
+
+ break;
+ }
+ case OpImageQueryLod:
+#ifdef AMD_EXTENSIONS
+ resultType = makeVectorType(getScalarTypeId(getTypeId(parameters.coords)), 2);
+#else
+ resultType = makeVectorType(makeFloatType(32), 2);
+#endif
+ break;
+ case OpImageQueryLevels:
+ case OpImageQuerySamples:
+ resultType = isUnsignedResult ? makeUintType(32) : makeIntType(32);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ Instruction* query = new Instruction(getUniqueId(), resultType, opCode);
+ query->addIdOperand(parameters.sampler);
+ if (parameters.coords)
+ query->addIdOperand(parameters.coords);
+ if (parameters.lod)
+ query->addIdOperand(parameters.lod);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(query));
+
+ return query->getResultId();
+}
+
+// External comments in header.
+// Operates recursively to visit the composite's hierarchy.
+Id Builder::createCompositeCompare(Decoration precision, Id value1, Id value2, bool equal)
+{
+ Id boolType = makeBoolType();
+ Id valueType = getTypeId(value1);
+
+ Id resultId = NoResult;
+
+ int numConstituents = getNumTypeConstituents(valueType);
+
+ // Scalars and Vectors
+
+ if (isScalarType(valueType) || isVectorType(valueType)) {
+ assert(valueType == getTypeId(value2));
+ // These just need a single comparison, just have
+ // to figure out what it is.
+ Op op;
+ switch (getMostBasicTypeClass(valueType)) {
+ case OpTypeFloat:
+ op = equal ? OpFOrdEqual : OpFOrdNotEqual;
+ break;
+ case OpTypeInt:
+ default:
+ op = equal ? OpIEqual : OpINotEqual;
+ break;
+ case OpTypeBool:
+ op = equal ? OpLogicalEqual : OpLogicalNotEqual;
+ precision = NoPrecision;
+ break;
+ }
+
+ if (isScalarType(valueType)) {
+ // scalar
+ resultId = createBinOp(op, boolType, value1, value2);
+ } else {
+ // vector
+ resultId = createBinOp(op, makeVectorType(boolType, numConstituents), value1, value2);
+ setPrecision(resultId, precision);
+ // reduce vector compares...
+ resultId = createUnaryOp(equal ? OpAll : OpAny, boolType, resultId);
+ }
+
+ return setPrecision(resultId, precision);
+ }
+
+ // Only structs, arrays, and matrices should be left.
+ // They share in common the reduction operation across their constituents.
+ assert(isAggregateType(valueType) || isMatrixType(valueType));
+
+ // Compare each pair of constituents
+ for (int constituent = 0; constituent < numConstituents; ++constituent) {
+ std::vector<unsigned> indexes(1, constituent);
+ Id constituentType1 = getContainedTypeId(getTypeId(value1), constituent);
+ Id constituentType2 = getContainedTypeId(getTypeId(value2), constituent);
+ Id constituent1 = createCompositeExtract(value1, constituentType1, indexes);
+ Id constituent2 = createCompositeExtract(value2, constituentType2, indexes);
+
+ Id subResultId = createCompositeCompare(precision, constituent1, constituent2, equal);
+
+ if (constituent == 0)
+ resultId = subResultId;
+ else
+ resultId = setPrecision(createBinOp(equal ? OpLogicalAnd : OpLogicalOr, boolType, resultId, subResultId), precision);
+ }
+
+ return resultId;
+}
+
+// OpCompositeConstruct
+Id Builder::createCompositeConstruct(Id typeId, const std::vector<Id>& constituents)
+{
+ assert(isAggregateType(typeId) || (getNumTypeConstituents(typeId) > 1 && getNumTypeConstituents(typeId) == (int)constituents.size()));
+
+ if (generatingOpCodeForSpecConst) {
+ // Sometime, even in spec-constant-op mode, the constant composite to be
+ // constructed may not be a specialization constant.
+ // e.g.:
+ // const mat2 m2 = mat2(a_spec_const, a_front_end_const, another_front_end_const, third_front_end_const);
+ // The first column vector should be a spec constant one, as a_spec_const is a spec constant.
+ // The second column vector should NOT be spec constant, as it does not contain any spec constants.
+ // To handle such cases, we check the constituents of the constant vector to determine whether this
+ // vector should be created as a spec constant.
+ return makeCompositeConstant(typeId, constituents,
+ std::any_of(constituents.begin(), constituents.end(),
+ [&](spv::Id id) { return isSpecConstant(id); }));
+ }
+
+ Instruction* op = new Instruction(getUniqueId(), typeId, OpCompositeConstruct);
+ for (int c = 0; c < (int)constituents.size(); ++c)
+ op->addIdOperand(constituents[c]);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(op));
+
+ return op->getResultId();
+}
+
+// Vector or scalar constructor
+Id Builder::createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId)
+{
+ Id result = NoResult;
+ unsigned int numTargetComponents = getNumTypeComponents(resultTypeId);
+ unsigned int targetComponent = 0;
+
+ // Special case: when calling a vector constructor with a single scalar
+ // argument, smear the scalar
+ if (sources.size() == 1 && isScalar(sources[0]) && numTargetComponents > 1)
+ return smearScalar(precision, sources[0], resultTypeId);
+
+ // accumulate the arguments for OpCompositeConstruct
+ std::vector<Id> constituents;
+ Id scalarTypeId = getScalarTypeId(resultTypeId);
+
+ // lambda to store the result of visiting an argument component
+ const auto latchResult = [&](Id comp) {
+ if (numTargetComponents > 1)
+ constituents.push_back(comp);
+ else
+ result = comp;
+ ++targetComponent;
+ };
+
+ // lambda to visit a vector argument's components
+ const auto accumulateVectorConstituents = [&](Id sourceArg) {
+ unsigned int sourceSize = getNumComponents(sourceArg);
+ unsigned int sourcesToUse = sourceSize;
+ if (sourcesToUse + targetComponent > numTargetComponents)
+ sourcesToUse = numTargetComponents - targetComponent;
+
+ for (unsigned int s = 0; s < sourcesToUse; ++s) {
+ std::vector<unsigned> swiz;
+ swiz.push_back(s);
+ latchResult(createRvalueSwizzle(precision, scalarTypeId, sourceArg, swiz));
+ }
+ };
+
+ // lambda to visit a matrix argument's components
+ const auto accumulateMatrixConstituents = [&](Id sourceArg) {
+ unsigned int sourceSize = getNumColumns(sourceArg) * getNumRows(sourceArg);
+ unsigned int sourcesToUse = sourceSize;
+ if (sourcesToUse + targetComponent > numTargetComponents)
+ sourcesToUse = numTargetComponents - targetComponent;
+
+ int col = 0;
+ int row = 0;
+ for (unsigned int s = 0; s < sourcesToUse; ++s) {
+ if (row >= getNumRows(sourceArg)) {
+ row = 0;
+ col++;
+ }
+ std::vector<Id> indexes;
+ indexes.push_back(col);
+ indexes.push_back(row);
+ latchResult(createCompositeExtract(sourceArg, scalarTypeId, indexes));
+ row++;
+ }
+ };
+
+ // Go through the source arguments, each one could have either
+ // a single or multiple components to contribute.
+ for (unsigned int i = 0; i < sources.size(); ++i) {
+
+ if (isScalar(sources[i]) || isPointer(sources[i]))
+ latchResult(sources[i]);
+ else if (isVector(sources[i]))
+ accumulateVectorConstituents(sources[i]);
+ else if (isMatrix(sources[i]))
+ accumulateMatrixConstituents(sources[i]);
+ else
+ assert(0);
+
+ if (targetComponent >= numTargetComponents)
+ break;
+ }
+
+ // If the result is a vector, make it from the gathered constituents.
+ if (constituents.size() > 0)
+ result = createCompositeConstruct(resultTypeId, constituents);
+
+ return setPrecision(result, precision);
+}
+
+// Comments in header
+Id Builder::createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId)
+{
+ Id componentTypeId = getScalarTypeId(resultTypeId);
+ int numCols = getTypeNumColumns(resultTypeId);
+ int numRows = getTypeNumRows(resultTypeId);
+
+ Instruction* instr = module.getInstruction(componentTypeId);
+ unsigned bitCount = instr->getImmediateOperand(0);
+
+ // Optimize matrix constructed from a bigger matrix
+ if (isMatrix(sources[0]) && getNumColumns(sources[0]) >= numCols && getNumRows(sources[0]) >= numRows) {
+ // To truncate the matrix to a smaller number of rows/columns, we need to:
+ // 1. For each column, extract the column and truncate it to the required size using shuffle
+ // 2. Assemble the resulting matrix from all columns
+ Id matrix = sources[0];
+ Id columnTypeId = getContainedTypeId(resultTypeId);
+ Id sourceColumnTypeId = getContainedTypeId(getTypeId(matrix));
+
+ std::vector<unsigned> channels;
+ for (int row = 0; row < numRows; ++row)
+ channels.push_back(row);
+
+ std::vector<Id> matrixColumns;
+ for (int col = 0; col < numCols; ++col) {
+ std::vector<unsigned> indexes;
+ indexes.push_back(col);
+ Id colv = createCompositeExtract(matrix, sourceColumnTypeId, indexes);
+ setPrecision(colv, precision);
+
+ if (numRows != getNumRows(matrix)) {
+ matrixColumns.push_back(createRvalueSwizzle(precision, columnTypeId, colv, channels));
+ } else {
+ matrixColumns.push_back(colv);
+ }
+ }
+
+ return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision);
+ }
+
+ // Otherwise, will use a two step process
+ // 1. make a compile-time 2D array of values
+ // 2. construct a matrix from that array
+
+ // Step 1.
+
+ // initialize the array to the identity matrix
+ Id ids[maxMatrixSize][maxMatrixSize];
+ Id one = (bitCount == 64 ? makeDoubleConstant(1.0) : makeFloatConstant(1.0));
+ Id zero = (bitCount == 64 ? makeDoubleConstant(0.0) : makeFloatConstant(0.0));
+ for (int col = 0; col < 4; ++col) {
+ for (int row = 0; row < 4; ++row) {
+ if (col == row)
+ ids[col][row] = one;
+ else
+ ids[col][row] = zero;
+ }
+ }
+
+ // modify components as dictated by the arguments
+ if (sources.size() == 1 && isScalar(sources[0])) {
+ // a single scalar; resets the diagonals
+ for (int col = 0; col < 4; ++col)
+ ids[col][col] = sources[0];
+ } else if (isMatrix(sources[0])) {
+ // constructing from another matrix; copy over the parts that exist in both the argument and constructee
+ Id matrix = sources[0];
+ int minCols = std::min(numCols, getNumColumns(matrix));
+ int minRows = std::min(numRows, getNumRows(matrix));
+ for (int col = 0; col < minCols; ++col) {
+ std::vector<unsigned> indexes;
+ indexes.push_back(col);
+ for (int row = 0; row < minRows; ++row) {
+ indexes.push_back(row);
+ ids[col][row] = createCompositeExtract(matrix, componentTypeId, indexes);
+ indexes.pop_back();
+ setPrecision(ids[col][row], precision);
+ }
+ }
+ } else {
+ // fill in the matrix in column-major order with whatever argument components are available
+ int row = 0;
+ int col = 0;
+
+ for (int arg = 0; arg < (int)sources.size(); ++arg) {
+ Id argComp = sources[arg];
+ for (int comp = 0; comp < getNumComponents(sources[arg]); ++comp) {
+ if (getNumComponents(sources[arg]) > 1) {
+ argComp = createCompositeExtract(sources[arg], componentTypeId, comp);
+ setPrecision(argComp, precision);
+ }
+ ids[col][row++] = argComp;
+ if (row == numRows) {
+ row = 0;
+ col++;
+ }
+ }
+ }
+ }
+
+ // Step 2: Construct a matrix from that array.
+ // First make the column vectors, then make the matrix.
+
+ // make the column vectors
+ Id columnTypeId = getContainedTypeId(resultTypeId);
+ std::vector<Id> matrixColumns;
+ for (int col = 0; col < numCols; ++col) {
+ std::vector<Id> vectorComponents;
+ for (int row = 0; row < numRows; ++row)
+ vectorComponents.push_back(ids[col][row]);
+ Id column = createCompositeConstruct(columnTypeId, vectorComponents);
+ setPrecision(column, precision);
+ matrixColumns.push_back(column);
+ }
+
+ // make the matrix
+ return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision);
+}
+
+// Comments in header
+Builder::If::If(Id cond, unsigned int ctrl, Builder& gb) :
+ builder(gb),
+ condition(cond),
+ control(ctrl),
+ elseBlock(0)
+{
+ function = &builder.getBuildPoint()->getParent();
+
+ // make the blocks, but only put the then-block into the function,
+ // the else-block and merge-block will be added later, in order, after
+ // earlier code is emitted
+ thenBlock = new Block(builder.getUniqueId(), *function);
+ mergeBlock = new Block(builder.getUniqueId(), *function);
+
+ // Save the current block, so that we can add in the flow control split when
+ // makeEndIf is called.
+ headerBlock = builder.getBuildPoint();
+
+ function->addBlock(thenBlock);
+ builder.setBuildPoint(thenBlock);
+}
+
+// Comments in header
+void Builder::If::makeBeginElse()
+{
+ // Close out the "then" by having it jump to the mergeBlock
+ builder.createBranch(mergeBlock);
+
+ // Make the first else block and add it to the function
+ elseBlock = new Block(builder.getUniqueId(), *function);
+ function->addBlock(elseBlock);
+
+ // Start building the else block
+ builder.setBuildPoint(elseBlock);
+}
+
+// Comments in header
+void Builder::If::makeEndIf()
+{
+ // jump to the merge block
+ builder.createBranch(mergeBlock);
+
+ // Go back to the headerBlock and make the flow control split
+ builder.setBuildPoint(headerBlock);
+ builder.createSelectionMerge(mergeBlock, control);
+ if (elseBlock)
+ builder.createConditionalBranch(condition, thenBlock, elseBlock);
+ else
+ builder.createConditionalBranch(condition, thenBlock, mergeBlock);
+
+ // add the merge block to the function
+ function->addBlock(mergeBlock);
+ builder.setBuildPoint(mergeBlock);
+}
+
+// Comments in header
+void Builder::makeSwitch(Id selector, unsigned int control, int numSegments, const std::vector<int>& caseValues,
+ const std::vector<int>& valueIndexToSegment, int defaultSegment,
+ std::vector<Block*>& segmentBlocks)
+{
+ Function& function = buildPoint->getParent();
+
+ // make all the blocks
+ for (int s = 0; s < numSegments; ++s)
+ segmentBlocks.push_back(new Block(getUniqueId(), function));
+
+ Block* mergeBlock = new Block(getUniqueId(), function);
+
+ // make and insert the switch's selection-merge instruction
+ createSelectionMerge(mergeBlock, control);
+
+ // make the switch instruction
+ Instruction* switchInst = new Instruction(NoResult, NoType, OpSwitch);
+ switchInst->addIdOperand(selector);
+ auto defaultOrMerge = (defaultSegment >= 0) ? segmentBlocks[defaultSegment] : mergeBlock;
+ switchInst->addIdOperand(defaultOrMerge->getId());
+ defaultOrMerge->addPredecessor(buildPoint);
+ for (int i = 0; i < (int)caseValues.size(); ++i) {
+ switchInst->addImmediateOperand(caseValues[i]);
+ switchInst->addIdOperand(segmentBlocks[valueIndexToSegment[i]]->getId());
+ segmentBlocks[valueIndexToSegment[i]]->addPredecessor(buildPoint);
+ }
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(switchInst));
+
+ // push the merge block
+ switchMerges.push(mergeBlock);
+}
+
+// Comments in header
+void Builder::addSwitchBreak()
+{
+ // branch to the top of the merge block stack
+ createBranch(switchMerges.top());
+ createAndSetNoPredecessorBlock("post-switch-break");
+}
+
+// Comments in header
+void Builder::nextSwitchSegment(std::vector<Block*>& segmentBlock, int nextSegment)
+{
+ int lastSegment = nextSegment - 1;
+ if (lastSegment >= 0) {
+ // Close out previous segment by jumping, if necessary, to next segment
+ if (! buildPoint->isTerminated())
+ createBranch(segmentBlock[nextSegment]);
+ }
+ Block* block = segmentBlock[nextSegment];
+ block->getParent().addBlock(block);
+ setBuildPoint(block);
+}
+
+// Comments in header
+void Builder::endSwitch(std::vector<Block*>& /*segmentBlock*/)
+{
+ // Close out previous segment by jumping, if necessary, to next segment
+ if (! buildPoint->isTerminated())
+ addSwitchBreak();
+
+ switchMerges.top()->getParent().addBlock(switchMerges.top());
+ setBuildPoint(switchMerges.top());
+
+ switchMerges.pop();
+}
+
+Block& Builder::makeNewBlock()
+{
+ Function& function = buildPoint->getParent();
+ auto block = new Block(getUniqueId(), function);
+ function.addBlock(block);
+ return *block;
+}
+
+Builder::LoopBlocks& Builder::makeNewLoop()
+{
+ // This verbosity is needed to simultaneously get the same behavior
+ // everywhere (id's in the same order), have a syntax that works
+ // across lots of versions of C++, have no warnings from pedantic
+ // compilation modes, and leave the rest of the code alone.
+ Block& head = makeNewBlock();
+ Block& body = makeNewBlock();
+ Block& merge = makeNewBlock();
+ Block& continue_target = makeNewBlock();
+ LoopBlocks blocks(head, body, merge, continue_target);
+ loops.push(blocks);
+ return loops.top();
+}
+
+void Builder::createLoopContinue()
+{
+ createBranch(&loops.top().continue_target);
+ // Set up a block for dead code.
+ createAndSetNoPredecessorBlock("post-loop-continue");
+}
+
+void Builder::createLoopExit()
+{
+ createBranch(&loops.top().merge);
+ // Set up a block for dead code.
+ createAndSetNoPredecessorBlock("post-loop-break");
+}
+
+void Builder::closeLoop()
+{
+ loops.pop();
+}
+
+void Builder::clearAccessChain()
+{
+ accessChain.base = NoResult;
+ accessChain.indexChain.clear();
+ accessChain.instr = NoResult;
+ accessChain.swizzle.clear();
+ accessChain.component = NoResult;
+ accessChain.preSwizzleBaseType = NoType;
+ accessChain.isRValue = false;
+ accessChain.coherentFlags.clear();
+ accessChain.alignment = 0;
+}
+
+// Comments in header
+void Builder::accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType, AccessChain::CoherentFlags coherentFlags, unsigned int alignment)
+{
+ accessChain.coherentFlags |= coherentFlags;
+ accessChain.alignment |= alignment;
+
+ // swizzles can be stacked in GLSL, but simplified to a single
+ // one here; the base type doesn't change
+ if (accessChain.preSwizzleBaseType == NoType)
+ accessChain.preSwizzleBaseType = preSwizzleBaseType;
+
+ // if needed, propagate the swizzle for the current access chain
+ if (accessChain.swizzle.size() > 0) {
+ std::vector<unsigned> oldSwizzle = accessChain.swizzle;
+ accessChain.swizzle.resize(0);
+ for (unsigned int i = 0; i < swizzle.size(); ++i) {
+ assert(swizzle[i] < oldSwizzle.size());
+ accessChain.swizzle.push_back(oldSwizzle[swizzle[i]]);
+ }
+ } else
+ accessChain.swizzle = swizzle;
+
+ // determine if we need to track this swizzle anymore
+ simplifyAccessChainSwizzle();
+}
+
+// Comments in header
+void Builder::accessChainStore(Id rvalue, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment)
+{
+ assert(accessChain.isRValue == false);
+
+ transferAccessChainSwizzle(true);
+ Id base = collapseAccessChain();
+ Id source = rvalue;
+
+ // dynamic component should be gone
+ assert(accessChain.component == NoResult);
+
+ // If swizzle still exists, it is out-of-order or not full, we must load the target vector,
+ // extract and insert elements to perform writeMask and/or swizzle.
+ if (accessChain.swizzle.size() > 0) {
+ Id tempBaseId = createLoad(base);
+ source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, source, accessChain.swizzle);
+ }
+
+ // take LSB of alignment
+ alignment = alignment & ~(alignment & (alignment-1));
+ if (getStorageClass(base) == StorageClassPhysicalStorageBufferEXT) {
+ memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask);
+ }
+
+ createStore(source, base, memoryAccess, scope, alignment);
+}
+
+// Comments in header
+Id Builder::accessChainLoad(Decoration precision, Decoration nonUniform, Id resultType, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment)
+{
+ Id id;
+
+ if (accessChain.isRValue) {
+ // transfer access chain, but try to stay in registers
+ transferAccessChainSwizzle(false);
+ if (accessChain.indexChain.size() > 0) {
+ Id swizzleBase = accessChain.preSwizzleBaseType != NoType ? accessChain.preSwizzleBaseType : resultType;
+
+ // if all the accesses are constants, we can use OpCompositeExtract
+ std::vector<unsigned> indexes;
+ bool constant = true;
+ for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) {
+ if (isConstantScalar(accessChain.indexChain[i]))
+ indexes.push_back(getConstantScalar(accessChain.indexChain[i]));
+ else {
+ constant = false;
+ break;
+ }
+ }
+
+ if (constant) {
+ id = createCompositeExtract(accessChain.base, swizzleBase, indexes);
+ } else {
+ // make a new function variable for this r-value
+ Id lValue = createVariable(StorageClassFunction, getTypeId(accessChain.base), "indexable");
+
+ // store into it
+ createStore(accessChain.base, lValue);
+
+ // move base to the new variable
+ accessChain.base = lValue;
+ accessChain.isRValue = false;
+
+ // load through the access chain
+ id = createLoad(collapseAccessChain());
+ }
+ setPrecision(id, precision);
+ } else
+ id = accessChain.base; // no precision, it was set when this was defined
+ } else {
+ transferAccessChainSwizzle(true);
+
+ // take LSB of alignment
+ alignment = alignment & ~(alignment & (alignment-1));
+ if (getStorageClass(accessChain.base) == StorageClassPhysicalStorageBufferEXT) {
+ memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask);
+ }
+
+ // load through the access chain
+ id = createLoad(collapseAccessChain(), memoryAccess, scope, alignment);
+ setPrecision(id, precision);
+ addDecoration(id, nonUniform);
+ }
+
+ // Done, unless there are swizzles to do
+ if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult)
+ return id;
+
+ // Do remaining swizzling
+
+ // Do the basic swizzle
+ if (accessChain.swizzle.size() > 0) {
+ Id swizzledType = getScalarTypeId(getTypeId(id));
+ if (accessChain.swizzle.size() > 1)
+ swizzledType = makeVectorType(swizzledType, (int)accessChain.swizzle.size());
+ id = createRvalueSwizzle(precision, swizzledType, id, accessChain.swizzle);
+ }
+
+ // Do the dynamic component
+ if (accessChain.component != NoResult)
+ id = setPrecision(createVectorExtractDynamic(id, resultType, accessChain.component), precision);
+
+ addDecoration(id, nonUniform);
+ return id;
+}
+
+Id Builder::accessChainGetLValue()
+{
+ assert(accessChain.isRValue == false);
+
+ transferAccessChainSwizzle(true);
+ Id lvalue = collapseAccessChain();
+
+ // If swizzle exists, it is out-of-order or not full, we must load the target vector,
+ // extract and insert elements to perform writeMask and/or swizzle. This does not
+ // go with getting a direct l-value pointer.
+ assert(accessChain.swizzle.size() == 0);
+ assert(accessChain.component == NoResult);
+
+ return lvalue;
+}
+
+// comment in header
+Id Builder::accessChainGetInferredType()
+{
+ // anything to operate on?
+ if (accessChain.base == NoResult)
+ return NoType;
+ Id type = getTypeId(accessChain.base);
+
+ // do initial dereference
+ if (! accessChain.isRValue)
+ type = getContainedTypeId(type);
+
+ // dereference each index
+ for (auto it = accessChain.indexChain.cbegin(); it != accessChain.indexChain.cend(); ++it) {
+ if (isStructType(type))
+ type = getContainedTypeId(type, getConstantScalar(*it));
+ else
+ type = getContainedTypeId(type);
+ }
+
+ // dereference swizzle
+ if (accessChain.swizzle.size() == 1)
+ type = getContainedTypeId(type);
+ else if (accessChain.swizzle.size() > 1)
+ type = makeVectorType(getContainedTypeId(type), (int)accessChain.swizzle.size());
+
+ // dereference component selection
+ if (accessChain.component)
+ type = getContainedTypeId(type);
+
+ return type;
+}
+
+void Builder::dump(std::vector<unsigned int>& out) const
+{
+ // Header, before first instructions:
+ out.push_back(MagicNumber);
+ out.push_back(spvVersion);
+ out.push_back(builderNumber);
+ out.push_back(uniqueId + 1);
+ out.push_back(0);
+
+ // Capabilities
+ for (auto it = capabilities.cbegin(); it != capabilities.cend(); ++it) {
+ Instruction capInst(0, 0, OpCapability);
+ capInst.addImmediateOperand(*it);
+ capInst.dump(out);
+ }
+
+ for (auto it = extensions.cbegin(); it != extensions.cend(); ++it) {
+ Instruction extInst(0, 0, OpExtension);
+ extInst.addStringOperand(it->c_str());
+ extInst.dump(out);
+ }
+
+ dumpInstructions(out, imports);
+ Instruction memInst(0, 0, OpMemoryModel);
+ memInst.addImmediateOperand(addressModel);
+ memInst.addImmediateOperand(memoryModel);
+ memInst.dump(out);
+
+ // Instructions saved up while building:
+ dumpInstructions(out, entryPoints);
+ dumpInstructions(out, executionModes);
+
+ // Debug instructions
+ dumpInstructions(out, strings);
+ dumpSourceInstructions(out);
+ for (int e = 0; e < (int)sourceExtensions.size(); ++e) {
+ Instruction sourceExtInst(0, 0, OpSourceExtension);
+ sourceExtInst.addStringOperand(sourceExtensions[e]);
+ sourceExtInst.dump(out);
+ }
+ dumpInstructions(out, names);
+ dumpModuleProcesses(out);
+
+ // Annotation instructions
+ dumpInstructions(out, decorations);
+
+ dumpInstructions(out, constantsTypesGlobals);
+ dumpInstructions(out, externals);
+
+ // The functions
+ module.dump(out);
+}
+
+//
+// Protected methods.
+//
+
+// Turn the described access chain in 'accessChain' into an instruction(s)
+// computing its address. This *cannot* include complex swizzles, which must
+// be handled after this is called.
+//
+// Can generate code.
+Id Builder::collapseAccessChain()
+{
+ assert(accessChain.isRValue == false);
+
+ // did we already emit an access chain for this?
+ if (accessChain.instr != NoResult)
+ return accessChain.instr;
+
+ // If we have a dynamic component, we can still transfer
+ // that into a final operand to the access chain. We need to remap the
+ // dynamic component through the swizzle to get a new dynamic component to
+ // update.
+ //
+ // This was not done in transferAccessChainSwizzle() because it might
+ // generate code.
+ remapDynamicSwizzle();
+ if (accessChain.component != NoResult) {
+ // transfer the dynamic component to the access chain
+ accessChain.indexChain.push_back(accessChain.component);
+ accessChain.component = NoResult;
+ }
+
+ // note that non-trivial swizzling is left pending
+
+ // do we have an access chain?
+ if (accessChain.indexChain.size() == 0)
+ return accessChain.base;
+
+ // emit the access chain
+ StorageClass storageClass = (StorageClass)module.getStorageClass(getTypeId(accessChain.base));
+ accessChain.instr = createAccessChain(storageClass, accessChain.base, accessChain.indexChain);
+
+ return accessChain.instr;
+}
+
+// For a dynamic component selection of a swizzle.
+//
+// Turn the swizzle and dynamic component into just a dynamic component.
+//
+// Generates code.
+void Builder::remapDynamicSwizzle()
+{
+ // do we have a swizzle to remap a dynamic component through?
+ if (accessChain.component != NoResult && accessChain.swizzle.size() > 1) {
+ // build a vector of the swizzle for the component to map into
+ std::vector<Id> components;
+ for (int c = 0; c < (int)accessChain.swizzle.size(); ++c)
+ components.push_back(makeUintConstant(accessChain.swizzle[c]));
+ Id mapType = makeVectorType(makeUintType(32), (int)accessChain.swizzle.size());
+ Id map = makeCompositeConstant(mapType, components);
+
+ // use it
+ accessChain.component = createVectorExtractDynamic(map, makeUintType(32), accessChain.component);
+ accessChain.swizzle.clear();
+ }
+}
+
+// clear out swizzle if it is redundant, that is reselecting the same components
+// that would be present without the swizzle.
+void Builder::simplifyAccessChainSwizzle()
+{
+ // If the swizzle has fewer components than the vector, it is subsetting, and must stay
+ // to preserve that fact.
+ if (getNumTypeComponents(accessChain.preSwizzleBaseType) > (int)accessChain.swizzle.size())
+ return;
+
+ // if components are out of order, it is a swizzle
+ for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) {
+ if (i != accessChain.swizzle[i])
+ return;
+ }
+
+ // otherwise, there is no need to track this swizzle
+ accessChain.swizzle.clear();
+ if (accessChain.component == NoResult)
+ accessChain.preSwizzleBaseType = NoType;
+}
+
+// To the extent any swizzling can become part of the chain
+// of accesses instead of a post operation, make it so.
+// If 'dynamic' is true, include transferring the dynamic component,
+// otherwise, leave it pending.
+//
+// Does not generate code. just updates the access chain.
+void Builder::transferAccessChainSwizzle(bool dynamic)
+{
+ // non existent?
+ if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult)
+ return;
+
+ // too complex?
+ // (this requires either a swizzle, or generating code for a dynamic component)
+ if (accessChain.swizzle.size() > 1)
+ return;
+
+ // single component, either in the swizzle and/or dynamic component
+ if (accessChain.swizzle.size() == 1) {
+ assert(accessChain.component == NoResult);
+ // handle static component selection
+ accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle.front()));
+ accessChain.swizzle.clear();
+ accessChain.preSwizzleBaseType = NoType;
+ } else if (dynamic && accessChain.component != NoResult) {
+ assert(accessChain.swizzle.size() == 0);
+ // handle dynamic component
+ accessChain.indexChain.push_back(accessChain.component);
+ accessChain.preSwizzleBaseType = NoType;
+ accessChain.component = NoResult;
+ }
+}
+
+// Utility method for creating a new block and setting the insert point to
+// be in it. This is useful for flow-control operations that need a "dummy"
+// block proceeding them (e.g. instructions after a discard, etc).
+void Builder::createAndSetNoPredecessorBlock(const char* /*name*/)
+{
+ Block* block = new Block(getUniqueId(), buildPoint->getParent());
+ block->setUnreachable();
+ buildPoint->getParent().addBlock(block);
+ setBuildPoint(block);
+
+ // if (name)
+ // addName(block->getId(), name);
+}
+
+// Comments in header
+void Builder::createBranch(Block* block)
+{
+ Instruction* branch = new Instruction(OpBranch);
+ branch->addIdOperand(block->getId());
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(branch));
+ block->addPredecessor(buildPoint);
+}
+
+void Builder::createSelectionMerge(Block* mergeBlock, unsigned int control)
+{
+ Instruction* merge = new Instruction(OpSelectionMerge);
+ merge->addIdOperand(mergeBlock->getId());
+ merge->addImmediateOperand(control);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(merge));
+}
+
+void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control,
+ unsigned int dependencyLength)
+{
+ Instruction* merge = new Instruction(OpLoopMerge);
+ merge->addIdOperand(mergeBlock->getId());
+ merge->addIdOperand(continueBlock->getId());
+ merge->addImmediateOperand(control);
+ if ((control & LoopControlDependencyLengthMask) != 0)
+ merge->addImmediateOperand(dependencyLength);
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(merge));
+}
+
+void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock)
+{
+ Instruction* branch = new Instruction(OpBranchConditional);
+ branch->addIdOperand(condition);
+ branch->addIdOperand(thenBlock->getId());
+ branch->addIdOperand(elseBlock->getId());
+ buildPoint->addInstruction(std::unique_ptr<Instruction>(branch));
+ thenBlock->addPredecessor(buildPoint);
+ elseBlock->addPredecessor(buildPoint);
+}
+
+// OpSource
+// [OpSourceContinued]
+// ...
+void Builder::dumpSourceInstructions(const spv::Id fileId, const std::string& text,
+ std::vector<unsigned int>& out) const
+{
+ const int maxWordCount = 0xFFFF;
+ const int opSourceWordCount = 4;
+ const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1;
+
+ if (source != SourceLanguageUnknown) {
+ // OpSource Language Version File Source
+ Instruction sourceInst(NoResult, NoType, OpSource);
+ sourceInst.addImmediateOperand(source);
+ sourceInst.addImmediateOperand(sourceVersion);
+ // File operand
+ if (fileId != NoResult) {
+ sourceInst.addIdOperand(fileId);
+ // Source operand
+ if (text.size() > 0) {
+ int nextByte = 0;
+ std::string subString;
+ while ((int)text.size() - nextByte > 0) {
+ subString = text.substr(nextByte, nonNullBytesPerInstruction);
+ if (nextByte == 0) {
+ // OpSource
+ sourceInst.addStringOperand(subString.c_str());
+ sourceInst.dump(out);
+ } else {
+ // OpSourcContinued
+ Instruction sourceContinuedInst(OpSourceContinued);
+ sourceContinuedInst.addStringOperand(subString.c_str());
+ sourceContinuedInst.dump(out);
+ }
+ nextByte += nonNullBytesPerInstruction;
+ }
+ } else
+ sourceInst.dump(out);
+ } else
+ sourceInst.dump(out);
+ }
+}
+
+// Dump an OpSource[Continued] sequence for the source and every include file
+void Builder::dumpSourceInstructions(std::vector<unsigned int>& out) const
+{
+ dumpSourceInstructions(sourceFileStringId, sourceText, out);
+ for (auto iItr = includeFiles.begin(); iItr != includeFiles.end(); ++iItr)
+ dumpSourceInstructions(iItr->first, *iItr->second, out);
+}
+
+void Builder::dumpInstructions(std::vector<unsigned int>& out, const std::vector<std::unique_ptr<Instruction> >& instructions) const
+{
+ for (int i = 0; i < (int)instructions.size(); ++i) {
+ instructions[i]->dump(out);
+ }
+}
+
+void Builder::dumpModuleProcesses(std::vector<unsigned int>& out) const
+{
+ for (int i = 0; i < (int)moduleProcesses.size(); ++i) {
+ Instruction moduleProcessed(OpModuleProcessed);
+ moduleProcessed.addStringOperand(moduleProcesses[i]);
+ moduleProcessed.dump(out);
+ }
+}
+
+}; // end spv namespace
diff --git a/src/3rdparty/glslang/SPIRV/SpvBuilder.h b/src/3rdparty/glslang/SPIRV/SpvBuilder.h
new file mode 100644
index 0000000..52f7fba
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/SpvBuilder.h
@@ -0,0 +1,749 @@
+//
+// Copyright (C) 2014-2015 LunarG, Inc.
+// Copyright (C) 2015-2018 Google, Inc.
+// Copyright (C) 2017 ARM Limited.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+//
+// "Builder" is an interface to fully build SPIR-V IR. Allocate one of
+// these to build (a thread safe) internal SPIR-V representation (IR),
+// and then dump it as a binary stream according to the SPIR-V specification.
+//
+// A Builder has a 1:1 relationship with a SPIR-V module.
+//
+
+#pragma once
+#ifndef SpvBuilder_H
+#define SpvBuilder_H
+
+#include "Logger.h"
+#include "spirv.hpp"
+#include "spvIR.h"
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <set>
+#include <sstream>
+#include <stack>
+#include <unordered_map>
+#include <map>
+
+namespace spv {
+
+class Builder {
+public:
+ Builder(unsigned int spvVersion, unsigned int userNumber, SpvBuildLogger* logger);
+ virtual ~Builder();
+
+ static const int maxMatrixSize = 4;
+
+ unsigned int getSpvVersion() const { return spvVersion; }
+
+ void setSource(spv::SourceLanguage lang, int version)
+ {
+ source = lang;
+ sourceVersion = version;
+ }
+ spv::Id getStringId(const std::string& str)
+ {
+ auto sItr = stringIds.find(str);
+ if (sItr != stringIds.end())
+ return sItr->second;
+ spv::Id strId = getUniqueId();
+ Instruction* fileString = new Instruction(strId, NoType, OpString);
+ const char* file_c_str = str.c_str();
+ fileString->addStringOperand(file_c_str);
+ strings.push_back(std::unique_ptr<Instruction>(fileString));
+ stringIds[file_c_str] = strId;
+ return strId;
+ }
+ void setSourceFile(const std::string& file)
+ {
+ sourceFileStringId = getStringId(file);
+ }
+ void setSourceText(const std::string& text) { sourceText = text; }
+ void addSourceExtension(const char* ext) { sourceExtensions.push_back(ext); }
+ void addModuleProcessed(const std::string& p) { moduleProcesses.push_back(p.c_str()); }
+ void setEmitOpLines() { emitOpLines = true; }
+ void addExtension(const char* ext) { extensions.insert(ext); }
+ void addInclude(const std::string& name, const std::string& text)
+ {
+ spv::Id incId = getStringId(name);
+ includeFiles[incId] = &text;
+ }
+ Id import(const char*);
+ void setMemoryModel(spv::AddressingModel addr, spv::MemoryModel mem)
+ {
+ addressModel = addr;
+ memoryModel = mem;
+ }
+
+ void addCapability(spv::Capability cap) { capabilities.insert(cap); }
+
+ // To get a new <id> for anything needing a new one.
+ Id getUniqueId() { return ++uniqueId; }
+
+ // To get a set of new <id>s, e.g., for a set of function parameters
+ Id getUniqueIds(int numIds)
+ {
+ Id id = uniqueId + 1;
+ uniqueId += numIds;
+ return id;
+ }
+
+ // Generate OpLine for non-filename-based #line directives (ie no filename
+ // seen yet): Log the current line, and if different than the last one,
+ // issue a new OpLine using the new line and current source file name.
+ void setLine(int line);
+
+ // If filename null, generate OpLine for non-filename-based line directives,
+ // else do filename-based: Log the current line and file, and if different
+ // than the last one, issue a new OpLine using the new line and file
+ // name.
+ void setLine(int line, const char* filename);
+ // Low-level OpLine. See setLine() for a layered helper.
+ void addLine(Id fileName, int line, int column);
+
+ // For creating new types (will return old type if the requested one was already made).
+ Id makeVoidType();
+ Id makeBoolType();
+ Id makePointer(StorageClass, Id pointee);
+ Id makeForwardPointer(StorageClass);
+ Id makePointerFromForwardPointer(StorageClass, Id forwardPointerType, Id pointee);
+ Id makeIntegerType(int width, bool hasSign); // generic
+ Id makeIntType(int width) { return makeIntegerType(width, true); }
+ Id makeUintType(int width) { return makeIntegerType(width, false); }
+ Id makeFloatType(int width);
+ Id makeStructType(const std::vector<Id>& members, const char*);
+ Id makeStructResultType(Id type0, Id type1);
+ Id makeVectorType(Id component, int size);
+ Id makeMatrixType(Id component, int cols, int rows);
+ Id makeArrayType(Id element, Id sizeId, int stride); // 0 stride means no stride decoration
+ Id makeRuntimeArray(Id element);
+ Id makeFunctionType(Id returnType, const std::vector<Id>& paramTypes);
+ Id makeImageType(Id sampledType, Dim, bool depth, bool arrayed, bool ms, unsigned sampled, ImageFormat format);
+ Id makeSamplerType();
+ Id makeSampledImageType(Id imageType);
+ Id makeCooperativeMatrixType(Id component, Id scope, Id rows, Id cols);
+
+ // accelerationStructureNV type
+ Id makeAccelerationStructureNVType();
+
+ // For querying about types.
+ Id getTypeId(Id resultId) const { return module.getTypeId(resultId); }
+ Id getDerefTypeId(Id resultId) const;
+ Op getOpCode(Id id) const { return module.getInstruction(id)->getOpCode(); }
+ Op getTypeClass(Id typeId) const { return getOpCode(typeId); }
+ Op getMostBasicTypeClass(Id typeId) const;
+ int getNumComponents(Id resultId) const { return getNumTypeComponents(getTypeId(resultId)); }
+ int getNumTypeConstituents(Id typeId) const;
+ int getNumTypeComponents(Id typeId) const { return getNumTypeConstituents(typeId); }
+ Id getScalarTypeId(Id typeId) const;
+ Id getContainedTypeId(Id typeId) const;
+ Id getContainedTypeId(Id typeId, int) const;
+ StorageClass getTypeStorageClass(Id typeId) const { return module.getStorageClass(typeId); }
+ ImageFormat getImageTypeFormat(Id typeId) const { return (ImageFormat)module.getInstruction(typeId)->getImmediateOperand(6); }
+
+ bool isPointer(Id resultId) const { return isPointerType(getTypeId(resultId)); }
+ bool isScalar(Id resultId) const { return isScalarType(getTypeId(resultId)); }
+ bool isVector(Id resultId) const { return isVectorType(getTypeId(resultId)); }
+ bool isMatrix(Id resultId) const { return isMatrixType(getTypeId(resultId)); }
+ bool isCooperativeMatrix(Id resultId)const { return isCooperativeMatrixType(getTypeId(resultId)); }
+ bool isAggregate(Id resultId) const { return isAggregateType(getTypeId(resultId)); }
+ bool isSampledImage(Id resultId) const { return isSampledImageType(getTypeId(resultId)); }
+
+ bool isBoolType(Id typeId) { return groupedTypes[OpTypeBool].size() > 0 && typeId == groupedTypes[OpTypeBool].back()->getResultId(); }
+ bool isIntType(Id typeId) const { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) != 0; }
+ bool isUintType(Id typeId) const { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) == 0; }
+ bool isFloatType(Id typeId) const { return getTypeClass(typeId) == OpTypeFloat; }
+ bool isPointerType(Id typeId) const { return getTypeClass(typeId) == OpTypePointer; }
+ bool isScalarType(Id typeId) const { return getTypeClass(typeId) == OpTypeFloat || getTypeClass(typeId) == OpTypeInt || getTypeClass(typeId) == OpTypeBool; }
+ bool isVectorType(Id typeId) const { return getTypeClass(typeId) == OpTypeVector; }
+ bool isMatrixType(Id typeId) const { return getTypeClass(typeId) == OpTypeMatrix; }
+ bool isStructType(Id typeId) const { return getTypeClass(typeId) == OpTypeStruct; }
+ bool isArrayType(Id typeId) const { return getTypeClass(typeId) == OpTypeArray; }
+ bool isCooperativeMatrixType(Id typeId)const { return getTypeClass(typeId) == OpTypeCooperativeMatrixNV; }
+ bool isAggregateType(Id typeId) const { return isArrayType(typeId) || isStructType(typeId) || isCooperativeMatrixType(typeId); }
+ bool isImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeImage; }
+ bool isSamplerType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampler; }
+ bool isSampledImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampledImage; }
+ bool containsType(Id typeId, Op typeOp, unsigned int width) const;
+ bool containsPhysicalStorageBufferOrArray(Id typeId) const;
+
+ bool isConstantOpCode(Op opcode) const;
+ bool isSpecConstantOpCode(Op opcode) const;
+ bool isConstant(Id resultId) const { return isConstantOpCode(getOpCode(resultId)); }
+ bool isConstantScalar(Id resultId) const { return getOpCode(resultId) == OpConstant; }
+ bool isSpecConstant(Id resultId) const { return isSpecConstantOpCode(getOpCode(resultId)); }
+ unsigned int getConstantScalar(Id resultId) const { return module.getInstruction(resultId)->getImmediateOperand(0); }
+ StorageClass getStorageClass(Id resultId) const { return getTypeStorageClass(getTypeId(resultId)); }
+
+ int getScalarTypeWidth(Id typeId) const
+ {
+ Id scalarTypeId = getScalarTypeId(typeId);
+ assert(getTypeClass(scalarTypeId) == OpTypeInt || getTypeClass(scalarTypeId) == OpTypeFloat);
+ return module.getInstruction(scalarTypeId)->getImmediateOperand(0);
+ }
+
+ int getTypeNumColumns(Id typeId) const
+ {
+ assert(isMatrixType(typeId));
+ return getNumTypeConstituents(typeId);
+ }
+ int getNumColumns(Id resultId) const { return getTypeNumColumns(getTypeId(resultId)); }
+ int getTypeNumRows(Id typeId) const
+ {
+ assert(isMatrixType(typeId));
+ return getNumTypeComponents(getContainedTypeId(typeId));
+ }
+ int getNumRows(Id resultId) const { return getTypeNumRows(getTypeId(resultId)); }
+
+ Dim getTypeDimensionality(Id typeId) const
+ {
+ assert(isImageType(typeId));
+ return (Dim)module.getInstruction(typeId)->getImmediateOperand(1);
+ }
+ Id getImageType(Id resultId) const
+ {
+ Id typeId = getTypeId(resultId);
+ assert(isImageType(typeId) || isSampledImageType(typeId));
+ return isSampledImageType(typeId) ? module.getInstruction(typeId)->getIdOperand(0) : typeId;
+ }
+ bool isArrayedImageType(Id typeId) const
+ {
+ assert(isImageType(typeId));
+ return module.getInstruction(typeId)->getImmediateOperand(3) != 0;
+ }
+
+ // For making new constants (will return old constant if the requested one was already made).
+ Id makeBoolConstant(bool b, bool specConstant = false);
+ Id makeInt8Constant(int i, bool specConstant = false) { return makeIntConstant(makeIntType(8), (unsigned)i, specConstant); }
+ Id makeUint8Constant(unsigned u, bool specConstant = false) { return makeIntConstant(makeUintType(8), u, specConstant); }
+ Id makeInt16Constant(int i, bool specConstant = false) { return makeIntConstant(makeIntType(16), (unsigned)i, specConstant); }
+ Id makeUint16Constant(unsigned u, bool specConstant = false) { return makeIntConstant(makeUintType(16), u, specConstant); }
+ Id makeIntConstant(int i, bool specConstant = false) { return makeIntConstant(makeIntType(32), (unsigned)i, specConstant); }
+ Id makeUintConstant(unsigned u, bool specConstant = false) { return makeIntConstant(makeUintType(32), u, specConstant); }
+ Id makeInt64Constant(long long i, bool specConstant = false) { return makeInt64Constant(makeIntType(64), (unsigned long long)i, specConstant); }
+ Id makeUint64Constant(unsigned long long u, bool specConstant = false) { return makeInt64Constant(makeUintType(64), u, specConstant); }
+ Id makeFloatConstant(float f, bool specConstant = false);
+ Id makeDoubleConstant(double d, bool specConstant = false);
+ Id makeFloat16Constant(float f16, bool specConstant = false);
+ Id makeFpConstant(Id type, double d, bool specConstant = false);
+
+ // Turn the array of constants into a proper spv constant of the requested type.
+ Id makeCompositeConstant(Id type, const std::vector<Id>& comps, bool specConst = false);
+
+ // Methods for adding information outside the CFG.
+ Instruction* addEntryPoint(ExecutionModel, Function*, const char* name);
+ void addExecutionMode(Function*, ExecutionMode mode, int value1 = -1, int value2 = -1, int value3 = -1);
+ void addName(Id, const char* name);
+ void addMemberName(Id, int member, const char* name);
+ void addDecoration(Id, Decoration, int num = -1);
+ void addDecoration(Id, Decoration, const char*);
+ void addDecorationId(Id id, Decoration, Id idDecoration);
+ void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1);
+ void addMemberDecoration(Id, unsigned int member, Decoration, const char*);
+
+ // At the end of what block do the next create*() instructions go?
+ void setBuildPoint(Block* bp) { buildPoint = bp; }
+ Block* getBuildPoint() const { return buildPoint; }
+
+ // Make the entry-point function. The returned pointer is only valid
+ // for the lifetime of this builder.
+ Function* makeEntryPoint(const char*);
+
+ // Make a shader-style function, and create its entry block if entry is non-zero.
+ // Return the function, pass back the entry.
+ // The returned pointer is only valid for the lifetime of this builder.
+ Function* makeFunctionEntry(Decoration precision, Id returnType, const char* name, const std::vector<Id>& paramTypes,
+ const std::vector<std::vector<Decoration>>& precisions, Block **entry = 0);
+
+ // Create a return. An 'implicit' return is one not appearing in the source
+ // code. In the case of an implicit return, no post-return block is inserted.
+ void makeReturn(bool implicit, Id retVal = 0);
+
+ // Generate all the code needed to finish up a function.
+ void leaveFunction();
+
+ // Create a discard.
+ void makeDiscard();
+
+ // Create a global or function local or IO variable.
+ Id createVariable(StorageClass, Id type, const char* name = 0);
+
+ // Create an intermediate with an undefined value.
+ Id createUndefined(Id type);
+
+ // Store into an Id and return the l-value
+ void createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
+
+ // Load from an Id and return it
+ Id createLoad(Id lValue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
+
+ // Create an OpAccessChain instruction
+ Id createAccessChain(StorageClass, Id base, const std::vector<Id>& offsets);
+
+ // Create an OpArrayLength instruction
+ Id createArrayLength(Id base, unsigned int member);
+
+ // Create an OpCooperativeMatrixLengthNV instruction
+ Id createCooperativeMatrixLength(Id type);
+
+ // Create an OpCompositeExtract instruction
+ Id createCompositeExtract(Id composite, Id typeId, unsigned index);
+ Id createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes);
+ Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index);
+ Id createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& indexes);
+
+ Id createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex);
+ Id createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex);
+
+ void createNoResultOp(Op);
+ void createNoResultOp(Op, Id operand);
+ void createNoResultOp(Op, const std::vector<Id>& operands);
+ void createNoResultOp(Op, const std::vector<IdImmediate>& operands);
+ void createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask);
+ void createMemoryBarrier(unsigned executionScope, unsigned memorySemantics);
+ Id createUnaryOp(Op, Id typeId, Id operand);
+ Id createBinOp(Op, Id typeId, Id operand1, Id operand2);
+ Id createTriOp(Op, Id typeId, Id operand1, Id operand2, Id operand3);
+ Id createOp(Op, Id typeId, const std::vector<Id>& operands);
+ Id createOp(Op, Id typeId, const std::vector<IdImmediate>& operands);
+ Id createFunctionCall(spv::Function*, const std::vector<spv::Id>&);
+ Id createSpecConstantOp(Op, Id typeId, const std::vector<spv::Id>& operands, const std::vector<unsigned>& literals);
+
+ // Take an rvalue (source) and a set of channels to extract from it to
+ // make a new rvalue, which is returned.
+ Id createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector<unsigned>& channels);
+
+ // Take a copy of an lvalue (target) and a source of components, and set the
+ // source components into the lvalue where the 'channels' say to put them.
+ // An updated version of the target is returned.
+ // (No true lvalue or stores are used.)
+ Id createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector<unsigned>& channels);
+
+ // If both the id and precision are valid, the id
+ // gets tagged with the requested precision.
+ // The passed in id is always the returned id, to simplify use patterns.
+ Id setPrecision(Id id, Decoration precision)
+ {
+ if (precision != NoPrecision && id != NoResult)
+ addDecoration(id, precision);
+
+ return id;
+ }
+
+ // Can smear a scalar to a vector for the following forms:
+ // - promoteScalar(scalar, vector) // smear scalar to width of vector
+ // - promoteScalar(vector, scalar) // smear scalar to width of vector
+ // - promoteScalar(pointer, scalar) // smear scalar to width of what pointer points to
+ // - promoteScalar(scalar, scalar) // do nothing
+ // Other forms are not allowed.
+ //
+ // Generally, the type of 'scalar' does not need to be the same type as the components in 'vector'.
+ // The type of the created vector is a vector of components of the same type as the scalar.
+ //
+ // Note: One of the arguments will change, with the result coming back that way rather than
+ // through the return value.
+ void promoteScalar(Decoration precision, Id& left, Id& right);
+
+ // Make a value by smearing the scalar to fill the type.
+ // vectorType should be the correct type for making a vector of scalarVal.
+ // (No conversions are done.)
+ Id smearScalar(Decoration precision, Id scalarVal, Id vectorType);
+
+ // Create a call to a built-in function.
+ Id createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& args);
+
+ // List of parameters used to create a texture operation
+ struct TextureParameters {
+ Id sampler;
+ Id coords;
+ Id bias;
+ Id lod;
+ Id Dref;
+ Id offset;
+ Id offsets;
+ Id gradX;
+ Id gradY;
+ Id sample;
+ Id component;
+ Id texelOut;
+ Id lodClamp;
+ Id granularity;
+ Id coarse;
+ bool nonprivate;
+ bool volatil;
+ };
+
+ // Select the correct texture operation based on all inputs, and emit the correct instruction
+ Id createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather, bool noImplicit, const TextureParameters&);
+
+ // Emit the OpTextureQuery* instruction that was passed in.
+ // Figure out the right return value and type, and return it.
+ Id createTextureQueryCall(Op, const TextureParameters&, bool isUnsignedResult);
+
+ Id createSamplePositionCall(Decoration precision, Id, Id);
+
+ Id createBitFieldExtractCall(Decoration precision, Id, Id, Id, bool isSigned);
+ Id createBitFieldInsertCall(Decoration precision, Id, Id, Id, Id);
+
+ // Reduction comparison for composites: For equal and not-equal resulting in a scalar.
+ Id createCompositeCompare(Decoration precision, Id, Id, bool /* true if for equal, false if for not-equal */);
+
+ // OpCompositeConstruct
+ Id createCompositeConstruct(Id typeId, const std::vector<Id>& constituents);
+
+ // vector or scalar constructor
+ Id createConstructor(Decoration precision, const std::vector<Id>& sources, Id resultTypeId);
+
+ // matrix constructor
+ Id createMatrixConstructor(Decoration precision, const std::vector<Id>& sources, Id constructee);
+
+ // Helper to use for building nested control flow with if-then-else.
+ class If {
+ public:
+ If(Id condition, unsigned int ctrl, Builder& builder);
+ ~If() {}
+
+ void makeBeginElse();
+ void makeEndIf();
+
+ private:
+ If(const If&);
+ If& operator=(If&);
+
+ Builder& builder;
+ Id condition;
+ unsigned int control;
+ Function* function;
+ Block* headerBlock;
+ Block* thenBlock;
+ Block* elseBlock;
+ Block* mergeBlock;
+ };
+
+ // Make a switch statement. A switch has 'numSegments' of pieces of code, not containing
+ // any case/default labels, all separated by one or more case/default labels. Each possible
+ // case value v is a jump to the caseValues[v] segment. The defaultSegment is also in this
+ // number space. How to compute the value is given by 'condition', as in switch(condition).
+ //
+ // The SPIR-V Builder will maintain the stack of post-switch merge blocks for nested switches.
+ //
+ // Use a defaultSegment < 0 if there is no default segment (to branch to post switch).
+ //
+ // Returns the right set of basic blocks to start each code segment with, so that the caller's
+ // recursion stack can hold the memory for it.
+ //
+ void makeSwitch(Id condition, unsigned int control, int numSegments, const std::vector<int>& caseValues,
+ const std::vector<int>& valueToSegment, int defaultSegment, std::vector<Block*>& segmentBB); // return argument
+
+ // Add a branch to the innermost switch's merge block.
+ void addSwitchBreak();
+
+ // Move to the next code segment, passing in the return argument in makeSwitch()
+ void nextSwitchSegment(std::vector<Block*>& segmentBB, int segment);
+
+ // Finish off the innermost switch.
+ void endSwitch(std::vector<Block*>& segmentBB);
+
+ struct LoopBlocks {
+ LoopBlocks(Block& head, Block& body, Block& merge, Block& continue_target) :
+ head(head), body(body), merge(merge), continue_target(continue_target) { }
+ Block &head, &body, &merge, &continue_target;
+ private:
+ LoopBlocks();
+ LoopBlocks& operator=(const LoopBlocks&);
+ };
+
+ // Start a new loop and prepare the builder to generate code for it. Until
+ // closeLoop() is called for this loop, createLoopContinue() and
+ // createLoopExit() will target its corresponding blocks.
+ LoopBlocks& makeNewLoop();
+
+ // Create a new block in the function containing the build point. Memory is
+ // owned by the function object.
+ Block& makeNewBlock();
+
+ // Add a branch to the continue_target of the current (innermost) loop.
+ void createLoopContinue();
+
+ // Add an exit (e.g. "break") from the innermost loop that we're currently
+ // in.
+ void createLoopExit();
+
+ // Close the innermost loop that you're in
+ void closeLoop();
+
+ //
+ // Access chain design for an R-Value vs. L-Value:
+ //
+ // There is a single access chain the builder is building at
+ // any particular time. Such a chain can be used to either to a load or
+ // a store, when desired.
+ //
+ // Expressions can be r-values, l-values, or both, or only r-values:
+ // a[b.c].d = .... // l-value
+ // ... = a[b.c].d; // r-value, that also looks like an l-value
+ // ++a[b.c].d; // r-value and l-value
+ // (x + y)[2]; // r-value only, can't possibly be l-value
+ //
+ // Computing an r-value means generating code. Hence,
+ // r-values should only be computed when they are needed, not speculatively.
+ //
+ // Computing an l-value means saving away information for later use in the compiler,
+ // no code is generated until the l-value is later dereferenced. It is okay
+ // to speculatively generate an l-value, just not okay to speculatively dereference it.
+ //
+ // The base of the access chain (the left-most variable or expression
+ // from which everything is based) can be set either as an l-value
+ // or as an r-value. Most efficient would be to set an l-value if one
+ // is available. If an expression was evaluated, the resulting r-value
+ // can be set as the chain base.
+ //
+ // The users of this single access chain can save and restore if they
+ // want to nest or manage multiple chains.
+ //
+
+ struct AccessChain {
+ Id base; // for l-values, pointer to the base object, for r-values, the base object
+ std::vector<Id> indexChain;
+ Id instr; // cache the instruction that generates this access chain
+ std::vector<unsigned> swizzle; // each std::vector element selects the next GLSL component number
+ Id component; // a dynamic component index, can coexist with a swizzle, done after the swizzle, NoResult if not present
+ Id preSwizzleBaseType; // dereferenced type, before swizzle or component is applied; NoType unless a swizzle or component is present
+ bool isRValue; // true if 'base' is an r-value, otherwise, base is an l-value
+ unsigned int alignment; // bitwise OR of alignment values passed in. Accumulates worst alignment. Only tracks base and (optional) component selection alignment.
+
+ // Accumulate whether anything in the chain of structures has coherent decorations.
+ struct CoherentFlags {
+ unsigned coherent : 1;
+ unsigned devicecoherent : 1;
+ unsigned queuefamilycoherent : 1;
+ unsigned workgroupcoherent : 1;
+ unsigned subgroupcoherent : 1;
+ unsigned nonprivate : 1;
+ unsigned volatil : 1;
+ unsigned isImage : 1;
+
+ void clear() {
+ coherent = 0;
+ devicecoherent = 0;
+ queuefamilycoherent = 0;
+ workgroupcoherent = 0;
+ subgroupcoherent = 0;
+ nonprivate = 0;
+ volatil = 0;
+ isImage = 0;
+ }
+
+ CoherentFlags() { clear(); }
+ CoherentFlags operator |=(const CoherentFlags &other) {
+ coherent |= other.coherent;
+ devicecoherent |= other.devicecoherent;
+ queuefamilycoherent |= other.queuefamilycoherent;
+ workgroupcoherent |= other.workgroupcoherent;
+ subgroupcoherent |= other.subgroupcoherent;
+ nonprivate |= other.nonprivate;
+ volatil |= other.volatil;
+ isImage |= other.isImage;
+ return *this;
+ }
+ };
+ CoherentFlags coherentFlags;
+ };
+
+ //
+ // the SPIR-V builder maintains a single active chain that
+ // the following methods operate on
+ //
+
+ // for external save and restore
+ AccessChain getAccessChain() { return accessChain; }
+ void setAccessChain(AccessChain newChain) { accessChain = newChain; }
+
+ // clear accessChain
+ void clearAccessChain();
+
+ // set new base as an l-value base
+ void setAccessChainLValue(Id lValue)
+ {
+ assert(isPointer(lValue));
+ accessChain.base = lValue;
+ }
+
+ // set new base value as an r-value
+ void setAccessChainRValue(Id rValue)
+ {
+ accessChain.isRValue = true;
+ accessChain.base = rValue;
+ }
+
+ // push offset onto the end of the chain
+ void accessChainPush(Id offset, AccessChain::CoherentFlags coherentFlags, unsigned int alignment)
+ {
+ accessChain.indexChain.push_back(offset);
+ accessChain.coherentFlags |= coherentFlags;
+ accessChain.alignment |= alignment;
+ }
+
+ // push new swizzle onto the end of any existing swizzle, merging into a single swizzle
+ void accessChainPushSwizzle(std::vector<unsigned>& swizzle, Id preSwizzleBaseType, AccessChain::CoherentFlags coherentFlags, unsigned int alignment);
+
+ // push a dynamic component selection onto the access chain, only applicable with a
+ // non-trivial swizzle or no swizzle
+ void accessChainPushComponent(Id component, Id preSwizzleBaseType, AccessChain::CoherentFlags coherentFlags, unsigned int alignment)
+ {
+ if (accessChain.swizzle.size() != 1) {
+ accessChain.component = component;
+ if (accessChain.preSwizzleBaseType == NoType)
+ accessChain.preSwizzleBaseType = preSwizzleBaseType;
+ }
+ accessChain.coherentFlags |= coherentFlags;
+ accessChain.alignment |= alignment;
+ }
+
+ // use accessChain and swizzle to store value
+ void accessChainStore(Id rvalue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
+
+ // use accessChain and swizzle to load an r-value
+ Id accessChainLoad(Decoration precision, Decoration nonUniform, Id ResultType, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0);
+
+ // get the direct pointer for an l-value
+ Id accessChainGetLValue();
+
+ // Get the inferred SPIR-V type of the result of the current access chain,
+ // based on the type of the base and the chain of dereferences.
+ Id accessChainGetInferredType();
+
+ // Add capabilities, extensions, remove unneeded decorations, etc.,
+ // based on the resulting SPIR-V.
+ void postProcess();
+
+ // Hook to visit each instruction in a block in a function
+ void postProcess(Instruction&);
+ // Hook to visit each instruction in a reachable block in a function.
+ void postProcessReachable(const Instruction&);
+ // Hook to visit each non-32-bit sized float/int operation in a block.
+ void postProcessType(const Instruction&, spv::Id typeId);
+
+ void dump(std::vector<unsigned int>&) const;
+
+ void createBranch(Block* block);
+ void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock);
+ void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, unsigned int dependencyLength);
+
+ // Sets to generate opcode for specialization constants.
+ void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; }
+ // Sets to generate opcode for non-specialization constants (normal mode).
+ void setToNormalCodeGenMode() { generatingOpCodeForSpecConst = false; }
+ // Check if the builder is generating code for spec constants.
+ bool isInSpecConstCodeGenMode() { return generatingOpCodeForSpecConst; }
+
+ protected:
+ Id makeIntConstant(Id typeId, unsigned value, bool specConstant);
+ Id makeInt64Constant(Id typeId, unsigned long long value, bool specConstant);
+ Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value);
+ Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2);
+ Id findCompositeConstant(Op typeClass, Id typeId, const std::vector<Id>& comps);
+ Id findStructConstant(Id typeId, const std::vector<Id>& comps);
+ Id collapseAccessChain();
+ void remapDynamicSwizzle();
+ void transferAccessChainSwizzle(bool dynamic);
+ void simplifyAccessChainSwizzle();
+ void createAndSetNoPredecessorBlock(const char*);
+ void createSelectionMerge(Block* mergeBlock, unsigned int control);
+ void dumpSourceInstructions(std::vector<unsigned int>&) const;
+ void dumpSourceInstructions(const spv::Id fileId, const std::string& text, std::vector<unsigned int>&) const;
+ void dumpInstructions(std::vector<unsigned int>&, const std::vector<std::unique_ptr<Instruction> >&) const;
+ void dumpModuleProcesses(std::vector<unsigned int>&) const;
+ spv::MemoryAccessMask sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc) const;
+
+ unsigned int spvVersion; // the version of SPIR-V to emit in the header
+ SourceLanguage source;
+ int sourceVersion;
+ spv::Id sourceFileStringId;
+ std::string sourceText;
+ int currentLine;
+ const char* currentFile;
+ bool emitOpLines;
+ std::set<std::string> extensions;
+ std::vector<const char*> sourceExtensions;
+ std::vector<const char*> moduleProcesses;
+ AddressingModel addressModel;
+ MemoryModel memoryModel;
+ std::set<spv::Capability> capabilities;
+ int builderNumber;
+ Module module;
+ Block* buildPoint;
+ Id uniqueId;
+ Function* entryPointFunction;
+ bool generatingOpCodeForSpecConst;
+ AccessChain accessChain;
+
+ // special blocks of instructions for output
+ std::vector<std::unique_ptr<Instruction> > strings;
+ std::vector<std::unique_ptr<Instruction> > imports;
+ std::vector<std::unique_ptr<Instruction> > entryPoints;
+ std::vector<std::unique_ptr<Instruction> > executionModes;
+ std::vector<std::unique_ptr<Instruction> > names;
+ std::vector<std::unique_ptr<Instruction> > decorations;
+ std::vector<std::unique_ptr<Instruction> > constantsTypesGlobals;
+ std::vector<std::unique_ptr<Instruction> > externals;
+ std::vector<std::unique_ptr<Function> > functions;
+
+ // not output, internally used for quick & dirty canonical (unique) creation
+ std::unordered_map<unsigned int, std::vector<Instruction*>> groupedConstants; // map type opcodes to constant inst.
+ std::unordered_map<unsigned int, std::vector<Instruction*>> groupedStructConstants; // map struct-id to constant instructions
+ std::unordered_map<unsigned int, std::vector<Instruction*>> groupedTypes; // map type opcodes to type instructions
+
+ // stack of switches
+ std::stack<Block*> switchMerges;
+
+ // Our loop stack.
+ std::stack<LoopBlocks> loops;
+
+ // map from strings to their string ids
+ std::unordered_map<std::string, spv::Id> stringIds;
+
+ // map from include file name ids to their contents
+ std::map<spv::Id, const std::string*> includeFiles;
+
+ // The stream for outputting warnings and errors.
+ SpvBuildLogger* logger;
+}; // end Builder class
+
+}; // end spv namespace
+
+#endif // SpvBuilder_H
diff --git a/src/3rdparty/glslang/SPIRV/SpvPostProcess.cpp b/src/3rdparty/glslang/SPIRV/SpvPostProcess.cpp
new file mode 100644
index 0000000..80471ca
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/SpvPostProcess.cpp
@@ -0,0 +1,389 @@
+//
+// Copyright (C) 2018 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+//
+// Post-processing for SPIR-V IR, in internal form, not standard binary form.
+//
+
+#include <cassert>
+#include <cstdlib>
+
+#include <unordered_set>
+#include <algorithm>
+
+#include "SpvBuilder.h"
+
+#include "spirv.hpp"
+#include "GlslangToSpv.h"
+#include "SpvBuilder.h"
+namespace spv {
+ #include "GLSL.std.450.h"
+ #include "GLSL.ext.KHR.h"
+ #include "GLSL.ext.EXT.h"
+#ifdef AMD_EXTENSIONS
+ #include "GLSL.ext.AMD.h"
+#endif
+#ifdef NV_EXTENSIONS
+ #include "GLSL.ext.NV.h"
+#endif
+}
+
+namespace spv {
+
+// Hook to visit each operand type and result type of an instruction.
+// Will be called multiple times for one instruction, once for each typed
+// operand and the result.
+void Builder::postProcessType(const Instruction& inst, Id typeId)
+{
+ // Characterize the type being questioned
+ Id basicTypeOp = getMostBasicTypeClass(typeId);
+ int width = 0;
+ if (basicTypeOp == OpTypeFloat || basicTypeOp == OpTypeInt)
+ width = getScalarTypeWidth(typeId);
+
+ // Do opcode-specific checks
+ switch (inst.getOpCode()) {
+ case OpLoad:
+ case OpStore:
+ if (basicTypeOp == OpTypeStruct) {
+ if (containsType(typeId, OpTypeInt, 8))
+ addCapability(CapabilityInt8);
+ if (containsType(typeId, OpTypeInt, 16))
+ addCapability(CapabilityInt16);
+ if (containsType(typeId, OpTypeFloat, 16))
+ addCapability(CapabilityFloat16);
+ } else {
+ StorageClass storageClass = getStorageClass(inst.getIdOperand(0));
+ if (width == 8) {
+ switch (storageClass) {
+ case StorageClassPhysicalStorageBufferEXT:
+ case StorageClassUniform:
+ case StorageClassStorageBuffer:
+ case StorageClassPushConstant:
+ break;
+ default:
+ addCapability(CapabilityInt8);
+ break;
+ }
+ } else if (width == 16) {
+ switch (storageClass) {
+ case StorageClassPhysicalStorageBufferEXT:
+ case StorageClassUniform:
+ case StorageClassStorageBuffer:
+ case StorageClassPushConstant:
+ case StorageClassInput:
+ case StorageClassOutput:
+ break;
+ default:
+ if (basicTypeOp == OpTypeInt)
+ addCapability(CapabilityInt16);
+ if (basicTypeOp == OpTypeFloat)
+ addCapability(CapabilityFloat16);
+ break;
+ }
+ }
+ }
+ break;
+ case OpAccessChain:
+ case OpPtrAccessChain:
+ case OpCopyObject:
+ case OpFConvert:
+ case OpSConvert:
+ case OpUConvert:
+ break;
+ case OpExtInst:
+#if AMD_EXTENSIONS
+ switch (inst.getImmediateOperand(1)) {
+ case GLSLstd450Frexp:
+ case GLSLstd450FrexpStruct:
+ if (getSpvVersion() < glslang::EShTargetSpv_1_3 && containsType(typeId, OpTypeInt, 16))
+ addExtension(spv::E_SPV_AMD_gpu_shader_int16);
+ break;
+ case GLSLstd450InterpolateAtCentroid:
+ case GLSLstd450InterpolateAtSample:
+ case GLSLstd450InterpolateAtOffset:
+ if (getSpvVersion() < glslang::EShTargetSpv_1_3 && containsType(typeId, OpTypeFloat, 16))
+ addExtension(spv::E_SPV_AMD_gpu_shader_half_float);
+ break;
+ default:
+ break;
+ }
+#endif
+ break;
+ default:
+ if (basicTypeOp == OpTypeFloat && width == 16)
+ addCapability(CapabilityFloat16);
+ if (basicTypeOp == OpTypeInt && width == 16)
+ addCapability(CapabilityInt16);
+ if (basicTypeOp == OpTypeInt && width == 8)
+ addCapability(CapabilityInt8);
+ break;
+ }
+}
+
+// Called for each instruction that resides in a block.
+void Builder::postProcess(Instruction& inst)
+{
+ // Add capabilities based simply on the opcode.
+ switch (inst.getOpCode()) {
+ case OpExtInst:
+ switch (inst.getImmediateOperand(1)) {
+ case GLSLstd450InterpolateAtCentroid:
+ case GLSLstd450InterpolateAtSample:
+ case GLSLstd450InterpolateAtOffset:
+ addCapability(CapabilityInterpolationFunction);
+ break;
+ default:
+ break;
+ }
+ break;
+ case OpDPdxFine:
+ case OpDPdyFine:
+ case OpFwidthFine:
+ case OpDPdxCoarse:
+ case OpDPdyCoarse:
+ case OpFwidthCoarse:
+ addCapability(CapabilityDerivativeControl);
+ break;
+
+ case OpImageQueryLod:
+ case OpImageQuerySize:
+ case OpImageQuerySizeLod:
+ case OpImageQuerySamples:
+ case OpImageQueryLevels:
+ addCapability(CapabilityImageQuery);
+ break;
+
+#ifdef NV_EXTENSIONS
+ case OpGroupNonUniformPartitionNV:
+ addExtension(E_SPV_NV_shader_subgroup_partitioned);
+ addCapability(CapabilityGroupNonUniformPartitionedNV);
+ break;
+#endif
+
+ case OpLoad:
+ case OpStore:
+ {
+ // For any load/store to a PhysicalStorageBufferEXT, walk the accesschain
+ // index list to compute the misalignment. The pre-existing alignment value
+ // (set via Builder::AccessChain::alignment) only accounts for the base of
+ // the reference type and any scalar component selection in the accesschain,
+ // and this function computes the rest from the SPIR-V Offset decorations.
+ Instruction *accessChain = module.getInstruction(inst.getIdOperand(0));
+ if (accessChain->getOpCode() == OpAccessChain) {
+ Instruction *base = module.getInstruction(accessChain->getIdOperand(0));
+ // Get the type of the base of the access chain. It must be a pointer type.
+ Id typeId = base->getTypeId();
+ Instruction *type = module.getInstruction(typeId);
+ assert(type->getOpCode() == OpTypePointer);
+ if (type->getImmediateOperand(0) != StorageClassPhysicalStorageBufferEXT) {
+ break;
+ }
+ // Get the pointee type.
+ typeId = type->getIdOperand(1);
+ type = module.getInstruction(typeId);
+ // Walk the index list for the access chain. For each index, find any
+ // misalignment that can apply when accessing the member/element via
+ // Offset/ArrayStride/MatrixStride decorations, and bitwise OR them all
+ // together.
+ int alignment = 0;
+ for (int i = 1; i < accessChain->getNumOperands(); ++i) {
+ Instruction *idx = module.getInstruction(accessChain->getIdOperand(i));
+ if (type->getOpCode() == OpTypeStruct) {
+ assert(idx->getOpCode() == OpConstant);
+ unsigned int c = idx->getImmediateOperand(0);
+
+ const auto function = [&](const std::unique_ptr<Instruction>& decoration) {
+ if (decoration.get()->getOpCode() == OpMemberDecorate &&
+ decoration.get()->getIdOperand(0) == typeId &&
+ decoration.get()->getImmediateOperand(1) == c &&
+ (decoration.get()->getImmediateOperand(2) == DecorationOffset ||
+ decoration.get()->getImmediateOperand(2) == DecorationMatrixStride)) {
+ alignment |= decoration.get()->getImmediateOperand(3);
+ }
+ };
+ std::for_each(decorations.begin(), decorations.end(), function);
+ // get the next member type
+ typeId = type->getIdOperand(c);
+ type = module.getInstruction(typeId);
+ } else if (type->getOpCode() == OpTypeArray ||
+ type->getOpCode() == OpTypeRuntimeArray) {
+ const auto function = [&](const std::unique_ptr<Instruction>& decoration) {
+ if (decoration.get()->getOpCode() == OpDecorate &&
+ decoration.get()->getIdOperand(0) == typeId &&
+ decoration.get()->getImmediateOperand(1) == DecorationArrayStride) {
+ alignment |= decoration.get()->getImmediateOperand(2);
+ }
+ };
+ std::for_each(decorations.begin(), decorations.end(), function);
+ // Get the element type
+ typeId = type->getIdOperand(0);
+ type = module.getInstruction(typeId);
+ } else {
+ // Once we get to any non-aggregate type, we're done.
+ break;
+ }
+ }
+ assert(inst.getNumOperands() >= 3);
+ unsigned int memoryAccess = inst.getImmediateOperand((inst.getOpCode() == OpStore) ? 2 : 1);
+ assert(memoryAccess & MemoryAccessAlignedMask);
+ static_cast<void>(memoryAccess);
+ // Compute the index of the alignment operand.
+ int alignmentIdx = 2;
+ if (inst.getOpCode() == OpStore)
+ alignmentIdx++;
+ // Merge new and old (mis)alignment
+ alignment |= inst.getImmediateOperand(alignmentIdx);
+ // Pick the LSB
+ alignment = alignment & ~(alignment & (alignment-1));
+ // update the Aligned operand
+ inst.setImmediateOperand(alignmentIdx, alignment);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ // Checks based on type
+ if (inst.getTypeId() != NoType)
+ postProcessType(inst, inst.getTypeId());
+ for (int op = 0; op < inst.getNumOperands(); ++op) {
+ if (inst.isIdOperand(op)) {
+ // In blocks, these are always result ids, but we are relying on
+ // getTypeId() to return NoType for things like OpLabel.
+ if (getTypeId(inst.getIdOperand(op)) != NoType)
+ postProcessType(inst, getTypeId(inst.getIdOperand(op)));
+ }
+ }
+}
+
+// Called for each instruction in a reachable block.
+void Builder::postProcessReachable(const Instruction&)
+{
+ // did have code here, but questionable to do so without deleting the instructions
+}
+
+// comment in header
+void Builder::postProcess()
+{
+ std::unordered_set<const Block*> reachableBlocks;
+ std::unordered_set<Id> unreachableDefinitions;
+ // Collect IDs defined in unreachable blocks. For each function, label the
+ // reachable blocks first. Then for each unreachable block, collect the
+ // result IDs of the instructions in it.
+ for (auto fi = module.getFunctions().cbegin(); fi != module.getFunctions().cend(); fi++) {
+ Function* f = *fi;
+ Block* entry = f->getEntryBlock();
+ inReadableOrder(entry, [&reachableBlocks](const Block* b) { reachableBlocks.insert(b); });
+ for (auto bi = f->getBlocks().cbegin(); bi != f->getBlocks().cend(); bi++) {
+ Block* b = *bi;
+ if (reachableBlocks.count(b) == 0) {
+ for (auto ii = b->getInstructions().cbegin(); ii != b->getInstructions().cend(); ii++)
+ unreachableDefinitions.insert(ii->get()->getResultId());
+ }
+ }
+ }
+
+ // Remove unneeded decorations, for unreachable instructions
+ decorations.erase(std::remove_if(decorations.begin(), decorations.end(),
+ [&unreachableDefinitions](std::unique_ptr<Instruction>& I) -> bool {
+ Id decoration_id = I.get()->getIdOperand(0);
+ return unreachableDefinitions.count(decoration_id) != 0;
+ }),
+ decorations.end());
+
+ // Add per-instruction capabilities, extensions, etc.,
+
+ // process all reachable instructions...
+ for (auto bi = reachableBlocks.cbegin(); bi != reachableBlocks.cend(); ++bi) {
+ const Block* block = *bi;
+ const auto function = [this](const std::unique_ptr<Instruction>& inst) { postProcessReachable(*inst.get()); };
+ std::for_each(block->getInstructions().begin(), block->getInstructions().end(), function);
+ }
+
+ // process all block-contained instructions
+ for (auto fi = module.getFunctions().cbegin(); fi != module.getFunctions().cend(); fi++) {
+ Function* f = *fi;
+ for (auto bi = f->getBlocks().cbegin(); bi != f->getBlocks().cend(); bi++) {
+ Block* b = *bi;
+ for (auto ii = b->getInstructions().cbegin(); ii != b->getInstructions().cend(); ii++)
+ postProcess(*ii->get());
+
+ // For all local variables that contain pointers to PhysicalStorageBufferEXT, check whether
+ // there is an existing restrict/aliased decoration. If we don't find one, add Aliased as the
+ // default.
+ for (auto vi = b->getLocalVariables().cbegin(); vi != b->getLocalVariables().cend(); vi++) {
+ const Instruction& inst = *vi->get();
+ Id resultId = inst.getResultId();
+ if (containsPhysicalStorageBufferOrArray(getDerefTypeId(resultId))) {
+ bool foundDecoration = false;
+ const auto function = [&](const std::unique_ptr<Instruction>& decoration) {
+ if (decoration.get()->getIdOperand(0) == resultId &&
+ decoration.get()->getOpCode() == OpDecorate &&
+ (decoration.get()->getImmediateOperand(1) == spv::DecorationAliasedPointerEXT ||
+ decoration.get()->getImmediateOperand(1) == spv::DecorationRestrictPointerEXT)) {
+ foundDecoration = true;
+ }
+ };
+ std::for_each(decorations.begin(), decorations.end(), function);
+ if (!foundDecoration) {
+ addDecoration(resultId, spv::DecorationAliasedPointerEXT);
+ }
+ }
+ }
+ }
+ }
+
+ // Look for any 8/16 bit type in physical storage buffer class, and set the
+ // appropriate capability. This happens in createSpvVariable for other storage
+ // classes, but there isn't always a variable for physical storage buffer.
+ for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) {
+ Instruction* type = groupedTypes[OpTypePointer][t];
+ if (type->getImmediateOperand(0) == (unsigned)StorageClassPhysicalStorageBufferEXT) {
+ if (containsType(type->getIdOperand(1), OpTypeInt, 8)) {
+ addExtension(spv::E_SPV_KHR_8bit_storage);
+ addCapability(spv::CapabilityStorageBuffer8BitAccess);
+ }
+ if (containsType(type->getIdOperand(1), OpTypeInt, 16) ||
+ containsType(type->getIdOperand(1), OpTypeFloat, 16)) {
+ addExtension(spv::E_SPV_KHR_16bit_storage);
+ addCapability(spv::CapabilityStorageBuffer16BitAccess);
+ }
+ }
+ }
+}
+
+}; // end spv namespace
diff --git a/src/3rdparty/glslang/SPIRV/SpvTools.cpp b/src/3rdparty/glslang/SPIRV/SpvTools.cpp
new file mode 100644
index 0000000..cce5fa7
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/SpvTools.cpp
@@ -0,0 +1,201 @@
+//
+// Copyright (C) 2014-2016 LunarG, Inc.
+// Copyright (C) 2018 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+//
+// Call into SPIRV-Tools to disassemble, validate, and optimize.
+//
+
+#if ENABLE_OPT
+
+#include <cstdio>
+#include <iostream>
+
+#include "SpvTools.h"
+#include "spirv-tools/optimizer.hpp"
+#include "spirv-tools/libspirv.h"
+
+namespace glslang {
+
+// Translate glslang's view of target versioning to what SPIRV-Tools uses.
+spv_target_env MapToSpirvToolsEnv(const SpvVersion& spvVersion, spv::SpvBuildLogger* logger)
+{
+ switch (spvVersion.vulkan) {
+ case glslang::EShTargetVulkan_1_0: return spv_target_env::SPV_ENV_VULKAN_1_0;
+ case glslang::EShTargetVulkan_1_1: return spv_target_env::SPV_ENV_VULKAN_1_1;
+ default:
+ break;
+ }
+
+ if (spvVersion.openGl > 0)
+ return spv_target_env::SPV_ENV_OPENGL_4_5;
+
+ logger->missingFunctionality("Target version for SPIRV-Tools validator");
+ return spv_target_env::SPV_ENV_UNIVERSAL_1_0;
+}
+
+
+// Use the SPIRV-Tools disassembler to print SPIR-V.
+void SpirvToolsDisassemble(std::ostream& out, const std::vector<unsigned int>& spirv)
+{
+ // disassemble
+ spv_context context = spvContextCreate(SPV_ENV_UNIVERSAL_1_3);
+ spv_text text;
+ spv_diagnostic diagnostic = nullptr;
+ spvBinaryToText(context, spirv.data(), spirv.size(),
+ SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | SPV_BINARY_TO_TEXT_OPTION_INDENT,
+ &text, &diagnostic);
+
+ // dump
+ if (diagnostic == nullptr)
+ out << text->str;
+ else
+ spvDiagnosticPrint(diagnostic);
+
+ // teardown
+ spvDiagnosticDestroy(diagnostic);
+ spvContextDestroy(context);
+}
+
+// Apply the SPIRV-Tools validator to generated SPIR-V.
+void SpirvToolsValidate(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv,
+ spv::SpvBuildLogger* logger)
+{
+ // validate
+ spv_context context = spvContextCreate(MapToSpirvToolsEnv(intermediate.getSpv(), logger));
+ spv_const_binary_t binary = { spirv.data(), spirv.size() };
+ spv_diagnostic diagnostic = nullptr;
+ spv_validator_options options = spvValidatorOptionsCreate();
+ spvValidatorOptionsSetRelaxBlockLayout(options, intermediate.usingHlslOffsets());
+ spvValidateWithOptions(context, options, &binary, &diagnostic);
+
+ // report
+ if (diagnostic != nullptr) {
+ logger->error("SPIRV-Tools Validation Errors");
+ logger->error(diagnostic->error);
+ }
+
+ // tear down
+ spvValidatorOptionsDestroy(options);
+ spvDiagnosticDestroy(diagnostic);
+ spvContextDestroy(context);
+}
+
+// Apply the SPIRV-Tools optimizer to generated SPIR-V, for the purpose of
+// legalizing HLSL SPIR-V.
+void SpirvToolsLegalize(const glslang::TIntermediate&, std::vector<unsigned int>& spirv,
+ spv::SpvBuildLogger*, const SpvOptions* options)
+{
+ spv_target_env target_env = SPV_ENV_UNIVERSAL_1_2;
+
+ spvtools::Optimizer optimizer(target_env);
+ optimizer.SetMessageConsumer(
+ [](spv_message_level_t level, const char *source, const spv_position_t &position, const char *message) {
+ auto &out = std::cerr;
+ switch (level)
+ {
+ case SPV_MSG_FATAL:
+ case SPV_MSG_INTERNAL_ERROR:
+ case SPV_MSG_ERROR:
+ out << "error: ";
+ break;
+ case SPV_MSG_WARNING:
+ out << "warning: ";
+ break;
+ case SPV_MSG_INFO:
+ case SPV_MSG_DEBUG:
+ out << "info: ";
+ break;
+ default:
+ break;
+ }
+ if (source)
+ {
+ out << source << ":";
+ }
+ out << position.line << ":" << position.column << ":" << position.index << ":";
+ if (message)
+ {
+ out << " " << message;
+ }
+ out << std::endl;
+ });
+
+ // If debug (specifically source line info) is being generated, propagate
+ // line information into all SPIR-V instructions. This avoids loss of
+ // information when instructions are deleted or moved. Later, remove
+ // redundant information to minimize final SPRIR-V size.
+ if (options->generateDebugInfo) {
+ optimizer.RegisterPass(spvtools::CreatePropagateLineInfoPass());
+ }
+ optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
+ optimizer.RegisterPass(spvtools::CreateMergeReturnPass());
+ optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass());
+ optimizer.RegisterPass(spvtools::CreateEliminateDeadFunctionsPass());
+ optimizer.RegisterPass(spvtools::CreateScalarReplacementPass());
+ optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass());
+ optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
+ optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
+ optimizer.RegisterPass(spvtools::CreateSimplificationPass());
+ optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
+ optimizer.RegisterPass(spvtools::CreateVectorDCEPass());
+ optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
+ optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
+ optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
+ optimizer.RegisterPass(spvtools::CreateBlockMergePass());
+ optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass());
+ optimizer.RegisterPass(spvtools::CreateIfConversionPass());
+ optimizer.RegisterPass(spvtools::CreateSimplificationPass());
+ optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
+ optimizer.RegisterPass(spvtools::CreateVectorDCEPass());
+ optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
+ if (options->optimizeSize) {
+ optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass());
+ // TODO(greg-lunarg): Add this when AMD driver issues are resolved
+ // optimizer.RegisterPass(CreateCommonUniformElimPass());
+ }
+ optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
+ optimizer.RegisterPass(spvtools::CreateCFGCleanupPass());
+ if (options->generateDebugInfo) {
+ optimizer.RegisterPass(spvtools::CreateRedundantLineInfoElimPass());
+ }
+
+ spvtools::OptimizerOptions spvOptOptions;
+ spvOptOptions.set_run_validator(false); // The validator may run as a seperate step later on
+ optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions);
+}
+
+}; // end namespace glslang
+
+#endif
diff --git a/src/3rdparty/glslang/SPIRV/SpvTools.h b/src/3rdparty/glslang/SPIRV/SpvTools.h
new file mode 100644
index 0000000..7e49ae0
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/SpvTools.h
@@ -0,0 +1,80 @@
+//
+// Copyright (C) 2014-2016 LunarG, Inc.
+// Copyright (C) 2018 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+//
+// Call into SPIRV-Tools to disassemble, validate, and optimize.
+//
+
+#pragma once
+#ifndef GLSLANG_SPV_TOOLS_H
+#define GLSLANG_SPV_TOOLS_H
+
+#include <vector>
+#include <ostream>
+
+#include "../glslang/MachineIndependent/localintermediate.h"
+#include "Logger.h"
+
+namespace glslang {
+
+struct SpvOptions {
+ SpvOptions() : generateDebugInfo(false), disableOptimizer(true),
+ optimizeSize(false), disassemble(false), validate(false) { }
+ bool generateDebugInfo;
+ bool disableOptimizer;
+ bool optimizeSize;
+ bool disassemble;
+ bool validate;
+};
+
+#if ENABLE_OPT
+
+// Use the SPIRV-Tools disassembler to print SPIR-V.
+void SpirvToolsDisassemble(std::ostream& out, const std::vector<unsigned int>& spirv);
+
+// Apply the SPIRV-Tools validator to generated SPIR-V.
+void SpirvToolsValidate(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv,
+ spv::SpvBuildLogger*);
+
+// Apply the SPIRV-Tools optimizer to generated SPIR-V, for the purpose of
+// legalizing HLSL SPIR-V.
+void SpirvToolsLegalize(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv,
+ spv::SpvBuildLogger*, const SpvOptions*);
+
+#endif
+
+} // end namespace glslang
+
+#endif // GLSLANG_SPV_TOOLS_H
diff --git a/src/3rdparty/glslang/SPIRV/bitutils.h b/src/3rdparty/glslang/SPIRV/bitutils.h
new file mode 100644
index 0000000..22e44ce
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/bitutils.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2015-2016 The Khronos Group Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef LIBSPIRV_UTIL_BITUTILS_H_
+#define LIBSPIRV_UTIL_BITUTILS_H_
+
+#include <cstdint>
+#include <cstring>
+
+namespace spvutils {
+
+// Performs a bitwise copy of source to the destination type Dest.
+template <typename Dest, typename Src>
+Dest BitwiseCast(Src source) {
+ Dest dest;
+ static_assert(sizeof(source) == sizeof(dest),
+ "BitwiseCast: Source and destination must have the same size");
+ std::memcpy(static_cast<void*>(&dest), &source, sizeof(dest));
+ return dest;
+}
+
+// SetBits<T, First, Num> returns an integer of type <T> with bits set
+// for position <First> through <First + Num - 1>, counting from the least
+// significant bit. In particular when Num == 0, no positions are set to 1.
+// A static assert will be triggered if First + Num > sizeof(T) * 8, that is,
+// a bit that will not fit in the underlying type is set.
+template <typename T, size_t First = 0, size_t Num = 0>
+struct SetBits {
+ static_assert(First < sizeof(T) * 8,
+ "Tried to set a bit that is shifted too far.");
+ const static T get = (T(1) << First) | SetBits<T, First + 1, Num - 1>::get;
+};
+
+template <typename T, size_t Last>
+struct SetBits<T, Last, 0> {
+ const static T get = T(0);
+};
+
+// This is all compile-time so we can put our tests right here.
+static_assert(SetBits<uint32_t, 0, 0>::get == uint32_t(0x00000000),
+ "SetBits failed");
+static_assert(SetBits<uint32_t, 0, 1>::get == uint32_t(0x00000001),
+ "SetBits failed");
+static_assert(SetBits<uint32_t, 31, 1>::get == uint32_t(0x80000000),
+ "SetBits failed");
+static_assert(SetBits<uint32_t, 1, 2>::get == uint32_t(0x00000006),
+ "SetBits failed");
+static_assert(SetBits<uint32_t, 30, 2>::get == uint32_t(0xc0000000),
+ "SetBits failed");
+static_assert(SetBits<uint32_t, 0, 31>::get == uint32_t(0x7FFFFFFF),
+ "SetBits failed");
+static_assert(SetBits<uint32_t, 0, 32>::get == uint32_t(0xFFFFFFFF),
+ "SetBits failed");
+static_assert(SetBits<uint32_t, 16, 16>::get == uint32_t(0xFFFF0000),
+ "SetBits failed");
+
+static_assert(SetBits<uint64_t, 0, 1>::get == uint64_t(0x0000000000000001LL),
+ "SetBits failed");
+static_assert(SetBits<uint64_t, 63, 1>::get == uint64_t(0x8000000000000000LL),
+ "SetBits failed");
+static_assert(SetBits<uint64_t, 62, 2>::get == uint64_t(0xc000000000000000LL),
+ "SetBits failed");
+static_assert(SetBits<uint64_t, 31, 1>::get == uint64_t(0x0000000080000000LL),
+ "SetBits failed");
+static_assert(SetBits<uint64_t, 16, 16>::get == uint64_t(0x00000000FFFF0000LL),
+ "SetBits failed");
+
+} // namespace spvutils
+
+#endif // LIBSPIRV_UTIL_BITUTILS_H_
diff --git a/src/3rdparty/glslang/SPIRV/disassemble.cpp b/src/3rdparty/glslang/SPIRV/disassemble.cpp
new file mode 100644
index 0000000..631173c
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/disassemble.cpp
@@ -0,0 +1,759 @@
+//
+// Copyright (C) 2014-2015 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+//
+// Disassembler for SPIR-V.
+//
+
+#include <cstdlib>
+#include <cstring>
+#include <cassert>
+#include <iomanip>
+#include <stack>
+#include <sstream>
+#include <cstring>
+
+#include "disassemble.h"
+#include "doc.h"
+#include "SpvTools.h"
+
+namespace spv {
+ extern "C" {
+ // Include C-based headers that don't have a namespace
+ #include "GLSL.std.450.h"
+#ifdef AMD_EXTENSIONS
+ #include "GLSL.ext.AMD.h"
+#endif
+
+#ifdef NV_EXTENSIONS
+ #include "GLSL.ext.NV.h"
+#endif
+ }
+}
+const char* GlslStd450DebugNames[spv::GLSLstd450Count];
+
+namespace spv {
+
+#ifdef AMD_EXTENSIONS
+static const char* GLSLextAMDGetDebugNames(const char*, unsigned);
+#endif
+
+#ifdef NV_EXTENSIONS
+static const char* GLSLextNVGetDebugNames(const char*, unsigned);
+#endif
+
+static void Kill(std::ostream& out, const char* message)
+{
+ out << std::endl << "Disassembly failed: " << message << std::endl;
+ exit(1);
+}
+
+// used to identify the extended instruction library imported when printing
+enum ExtInstSet {
+ GLSL450Inst,
+
+#ifdef AMD_EXTENSIONS
+ GLSLextAMDInst,
+#endif
+
+#ifdef NV_EXTENSIONS
+ GLSLextNVInst,
+#endif
+
+ OpenCLExtInst,
+};
+
+// Container class for a single instance of a SPIR-V stream, with methods for disassembly.
+class SpirvStream {
+public:
+ SpirvStream(std::ostream& out, const std::vector<unsigned int>& stream) : out(out), stream(stream), word(0), nextNestedControl(0) { }
+ virtual ~SpirvStream() { }
+
+ void validate();
+ void processInstructions();
+
+protected:
+ SpirvStream(const SpirvStream&);
+ SpirvStream& operator=(const SpirvStream&);
+ Op getOpCode(int id) const { return idInstruction[id] ? (Op)(stream[idInstruction[id]] & OpCodeMask) : OpNop; }
+
+ // Output methods
+ void outputIndent();
+ void formatId(Id id, std::stringstream&);
+ void outputResultId(Id id);
+ void outputTypeId(Id id);
+ void outputId(Id id);
+ void outputMask(OperandClass operandClass, unsigned mask);
+ void disassembleImmediates(int numOperands);
+ void disassembleIds(int numOperands);
+ int disassembleString();
+ void disassembleInstruction(Id resultId, Id typeId, Op opCode, int numOperands);
+
+ // Data
+ std::ostream& out; // where to write the disassembly
+ const std::vector<unsigned int>& stream; // the actual word stream
+ int size; // the size of the word stream
+ int word; // the next word of the stream to read
+
+ // map each <id> to the instruction that created it
+ Id bound;
+ std::vector<unsigned int> idInstruction; // the word offset into the stream where the instruction for result [id] starts; 0 if not yet seen (forward reference or function parameter)
+
+ std::vector<std::string> idDescriptor; // the best text string known for explaining the <id>
+
+ // schema
+ unsigned int schema;
+
+ // stack of structured-merge points
+ std::stack<Id> nestedControl;
+ Id nextNestedControl; // need a slight delay for when we are nested
+};
+
+void SpirvStream::validate()
+{
+ size = (int)stream.size();
+ if (size < 4)
+ Kill(out, "stream is too short");
+
+ // Magic number
+ if (stream[word++] != MagicNumber) {
+ out << "Bad magic number";
+ return;
+ }
+
+ // Version
+ out << "// Module Version " << std::hex << stream[word++] << std::endl;
+
+ // Generator's magic number
+ out << "// Generated by (magic number): " << std::hex << stream[word++] << std::dec << std::endl;
+
+ // Result <id> bound
+ bound = stream[word++];
+ idInstruction.resize(bound);
+ idDescriptor.resize(bound);
+ out << "// Id's are bound by " << bound << std::endl;
+ out << std::endl;
+
+ // Reserved schema, must be 0 for now
+ schema = stream[word++];
+ if (schema != 0)
+ Kill(out, "bad schema, must be 0");
+}
+
+// Loop over all the instructions, in order, processing each.
+// Boiler plate for each is handled here directly, the rest is dispatched.
+void SpirvStream::processInstructions()
+{
+ // Instructions
+ while (word < size) {
+ int instructionStart = word;
+
+ // Instruction wordCount and opcode
+ unsigned int firstWord = stream[word];
+ unsigned wordCount = firstWord >> WordCountShift;
+ Op opCode = (Op)(firstWord & OpCodeMask);
+ int nextInst = word + wordCount;
+ ++word;
+
+ // Presence of full instruction
+ if (nextInst > size)
+ Kill(out, "stream instruction terminated too early");
+
+ // Base for computing number of operands; will be updated as more is learned
+ unsigned numOperands = wordCount - 1;
+
+ // Type <id>
+ Id typeId = 0;
+ if (InstructionDesc[opCode].hasType()) {
+ typeId = stream[word++];
+ --numOperands;
+ }
+
+ // Result <id>
+ Id resultId = 0;
+ if (InstructionDesc[opCode].hasResult()) {
+ resultId = stream[word++];
+ --numOperands;
+
+ // save instruction for future reference
+ idInstruction[resultId] = instructionStart;
+ }
+
+ outputResultId(resultId);
+ outputTypeId(typeId);
+ outputIndent();
+
+ // Hand off the Op and all its operands
+ disassembleInstruction(resultId, typeId, opCode, numOperands);
+ if (word != nextInst) {
+ out << " ERROR, incorrect number of operands consumed. At " << word << " instead of " << nextInst << " instruction start was " << instructionStart;
+ word = nextInst;
+ }
+ out << std::endl;
+ }
+}
+
+void SpirvStream::outputIndent()
+{
+ for (int i = 0; i < (int)nestedControl.size(); ++i)
+ out << " ";
+}
+
+void SpirvStream::formatId(Id id, std::stringstream& idStream)
+{
+ if (id != 0) {
+ // On instructions with no IDs, this is called with "0", which does not
+ // have to be within ID bounds on null shaders.
+ if (id >= bound)
+ Kill(out, "Bad <id>");
+
+ idStream << id;
+ if (idDescriptor[id].size() > 0)
+ idStream << "(" << idDescriptor[id] << ")";
+ }
+}
+
+void SpirvStream::outputResultId(Id id)
+{
+ const int width = 16;
+ std::stringstream idStream;
+ formatId(id, idStream);
+ out << std::setw(width) << std::right << idStream.str();
+ if (id != 0)
+ out << ":";
+ else
+ out << " ";
+
+ if (nestedControl.size() && id == nestedControl.top())
+ nestedControl.pop();
+}
+
+void SpirvStream::outputTypeId(Id id)
+{
+ const int width = 12;
+ std::stringstream idStream;
+ formatId(id, idStream);
+ out << std::setw(width) << std::right << idStream.str() << " ";
+}
+
+void SpirvStream::outputId(Id id)
+{
+ if (id >= bound)
+ Kill(out, "Bad <id>");
+
+ out << id;
+ if (idDescriptor[id].size() > 0)
+ out << "(" << idDescriptor[id] << ")";
+}
+
+void SpirvStream::outputMask(OperandClass operandClass, unsigned mask)
+{
+ if (mask == 0)
+ out << "None";
+ else {
+ for (int m = 0; m < OperandClassParams[operandClass].ceiling; ++m) {
+ if (mask & (1 << m))
+ out << OperandClassParams[operandClass].getName(m) << " ";
+ }
+ }
+}
+
+void SpirvStream::disassembleImmediates(int numOperands)
+{
+ for (int i = 0; i < numOperands; ++i) {
+ out << stream[word++];
+ if (i < numOperands - 1)
+ out << " ";
+ }
+}
+
+void SpirvStream::disassembleIds(int numOperands)
+{
+ for (int i = 0; i < numOperands; ++i) {
+ outputId(stream[word++]);
+ if (i < numOperands - 1)
+ out << " ";
+ }
+}
+
+// return the number of operands consumed by the string
+int SpirvStream::disassembleString()
+{
+ int startWord = word;
+
+ out << " \"";
+
+ const char* wordString;
+ bool done = false;
+ do {
+ unsigned int content = stream[word];
+ wordString = (const char*)&content;
+ for (int charCount = 0; charCount < 4; ++charCount) {
+ if (*wordString == 0) {
+ done = true;
+ break;
+ }
+ out << *(wordString++);
+ }
+ ++word;
+ } while (! done);
+
+ out << "\"";
+
+ return word - startWord;
+}
+
+void SpirvStream::disassembleInstruction(Id resultId, Id /*typeId*/, Op opCode, int numOperands)
+{
+ // Process the opcode
+
+ out << (OpcodeString(opCode) + 2); // leave out the "Op"
+
+ if (opCode == OpLoopMerge || opCode == OpSelectionMerge)
+ nextNestedControl = stream[word];
+ else if (opCode == OpBranchConditional || opCode == OpSwitch) {
+ if (nextNestedControl) {
+ nestedControl.push(nextNestedControl);
+ nextNestedControl = 0;
+ }
+ } else if (opCode == OpExtInstImport) {
+ idDescriptor[resultId] = (const char*)(&stream[word]);
+ }
+ else {
+ if (resultId != 0 && idDescriptor[resultId].size() == 0) {
+ switch (opCode) {
+ case OpTypeInt:
+ switch (stream[word]) {
+ case 8: idDescriptor[resultId] = "int8_t"; break;
+ case 16: idDescriptor[resultId] = "int16_t"; break;
+ default: assert(0); // fallthrough
+ case 32: idDescriptor[resultId] = "int"; break;
+ case 64: idDescriptor[resultId] = "int64_t"; break;
+ }
+ break;
+ case OpTypeFloat:
+ switch (stream[word]) {
+ case 16: idDescriptor[resultId] = "float16_t"; break;
+ default: assert(0); // fallthrough
+ case 32: idDescriptor[resultId] = "float"; break;
+ case 64: idDescriptor[resultId] = "float64_t"; break;
+ }
+ break;
+ case OpTypeBool:
+ idDescriptor[resultId] = "bool";
+ break;
+ case OpTypeStruct:
+ idDescriptor[resultId] = "struct";
+ break;
+ case OpTypePointer:
+ idDescriptor[resultId] = "ptr";
+ break;
+ case OpTypeVector:
+ if (idDescriptor[stream[word]].size() > 0) {
+ idDescriptor[resultId].append(idDescriptor[stream[word]].begin(), idDescriptor[stream[word]].begin() + 1);
+ if (strstr(idDescriptor[stream[word]].c_str(), "8")) {
+ idDescriptor[resultId].append("8");
+ }
+ if (strstr(idDescriptor[stream[word]].c_str(), "16")) {
+ idDescriptor[resultId].append("16");
+ }
+ if (strstr(idDescriptor[stream[word]].c_str(), "64")) {
+ idDescriptor[resultId].append("64");
+ }
+ }
+ idDescriptor[resultId].append("vec");
+ switch (stream[word + 1]) {
+ case 2: idDescriptor[resultId].append("2"); break;
+ case 3: idDescriptor[resultId].append("3"); break;
+ case 4: idDescriptor[resultId].append("4"); break;
+ case 8: idDescriptor[resultId].append("8"); break;
+ case 16: idDescriptor[resultId].append("16"); break;
+ case 32: idDescriptor[resultId].append("32"); break;
+ default: break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // Process the operands. Note, a new context-dependent set could be
+ // swapped in mid-traversal.
+
+ // Handle images specially, so can put out helpful strings.
+ if (opCode == OpTypeImage) {
+ out << " ";
+ disassembleIds(1);
+ out << " " << DimensionString((Dim)stream[word++]);
+ out << (stream[word++] != 0 ? " depth" : "");
+ out << (stream[word++] != 0 ? " array" : "");
+ out << (stream[word++] != 0 ? " multi-sampled" : "");
+ switch (stream[word++]) {
+ case 0: out << " runtime"; break;
+ case 1: out << " sampled"; break;
+ case 2: out << " nonsampled"; break;
+ }
+ out << " format:" << ImageFormatString((ImageFormat)stream[word++]);
+
+ if (numOperands == 8) {
+ out << " " << AccessQualifierString(stream[word++]);
+ }
+ return;
+ }
+
+ // Handle all the parameterized operands
+ for (int op = 0; op < InstructionDesc[opCode].operands.getNum() && numOperands > 0; ++op) {
+ out << " ";
+ OperandClass operandClass = InstructionDesc[opCode].operands.getClass(op);
+ switch (operandClass) {
+ case OperandId:
+ case OperandScope:
+ case OperandMemorySemantics:
+ disassembleIds(1);
+ --numOperands;
+ // Get names for printing "(XXX)" for readability, *after* this id
+ if (opCode == OpName)
+ idDescriptor[stream[word - 1]] = (const char*)(&stream[word]);
+ break;
+ case OperandVariableIds:
+ disassembleIds(numOperands);
+ return;
+ case OperandImageOperands:
+ outputMask(OperandImageOperands, stream[word++]);
+ --numOperands;
+ disassembleIds(numOperands);
+ return;
+ case OperandOptionalLiteral:
+ case OperandVariableLiterals:
+ if ((opCode == OpDecorate && stream[word - 1] == DecorationBuiltIn) ||
+ (opCode == OpMemberDecorate && stream[word - 1] == DecorationBuiltIn)) {
+ out << BuiltInString(stream[word++]);
+ --numOperands;
+ ++op;
+ }
+ disassembleImmediates(numOperands);
+ return;
+ case OperandVariableIdLiteral:
+ while (numOperands > 0) {
+ out << std::endl;
+ outputResultId(0);
+ outputTypeId(0);
+ outputIndent();
+ out << " Type ";
+ disassembleIds(1);
+ out << ", member ";
+ disassembleImmediates(1);
+ numOperands -= 2;
+ }
+ return;
+ case OperandVariableLiteralId:
+ while (numOperands > 0) {
+ out << std::endl;
+ outputResultId(0);
+ outputTypeId(0);
+ outputIndent();
+ out << " case ";
+ disassembleImmediates(1);
+ out << ": ";
+ disassembleIds(1);
+ numOperands -= 2;
+ }
+ return;
+ case OperandLiteralNumber:
+ disassembleImmediates(1);
+ --numOperands;
+ if (opCode == OpExtInst) {
+ ExtInstSet extInstSet = GLSL450Inst;
+ const char* name = idDescriptor[stream[word - 2]].c_str();
+ if (0 == memcmp("OpenCL", name, 6)) {
+ extInstSet = OpenCLExtInst;
+#ifdef AMD_EXTENSIONS
+ } else if (strcmp(spv::E_SPV_AMD_shader_ballot, name) == 0 ||
+ strcmp(spv::E_SPV_AMD_shader_trinary_minmax, name) == 0 ||
+ strcmp(spv::E_SPV_AMD_shader_explicit_vertex_parameter, name) == 0 ||
+ strcmp(spv::E_SPV_AMD_gcn_shader, name) == 0) {
+ extInstSet = GLSLextAMDInst;
+#endif
+#ifdef NV_EXTENSIONS
+ }else if (strcmp(spv::E_SPV_NV_sample_mask_override_coverage, name) == 0 ||
+ strcmp(spv::E_SPV_NV_geometry_shader_passthrough, name) == 0 ||
+ strcmp(spv::E_SPV_NV_viewport_array2, name) == 0 ||
+ strcmp(spv::E_SPV_NVX_multiview_per_view_attributes, name) == 0 ||
+ strcmp(spv::E_SPV_NV_fragment_shader_barycentric, name) == 0 ||
+ strcmp(spv::E_SPV_NV_mesh_shader, name) == 0) {
+ extInstSet = GLSLextNVInst;
+#endif
+ }
+ unsigned entrypoint = stream[word - 1];
+ if (extInstSet == GLSL450Inst) {
+ if (entrypoint < GLSLstd450Count) {
+ out << "(" << GlslStd450DebugNames[entrypoint] << ")";
+ }
+#ifdef AMD_EXTENSIONS
+ } else if (extInstSet == GLSLextAMDInst) {
+ out << "(" << GLSLextAMDGetDebugNames(name, entrypoint) << ")";
+#endif
+#ifdef NV_EXTENSIONS
+ }
+ else if (extInstSet == GLSLextNVInst) {
+ out << "(" << GLSLextNVGetDebugNames(name, entrypoint) << ")";
+#endif
+ }
+ }
+ break;
+ case OperandOptionalLiteralString:
+ case OperandLiteralString:
+ numOperands -= disassembleString();
+ break;
+ case OperandMemoryAccess:
+ outputMask(OperandMemoryAccess, stream[word++]);
+ --numOperands;
+ // Aligned is the only memory access operand that uses an immediate
+ // value, and it is also the first operand that uses a value at all.
+ if (stream[word-1] & MemoryAccessAlignedMask) {
+ disassembleImmediates(1);
+ numOperands--;
+ if (numOperands)
+ out << " ";
+ }
+ disassembleIds(numOperands);
+ return;
+ default:
+ assert(operandClass >= OperandSource && operandClass < OperandOpcode);
+
+ if (OperandClassParams[operandClass].bitmask)
+ outputMask(operandClass, stream[word++]);
+ else
+ out << OperandClassParams[operandClass].getName(stream[word++]);
+ --numOperands;
+
+ break;
+ }
+ }
+
+ return;
+}
+
+static void GLSLstd450GetDebugNames(const char** names)
+{
+ for (int i = 0; i < GLSLstd450Count; ++i)
+ names[i] = "Unknown";
+
+ names[GLSLstd450Round] = "Round";
+ names[GLSLstd450RoundEven] = "RoundEven";
+ names[GLSLstd450Trunc] = "Trunc";
+ names[GLSLstd450FAbs] = "FAbs";
+ names[GLSLstd450SAbs] = "SAbs";
+ names[GLSLstd450FSign] = "FSign";
+ names[GLSLstd450SSign] = "SSign";
+ names[GLSLstd450Floor] = "Floor";
+ names[GLSLstd450Ceil] = "Ceil";
+ names[GLSLstd450Fract] = "Fract";
+ names[GLSLstd450Radians] = "Radians";
+ names[GLSLstd450Degrees] = "Degrees";
+ names[GLSLstd450Sin] = "Sin";
+ names[GLSLstd450Cos] = "Cos";
+ names[GLSLstd450Tan] = "Tan";
+ names[GLSLstd450Asin] = "Asin";
+ names[GLSLstd450Acos] = "Acos";
+ names[GLSLstd450Atan] = "Atan";
+ names[GLSLstd450Sinh] = "Sinh";
+ names[GLSLstd450Cosh] = "Cosh";
+ names[GLSLstd450Tanh] = "Tanh";
+ names[GLSLstd450Asinh] = "Asinh";
+ names[GLSLstd450Acosh] = "Acosh";
+ names[GLSLstd450Atanh] = "Atanh";
+ names[GLSLstd450Atan2] = "Atan2";
+ names[GLSLstd450Pow] = "Pow";
+ names[GLSLstd450Exp] = "Exp";
+ names[GLSLstd450Log] = "Log";
+ names[GLSLstd450Exp2] = "Exp2";
+ names[GLSLstd450Log2] = "Log2";
+ names[GLSLstd450Sqrt] = "Sqrt";
+ names[GLSLstd450InverseSqrt] = "InverseSqrt";
+ names[GLSLstd450Determinant] = "Determinant";
+ names[GLSLstd450MatrixInverse] = "MatrixInverse";
+ names[GLSLstd450Modf] = "Modf";
+ names[GLSLstd450ModfStruct] = "ModfStruct";
+ names[GLSLstd450FMin] = "FMin";
+ names[GLSLstd450SMin] = "SMin";
+ names[GLSLstd450UMin] = "UMin";
+ names[GLSLstd450FMax] = "FMax";
+ names[GLSLstd450SMax] = "SMax";
+ names[GLSLstd450UMax] = "UMax";
+ names[GLSLstd450FClamp] = "FClamp";
+ names[GLSLstd450SClamp] = "SClamp";
+ names[GLSLstd450UClamp] = "UClamp";
+ names[GLSLstd450FMix] = "FMix";
+ names[GLSLstd450Step] = "Step";
+ names[GLSLstd450SmoothStep] = "SmoothStep";
+ names[GLSLstd450Fma] = "Fma";
+ names[GLSLstd450Frexp] = "Frexp";
+ names[GLSLstd450FrexpStruct] = "FrexpStruct";
+ names[GLSLstd450Ldexp] = "Ldexp";
+ names[GLSLstd450PackSnorm4x8] = "PackSnorm4x8";
+ names[GLSLstd450PackUnorm4x8] = "PackUnorm4x8";
+ names[GLSLstd450PackSnorm2x16] = "PackSnorm2x16";
+ names[GLSLstd450PackUnorm2x16] = "PackUnorm2x16";
+ names[GLSLstd450PackHalf2x16] = "PackHalf2x16";
+ names[GLSLstd450PackDouble2x32] = "PackDouble2x32";
+ names[GLSLstd450UnpackSnorm2x16] = "UnpackSnorm2x16";
+ names[GLSLstd450UnpackUnorm2x16] = "UnpackUnorm2x16";
+ names[GLSLstd450UnpackHalf2x16] = "UnpackHalf2x16";
+ names[GLSLstd450UnpackSnorm4x8] = "UnpackSnorm4x8";
+ names[GLSLstd450UnpackUnorm4x8] = "UnpackUnorm4x8";
+ names[GLSLstd450UnpackDouble2x32] = "UnpackDouble2x32";
+ names[GLSLstd450Length] = "Length";
+ names[GLSLstd450Distance] = "Distance";
+ names[GLSLstd450Cross] = "Cross";
+ names[GLSLstd450Normalize] = "Normalize";
+ names[GLSLstd450FaceForward] = "FaceForward";
+ names[GLSLstd450Reflect] = "Reflect";
+ names[GLSLstd450Refract] = "Refract";
+ names[GLSLstd450FindILsb] = "FindILsb";
+ names[GLSLstd450FindSMsb] = "FindSMsb";
+ names[GLSLstd450FindUMsb] = "FindUMsb";
+ names[GLSLstd450InterpolateAtCentroid] = "InterpolateAtCentroid";
+ names[GLSLstd450InterpolateAtSample] = "InterpolateAtSample";
+ names[GLSLstd450InterpolateAtOffset] = "InterpolateAtOffset";
+}
+
+#ifdef AMD_EXTENSIONS
+static const char* GLSLextAMDGetDebugNames(const char* name, unsigned entrypoint)
+{
+ if (strcmp(name, spv::E_SPV_AMD_shader_ballot) == 0) {
+ switch (entrypoint) {
+ case SwizzleInvocationsAMD: return "SwizzleInvocationsAMD";
+ case SwizzleInvocationsMaskedAMD: return "SwizzleInvocationsMaskedAMD";
+ case WriteInvocationAMD: return "WriteInvocationAMD";
+ case MbcntAMD: return "MbcntAMD";
+ default: return "Bad";
+ }
+ } else if (strcmp(name, spv::E_SPV_AMD_shader_trinary_minmax) == 0) {
+ switch (entrypoint) {
+ case FMin3AMD: return "FMin3AMD";
+ case UMin3AMD: return "UMin3AMD";
+ case SMin3AMD: return "SMin3AMD";
+ case FMax3AMD: return "FMax3AMD";
+ case UMax3AMD: return "UMax3AMD";
+ case SMax3AMD: return "SMax3AMD";
+ case FMid3AMD: return "FMid3AMD";
+ case UMid3AMD: return "UMid3AMD";
+ case SMid3AMD: return "SMid3AMD";
+ default: return "Bad";
+ }
+ } else if (strcmp(name, spv::E_SPV_AMD_shader_explicit_vertex_parameter) == 0) {
+ switch (entrypoint) {
+ case InterpolateAtVertexAMD: return "InterpolateAtVertexAMD";
+ default: return "Bad";
+ }
+ }
+ else if (strcmp(name, spv::E_SPV_AMD_gcn_shader) == 0) {
+ switch (entrypoint) {
+ case CubeFaceIndexAMD: return "CubeFaceIndexAMD";
+ case CubeFaceCoordAMD: return "CubeFaceCoordAMD";
+ case TimeAMD: return "TimeAMD";
+ default:
+ break;
+ }
+ }
+
+ return "Bad";
+}
+#endif
+
+#ifdef NV_EXTENSIONS
+static const char* GLSLextNVGetDebugNames(const char* name, unsigned entrypoint)
+{
+ if (strcmp(name, spv::E_SPV_NV_sample_mask_override_coverage) == 0 ||
+ strcmp(name, spv::E_SPV_NV_geometry_shader_passthrough) == 0 ||
+ strcmp(name, spv::E_ARB_shader_viewport_layer_array) == 0 ||
+ strcmp(name, spv::E_SPV_NV_viewport_array2) == 0 ||
+ strcmp(spv::E_SPV_NVX_multiview_per_view_attributes, name) == 0 ||
+ strcmp(spv::E_SPV_NV_fragment_shader_barycentric, name) == 0 ||
+ strcmp(name, spv::E_SPV_NV_mesh_shader) == 0) {
+ switch (entrypoint) {
+ // NV builtins
+ case BuiltInViewportMaskNV: return "ViewportMaskNV";
+ case BuiltInSecondaryPositionNV: return "SecondaryPositionNV";
+ case BuiltInSecondaryViewportMaskNV: return "SecondaryViewportMaskNV";
+ case BuiltInPositionPerViewNV: return "PositionPerViewNV";
+ case BuiltInViewportMaskPerViewNV: return "ViewportMaskPerViewNV";
+ case BuiltInBaryCoordNV: return "BaryCoordNV";
+ case BuiltInBaryCoordNoPerspNV: return "BaryCoordNoPerspNV";
+ case BuiltInTaskCountNV: return "TaskCountNV";
+ case BuiltInPrimitiveCountNV: return "PrimitiveCountNV";
+ case BuiltInPrimitiveIndicesNV: return "PrimitiveIndicesNV";
+ case BuiltInClipDistancePerViewNV: return "ClipDistancePerViewNV";
+ case BuiltInCullDistancePerViewNV: return "CullDistancePerViewNV";
+ case BuiltInLayerPerViewNV: return "LayerPerViewNV";
+ case BuiltInMeshViewCountNV: return "MeshViewCountNV";
+ case BuiltInMeshViewIndicesNV: return "MeshViewIndicesNV";
+
+ // NV Capabilities
+ case CapabilityGeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV";
+ case CapabilityShaderViewportMaskNV: return "ShaderViewportMaskNV";
+ case CapabilityShaderStereoViewNV: return "ShaderStereoViewNV";
+ case CapabilityPerViewAttributesNV: return "PerViewAttributesNV";
+ case CapabilityFragmentBarycentricNV: return "FragmentBarycentricNV";
+ case CapabilityMeshShadingNV: return "MeshShadingNV";
+
+ // NV Decorations
+ case DecorationOverrideCoverageNV: return "OverrideCoverageNV";
+ case DecorationPassthroughNV: return "PassthroughNV";
+ case DecorationViewportRelativeNV: return "ViewportRelativeNV";
+ case DecorationSecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV";
+ case DecorationPerVertexNV: return "PerVertexNV";
+ case DecorationPerPrimitiveNV: return "PerPrimitiveNV";
+ case DecorationPerViewNV: return "PerViewNV";
+ case DecorationPerTaskNV: return "PerTaskNV";
+
+ default: return "Bad";
+ }
+ }
+ return "Bad";
+}
+#endif
+
+void Disassemble(std::ostream& out, const std::vector<unsigned int>& stream)
+{
+ SpirvStream SpirvStream(out, stream);
+ spv::Parameterize();
+ GLSLstd450GetDebugNames(GlslStd450DebugNames);
+ SpirvStream.validate();
+ SpirvStream.processInstructions();
+}
+
+}; // end namespace spv
diff --git a/src/3rdparty/glslang/SPIRV/disassemble.h b/src/3rdparty/glslang/SPIRV/disassemble.h
new file mode 100644
index 0000000..b6a4635
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/disassemble.h
@@ -0,0 +1,53 @@
+//
+// Copyright (C) 2014-2015 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+//
+// Disassembler for SPIR-V.
+//
+
+#pragma once
+#ifndef disassembler_H
+#define disassembler_H
+
+#include <iostream>
+#include <vector>
+
+namespace spv {
+
+ // disassemble with glslang custom disassembler
+ void Disassemble(std::ostream& out, const std::vector<unsigned int>&);
+
+} // end namespace spv
+
+#endif // disassembler_H
diff --git a/src/3rdparty/glslang/SPIRV/doc.cpp b/src/3rdparty/glslang/SPIRV/doc.cpp
new file mode 100644
index 0000000..76e1df8
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/doc.cpp
@@ -0,0 +1,2757 @@
+//
+// Copyright (C) 2014-2015 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+//
+// 1) Programmatically fill in instruction/operand information.
+// This can be used for disassembly, printing documentation, etc.
+//
+// 2) Print documentation from this parameterization.
+//
+
+#include "doc.h"
+
+#include <cstdio>
+#include <cstring>
+#include <algorithm>
+
+namespace spv {
+ extern "C" {
+ // Include C-based headers that don't have a namespace
+ #include "GLSL.ext.KHR.h"
+ #include "GLSL.ext.EXT.h"
+#ifdef AMD_EXTENSIONS
+ #include "GLSL.ext.AMD.h"
+#endif
+#ifdef NV_EXTENSIONS
+ #include "GLSL.ext.NV.h"
+#endif
+ }
+}
+
+namespace spv {
+
+//
+// Whole set of functions that translate enumerants to their text strings for
+// the specification (or their sanitized versions for auto-generating the
+// spirv headers.
+//
+// Also, for masks the ceilings are declared next to these, to help keep them in sync.
+// Ceilings should be
+// - one more than the maximum value an enumerant takes on, for non-mask enumerants
+// (for non-sparse enums, this is the number of enumerants)
+// - the number of bits consumed by the set of masks
+// (for non-sparse mask enums, this is the number of enumerants)
+//
+
+const char* SourceString(int source)
+{
+ switch (source) {
+ case 0: return "Unknown";
+ case 1: return "ESSL";
+ case 2: return "GLSL";
+ case 3: return "OpenCL_C";
+ case 4: return "OpenCL_CPP";
+ case 5: return "HLSL";
+
+ default: return "Bad";
+ }
+}
+
+const char* ExecutionModelString(int model)
+{
+ switch (model) {
+ case 0: return "Vertex";
+ case 1: return "TessellationControl";
+ case 2: return "TessellationEvaluation";
+ case 3: return "Geometry";
+ case 4: return "Fragment";
+ case 5: return "GLCompute";
+ case 6: return "Kernel";
+#ifdef NV_EXTENSIONS
+ case ExecutionModelTaskNV: return "TaskNV";
+ case ExecutionModelMeshNV: return "MeshNV";
+#endif
+
+ default: return "Bad";
+
+#ifdef NV_EXTENSIONS
+ case ExecutionModelRayGenerationNV: return "RayGenerationNV";
+ case ExecutionModelIntersectionNV: return "IntersectionNV";
+ case ExecutionModelAnyHitNV: return "AnyHitNV";
+ case ExecutionModelClosestHitNV: return "ClosestHitNV";
+ case ExecutionModelMissNV: return "MissNV";
+ case ExecutionModelCallableNV: return "CallableNV";
+#endif
+
+ }
+}
+
+const char* AddressingString(int addr)
+{
+ switch (addr) {
+ case 0: return "Logical";
+ case 1: return "Physical32";
+ case 2: return "Physical64";
+
+ case AddressingModelPhysicalStorageBuffer64EXT: return "PhysicalStorageBuffer64EXT";
+
+ default: return "Bad";
+ }
+}
+
+const char* MemoryString(int mem)
+{
+ switch (mem) {
+ case MemoryModelSimple: return "Simple";
+ case MemoryModelGLSL450: return "GLSL450";
+ case MemoryModelOpenCL: return "OpenCL";
+ case MemoryModelVulkanKHR: return "VulkanKHR";
+
+ default: return "Bad";
+ }
+}
+
+const int ExecutionModeCeiling = 33;
+
+const char* ExecutionModeString(int mode)
+{
+ switch (mode) {
+ case 0: return "Invocations";
+ case 1: return "SpacingEqual";
+ case 2: return "SpacingFractionalEven";
+ case 3: return "SpacingFractionalOdd";
+ case 4: return "VertexOrderCw";
+ case 5: return "VertexOrderCcw";
+ case 6: return "PixelCenterInteger";
+ case 7: return "OriginUpperLeft";
+ case 8: return "OriginLowerLeft";
+ case 9: return "EarlyFragmentTests";
+ case 10: return "PointMode";
+ case 11: return "Xfb";
+ case 12: return "DepthReplacing";
+ case 13: return "Bad";
+ case 14: return "DepthGreater";
+ case 15: return "DepthLess";
+ case 16: return "DepthUnchanged";
+ case 17: return "LocalSize";
+ case 18: return "LocalSizeHint";
+ case 19: return "InputPoints";
+ case 20: return "InputLines";
+ case 21: return "InputLinesAdjacency";
+ case 22: return "Triangles";
+ case 23: return "InputTrianglesAdjacency";
+ case 24: return "Quads";
+ case 25: return "Isolines";
+ case 26: return "OutputVertices";
+ case 27: return "OutputPoints";
+ case 28: return "OutputLineStrip";
+ case 29: return "OutputTriangleStrip";
+ case 30: return "VecTypeHint";
+ case 31: return "ContractionOff";
+ case 32: return "Bad";
+
+ case 4446: return "PostDepthCoverage";
+
+#ifdef NV_EXTENSIONS
+ case ExecutionModeOutputLinesNV: return "OutputLinesNV";
+ case ExecutionModeOutputPrimitivesNV: return "OutputPrimitivesNV";
+ case ExecutionModeOutputTrianglesNV: return "OutputTrianglesNV";
+ case ExecutionModeDerivativeGroupQuadsNV: return "DerivativeGroupQuadsNV";
+ case ExecutionModeDerivativeGroupLinearNV: return "DerivativeGroupLinearNV";
+#endif
+
+ case ExecutionModeCeiling:
+ default: return "Bad";
+ }
+}
+
+const char* StorageClassString(int StorageClass)
+{
+ switch (StorageClass) {
+ case 0: return "UniformConstant";
+ case 1: return "Input";
+ case 2: return "Uniform";
+ case 3: return "Output";
+ case 4: return "Workgroup";
+ case 5: return "CrossWorkgroup";
+ case 6: return "Private";
+ case 7: return "Function";
+ case 8: return "Generic";
+ case 9: return "PushConstant";
+ case 10: return "AtomicCounter";
+ case 11: return "Image";
+ case 12: return "StorageBuffer";
+
+#ifdef NV_EXTENSIONS
+ case StorageClassRayPayloadNV: return "RayPayloadNV";
+ case StorageClassHitAttributeNV: return "HitAttributeNV";
+ case StorageClassIncomingRayPayloadNV: return "IncomingRayPayloadNV";
+ case StorageClassShaderRecordBufferNV: return "ShaderRecordBufferNV";
+ case StorageClassCallableDataNV: return "CallableDataNV";
+ case StorageClassIncomingCallableDataNV: return "IncomingCallableDataNV";
+#endif
+
+ case StorageClassPhysicalStorageBufferEXT: return "PhysicalStorageBufferEXT";
+
+ default: return "Bad";
+ }
+}
+
+const int DecorationCeiling = 45;
+
+const char* DecorationString(int decoration)
+{
+ switch (decoration) {
+ case 0: return "RelaxedPrecision";
+ case 1: return "SpecId";
+ case 2: return "Block";
+ case 3: return "BufferBlock";
+ case 4: return "RowMajor";
+ case 5: return "ColMajor";
+ case 6: return "ArrayStride";
+ case 7: return "MatrixStride";
+ case 8: return "GLSLShared";
+ case 9: return "GLSLPacked";
+ case 10: return "CPacked";
+ case 11: return "BuiltIn";
+ case 12: return "Bad";
+ case 13: return "NoPerspective";
+ case 14: return "Flat";
+ case 15: return "Patch";
+ case 16: return "Centroid";
+ case 17: return "Sample";
+ case 18: return "Invariant";
+ case 19: return "Restrict";
+ case 20: return "Aliased";
+ case 21: return "Volatile";
+ case 22: return "Constant";
+ case 23: return "Coherent";
+ case 24: return "NonWritable";
+ case 25: return "NonReadable";
+ case 26: return "Uniform";
+ case 27: return "Bad";
+ case 28: return "SaturatedConversion";
+ case 29: return "Stream";
+ case 30: return "Location";
+ case 31: return "Component";
+ case 32: return "Index";
+ case 33: return "Binding";
+ case 34: return "DescriptorSet";
+ case 35: return "Offset";
+ case 36: return "XfbBuffer";
+ case 37: return "XfbStride";
+ case 38: return "FuncParamAttr";
+ case 39: return "FP Rounding Mode";
+ case 40: return "FP Fast Math Mode";
+ case 41: return "Linkage Attributes";
+ case 42: return "NoContraction";
+ case 43: return "InputAttachmentIndex";
+ case 44: return "Alignment";
+
+ case DecorationCeiling:
+ default: return "Bad";
+
+#ifdef AMD_EXTENSIONS
+ case DecorationExplicitInterpAMD: return "ExplicitInterpAMD";
+#endif
+#ifdef NV_EXTENSIONS
+ case DecorationOverrideCoverageNV: return "OverrideCoverageNV";
+ case DecorationPassthroughNV: return "PassthroughNV";
+ case DecorationViewportRelativeNV: return "ViewportRelativeNV";
+ case DecorationSecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV";
+ case DecorationPerPrimitiveNV: return "PerPrimitiveNV";
+ case DecorationPerViewNV: return "PerViewNV";
+ case DecorationPerTaskNV: return "PerTaskNV";
+ case DecorationPerVertexNV: return "PerVertexNV";
+#endif
+
+ case DecorationNonUniformEXT: return "DecorationNonUniformEXT";
+ case DecorationHlslCounterBufferGOOGLE: return "DecorationHlslCounterBufferGOOGLE";
+ case DecorationHlslSemanticGOOGLE: return "DecorationHlslSemanticGOOGLE";
+ case DecorationRestrictPointerEXT: return "DecorationRestrictPointerEXT";
+ case DecorationAliasedPointerEXT: return "DecorationAliasedPointerEXT";
+ }
+}
+
+const char* BuiltInString(int builtIn)
+{
+ switch (builtIn) {
+ case 0: return "Position";
+ case 1: return "PointSize";
+ case 2: return "Bad";
+ case 3: return "ClipDistance";
+ case 4: return "CullDistance";
+ case 5: return "VertexId";
+ case 6: return "InstanceId";
+ case 7: return "PrimitiveId";
+ case 8: return "InvocationId";
+ case 9: return "Layer";
+ case 10: return "ViewportIndex";
+ case 11: return "TessLevelOuter";
+ case 12: return "TessLevelInner";
+ case 13: return "TessCoord";
+ case 14: return "PatchVertices";
+ case 15: return "FragCoord";
+ case 16: return "PointCoord";
+ case 17: return "FrontFacing";
+ case 18: return "SampleId";
+ case 19: return "SamplePosition";
+ case 20: return "SampleMask";
+ case 21: return "Bad";
+ case 22: return "FragDepth";
+ case 23: return "HelperInvocation";
+ case 24: return "NumWorkgroups";
+ case 25: return "WorkgroupSize";
+ case 26: return "WorkgroupId";
+ case 27: return "LocalInvocationId";
+ case 28: return "GlobalInvocationId";
+ case 29: return "LocalInvocationIndex";
+ case 30: return "WorkDim";
+ case 31: return "GlobalSize";
+ case 32: return "EnqueuedWorkgroupSize";
+ case 33: return "GlobalOffset";
+ case 34: return "GlobalLinearId";
+ case 35: return "Bad";
+ case 36: return "SubgroupSize";
+ case 37: return "SubgroupMaxSize";
+ case 38: return "NumSubgroups";
+ case 39: return "NumEnqueuedSubgroups";
+ case 40: return "SubgroupId";
+ case 41: return "SubgroupLocalInvocationId";
+ case 42: return "VertexIndex"; // TBD: put next to VertexId?
+ case 43: return "InstanceIndex"; // TBD: put next to InstanceId?
+
+ case 4416: return "SubgroupEqMaskKHR";
+ case 4417: return "SubgroupGeMaskKHR";
+ case 4418: return "SubgroupGtMaskKHR";
+ case 4419: return "SubgroupLeMaskKHR";
+ case 4420: return "SubgroupLtMaskKHR";
+ case 4438: return "DeviceIndex";
+ case 4440: return "ViewIndex";
+ case 4424: return "BaseVertex";
+ case 4425: return "BaseInstance";
+ case 4426: return "DrawIndex";
+ case 5014: return "FragStencilRefEXT";
+
+#ifdef AMD_EXTENSIONS
+ case 4992: return "BaryCoordNoPerspAMD";
+ case 4993: return "BaryCoordNoPerspCentroidAMD";
+ case 4994: return "BaryCoordNoPerspSampleAMD";
+ case 4995: return "BaryCoordSmoothAMD";
+ case 4996: return "BaryCoordSmoothCentroidAMD";
+ case 4997: return "BaryCoordSmoothSampleAMD";
+ case 4998: return "BaryCoordPullModelAMD";
+#endif
+
+#ifdef NV_EXTENSIONS
+ case BuiltInLaunchIdNV: return "LaunchIdNV";
+ case BuiltInLaunchSizeNV: return "LaunchSizeNV";
+ case BuiltInWorldRayOriginNV: return "WorldRayOriginNV";
+ case BuiltInWorldRayDirectionNV: return "WorldRayDirectionNV";
+ case BuiltInObjectRayOriginNV: return "ObjectRayOriginNV";
+ case BuiltInObjectRayDirectionNV: return "ObjectRayDirectionNV";
+ case BuiltInRayTminNV: return "RayTminNV";
+ case BuiltInRayTmaxNV: return "RayTmaxNV";
+ case BuiltInInstanceCustomIndexNV: return "InstanceCustomIndexNV";
+ case BuiltInObjectToWorldNV: return "ObjectToWorldNV";
+ case BuiltInWorldToObjectNV: return "WorldToObjectNV";
+ case BuiltInHitTNV: return "HitTNV";
+ case BuiltInHitKindNV: return "HitKindNV";
+ case BuiltInIncomingRayFlagsNV: return "IncomingRayFlagsNV";
+ case BuiltInViewportMaskNV: return "ViewportMaskNV";
+ case BuiltInSecondaryPositionNV: return "SecondaryPositionNV";
+ case BuiltInSecondaryViewportMaskNV: return "SecondaryViewportMaskNV";
+ case BuiltInPositionPerViewNV: return "PositionPerViewNV";
+ case BuiltInViewportMaskPerViewNV: return "ViewportMaskPerViewNV";
+// case BuiltInFragmentSizeNV: return "FragmentSizeNV"; // superseded by BuiltInFragSizeEXT
+// case BuiltInInvocationsPerPixelNV: return "InvocationsPerPixelNV"; // superseded by BuiltInFragInvocationCountEXT
+ case BuiltInBaryCoordNV: return "BaryCoordNV";
+ case BuiltInBaryCoordNoPerspNV: return "BaryCoordNoPerspNV";
+#endif
+
+ case BuiltInFragSizeEXT: return "FragSizeEXT";
+ case BuiltInFragInvocationCountEXT: return "FragInvocationCountEXT";
+
+ case 5264: return "FullyCoveredEXT";
+
+
+#ifdef NV_EXTENSIONS
+ case BuiltInTaskCountNV: return "TaskCountNV";
+ case BuiltInPrimitiveCountNV: return "PrimitiveCountNV";
+ case BuiltInPrimitiveIndicesNV: return "PrimitiveIndicesNV";
+ case BuiltInClipDistancePerViewNV: return "ClipDistancePerViewNV";
+ case BuiltInCullDistancePerViewNV: return "CullDistancePerViewNV";
+ case BuiltInLayerPerViewNV: return "LayerPerViewNV";
+ case BuiltInMeshViewCountNV: return "MeshViewCountNV";
+ case BuiltInMeshViewIndicesNV: return "MeshViewIndicesNV";
+#endif
+
+ default: return "Bad";
+ }
+}
+
+const char* DimensionString(int dim)
+{
+ switch (dim) {
+ case 0: return "1D";
+ case 1: return "2D";
+ case 2: return "3D";
+ case 3: return "Cube";
+ case 4: return "Rect";
+ case 5: return "Buffer";
+ case 6: return "SubpassData";
+
+ default: return "Bad";
+ }
+}
+
+const char* SamplerAddressingModeString(int mode)
+{
+ switch (mode) {
+ case 0: return "None";
+ case 1: return "ClampToEdge";
+ case 2: return "Clamp";
+ case 3: return "Repeat";
+ case 4: return "RepeatMirrored";
+
+ default: return "Bad";
+ }
+}
+
+const char* SamplerFilterModeString(int mode)
+{
+ switch (mode) {
+ case 0: return "Nearest";
+ case 1: return "Linear";
+
+ default: return "Bad";
+ }
+}
+
+const char* ImageFormatString(int format)
+{
+ switch (format) {
+ case 0: return "Unknown";
+
+ // ES/Desktop float
+ case 1: return "Rgba32f";
+ case 2: return "Rgba16f";
+ case 3: return "R32f";
+ case 4: return "Rgba8";
+ case 5: return "Rgba8Snorm";
+
+ // Desktop float
+ case 6: return "Rg32f";
+ case 7: return "Rg16f";
+ case 8: return "R11fG11fB10f";
+ case 9: return "R16f";
+ case 10: return "Rgba16";
+ case 11: return "Rgb10A2";
+ case 12: return "Rg16";
+ case 13: return "Rg8";
+ case 14: return "R16";
+ case 15: return "R8";
+ case 16: return "Rgba16Snorm";
+ case 17: return "Rg16Snorm";
+ case 18: return "Rg8Snorm";
+ case 19: return "R16Snorm";
+ case 20: return "R8Snorm";
+
+ // ES/Desktop int
+ case 21: return "Rgba32i";
+ case 22: return "Rgba16i";
+ case 23: return "Rgba8i";
+ case 24: return "R32i";
+
+ // Desktop int
+ case 25: return "Rg32i";
+ case 26: return "Rg16i";
+ case 27: return "Rg8i";
+ case 28: return "R16i";
+ case 29: return "R8i";
+
+ // ES/Desktop uint
+ case 30: return "Rgba32ui";
+ case 31: return "Rgba16ui";
+ case 32: return "Rgba8ui";
+ case 33: return "R32ui";
+
+ // Desktop uint
+ case 34: return "Rgb10a2ui";
+ case 35: return "Rg32ui";
+ case 36: return "Rg16ui";
+ case 37: return "Rg8ui";
+ case 38: return "R16ui";
+ case 39: return "R8ui";
+
+ default:
+ return "Bad";
+ }
+}
+
+const char* ImageChannelOrderString(int format)
+{
+ switch (format) {
+ case 0: return "R";
+ case 1: return "A";
+ case 2: return "RG";
+ case 3: return "RA";
+ case 4: return "RGB";
+ case 5: return "RGBA";
+ case 6: return "BGRA";
+ case 7: return "ARGB";
+ case 8: return "Intensity";
+ case 9: return "Luminance";
+ case 10: return "Rx";
+ case 11: return "RGx";
+ case 12: return "RGBx";
+ case 13: return "Depth";
+ case 14: return "DepthStencil";
+ case 15: return "sRGB";
+ case 16: return "sRGBx";
+ case 17: return "sRGBA";
+ case 18: return "sBGRA";
+
+ default:
+ return "Bad";
+ }
+}
+
+const char* ImageChannelDataTypeString(int type)
+{
+ switch (type)
+ {
+ case 0: return "SnormInt8";
+ case 1: return "SnormInt16";
+ case 2: return "UnormInt8";
+ case 3: return "UnormInt16";
+ case 4: return "UnormShort565";
+ case 5: return "UnormShort555";
+ case 6: return "UnormInt101010";
+ case 7: return "SignedInt8";
+ case 8: return "SignedInt16";
+ case 9: return "SignedInt32";
+ case 10: return "UnsignedInt8";
+ case 11: return "UnsignedInt16";
+ case 12: return "UnsignedInt32";
+ case 13: return "HalfFloat";
+ case 14: return "Float";
+ case 15: return "UnormInt24";
+ case 16: return "UnormInt101010_2";
+
+ default:
+ return "Bad";
+ }
+}
+
+const int ImageOperandsCeiling = 12;
+
+const char* ImageOperandsString(int format)
+{
+ switch (format) {
+ case ImageOperandsBiasShift: return "Bias";
+ case ImageOperandsLodShift: return "Lod";
+ case ImageOperandsGradShift: return "Grad";
+ case ImageOperandsConstOffsetShift: return "ConstOffset";
+ case ImageOperandsOffsetShift: return "Offset";
+ case ImageOperandsConstOffsetsShift: return "ConstOffsets";
+ case ImageOperandsSampleShift: return "Sample";
+ case ImageOperandsMinLodShift: return "MinLod";
+ case ImageOperandsMakeTexelAvailableKHRShift: return "MakeTexelAvailableKHR";
+ case ImageOperandsMakeTexelVisibleKHRShift: return "MakeTexelVisibleKHR";
+ case ImageOperandsNonPrivateTexelKHRShift: return "NonPrivateTexelKHR";
+ case ImageOperandsVolatileTexelKHRShift: return "VolatileTexelKHR";
+
+ case ImageOperandsCeiling:
+ default:
+ return "Bad";
+ }
+}
+
+const char* FPFastMathString(int mode)
+{
+ switch (mode) {
+ case 0: return "NotNaN";
+ case 1: return "NotInf";
+ case 2: return "NSZ";
+ case 3: return "AllowRecip";
+ case 4: return "Fast";
+
+ default: return "Bad";
+ }
+}
+
+const char* FPRoundingModeString(int mode)
+{
+ switch (mode) {
+ case 0: return "RTE";
+ case 1: return "RTZ";
+ case 2: return "RTP";
+ case 3: return "RTN";
+
+ default: return "Bad";
+ }
+}
+
+const char* LinkageTypeString(int type)
+{
+ switch (type) {
+ case 0: return "Export";
+ case 1: return "Import";
+
+ default: return "Bad";
+ }
+}
+
+const char* FuncParamAttrString(int attr)
+{
+ switch (attr) {
+ case 0: return "Zext";
+ case 1: return "Sext";
+ case 2: return "ByVal";
+ case 3: return "Sret";
+ case 4: return "NoAlias";
+ case 5: return "NoCapture";
+ case 6: return "NoWrite";
+ case 7: return "NoReadWrite";
+
+ default: return "Bad";
+ }
+}
+
+const char* AccessQualifierString(int attr)
+{
+ switch (attr) {
+ case 0: return "ReadOnly";
+ case 1: return "WriteOnly";
+ case 2: return "ReadWrite";
+
+ default: return "Bad";
+ }
+}
+
+const int SelectControlCeiling = 2;
+
+const char* SelectControlString(int cont)
+{
+ switch (cont) {
+ case 0: return "Flatten";
+ case 1: return "DontFlatten";
+
+ case SelectControlCeiling:
+ default: return "Bad";
+ }
+}
+
+const int LoopControlCeiling = 4;
+
+const char* LoopControlString(int cont)
+{
+ switch (cont) {
+ case 0: return "Unroll";
+ case 1: return "DontUnroll";
+ case 2: return "DependencyInfinite";
+ case 3: return "DependencyLength";
+
+ case LoopControlCeiling:
+ default: return "Bad";
+ }
+}
+
+const int FunctionControlCeiling = 4;
+
+const char* FunctionControlString(int cont)
+{
+ switch (cont) {
+ case 0: return "Inline";
+ case 1: return "DontInline";
+ case 2: return "Pure";
+ case 3: return "Const";
+
+ case FunctionControlCeiling:
+ default: return "Bad";
+ }
+}
+
+const char* MemorySemanticsString(int mem)
+{
+ // Note: No bits set (None) means "Relaxed"
+ switch (mem) {
+ case 0: return "Bad"; // Note: this is a placeholder for 'Consume'
+ case 1: return "Acquire";
+ case 2: return "Release";
+ case 3: return "AcquireRelease";
+ case 4: return "SequentiallyConsistent";
+ case 5: return "Bad"; // Note: reserved for future expansion
+ case 6: return "UniformMemory";
+ case 7: return "SubgroupMemory";
+ case 8: return "WorkgroupMemory";
+ case 9: return "CrossWorkgroupMemory";
+ case 10: return "AtomicCounterMemory";
+ case 11: return "ImageMemory";
+
+ default: return "Bad";
+ }
+}
+
+const int MemoryAccessCeiling = 6;
+
+const char* MemoryAccessString(int mem)
+{
+ switch (mem) {
+ case MemoryAccessVolatileShift: return "Volatile";
+ case MemoryAccessAlignedShift: return "Aligned";
+ case MemoryAccessNontemporalShift: return "Nontemporal";
+ case MemoryAccessMakePointerAvailableKHRShift: return "MakePointerAvailableKHR";
+ case MemoryAccessMakePointerVisibleKHRShift: return "MakePointerVisibleKHR";
+ case MemoryAccessNonPrivatePointerKHRShift: return "NonPrivatePointerKHR";
+
+ default: return "Bad";
+ }
+}
+
+const char* ScopeString(int mem)
+{
+ switch (mem) {
+ case 0: return "CrossDevice";
+ case 1: return "Device";
+ case 2: return "Workgroup";
+ case 3: return "Subgroup";
+ case 4: return "Invocation";
+
+ default: return "Bad";
+ }
+}
+
+const char* GroupOperationString(int gop)
+{
+
+ switch (gop)
+ {
+ case GroupOperationReduce: return "Reduce";
+ case GroupOperationInclusiveScan: return "InclusiveScan";
+ case GroupOperationExclusiveScan: return "ExclusiveScan";
+ case GroupOperationClusteredReduce: return "ClusteredReduce";
+#ifdef NV_EXTENSIONS
+ case GroupOperationPartitionedReduceNV: return "PartitionedReduceNV";
+ case GroupOperationPartitionedInclusiveScanNV: return "PartitionedInclusiveScanNV";
+ case GroupOperationPartitionedExclusiveScanNV: return "PartitionedExclusiveScanNV";
+#endif
+
+ default: return "Bad";
+ }
+}
+
+const char* KernelEnqueueFlagsString(int flag)
+{
+ switch (flag)
+ {
+ case 0: return "NoWait";
+ case 1: return "WaitKernel";
+ case 2: return "WaitWorkGroup";
+
+ default: return "Bad";
+ }
+}
+
+const char* KernelProfilingInfoString(int info)
+{
+ switch (info)
+ {
+ case 0: return "CmdExecTime";
+
+ default: return "Bad";
+ }
+}
+
+const char* CapabilityString(int info)
+{
+ switch (info)
+ {
+ case 0: return "Matrix";
+ case 1: return "Shader";
+ case 2: return "Geometry";
+ case 3: return "Tessellation";
+ case 4: return "Addresses";
+ case 5: return "Linkage";
+ case 6: return "Kernel";
+ case 7: return "Vector16";
+ case 8: return "Float16Buffer";
+ case 9: return "Float16";
+ case 10: return "Float64";
+ case 11: return "Int64";
+ case 12: return "Int64Atomics";
+ case 13: return "ImageBasic";
+ case 14: return "ImageReadWrite";
+ case 15: return "ImageMipmap";
+ case 16: return "Bad";
+ case 17: return "Pipes";
+ case 18: return "Groups";
+ case 19: return "DeviceEnqueue";
+ case 20: return "LiteralSampler";
+ case 21: return "AtomicStorage";
+ case 22: return "Int16";
+ case 23: return "TessellationPointSize";
+ case 24: return "GeometryPointSize";
+ case 25: return "ImageGatherExtended";
+ case 26: return "Bad";
+ case 27: return "StorageImageMultisample";
+ case 28: return "UniformBufferArrayDynamicIndexing";
+ case 29: return "SampledImageArrayDynamicIndexing";
+ case 30: return "StorageBufferArrayDynamicIndexing";
+ case 31: return "StorageImageArrayDynamicIndexing";
+ case 32: return "ClipDistance";
+ case 33: return "CullDistance";
+ case 34: return "ImageCubeArray";
+ case 35: return "SampleRateShading";
+ case 36: return "ImageRect";
+ case 37: return "SampledRect";
+ case 38: return "GenericPointer";
+ case 39: return "Int8";
+ case 40: return "InputAttachment";
+ case 41: return "SparseResidency";
+ case 42: return "MinLod";
+ case 43: return "Sampled1D";
+ case 44: return "Image1D";
+ case 45: return "SampledCubeArray";
+ case 46: return "SampledBuffer";
+ case 47: return "ImageBuffer";
+ case 48: return "ImageMSArray";
+ case 49: return "StorageImageExtendedFormats";
+ case 50: return "ImageQuery";
+ case 51: return "DerivativeControl";
+ case 52: return "InterpolationFunction";
+ case 53: return "TransformFeedback";
+ case 54: return "GeometryStreams";
+ case 55: return "StorageImageReadWithoutFormat";
+ case 56: return "StorageImageWriteWithoutFormat";
+ case 57: return "MultiViewport";
+ case 61: return "GroupNonUniform";
+ case 62: return "GroupNonUniformVote";
+ case 63: return "GroupNonUniformArithmetic";
+ case 64: return "GroupNonUniformBallot";
+ case 65: return "GroupNonUniformShuffle";
+ case 66: return "GroupNonUniformShuffleRelative";
+ case 67: return "GroupNonUniformClustered";
+ case 68: return "GroupNonUniformQuad";
+
+ case CapabilitySubgroupBallotKHR: return "SubgroupBallotKHR";
+ case CapabilityDrawParameters: return "DrawParameters";
+ case CapabilitySubgroupVoteKHR: return "SubgroupVoteKHR";
+
+ case CapabilityStorageUniformBufferBlock16: return "StorageUniformBufferBlock16";
+ case CapabilityStorageUniform16: return "StorageUniform16";
+ case CapabilityStoragePushConstant16: return "StoragePushConstant16";
+ case CapabilityStorageInputOutput16: return "StorageInputOutput16";
+
+ case CapabilityStorageBuffer8BitAccess: return "CapabilityStorageBuffer8BitAccess";
+ case CapabilityUniformAndStorageBuffer8BitAccess: return "CapabilityUniformAndStorageBuffer8BitAccess";
+ case CapabilityStoragePushConstant8: return "CapabilityStoragePushConstant8";
+
+ case CapabilityDeviceGroup: return "DeviceGroup";
+ case CapabilityMultiView: return "MultiView";
+
+ case CapabilityStencilExportEXT: return "StencilExportEXT";
+
+#ifdef AMD_EXTENSIONS
+ case CapabilityFloat16ImageAMD: return "Float16ImageAMD";
+ case CapabilityImageGatherBiasLodAMD: return "ImageGatherBiasLodAMD";
+ case CapabilityFragmentMaskAMD: return "FragmentMaskAMD";
+ case CapabilityImageReadWriteLodAMD: return "ImageReadWriteLodAMD";
+#endif
+
+ case CapabilityAtomicStorageOps: return "AtomicStorageOps";
+
+ case CapabilitySampleMaskPostDepthCoverage: return "SampleMaskPostDepthCoverage";
+#ifdef NV_EXTENSIONS
+ case CapabilityGeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV";
+ case CapabilityShaderViewportIndexLayerNV: return "ShaderViewportIndexLayerNV";
+ case CapabilityShaderViewportMaskNV: return "ShaderViewportMaskNV";
+ case CapabilityShaderStereoViewNV: return "ShaderStereoViewNV";
+ case CapabilityPerViewAttributesNV: return "PerViewAttributesNV";
+ case CapabilityGroupNonUniformPartitionedNV: return "GroupNonUniformPartitionedNV";
+ case CapabilityRayTracingNV: return "RayTracingNV";
+ case CapabilityComputeDerivativeGroupQuadsNV: return "ComputeDerivativeGroupQuadsNV";
+ case CapabilityComputeDerivativeGroupLinearNV: return "ComputeDerivativeGroupLinearNV";
+ case CapabilityFragmentBarycentricNV: return "FragmentBarycentricNV";
+ case CapabilityMeshShadingNV: return "MeshShadingNV";
+// case CapabilityShadingRateNV: return "ShadingRateNV"; // superseded by CapabilityFragmentDensityEXT
+#endif
+ case CapabilityFragmentDensityEXT: return "FragmentDensityEXT";
+
+ case CapabilityFragmentFullyCoveredEXT: return "FragmentFullyCoveredEXT";
+
+ case CapabilityShaderNonUniformEXT: return "CapabilityShaderNonUniformEXT";
+ case CapabilityRuntimeDescriptorArrayEXT: return "CapabilityRuntimeDescriptorArrayEXT";
+ case CapabilityInputAttachmentArrayDynamicIndexingEXT: return "CapabilityInputAttachmentArrayDynamicIndexingEXT";
+ case CapabilityUniformTexelBufferArrayDynamicIndexingEXT: return "CapabilityUniformTexelBufferArrayDynamicIndexingEXT";
+ case CapabilityStorageTexelBufferArrayDynamicIndexingEXT: return "CapabilityStorageTexelBufferArrayDynamicIndexingEXT";
+ case CapabilityUniformBufferArrayNonUniformIndexingEXT: return "CapabilityUniformBufferArrayNonUniformIndexingEXT";
+ case CapabilitySampledImageArrayNonUniformIndexingEXT: return "CapabilitySampledImageArrayNonUniformIndexingEXT";
+ case CapabilityStorageBufferArrayNonUniformIndexingEXT: return "CapabilityStorageBufferArrayNonUniformIndexingEXT";
+ case CapabilityStorageImageArrayNonUniformIndexingEXT: return "CapabilityStorageImageArrayNonUniformIndexingEXT";
+ case CapabilityInputAttachmentArrayNonUniformIndexingEXT: return "CapabilityInputAttachmentArrayNonUniformIndexingEXT";
+ case CapabilityUniformTexelBufferArrayNonUniformIndexingEXT: return "CapabilityUniformTexelBufferArrayNonUniformIndexingEXT";
+ case CapabilityStorageTexelBufferArrayNonUniformIndexingEXT: return "CapabilityStorageTexelBufferArrayNonUniformIndexingEXT";
+
+ case CapabilityVulkanMemoryModelKHR: return "CapabilityVulkanMemoryModelKHR";
+ case CapabilityVulkanMemoryModelDeviceScopeKHR: return "CapabilityVulkanMemoryModelDeviceScopeKHR";
+
+ case CapabilityPhysicalStorageBufferAddressesEXT: return "CapabilityPhysicalStorageBufferAddressesEXT";
+
+ case CapabilityVariablePointers: return "CapabilityVariablePointers";
+
+ case CapabilityCooperativeMatrixNV: return "CapabilityCooperativeMatrixNV";
+
+ default: return "Bad";
+ }
+}
+
+const char* OpcodeString(int op)
+{
+ switch (op) {
+ case 0: return "OpNop";
+ case 1: return "OpUndef";
+ case 2: return "OpSourceContinued";
+ case 3: return "OpSource";
+ case 4: return "OpSourceExtension";
+ case 5: return "OpName";
+ case 6: return "OpMemberName";
+ case 7: return "OpString";
+ case 8: return "OpLine";
+ case 9: return "Bad";
+ case 10: return "OpExtension";
+ case 11: return "OpExtInstImport";
+ case 12: return "OpExtInst";
+ case 13: return "Bad";
+ case 14: return "OpMemoryModel";
+ case 15: return "OpEntryPoint";
+ case 16: return "OpExecutionMode";
+ case 17: return "OpCapability";
+ case 18: return "Bad";
+ case 19: return "OpTypeVoid";
+ case 20: return "OpTypeBool";
+ case 21: return "OpTypeInt";
+ case 22: return "OpTypeFloat";
+ case 23: return "OpTypeVector";
+ case 24: return "OpTypeMatrix";
+ case 25: return "OpTypeImage";
+ case 26: return "OpTypeSampler";
+ case 27: return "OpTypeSampledImage";
+ case 28: return "OpTypeArray";
+ case 29: return "OpTypeRuntimeArray";
+ case 30: return "OpTypeStruct";
+ case 31: return "OpTypeOpaque";
+ case 32: return "OpTypePointer";
+ case 33: return "OpTypeFunction";
+ case 34: return "OpTypeEvent";
+ case 35: return "OpTypeDeviceEvent";
+ case 36: return "OpTypeReserveId";
+ case 37: return "OpTypeQueue";
+ case 38: return "OpTypePipe";
+ case 39: return "OpTypeForwardPointer";
+ case 40: return "Bad";
+ case 41: return "OpConstantTrue";
+ case 42: return "OpConstantFalse";
+ case 43: return "OpConstant";
+ case 44: return "OpConstantComposite";
+ case 45: return "OpConstantSampler";
+ case 46: return "OpConstantNull";
+ case 47: return "Bad";
+ case 48: return "OpSpecConstantTrue";
+ case 49: return "OpSpecConstantFalse";
+ case 50: return "OpSpecConstant";
+ case 51: return "OpSpecConstantComposite";
+ case 52: return "OpSpecConstantOp";
+ case 53: return "Bad";
+ case 54: return "OpFunction";
+ case 55: return "OpFunctionParameter";
+ case 56: return "OpFunctionEnd";
+ case 57: return "OpFunctionCall";
+ case 58: return "Bad";
+ case 59: return "OpVariable";
+ case 60: return "OpImageTexelPointer";
+ case 61: return "OpLoad";
+ case 62: return "OpStore";
+ case 63: return "OpCopyMemory";
+ case 64: return "OpCopyMemorySized";
+ case 65: return "OpAccessChain";
+ case 66: return "OpInBoundsAccessChain";
+ case 67: return "OpPtrAccessChain";
+ case 68: return "OpArrayLength";
+ case 69: return "OpGenericPtrMemSemantics";
+ case 70: return "OpInBoundsPtrAccessChain";
+ case 71: return "OpDecorate";
+ case 72: return "OpMemberDecorate";
+ case 73: return "OpDecorationGroup";
+ case 74: return "OpGroupDecorate";
+ case 75: return "OpGroupMemberDecorate";
+ case 76: return "Bad";
+ case 77: return "OpVectorExtractDynamic";
+ case 78: return "OpVectorInsertDynamic";
+ case 79: return "OpVectorShuffle";
+ case 80: return "OpCompositeConstruct";
+ case 81: return "OpCompositeExtract";
+ case 82: return "OpCompositeInsert";
+ case 83: return "OpCopyObject";
+ case 84: return "OpTranspose";
+ case 85: return "Bad";
+ case 86: return "OpSampledImage";
+ case 87: return "OpImageSampleImplicitLod";
+ case 88: return "OpImageSampleExplicitLod";
+ case 89: return "OpImageSampleDrefImplicitLod";
+ case 90: return "OpImageSampleDrefExplicitLod";
+ case 91: return "OpImageSampleProjImplicitLod";
+ case 92: return "OpImageSampleProjExplicitLod";
+ case 93: return "OpImageSampleProjDrefImplicitLod";
+ case 94: return "OpImageSampleProjDrefExplicitLod";
+ case 95: return "OpImageFetch";
+ case 96: return "OpImageGather";
+ case 97: return "OpImageDrefGather";
+ case 98: return "OpImageRead";
+ case 99: return "OpImageWrite";
+ case 100: return "OpImage";
+ case 101: return "OpImageQueryFormat";
+ case 102: return "OpImageQueryOrder";
+ case 103: return "OpImageQuerySizeLod";
+ case 104: return "OpImageQuerySize";
+ case 105: return "OpImageQueryLod";
+ case 106: return "OpImageQueryLevels";
+ case 107: return "OpImageQuerySamples";
+ case 108: return "Bad";
+ case 109: return "OpConvertFToU";
+ case 110: return "OpConvertFToS";
+ case 111: return "OpConvertSToF";
+ case 112: return "OpConvertUToF";
+ case 113: return "OpUConvert";
+ case 114: return "OpSConvert";
+ case 115: return "OpFConvert";
+ case 116: return "OpQuantizeToF16";
+ case 117: return "OpConvertPtrToU";
+ case 118: return "OpSatConvertSToU";
+ case 119: return "OpSatConvertUToS";
+ case 120: return "OpConvertUToPtr";
+ case 121: return "OpPtrCastToGeneric";
+ case 122: return "OpGenericCastToPtr";
+ case 123: return "OpGenericCastToPtrExplicit";
+ case 124: return "OpBitcast";
+ case 125: return "Bad";
+ case 126: return "OpSNegate";
+ case 127: return "OpFNegate";
+ case 128: return "OpIAdd";
+ case 129: return "OpFAdd";
+ case 130: return "OpISub";
+ case 131: return "OpFSub";
+ case 132: return "OpIMul";
+ case 133: return "OpFMul";
+ case 134: return "OpUDiv";
+ case 135: return "OpSDiv";
+ case 136: return "OpFDiv";
+ case 137: return "OpUMod";
+ case 138: return "OpSRem";
+ case 139: return "OpSMod";
+ case 140: return "OpFRem";
+ case 141: return "OpFMod";
+ case 142: return "OpVectorTimesScalar";
+ case 143: return "OpMatrixTimesScalar";
+ case 144: return "OpVectorTimesMatrix";
+ case 145: return "OpMatrixTimesVector";
+ case 146: return "OpMatrixTimesMatrix";
+ case 147: return "OpOuterProduct";
+ case 148: return "OpDot";
+ case 149: return "OpIAddCarry";
+ case 150: return "OpISubBorrow";
+ case 151: return "OpUMulExtended";
+ case 152: return "OpSMulExtended";
+ case 153: return "Bad";
+ case 154: return "OpAny";
+ case 155: return "OpAll";
+ case 156: return "OpIsNan";
+ case 157: return "OpIsInf";
+ case 158: return "OpIsFinite";
+ case 159: return "OpIsNormal";
+ case 160: return "OpSignBitSet";
+ case 161: return "OpLessOrGreater";
+ case 162: return "OpOrdered";
+ case 163: return "OpUnordered";
+ case 164: return "OpLogicalEqual";
+ case 165: return "OpLogicalNotEqual";
+ case 166: return "OpLogicalOr";
+ case 167: return "OpLogicalAnd";
+ case 168: return "OpLogicalNot";
+ case 169: return "OpSelect";
+ case 170: return "OpIEqual";
+ case 171: return "OpINotEqual";
+ case 172: return "OpUGreaterThan";
+ case 173: return "OpSGreaterThan";
+ case 174: return "OpUGreaterThanEqual";
+ case 175: return "OpSGreaterThanEqual";
+ case 176: return "OpULessThan";
+ case 177: return "OpSLessThan";
+ case 178: return "OpULessThanEqual";
+ case 179: return "OpSLessThanEqual";
+ case 180: return "OpFOrdEqual";
+ case 181: return "OpFUnordEqual";
+ case 182: return "OpFOrdNotEqual";
+ case 183: return "OpFUnordNotEqual";
+ case 184: return "OpFOrdLessThan";
+ case 185: return "OpFUnordLessThan";
+ case 186: return "OpFOrdGreaterThan";
+ case 187: return "OpFUnordGreaterThan";
+ case 188: return "OpFOrdLessThanEqual";
+ case 189: return "OpFUnordLessThanEqual";
+ case 190: return "OpFOrdGreaterThanEqual";
+ case 191: return "OpFUnordGreaterThanEqual";
+ case 192: return "Bad";
+ case 193: return "Bad";
+ case 194: return "OpShiftRightLogical";
+ case 195: return "OpShiftRightArithmetic";
+ case 196: return "OpShiftLeftLogical";
+ case 197: return "OpBitwiseOr";
+ case 198: return "OpBitwiseXor";
+ case 199: return "OpBitwiseAnd";
+ case 200: return "OpNot";
+ case 201: return "OpBitFieldInsert";
+ case 202: return "OpBitFieldSExtract";
+ case 203: return "OpBitFieldUExtract";
+ case 204: return "OpBitReverse";
+ case 205: return "OpBitCount";
+ case 206: return "Bad";
+ case 207: return "OpDPdx";
+ case 208: return "OpDPdy";
+ case 209: return "OpFwidth";
+ case 210: return "OpDPdxFine";
+ case 211: return "OpDPdyFine";
+ case 212: return "OpFwidthFine";
+ case 213: return "OpDPdxCoarse";
+ case 214: return "OpDPdyCoarse";
+ case 215: return "OpFwidthCoarse";
+ case 216: return "Bad";
+ case 217: return "Bad";
+ case 218: return "OpEmitVertex";
+ case 219: return "OpEndPrimitive";
+ case 220: return "OpEmitStreamVertex";
+ case 221: return "OpEndStreamPrimitive";
+ case 222: return "Bad";
+ case 223: return "Bad";
+ case 224: return "OpControlBarrier";
+ case 225: return "OpMemoryBarrier";
+ case 226: return "Bad";
+ case 227: return "OpAtomicLoad";
+ case 228: return "OpAtomicStore";
+ case 229: return "OpAtomicExchange";
+ case 230: return "OpAtomicCompareExchange";
+ case 231: return "OpAtomicCompareExchangeWeak";
+ case 232: return "OpAtomicIIncrement";
+ case 233: return "OpAtomicIDecrement";
+ case 234: return "OpAtomicIAdd";
+ case 235: return "OpAtomicISub";
+ case 236: return "OpAtomicSMin";
+ case 237: return "OpAtomicUMin";
+ case 238: return "OpAtomicSMax";
+ case 239: return "OpAtomicUMax";
+ case 240: return "OpAtomicAnd";
+ case 241: return "OpAtomicOr";
+ case 242: return "OpAtomicXor";
+ case 243: return "Bad";
+ case 244: return "Bad";
+ case 245: return "OpPhi";
+ case 246: return "OpLoopMerge";
+ case 247: return "OpSelectionMerge";
+ case 248: return "OpLabel";
+ case 249: return "OpBranch";
+ case 250: return "OpBranchConditional";
+ case 251: return "OpSwitch";
+ case 252: return "OpKill";
+ case 253: return "OpReturn";
+ case 254: return "OpReturnValue";
+ case 255: return "OpUnreachable";
+ case 256: return "OpLifetimeStart";
+ case 257: return "OpLifetimeStop";
+ case 258: return "Bad";
+ case 259: return "OpGroupAsyncCopy";
+ case 260: return "OpGroupWaitEvents";
+ case 261: return "OpGroupAll";
+ case 262: return "OpGroupAny";
+ case 263: return "OpGroupBroadcast";
+ case 264: return "OpGroupIAdd";
+ case 265: return "OpGroupFAdd";
+ case 266: return "OpGroupFMin";
+ case 267: return "OpGroupUMin";
+ case 268: return "OpGroupSMin";
+ case 269: return "OpGroupFMax";
+ case 270: return "OpGroupUMax";
+ case 271: return "OpGroupSMax";
+ case 272: return "Bad";
+ case 273: return "Bad";
+ case 274: return "OpReadPipe";
+ case 275: return "OpWritePipe";
+ case 276: return "OpReservedReadPipe";
+ case 277: return "OpReservedWritePipe";
+ case 278: return "OpReserveReadPipePackets";
+ case 279: return "OpReserveWritePipePackets";
+ case 280: return "OpCommitReadPipe";
+ case 281: return "OpCommitWritePipe";
+ case 282: return "OpIsValidReserveId";
+ case 283: return "OpGetNumPipePackets";
+ case 284: return "OpGetMaxPipePackets";
+ case 285: return "OpGroupReserveReadPipePackets";
+ case 286: return "OpGroupReserveWritePipePackets";
+ case 287: return "OpGroupCommitReadPipe";
+ case 288: return "OpGroupCommitWritePipe";
+ case 289: return "Bad";
+ case 290: return "Bad";
+ case 291: return "OpEnqueueMarker";
+ case 292: return "OpEnqueueKernel";
+ case 293: return "OpGetKernelNDrangeSubGroupCount";
+ case 294: return "OpGetKernelNDrangeMaxSubGroupSize";
+ case 295: return "OpGetKernelWorkGroupSize";
+ case 296: return "OpGetKernelPreferredWorkGroupSizeMultiple";
+ case 297: return "OpRetainEvent";
+ case 298: return "OpReleaseEvent";
+ case 299: return "OpCreateUserEvent";
+ case 300: return "OpIsValidEvent";
+ case 301: return "OpSetUserEventStatus";
+ case 302: return "OpCaptureEventProfilingInfo";
+ case 303: return "OpGetDefaultQueue";
+ case 304: return "OpBuildNDRange";
+ case 305: return "OpImageSparseSampleImplicitLod";
+ case 306: return "OpImageSparseSampleExplicitLod";
+ case 307: return "OpImageSparseSampleDrefImplicitLod";
+ case 308: return "OpImageSparseSampleDrefExplicitLod";
+ case 309: return "OpImageSparseSampleProjImplicitLod";
+ case 310: return "OpImageSparseSampleProjExplicitLod";
+ case 311: return "OpImageSparseSampleProjDrefImplicitLod";
+ case 312: return "OpImageSparseSampleProjDrefExplicitLod";
+ case 313: return "OpImageSparseFetch";
+ case 314: return "OpImageSparseGather";
+ case 315: return "OpImageSparseDrefGather";
+ case 316: return "OpImageSparseTexelsResident";
+ case 317: return "OpNoLine";
+ case 318: return "OpAtomicFlagTestAndSet";
+ case 319: return "OpAtomicFlagClear";
+ case 320: return "OpImageSparseRead";
+
+ case OpModuleProcessed: return "OpModuleProcessed";
+ case OpDecorateId: return "OpDecorateId";
+
+ case 333: return "OpGroupNonUniformElect";
+ case 334: return "OpGroupNonUniformAll";
+ case 335: return "OpGroupNonUniformAny";
+ case 336: return "OpGroupNonUniformAllEqual";
+ case 337: return "OpGroupNonUniformBroadcast";
+ case 338: return "OpGroupNonUniformBroadcastFirst";
+ case 339: return "OpGroupNonUniformBallot";
+ case 340: return "OpGroupNonUniformInverseBallot";
+ case 341: return "OpGroupNonUniformBallotBitExtract";
+ case 342: return "OpGroupNonUniformBallotBitCount";
+ case 343: return "OpGroupNonUniformBallotFindLSB";
+ case 344: return "OpGroupNonUniformBallotFindMSB";
+ case 345: return "OpGroupNonUniformShuffle";
+ case 346: return "OpGroupNonUniformShuffleXor";
+ case 347: return "OpGroupNonUniformShuffleUp";
+ case 348: return "OpGroupNonUniformShuffleDown";
+ case 349: return "OpGroupNonUniformIAdd";
+ case 350: return "OpGroupNonUniformFAdd";
+ case 351: return "OpGroupNonUniformIMul";
+ case 352: return "OpGroupNonUniformFMul";
+ case 353: return "OpGroupNonUniformSMin";
+ case 354: return "OpGroupNonUniformUMin";
+ case 355: return "OpGroupNonUniformFMin";
+ case 356: return "OpGroupNonUniformSMax";
+ case 357: return "OpGroupNonUniformUMax";
+ case 358: return "OpGroupNonUniformFMax";
+ case 359: return "OpGroupNonUniformBitwiseAnd";
+ case 360: return "OpGroupNonUniformBitwiseOr";
+ case 361: return "OpGroupNonUniformBitwiseXor";
+ case 362: return "OpGroupNonUniformLogicalAnd";
+ case 363: return "OpGroupNonUniformLogicalOr";
+ case 364: return "OpGroupNonUniformLogicalXor";
+ case 365: return "OpGroupNonUniformQuadBroadcast";
+ case 366: return "OpGroupNonUniformQuadSwap";
+
+ case 4421: return "OpSubgroupBallotKHR";
+ case 4422: return "OpSubgroupFirstInvocationKHR";
+ case 4428: return "OpSubgroupAllKHR";
+ case 4429: return "OpSubgroupAnyKHR";
+ case 4430: return "OpSubgroupAllEqualKHR";
+ case 4432: return "OpSubgroupReadInvocationKHR";
+
+#ifdef AMD_EXTENSIONS
+ case 5000: return "OpGroupIAddNonUniformAMD";
+ case 5001: return "OpGroupFAddNonUniformAMD";
+ case 5002: return "OpGroupFMinNonUniformAMD";
+ case 5003: return "OpGroupUMinNonUniformAMD";
+ case 5004: return "OpGroupSMinNonUniformAMD";
+ case 5005: return "OpGroupFMaxNonUniformAMD";
+ case 5006: return "OpGroupUMaxNonUniformAMD";
+ case 5007: return "OpGroupSMaxNonUniformAMD";
+
+ case 5011: return "OpFragmentMaskFetchAMD";
+ case 5012: return "OpFragmentFetchAMD";
+#endif
+
+ case OpDecorateStringGOOGLE: return "OpDecorateStringGOOGLE";
+ case OpMemberDecorateStringGOOGLE: return "OpMemberDecorateStringGOOGLE";
+
+#ifdef NV_EXTENSIONS
+ case OpGroupNonUniformPartitionNV: return "OpGroupNonUniformPartitionNV";
+ case OpReportIntersectionNV: return "OpReportIntersectionNV";
+ case OpIgnoreIntersectionNV: return "OpIgnoreIntersectionNV";
+ case OpTerminateRayNV: return "OpTerminateRayNV";
+ case OpTraceNV: return "OpTraceNV";
+ case OpTypeAccelerationStructureNV: return "OpTypeAccelerationStructureNV";
+ case OpExecuteCallableNV: return "OpExecuteCallableNV";
+ case OpImageSampleFootprintNV: return "OpImageSampleFootprintNV";
+ case OpWritePackedPrimitiveIndices4x8NV: return "OpWritePackedPrimitiveIndices4x8NV";
+#endif
+
+ case OpTypeCooperativeMatrixNV: return "OpTypeCooperativeMatrixNV";
+ case OpCooperativeMatrixLoadNV: return "OpCooperativeMatrixLoadNV";
+ case OpCooperativeMatrixStoreNV: return "OpCooperativeMatrixStoreNV";
+ case OpCooperativeMatrixMulAddNV: return "OpCooperativeMatrixMulAddNV";
+ case OpCooperativeMatrixLengthNV: return "OpCooperativeMatrixLengthNV";
+
+ default:
+ return "Bad";
+ }
+}
+
+// The set of objects that hold all the instruction/operand
+// parameterization information.
+InstructionParameters InstructionDesc[OpCodeMask + 1];
+OperandParameters ExecutionModeOperands[ExecutionModeCeiling];
+OperandParameters DecorationOperands[DecorationCeiling];
+
+EnumDefinition OperandClassParams[OperandCount];
+EnumParameters ExecutionModeParams[ExecutionModeCeiling];
+EnumParameters ImageOperandsParams[ImageOperandsCeiling];
+EnumParameters DecorationParams[DecorationCeiling];
+EnumParameters LoopControlParams[FunctionControlCeiling];
+EnumParameters SelectionControlParams[SelectControlCeiling];
+EnumParameters FunctionControlParams[FunctionControlCeiling];
+EnumParameters MemoryAccessParams[MemoryAccessCeiling];
+
+// Set up all the parameterizing descriptions of the opcodes, operands, etc.
+void Parameterize()
+{
+ // only do this once.
+ static bool initialized = false;
+ if (initialized)
+ return;
+ initialized = true;
+
+ // Exceptions to having a result <id> and a resulting type <id>.
+ // (Everything is initialized to have both).
+
+ InstructionDesc[OpNop].setResultAndType(false, false);
+ InstructionDesc[OpSource].setResultAndType(false, false);
+ InstructionDesc[OpSourceContinued].setResultAndType(false, false);
+ InstructionDesc[OpSourceExtension].setResultAndType(false, false);
+ InstructionDesc[OpExtension].setResultAndType(false, false);
+ InstructionDesc[OpExtInstImport].setResultAndType(true, false);
+ InstructionDesc[OpCapability].setResultAndType(false, false);
+ InstructionDesc[OpMemoryModel].setResultAndType(false, false);
+ InstructionDesc[OpEntryPoint].setResultAndType(false, false);
+ InstructionDesc[OpExecutionMode].setResultAndType(false, false);
+ InstructionDesc[OpTypeVoid].setResultAndType(true, false);
+ InstructionDesc[OpTypeBool].setResultAndType(true, false);
+ InstructionDesc[OpTypeInt].setResultAndType(true, false);
+ InstructionDesc[OpTypeFloat].setResultAndType(true, false);
+ InstructionDesc[OpTypeVector].setResultAndType(true, false);
+ InstructionDesc[OpTypeMatrix].setResultAndType(true, false);
+ InstructionDesc[OpTypeImage].setResultAndType(true, false);
+ InstructionDesc[OpTypeSampler].setResultAndType(true, false);
+ InstructionDesc[OpTypeSampledImage].setResultAndType(true, false);
+ InstructionDesc[OpTypeArray].setResultAndType(true, false);
+ InstructionDesc[OpTypeRuntimeArray].setResultAndType(true, false);
+ InstructionDesc[OpTypeStruct].setResultAndType(true, false);
+ InstructionDesc[OpTypeOpaque].setResultAndType(true, false);
+ InstructionDesc[OpTypePointer].setResultAndType(true, false);
+ InstructionDesc[OpTypeForwardPointer].setResultAndType(false, false);
+ InstructionDesc[OpTypeFunction].setResultAndType(true, false);
+ InstructionDesc[OpTypeEvent].setResultAndType(true, false);
+ InstructionDesc[OpTypeDeviceEvent].setResultAndType(true, false);
+ InstructionDesc[OpTypeReserveId].setResultAndType(true, false);
+ InstructionDesc[OpTypeQueue].setResultAndType(true, false);
+ InstructionDesc[OpTypePipe].setResultAndType(true, false);
+ InstructionDesc[OpFunctionEnd].setResultAndType(false, false);
+ InstructionDesc[OpStore].setResultAndType(false, false);
+ InstructionDesc[OpImageWrite].setResultAndType(false, false);
+ InstructionDesc[OpDecorationGroup].setResultAndType(true, false);
+ InstructionDesc[OpDecorate].setResultAndType(false, false);
+ InstructionDesc[OpDecorateId].setResultAndType(false, false);
+ InstructionDesc[OpDecorateStringGOOGLE].setResultAndType(false, false);
+ InstructionDesc[OpMemberDecorate].setResultAndType(false, false);
+ InstructionDesc[OpMemberDecorateStringGOOGLE].setResultAndType(false, false);
+ InstructionDesc[OpGroupDecorate].setResultAndType(false, false);
+ InstructionDesc[OpGroupMemberDecorate].setResultAndType(false, false);
+ InstructionDesc[OpName].setResultAndType(false, false);
+ InstructionDesc[OpMemberName].setResultAndType(false, false);
+ InstructionDesc[OpString].setResultAndType(true, false);
+ InstructionDesc[OpLine].setResultAndType(false, false);
+ InstructionDesc[OpNoLine].setResultAndType(false, false);
+ InstructionDesc[OpCopyMemory].setResultAndType(false, false);
+ InstructionDesc[OpCopyMemorySized].setResultAndType(false, false);
+ InstructionDesc[OpEmitVertex].setResultAndType(false, false);
+ InstructionDesc[OpEndPrimitive].setResultAndType(false, false);
+ InstructionDesc[OpEmitStreamVertex].setResultAndType(false, false);
+ InstructionDesc[OpEndStreamPrimitive].setResultAndType(false, false);
+ InstructionDesc[OpControlBarrier].setResultAndType(false, false);
+ InstructionDesc[OpMemoryBarrier].setResultAndType(false, false);
+ InstructionDesc[OpAtomicStore].setResultAndType(false, false);
+ InstructionDesc[OpLoopMerge].setResultAndType(false, false);
+ InstructionDesc[OpSelectionMerge].setResultAndType(false, false);
+ InstructionDesc[OpLabel].setResultAndType(true, false);
+ InstructionDesc[OpBranch].setResultAndType(false, false);
+ InstructionDesc[OpBranchConditional].setResultAndType(false, false);
+ InstructionDesc[OpSwitch].setResultAndType(false, false);
+ InstructionDesc[OpKill].setResultAndType(false, false);
+ InstructionDesc[OpReturn].setResultAndType(false, false);
+ InstructionDesc[OpReturnValue].setResultAndType(false, false);
+ InstructionDesc[OpUnreachable].setResultAndType(false, false);
+ InstructionDesc[OpLifetimeStart].setResultAndType(false, false);
+ InstructionDesc[OpLifetimeStop].setResultAndType(false, false);
+ InstructionDesc[OpCommitReadPipe].setResultAndType(false, false);
+ InstructionDesc[OpCommitWritePipe].setResultAndType(false, false);
+ InstructionDesc[OpGroupCommitWritePipe].setResultAndType(false, false);
+ InstructionDesc[OpGroupCommitReadPipe].setResultAndType(false, false);
+ InstructionDesc[OpCaptureEventProfilingInfo].setResultAndType(false, false);
+ InstructionDesc[OpSetUserEventStatus].setResultAndType(false, false);
+ InstructionDesc[OpRetainEvent].setResultAndType(false, false);
+ InstructionDesc[OpReleaseEvent].setResultAndType(false, false);
+ InstructionDesc[OpGroupWaitEvents].setResultAndType(false, false);
+ InstructionDesc[OpAtomicFlagClear].setResultAndType(false, false);
+ InstructionDesc[OpModuleProcessed].setResultAndType(false, false);
+ InstructionDesc[OpTypeCooperativeMatrixNV].setResultAndType(true, false);
+ InstructionDesc[OpCooperativeMatrixStoreNV].setResultAndType(false, false);
+
+ // Specific additional context-dependent operands
+
+ ExecutionModeOperands[ExecutionModeInvocations].push(OperandLiteralNumber, "'Number of <<Invocation,invocations>>'");
+
+ ExecutionModeOperands[ExecutionModeLocalSize].push(OperandLiteralNumber, "'x size'");
+ ExecutionModeOperands[ExecutionModeLocalSize].push(OperandLiteralNumber, "'y size'");
+ ExecutionModeOperands[ExecutionModeLocalSize].push(OperandLiteralNumber, "'z size'");
+
+ ExecutionModeOperands[ExecutionModeLocalSizeHint].push(OperandLiteralNumber, "'x size'");
+ ExecutionModeOperands[ExecutionModeLocalSizeHint].push(OperandLiteralNumber, "'y size'");
+ ExecutionModeOperands[ExecutionModeLocalSizeHint].push(OperandLiteralNumber, "'z size'");
+
+ ExecutionModeOperands[ExecutionModeOutputVertices].push(OperandLiteralNumber, "'Vertex count'");
+ ExecutionModeOperands[ExecutionModeVecTypeHint].push(OperandLiteralNumber, "'Vector type'");
+
+ DecorationOperands[DecorationStream].push(OperandLiteralNumber, "'Stream Number'");
+ DecorationOperands[DecorationLocation].push(OperandLiteralNumber, "'Location'");
+ DecorationOperands[DecorationComponent].push(OperandLiteralNumber, "'Component'");
+ DecorationOperands[DecorationIndex].push(OperandLiteralNumber, "'Index'");
+ DecorationOperands[DecorationBinding].push(OperandLiteralNumber, "'Binding Point'");
+ DecorationOperands[DecorationDescriptorSet].push(OperandLiteralNumber, "'Descriptor Set'");
+ DecorationOperands[DecorationOffset].push(OperandLiteralNumber, "'Byte Offset'");
+ DecorationOperands[DecorationXfbBuffer].push(OperandLiteralNumber, "'XFB Buffer Number'");
+ DecorationOperands[DecorationXfbStride].push(OperandLiteralNumber, "'XFB Stride'");
+ DecorationOperands[DecorationArrayStride].push(OperandLiteralNumber, "'Array Stride'");
+ DecorationOperands[DecorationMatrixStride].push(OperandLiteralNumber, "'Matrix Stride'");
+ DecorationOperands[DecorationBuiltIn].push(OperandLiteralNumber, "See <<BuiltIn,*BuiltIn*>>");
+ DecorationOperands[DecorationFPRoundingMode].push(OperandFPRoundingMode, "'Floating-Point Rounding Mode'");
+ DecorationOperands[DecorationFPFastMathMode].push(OperandFPFastMath, "'Fast-Math Mode'");
+ DecorationOperands[DecorationLinkageAttributes].push(OperandLiteralString, "'Name'");
+ DecorationOperands[DecorationLinkageAttributes].push(OperandLinkageType, "'Linkage Type'");
+ DecorationOperands[DecorationFuncParamAttr].push(OperandFuncParamAttr, "'Function Parameter Attribute'");
+ DecorationOperands[DecorationSpecId].push(OperandLiteralNumber, "'Specialization Constant ID'");
+ DecorationOperands[DecorationInputAttachmentIndex].push(OperandLiteralNumber, "'Attachment Index'");
+ DecorationOperands[DecorationAlignment].push(OperandLiteralNumber, "'Alignment'");
+
+ OperandClassParams[OperandSource].set(0, SourceString, 0);
+ OperandClassParams[OperandExecutionModel].set(0, ExecutionModelString, nullptr);
+ OperandClassParams[OperandAddressing].set(0, AddressingString, nullptr);
+ OperandClassParams[OperandMemory].set(0, MemoryString, nullptr);
+ OperandClassParams[OperandExecutionMode].set(ExecutionModeCeiling, ExecutionModeString, ExecutionModeParams);
+ OperandClassParams[OperandExecutionMode].setOperands(ExecutionModeOperands);
+ OperandClassParams[OperandStorage].set(0, StorageClassString, nullptr);
+ OperandClassParams[OperandDimensionality].set(0, DimensionString, nullptr);
+ OperandClassParams[OperandSamplerAddressingMode].set(0, SamplerAddressingModeString, nullptr);
+ OperandClassParams[OperandSamplerFilterMode].set(0, SamplerFilterModeString, nullptr);
+ OperandClassParams[OperandSamplerImageFormat].set(0, ImageFormatString, nullptr);
+ OperandClassParams[OperandImageChannelOrder].set(0, ImageChannelOrderString, nullptr);
+ OperandClassParams[OperandImageChannelDataType].set(0, ImageChannelDataTypeString, nullptr);
+ OperandClassParams[OperandImageOperands].set(ImageOperandsCeiling, ImageOperandsString, ImageOperandsParams, true);
+ OperandClassParams[OperandFPFastMath].set(0, FPFastMathString, nullptr, true);
+ OperandClassParams[OperandFPRoundingMode].set(0, FPRoundingModeString, nullptr);
+ OperandClassParams[OperandLinkageType].set(0, LinkageTypeString, nullptr);
+ OperandClassParams[OperandFuncParamAttr].set(0, FuncParamAttrString, nullptr);
+ OperandClassParams[OperandAccessQualifier].set(0, AccessQualifierString, nullptr);
+ OperandClassParams[OperandDecoration].set(DecorationCeiling, DecorationString, DecorationParams);
+ OperandClassParams[OperandDecoration].setOperands(DecorationOperands);
+ OperandClassParams[OperandBuiltIn].set(0, BuiltInString, nullptr);
+ OperandClassParams[OperandSelect].set(SelectControlCeiling, SelectControlString, SelectionControlParams, true);
+ OperandClassParams[OperandLoop].set(LoopControlCeiling, LoopControlString, LoopControlParams, true);
+ OperandClassParams[OperandFunction].set(FunctionControlCeiling, FunctionControlString, FunctionControlParams, true);
+ OperandClassParams[OperandMemorySemantics].set(0, MemorySemanticsString, nullptr, true);
+ OperandClassParams[OperandMemoryAccess].set(MemoryAccessCeiling, MemoryAccessString, MemoryAccessParams, true);
+ OperandClassParams[OperandScope].set(0, ScopeString, nullptr);
+ OperandClassParams[OperandGroupOperation].set(0, GroupOperationString, nullptr);
+ OperandClassParams[OperandKernelEnqueueFlags].set(0, KernelEnqueueFlagsString, nullptr);
+ OperandClassParams[OperandKernelProfilingInfo].set(0, KernelProfilingInfoString, nullptr, true);
+ OperandClassParams[OperandCapability].set(0, CapabilityString, nullptr);
+ OperandClassParams[OperandOpcode].set(OpCodeMask + 1, OpcodeString, 0);
+
+ // set name of operator, an initial set of <id> style operands, and the description
+
+ InstructionDesc[OpSource].operands.push(OperandSource, "");
+ InstructionDesc[OpSource].operands.push(OperandLiteralNumber, "'Version'");
+ InstructionDesc[OpSource].operands.push(OperandId, "'File'", true);
+ InstructionDesc[OpSource].operands.push(OperandLiteralString, "'Source'", true);
+
+ InstructionDesc[OpSourceContinued].operands.push(OperandLiteralString, "'Continued Source'");
+
+ InstructionDesc[OpSourceExtension].operands.push(OperandLiteralString, "'Extension'");
+
+ InstructionDesc[OpName].operands.push(OperandId, "'Target'");
+ InstructionDesc[OpName].operands.push(OperandLiteralString, "'Name'");
+
+ InstructionDesc[OpMemberName].operands.push(OperandId, "'Type'");
+ InstructionDesc[OpMemberName].operands.push(OperandLiteralNumber, "'Member'");
+ InstructionDesc[OpMemberName].operands.push(OperandLiteralString, "'Name'");
+
+ InstructionDesc[OpString].operands.push(OperandLiteralString, "'String'");
+
+ InstructionDesc[OpLine].operands.push(OperandId, "'File'");
+ InstructionDesc[OpLine].operands.push(OperandLiteralNumber, "'Line'");
+ InstructionDesc[OpLine].operands.push(OperandLiteralNumber, "'Column'");
+
+ InstructionDesc[OpExtension].operands.push(OperandLiteralString, "'Name'");
+
+ InstructionDesc[OpExtInstImport].operands.push(OperandLiteralString, "'Name'");
+
+ InstructionDesc[OpCapability].operands.push(OperandCapability, "'Capability'");
+
+ InstructionDesc[OpMemoryModel].operands.push(OperandAddressing, "");
+ InstructionDesc[OpMemoryModel].operands.push(OperandMemory, "");
+
+ InstructionDesc[OpEntryPoint].operands.push(OperandExecutionModel, "");
+ InstructionDesc[OpEntryPoint].operands.push(OperandId, "'Entry Point'");
+ InstructionDesc[OpEntryPoint].operands.push(OperandLiteralString, "'Name'");
+ InstructionDesc[OpEntryPoint].operands.push(OperandVariableIds, "'Interface'");
+
+ InstructionDesc[OpExecutionMode].operands.push(OperandId, "'Entry Point'");
+ InstructionDesc[OpExecutionMode].operands.push(OperandExecutionMode, "'Mode'");
+ InstructionDesc[OpExecutionMode].operands.push(OperandOptionalLiteral, "See <<Execution_Mode,Execution Mode>>");
+
+ InstructionDesc[OpTypeInt].operands.push(OperandLiteralNumber, "'Width'");
+ InstructionDesc[OpTypeInt].operands.push(OperandLiteralNumber, "'Signedness'");
+
+ InstructionDesc[OpTypeFloat].operands.push(OperandLiteralNumber, "'Width'");
+
+ InstructionDesc[OpTypeVector].operands.push(OperandId, "'Component Type'");
+ InstructionDesc[OpTypeVector].operands.push(OperandLiteralNumber, "'Component Count'");
+
+ InstructionDesc[OpTypeMatrix].operands.push(OperandId, "'Column Type'");
+ InstructionDesc[OpTypeMatrix].operands.push(OperandLiteralNumber, "'Column Count'");
+
+ InstructionDesc[OpTypeImage].operands.push(OperandId, "'Sampled Type'");
+ InstructionDesc[OpTypeImage].operands.push(OperandDimensionality, "");
+ InstructionDesc[OpTypeImage].operands.push(OperandLiteralNumber, "'Depth'");
+ InstructionDesc[OpTypeImage].operands.push(OperandLiteralNumber, "'Arrayed'");
+ InstructionDesc[OpTypeImage].operands.push(OperandLiteralNumber, "'MS'");
+ InstructionDesc[OpTypeImage].operands.push(OperandLiteralNumber, "'Sampled'");
+ InstructionDesc[OpTypeImage].operands.push(OperandSamplerImageFormat, "");
+ InstructionDesc[OpTypeImage].operands.push(OperandAccessQualifier, "", true);
+
+ InstructionDesc[OpTypeSampledImage].operands.push(OperandId, "'Image Type'");
+
+ InstructionDesc[OpTypeArray].operands.push(OperandId, "'Element Type'");
+ InstructionDesc[OpTypeArray].operands.push(OperandId, "'Length'");
+
+ InstructionDesc[OpTypeRuntimeArray].operands.push(OperandId, "'Element Type'");
+
+ InstructionDesc[OpTypeStruct].operands.push(OperandVariableIds, "'Member 0 type', +\n'member 1 type', +\n...");
+
+ InstructionDesc[OpTypeOpaque].operands.push(OperandLiteralString, "The name of the opaque type.");
+
+ InstructionDesc[OpTypePointer].operands.push(OperandStorage, "");
+ InstructionDesc[OpTypePointer].operands.push(OperandId, "'Type'");
+
+ InstructionDesc[OpTypeForwardPointer].operands.push(OperandId, "'Pointer Type'");
+ InstructionDesc[OpTypeForwardPointer].operands.push(OperandStorage, "");
+
+ InstructionDesc[OpTypePipe].operands.push(OperandAccessQualifier, "'Qualifier'");
+
+ InstructionDesc[OpTypeFunction].operands.push(OperandId, "'Return Type'");
+ InstructionDesc[OpTypeFunction].operands.push(OperandVariableIds, "'Parameter 0 Type', +\n'Parameter 1 Type', +\n...");
+
+ InstructionDesc[OpConstant].operands.push(OperandVariableLiterals, "'Value'");
+
+ InstructionDesc[OpConstantComposite].operands.push(OperandVariableIds, "'Constituents'");
+
+ InstructionDesc[OpConstantSampler].operands.push(OperandSamplerAddressingMode, "");
+ InstructionDesc[OpConstantSampler].operands.push(OperandLiteralNumber, "'Param'");
+ InstructionDesc[OpConstantSampler].operands.push(OperandSamplerFilterMode, "");
+
+ InstructionDesc[OpSpecConstant].operands.push(OperandVariableLiterals, "'Value'");
+
+ InstructionDesc[OpSpecConstantComposite].operands.push(OperandVariableIds, "'Constituents'");
+
+ InstructionDesc[OpSpecConstantOp].operands.push(OperandLiteralNumber, "'Opcode'");
+ InstructionDesc[OpSpecConstantOp].operands.push(OperandVariableIds, "'Operands'");
+
+ InstructionDesc[OpVariable].operands.push(OperandStorage, "");
+ InstructionDesc[OpVariable].operands.push(OperandId, "'Initializer'", true);
+
+ InstructionDesc[OpFunction].operands.push(OperandFunction, "");
+ InstructionDesc[OpFunction].operands.push(OperandId, "'Function Type'");
+
+ InstructionDesc[OpFunctionCall].operands.push(OperandId, "'Function'");
+ InstructionDesc[OpFunctionCall].operands.push(OperandVariableIds, "'Argument 0', +\n'Argument 1', +\n...");
+
+ InstructionDesc[OpExtInst].operands.push(OperandId, "'Set'");
+ InstructionDesc[OpExtInst].operands.push(OperandLiteralNumber, "'Instruction'");
+ InstructionDesc[OpExtInst].operands.push(OperandVariableIds, "'Operand 1', +\n'Operand 2', +\n...");
+
+ InstructionDesc[OpLoad].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpLoad].operands.push(OperandMemoryAccess, "", true);
+ InstructionDesc[OpLoad].operands.push(OperandLiteralNumber, "", true);
+ InstructionDesc[OpLoad].operands.push(OperandId, "", true);
+
+ InstructionDesc[OpStore].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpStore].operands.push(OperandId, "'Object'");
+ InstructionDesc[OpStore].operands.push(OperandMemoryAccess, "", true);
+ InstructionDesc[OpStore].operands.push(OperandLiteralNumber, "", true);
+ InstructionDesc[OpStore].operands.push(OperandId, "", true);
+
+ InstructionDesc[OpPhi].operands.push(OperandVariableIds, "'Variable, Parent, ...'");
+
+ InstructionDesc[OpDecorate].operands.push(OperandId, "'Target'");
+ InstructionDesc[OpDecorate].operands.push(OperandDecoration, "");
+ InstructionDesc[OpDecorate].operands.push(OperandVariableLiterals, "See <<Decoration,'Decoration'>>.");
+
+ InstructionDesc[OpDecorateId].operands.push(OperandId, "'Target'");
+ InstructionDesc[OpDecorateId].operands.push(OperandDecoration, "");
+ InstructionDesc[OpDecorateId].operands.push(OperandVariableIds, "See <<Decoration,'Decoration'>>.");
+
+ InstructionDesc[OpDecorateStringGOOGLE].operands.push(OperandId, "'Target'");
+ InstructionDesc[OpDecorateStringGOOGLE].operands.push(OperandDecoration, "");
+ InstructionDesc[OpDecorateStringGOOGLE].operands.push(OperandLiteralString, "'Literal String'");
+
+ InstructionDesc[OpMemberDecorate].operands.push(OperandId, "'Structure Type'");
+ InstructionDesc[OpMemberDecorate].operands.push(OperandLiteralNumber, "'Member'");
+ InstructionDesc[OpMemberDecorate].operands.push(OperandDecoration, "");
+ InstructionDesc[OpMemberDecorate].operands.push(OperandVariableLiterals, "See <<Decoration,'Decoration'>>.");
+
+ InstructionDesc[OpMemberDecorateStringGOOGLE].operands.push(OperandId, "'Structure Type'");
+ InstructionDesc[OpMemberDecorateStringGOOGLE].operands.push(OperandLiteralNumber, "'Member'");
+ InstructionDesc[OpMemberDecorateStringGOOGLE].operands.push(OperandDecoration, "");
+ InstructionDesc[OpMemberDecorateStringGOOGLE].operands.push(OperandLiteralString, "'Literal String'");
+
+ InstructionDesc[OpGroupDecorate].operands.push(OperandId, "'Decoration Group'");
+ InstructionDesc[OpGroupDecorate].operands.push(OperandVariableIds, "'Targets'");
+
+ InstructionDesc[OpGroupMemberDecorate].operands.push(OperandId, "'Decoration Group'");
+ InstructionDesc[OpGroupMemberDecorate].operands.push(OperandVariableIdLiteral, "'Targets'");
+
+ InstructionDesc[OpVectorExtractDynamic].operands.push(OperandId, "'Vector'");
+ InstructionDesc[OpVectorExtractDynamic].operands.push(OperandId, "'Index'");
+
+ InstructionDesc[OpVectorInsertDynamic].operands.push(OperandId, "'Vector'");
+ InstructionDesc[OpVectorInsertDynamic].operands.push(OperandId, "'Component'");
+ InstructionDesc[OpVectorInsertDynamic].operands.push(OperandId, "'Index'");
+
+ InstructionDesc[OpVectorShuffle].operands.push(OperandId, "'Vector 1'");
+ InstructionDesc[OpVectorShuffle].operands.push(OperandId, "'Vector 2'");
+ InstructionDesc[OpVectorShuffle].operands.push(OperandVariableLiterals, "'Components'");
+
+ InstructionDesc[OpCompositeConstruct].operands.push(OperandVariableIds, "'Constituents'");
+
+ InstructionDesc[OpCompositeExtract].operands.push(OperandId, "'Composite'");
+ InstructionDesc[OpCompositeExtract].operands.push(OperandVariableLiterals, "'Indexes'");
+
+ InstructionDesc[OpCompositeInsert].operands.push(OperandId, "'Object'");
+ InstructionDesc[OpCompositeInsert].operands.push(OperandId, "'Composite'");
+ InstructionDesc[OpCompositeInsert].operands.push(OperandVariableLiterals, "'Indexes'");
+
+ InstructionDesc[OpCopyObject].operands.push(OperandId, "'Operand'");
+
+ InstructionDesc[OpCopyMemory].operands.push(OperandId, "'Target'");
+ InstructionDesc[OpCopyMemory].operands.push(OperandId, "'Source'");
+ InstructionDesc[OpCopyMemory].operands.push(OperandMemoryAccess, "", true);
+
+ InstructionDesc[OpCopyMemorySized].operands.push(OperandId, "'Target'");
+ InstructionDesc[OpCopyMemorySized].operands.push(OperandId, "'Source'");
+ InstructionDesc[OpCopyMemorySized].operands.push(OperandId, "'Size'");
+ InstructionDesc[OpCopyMemorySized].operands.push(OperandMemoryAccess, "", true);
+
+ InstructionDesc[OpSampledImage].operands.push(OperandId, "'Image'");
+ InstructionDesc[OpSampledImage].operands.push(OperandId, "'Sampler'");
+
+ InstructionDesc[OpImage].operands.push(OperandId, "'Sampled Image'");
+
+ InstructionDesc[OpImageRead].operands.push(OperandId, "'Image'");
+ InstructionDesc[OpImageRead].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageRead].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageRead].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageWrite].operands.push(OperandId, "'Image'");
+ InstructionDesc[OpImageWrite].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageWrite].operands.push(OperandId, "'Texel'");
+ InstructionDesc[OpImageWrite].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageWrite].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageSampleImplicitLod].operands.push(OperandId, "'Sampled Image'");
+ InstructionDesc[OpImageSampleImplicitLod].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageSampleImplicitLod].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageSampleImplicitLod].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageSampleExplicitLod].operands.push(OperandId, "'Sampled Image'");
+ InstructionDesc[OpImageSampleExplicitLod].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageSampleExplicitLod].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageSampleExplicitLod].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageSampleDrefImplicitLod].operands.push(OperandId, "'Sampled Image'");
+ InstructionDesc[OpImageSampleDrefImplicitLod].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageSampleDrefImplicitLod].operands.push(OperandId, "'D~ref~'");
+ InstructionDesc[OpImageSampleDrefImplicitLod].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageSampleDrefImplicitLod].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageSampleDrefExplicitLod].operands.push(OperandId, "'Sampled Image'");
+ InstructionDesc[OpImageSampleDrefExplicitLod].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageSampleDrefExplicitLod].operands.push(OperandId, "'D~ref~'");
+ InstructionDesc[OpImageSampleDrefExplicitLod].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageSampleDrefExplicitLod].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageSampleProjImplicitLod].operands.push(OperandId, "'Sampled Image'");
+ InstructionDesc[OpImageSampleProjImplicitLod].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageSampleProjImplicitLod].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageSampleProjImplicitLod].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageSampleProjExplicitLod].operands.push(OperandId, "'Sampled Image'");
+ InstructionDesc[OpImageSampleProjExplicitLod].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageSampleProjExplicitLod].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageSampleProjExplicitLod].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageSampleProjDrefImplicitLod].operands.push(OperandId, "'Sampled Image'");
+ InstructionDesc[OpImageSampleProjDrefImplicitLod].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageSampleProjDrefImplicitLod].operands.push(OperandId, "'D~ref~'");
+ InstructionDesc[OpImageSampleProjDrefImplicitLod].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageSampleProjDrefImplicitLod].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageSampleProjDrefExplicitLod].operands.push(OperandId, "'Sampled Image'");
+ InstructionDesc[OpImageSampleProjDrefExplicitLod].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageSampleProjDrefExplicitLod].operands.push(OperandId, "'D~ref~'");
+ InstructionDesc[OpImageSampleProjDrefExplicitLod].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageSampleProjDrefExplicitLod].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageFetch].operands.push(OperandId, "'Image'");
+ InstructionDesc[OpImageFetch].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageFetch].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageFetch].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageGather].operands.push(OperandId, "'Sampled Image'");
+ InstructionDesc[OpImageGather].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageGather].operands.push(OperandId, "'Component'");
+ InstructionDesc[OpImageGather].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageGather].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageDrefGather].operands.push(OperandId, "'Sampled Image'");
+ InstructionDesc[OpImageDrefGather].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageDrefGather].operands.push(OperandId, "'D~ref~'");
+ InstructionDesc[OpImageDrefGather].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageDrefGather].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageSparseSampleImplicitLod].operands.push(OperandId, "'Sampled Image'");
+ InstructionDesc[OpImageSparseSampleImplicitLod].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageSparseSampleImplicitLod].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageSparseSampleImplicitLod].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageSparseSampleExplicitLod].operands.push(OperandId, "'Sampled Image'");
+ InstructionDesc[OpImageSparseSampleExplicitLod].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageSparseSampleExplicitLod].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageSparseSampleExplicitLod].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageSparseSampleDrefImplicitLod].operands.push(OperandId, "'Sampled Image'");
+ InstructionDesc[OpImageSparseSampleDrefImplicitLod].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageSparseSampleDrefImplicitLod].operands.push(OperandId, "'D~ref~'");
+ InstructionDesc[OpImageSparseSampleDrefImplicitLod].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageSparseSampleDrefImplicitLod].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageSparseSampleDrefExplicitLod].operands.push(OperandId, "'Sampled Image'");
+ InstructionDesc[OpImageSparseSampleDrefExplicitLod].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageSparseSampleDrefExplicitLod].operands.push(OperandId, "'D~ref~'");
+ InstructionDesc[OpImageSparseSampleDrefExplicitLod].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageSparseSampleDrefExplicitLod].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageSparseSampleProjImplicitLod].operands.push(OperandId, "'Sampled Image'");
+ InstructionDesc[OpImageSparseSampleProjImplicitLod].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageSparseSampleProjImplicitLod].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageSparseSampleProjImplicitLod].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageSparseSampleProjExplicitLod].operands.push(OperandId, "'Sampled Image'");
+ InstructionDesc[OpImageSparseSampleProjExplicitLod].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageSparseSampleProjExplicitLod].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageSparseSampleProjExplicitLod].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageSparseSampleProjDrefImplicitLod].operands.push(OperandId, "'Sampled Image'");
+ InstructionDesc[OpImageSparseSampleProjDrefImplicitLod].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageSparseSampleProjDrefImplicitLod].operands.push(OperandId, "'D~ref~'");
+ InstructionDesc[OpImageSparseSampleProjDrefImplicitLod].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageSparseSampleProjDrefImplicitLod].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageSparseSampleProjDrefExplicitLod].operands.push(OperandId, "'Sampled Image'");
+ InstructionDesc[OpImageSparseSampleProjDrefExplicitLod].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageSparseSampleProjDrefExplicitLod].operands.push(OperandId, "'D~ref~'");
+ InstructionDesc[OpImageSparseSampleProjDrefExplicitLod].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageSparseSampleProjDrefExplicitLod].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageSparseFetch].operands.push(OperandId, "'Image'");
+ InstructionDesc[OpImageSparseFetch].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageSparseFetch].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageSparseFetch].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageSparseGather].operands.push(OperandId, "'Sampled Image'");
+ InstructionDesc[OpImageSparseGather].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageSparseGather].operands.push(OperandId, "'Component'");
+ InstructionDesc[OpImageSparseGather].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageSparseGather].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageSparseDrefGather].operands.push(OperandId, "'Sampled Image'");
+ InstructionDesc[OpImageSparseDrefGather].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageSparseDrefGather].operands.push(OperandId, "'D~ref~'");
+ InstructionDesc[OpImageSparseDrefGather].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageSparseDrefGather].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageSparseRead].operands.push(OperandId, "'Image'");
+ InstructionDesc[OpImageSparseRead].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageSparseRead].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageSparseRead].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpImageSparseTexelsResident].operands.push(OperandId, "'Resident Code'");
+
+ InstructionDesc[OpImageQuerySizeLod].operands.push(OperandId, "'Image'");
+ InstructionDesc[OpImageQuerySizeLod].operands.push(OperandId, "'Level of Detail'");
+
+ InstructionDesc[OpImageQuerySize].operands.push(OperandId, "'Image'");
+
+ InstructionDesc[OpImageQueryLod].operands.push(OperandId, "'Image'");
+ InstructionDesc[OpImageQueryLod].operands.push(OperandId, "'Coordinate'");
+
+ InstructionDesc[OpImageQueryLevels].operands.push(OperandId, "'Image'");
+
+ InstructionDesc[OpImageQuerySamples].operands.push(OperandId, "'Image'");
+
+ InstructionDesc[OpImageQueryFormat].operands.push(OperandId, "'Image'");
+
+ InstructionDesc[OpImageQueryOrder].operands.push(OperandId, "'Image'");
+
+ InstructionDesc[OpAccessChain].operands.push(OperandId, "'Base'");
+ InstructionDesc[OpAccessChain].operands.push(OperandVariableIds, "'Indexes'");
+
+ InstructionDesc[OpInBoundsAccessChain].operands.push(OperandId, "'Base'");
+ InstructionDesc[OpInBoundsAccessChain].operands.push(OperandVariableIds, "'Indexes'");
+
+ InstructionDesc[OpPtrAccessChain].operands.push(OperandId, "'Base'");
+ InstructionDesc[OpPtrAccessChain].operands.push(OperandId, "'Element'");
+ InstructionDesc[OpPtrAccessChain].operands.push(OperandVariableIds, "'Indexes'");
+
+ InstructionDesc[OpInBoundsPtrAccessChain].operands.push(OperandId, "'Base'");
+ InstructionDesc[OpInBoundsPtrAccessChain].operands.push(OperandId, "'Element'");
+ InstructionDesc[OpInBoundsPtrAccessChain].operands.push(OperandVariableIds, "'Indexes'");
+
+ InstructionDesc[OpSNegate].operands.push(OperandId, "'Operand'");
+
+ InstructionDesc[OpFNegate].operands.push(OperandId, "'Operand'");
+
+ InstructionDesc[OpNot].operands.push(OperandId, "'Operand'");
+
+ InstructionDesc[OpAny].operands.push(OperandId, "'Vector'");
+
+ InstructionDesc[OpAll].operands.push(OperandId, "'Vector'");
+
+ InstructionDesc[OpConvertFToU].operands.push(OperandId, "'Float Value'");
+
+ InstructionDesc[OpConvertFToS].operands.push(OperandId, "'Float Value'");
+
+ InstructionDesc[OpConvertSToF].operands.push(OperandId, "'Signed Value'");
+
+ InstructionDesc[OpConvertUToF].operands.push(OperandId, "'Unsigned Value'");
+
+ InstructionDesc[OpUConvert].operands.push(OperandId, "'Unsigned Value'");
+
+ InstructionDesc[OpSConvert].operands.push(OperandId, "'Signed Value'");
+
+ InstructionDesc[OpFConvert].operands.push(OperandId, "'Float Value'");
+
+ InstructionDesc[OpSatConvertSToU].operands.push(OperandId, "'Signed Value'");
+
+ InstructionDesc[OpSatConvertUToS].operands.push(OperandId, "'Unsigned Value'");
+
+ InstructionDesc[OpConvertPtrToU].operands.push(OperandId, "'Pointer'");
+
+ InstructionDesc[OpConvertUToPtr].operands.push(OperandId, "'Integer Value'");
+
+ InstructionDesc[OpPtrCastToGeneric].operands.push(OperandId, "'Pointer'");
+
+ InstructionDesc[OpGenericCastToPtr].operands.push(OperandId, "'Pointer'");
+
+ InstructionDesc[OpGenericCastToPtrExplicit].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpGenericCastToPtrExplicit].operands.push(OperandStorage, "'Storage'");
+
+ InstructionDesc[OpGenericPtrMemSemantics].operands.push(OperandId, "'Pointer'");
+
+ InstructionDesc[OpBitcast].operands.push(OperandId, "'Operand'");
+
+ InstructionDesc[OpQuantizeToF16].operands.push(OperandId, "'Value'");
+
+ InstructionDesc[OpTranspose].operands.push(OperandId, "'Matrix'");
+
+ InstructionDesc[OpIsNan].operands.push(OperandId, "'x'");
+
+ InstructionDesc[OpIsInf].operands.push(OperandId, "'x'");
+
+ InstructionDesc[OpIsFinite].operands.push(OperandId, "'x'");
+
+ InstructionDesc[OpIsNormal].operands.push(OperandId, "'x'");
+
+ InstructionDesc[OpSignBitSet].operands.push(OperandId, "'x'");
+
+ InstructionDesc[OpLessOrGreater].operands.push(OperandId, "'x'");
+ InstructionDesc[OpLessOrGreater].operands.push(OperandId, "'y'");
+
+ InstructionDesc[OpOrdered].operands.push(OperandId, "'x'");
+ InstructionDesc[OpOrdered].operands.push(OperandId, "'y'");
+
+ InstructionDesc[OpUnordered].operands.push(OperandId, "'x'");
+ InstructionDesc[OpUnordered].operands.push(OperandId, "'y'");
+
+ InstructionDesc[OpArrayLength].operands.push(OperandId, "'Structure'");
+ InstructionDesc[OpArrayLength].operands.push(OperandLiteralNumber, "'Array member'");
+
+ InstructionDesc[OpIAdd].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpIAdd].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpFAdd].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpFAdd].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpISub].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpISub].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpFSub].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpFSub].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpIMul].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpIMul].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpFMul].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpFMul].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpUDiv].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpUDiv].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpSDiv].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpSDiv].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpFDiv].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpFDiv].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpUMod].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpUMod].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpSRem].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpSRem].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpSMod].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpSMod].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpFRem].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpFRem].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpFMod].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpFMod].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpVectorTimesScalar].operands.push(OperandId, "'Vector'");
+ InstructionDesc[OpVectorTimesScalar].operands.push(OperandId, "'Scalar'");
+
+ InstructionDesc[OpMatrixTimesScalar].operands.push(OperandId, "'Matrix'");
+ InstructionDesc[OpMatrixTimesScalar].operands.push(OperandId, "'Scalar'");
+
+ InstructionDesc[OpVectorTimesMatrix].operands.push(OperandId, "'Vector'");
+ InstructionDesc[OpVectorTimesMatrix].operands.push(OperandId, "'Matrix'");
+
+ InstructionDesc[OpMatrixTimesVector].operands.push(OperandId, "'Matrix'");
+ InstructionDesc[OpMatrixTimesVector].operands.push(OperandId, "'Vector'");
+
+ InstructionDesc[OpMatrixTimesMatrix].operands.push(OperandId, "'LeftMatrix'");
+ InstructionDesc[OpMatrixTimesMatrix].operands.push(OperandId, "'RightMatrix'");
+
+ InstructionDesc[OpOuterProduct].operands.push(OperandId, "'Vector 1'");
+ InstructionDesc[OpOuterProduct].operands.push(OperandId, "'Vector 2'");
+
+ InstructionDesc[OpDot].operands.push(OperandId, "'Vector 1'");
+ InstructionDesc[OpDot].operands.push(OperandId, "'Vector 2'");
+
+ InstructionDesc[OpIAddCarry].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpIAddCarry].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpISubBorrow].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpISubBorrow].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpUMulExtended].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpUMulExtended].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpSMulExtended].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpSMulExtended].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpShiftRightLogical].operands.push(OperandId, "'Base'");
+ InstructionDesc[OpShiftRightLogical].operands.push(OperandId, "'Shift'");
+
+ InstructionDesc[OpShiftRightArithmetic].operands.push(OperandId, "'Base'");
+ InstructionDesc[OpShiftRightArithmetic].operands.push(OperandId, "'Shift'");
+
+ InstructionDesc[OpShiftLeftLogical].operands.push(OperandId, "'Base'");
+ InstructionDesc[OpShiftLeftLogical].operands.push(OperandId, "'Shift'");
+
+ InstructionDesc[OpLogicalOr].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpLogicalOr].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpLogicalAnd].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpLogicalAnd].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpLogicalEqual].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpLogicalEqual].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpLogicalNotEqual].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpLogicalNotEqual].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpLogicalNot].operands.push(OperandId, "'Operand'");
+
+ InstructionDesc[OpBitwiseOr].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpBitwiseOr].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpBitwiseXor].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpBitwiseXor].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpBitwiseAnd].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpBitwiseAnd].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpBitFieldInsert].operands.push(OperandId, "'Base'");
+ InstructionDesc[OpBitFieldInsert].operands.push(OperandId, "'Insert'");
+ InstructionDesc[OpBitFieldInsert].operands.push(OperandId, "'Offset'");
+ InstructionDesc[OpBitFieldInsert].operands.push(OperandId, "'Count'");
+
+ InstructionDesc[OpBitFieldSExtract].operands.push(OperandId, "'Base'");
+ InstructionDesc[OpBitFieldSExtract].operands.push(OperandId, "'Offset'");
+ InstructionDesc[OpBitFieldSExtract].operands.push(OperandId, "'Count'");
+
+ InstructionDesc[OpBitFieldUExtract].operands.push(OperandId, "'Base'");
+ InstructionDesc[OpBitFieldUExtract].operands.push(OperandId, "'Offset'");
+ InstructionDesc[OpBitFieldUExtract].operands.push(OperandId, "'Count'");
+
+ InstructionDesc[OpBitReverse].operands.push(OperandId, "'Base'");
+
+ InstructionDesc[OpBitCount].operands.push(OperandId, "'Base'");
+
+ InstructionDesc[OpSelect].operands.push(OperandId, "'Condition'");
+ InstructionDesc[OpSelect].operands.push(OperandId, "'Object 1'");
+ InstructionDesc[OpSelect].operands.push(OperandId, "'Object 2'");
+
+ InstructionDesc[OpIEqual].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpIEqual].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpFOrdEqual].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpFOrdEqual].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpFUnordEqual].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpFUnordEqual].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpINotEqual].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpINotEqual].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpFOrdNotEqual].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpFOrdNotEqual].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpFUnordNotEqual].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpFUnordNotEqual].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpULessThan].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpULessThan].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpSLessThan].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpSLessThan].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpFOrdLessThan].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpFOrdLessThan].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpFUnordLessThan].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpFUnordLessThan].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpUGreaterThan].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpUGreaterThan].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpSGreaterThan].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpSGreaterThan].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpFOrdGreaterThan].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpFOrdGreaterThan].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpFUnordGreaterThan].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpFUnordGreaterThan].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpULessThanEqual].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpULessThanEqual].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpSLessThanEqual].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpSLessThanEqual].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpFOrdLessThanEqual].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpFOrdLessThanEqual].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpFUnordLessThanEqual].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpFUnordLessThanEqual].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpUGreaterThanEqual].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpUGreaterThanEqual].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpSGreaterThanEqual].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpSGreaterThanEqual].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpFOrdGreaterThanEqual].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpFOrdGreaterThanEqual].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpFUnordGreaterThanEqual].operands.push(OperandId, "'Operand 1'");
+ InstructionDesc[OpFUnordGreaterThanEqual].operands.push(OperandId, "'Operand 2'");
+
+ InstructionDesc[OpDPdx].operands.push(OperandId, "'P'");
+
+ InstructionDesc[OpDPdy].operands.push(OperandId, "'P'");
+
+ InstructionDesc[OpFwidth].operands.push(OperandId, "'P'");
+
+ InstructionDesc[OpDPdxFine].operands.push(OperandId, "'P'");
+
+ InstructionDesc[OpDPdyFine].operands.push(OperandId, "'P'");
+
+ InstructionDesc[OpFwidthFine].operands.push(OperandId, "'P'");
+
+ InstructionDesc[OpDPdxCoarse].operands.push(OperandId, "'P'");
+
+ InstructionDesc[OpDPdyCoarse].operands.push(OperandId, "'P'");
+
+ InstructionDesc[OpFwidthCoarse].operands.push(OperandId, "'P'");
+
+ InstructionDesc[OpEmitStreamVertex].operands.push(OperandId, "'Stream'");
+
+ InstructionDesc[OpEndStreamPrimitive].operands.push(OperandId, "'Stream'");
+
+ InstructionDesc[OpControlBarrier].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpControlBarrier].operands.push(OperandScope, "'Memory'");
+ InstructionDesc[OpControlBarrier].operands.push(OperandMemorySemantics, "'Semantics'");
+
+ InstructionDesc[OpMemoryBarrier].operands.push(OperandScope, "'Memory'");
+ InstructionDesc[OpMemoryBarrier].operands.push(OperandMemorySemantics, "'Semantics'");
+
+ InstructionDesc[OpImageTexelPointer].operands.push(OperandId, "'Image'");
+ InstructionDesc[OpImageTexelPointer].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageTexelPointer].operands.push(OperandId, "'Sample'");
+
+ InstructionDesc[OpAtomicLoad].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpAtomicLoad].operands.push(OperandScope, "'Scope'");
+ InstructionDesc[OpAtomicLoad].operands.push(OperandMemorySemantics, "'Semantics'");
+
+ InstructionDesc[OpAtomicStore].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpAtomicStore].operands.push(OperandScope, "'Scope'");
+ InstructionDesc[OpAtomicStore].operands.push(OperandMemorySemantics, "'Semantics'");
+ InstructionDesc[OpAtomicStore].operands.push(OperandId, "'Value'");
+
+ InstructionDesc[OpAtomicExchange].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpAtomicExchange].operands.push(OperandScope, "'Scope'");
+ InstructionDesc[OpAtomicExchange].operands.push(OperandMemorySemantics, "'Semantics'");
+ InstructionDesc[OpAtomicExchange].operands.push(OperandId, "'Value'");
+
+ InstructionDesc[OpAtomicCompareExchange].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpAtomicCompareExchange].operands.push(OperandScope, "'Scope'");
+ InstructionDesc[OpAtomicCompareExchange].operands.push(OperandMemorySemantics, "'Equal'");
+ InstructionDesc[OpAtomicCompareExchange].operands.push(OperandMemorySemantics, "'Unequal'");
+ InstructionDesc[OpAtomicCompareExchange].operands.push(OperandId, "'Value'");
+ InstructionDesc[OpAtomicCompareExchange].operands.push(OperandId, "'Comparator'");
+
+ InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandScope, "'Scope'");
+ InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandMemorySemantics, "'Equal'");
+ InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandMemorySemantics, "'Unequal'");
+ InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandId, "'Value'");
+ InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandId, "'Comparator'");
+
+ InstructionDesc[OpAtomicIIncrement].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpAtomicIIncrement].operands.push(OperandScope, "'Scope'");
+ InstructionDesc[OpAtomicIIncrement].operands.push(OperandMemorySemantics, "'Semantics'");
+
+ InstructionDesc[OpAtomicIDecrement].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpAtomicIDecrement].operands.push(OperandScope, "'Scope'");
+ InstructionDesc[OpAtomicIDecrement].operands.push(OperandMemorySemantics, "'Semantics'");
+
+ InstructionDesc[OpAtomicIAdd].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpAtomicIAdd].operands.push(OperandScope, "'Scope'");
+ InstructionDesc[OpAtomicIAdd].operands.push(OperandMemorySemantics, "'Semantics'");
+ InstructionDesc[OpAtomicIAdd].operands.push(OperandId, "'Value'");
+
+ InstructionDesc[OpAtomicISub].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpAtomicISub].operands.push(OperandScope, "'Scope'");
+ InstructionDesc[OpAtomicISub].operands.push(OperandMemorySemantics, "'Semantics'");
+ InstructionDesc[OpAtomicISub].operands.push(OperandId, "'Value'");
+
+ InstructionDesc[OpAtomicUMin].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpAtomicUMin].operands.push(OperandScope, "'Scope'");
+ InstructionDesc[OpAtomicUMin].operands.push(OperandMemorySemantics, "'Semantics'");
+ InstructionDesc[OpAtomicUMin].operands.push(OperandId, "'Value'");
+
+ InstructionDesc[OpAtomicUMax].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpAtomicUMax].operands.push(OperandScope, "'Scope'");
+ InstructionDesc[OpAtomicUMax].operands.push(OperandMemorySemantics, "'Semantics'");
+ InstructionDesc[OpAtomicUMax].operands.push(OperandId, "'Value'");
+
+ InstructionDesc[OpAtomicSMin].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpAtomicSMin].operands.push(OperandScope, "'Scope'");
+ InstructionDesc[OpAtomicSMin].operands.push(OperandMemorySemantics, "'Semantics'");
+ InstructionDesc[OpAtomicSMin].operands.push(OperandId, "'Value'");
+
+ InstructionDesc[OpAtomicSMax].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpAtomicSMax].operands.push(OperandScope, "'Scope'");
+ InstructionDesc[OpAtomicSMax].operands.push(OperandMemorySemantics, "'Semantics'");
+ InstructionDesc[OpAtomicSMax].operands.push(OperandId, "'Value'");
+
+ InstructionDesc[OpAtomicAnd].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpAtomicAnd].operands.push(OperandScope, "'Scope'");
+ InstructionDesc[OpAtomicAnd].operands.push(OperandMemorySemantics, "'Semantics'");
+ InstructionDesc[OpAtomicAnd].operands.push(OperandId, "'Value'");
+
+ InstructionDesc[OpAtomicOr].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpAtomicOr].operands.push(OperandScope, "'Scope'");
+ InstructionDesc[OpAtomicOr].operands.push(OperandMemorySemantics, "'Semantics'");
+ InstructionDesc[OpAtomicOr].operands.push(OperandId, "'Value'");
+
+ InstructionDesc[OpAtomicXor].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpAtomicXor].operands.push(OperandScope, "'Scope'");
+ InstructionDesc[OpAtomicXor].operands.push(OperandMemorySemantics, "'Semantics'");
+ InstructionDesc[OpAtomicXor].operands.push(OperandId, "'Value'");
+
+ InstructionDesc[OpAtomicFlagTestAndSet].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpAtomicFlagTestAndSet].operands.push(OperandScope, "'Scope'");
+ InstructionDesc[OpAtomicFlagTestAndSet].operands.push(OperandMemorySemantics, "'Semantics'");
+
+ InstructionDesc[OpAtomicFlagClear].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpAtomicFlagClear].operands.push(OperandScope, "'Scope'");
+ InstructionDesc[OpAtomicFlagClear].operands.push(OperandMemorySemantics, "'Semantics'");
+
+ InstructionDesc[OpLoopMerge].operands.push(OperandId, "'Merge Block'");
+ InstructionDesc[OpLoopMerge].operands.push(OperandId, "'Continue Target'");
+ InstructionDesc[OpLoopMerge].operands.push(OperandLoop, "");
+ InstructionDesc[OpLoopMerge].operands.push(OperandOptionalLiteral, "");
+
+ InstructionDesc[OpSelectionMerge].operands.push(OperandId, "'Merge Block'");
+ InstructionDesc[OpSelectionMerge].operands.push(OperandSelect, "");
+
+ InstructionDesc[OpBranch].operands.push(OperandId, "'Target Label'");
+
+ InstructionDesc[OpBranchConditional].operands.push(OperandId, "'Condition'");
+ InstructionDesc[OpBranchConditional].operands.push(OperandId, "'True Label'");
+ InstructionDesc[OpBranchConditional].operands.push(OperandId, "'False Label'");
+ InstructionDesc[OpBranchConditional].operands.push(OperandVariableLiterals, "'Branch weights'");
+
+ InstructionDesc[OpSwitch].operands.push(OperandId, "'Selector'");
+ InstructionDesc[OpSwitch].operands.push(OperandId, "'Default'");
+ InstructionDesc[OpSwitch].operands.push(OperandVariableLiteralId, "'Target'");
+
+
+ InstructionDesc[OpReturnValue].operands.push(OperandId, "'Value'");
+
+ InstructionDesc[OpLifetimeStart].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpLifetimeStart].operands.push(OperandLiteralNumber, "'Size'");
+
+ InstructionDesc[OpLifetimeStop].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpLifetimeStop].operands.push(OperandLiteralNumber, "'Size'");
+
+ InstructionDesc[OpGroupAsyncCopy].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupAsyncCopy].operands.push(OperandId, "'Destination'");
+ InstructionDesc[OpGroupAsyncCopy].operands.push(OperandId, "'Source'");
+ InstructionDesc[OpGroupAsyncCopy].operands.push(OperandId, "'Num Elements'");
+ InstructionDesc[OpGroupAsyncCopy].operands.push(OperandId, "'Stride'");
+ InstructionDesc[OpGroupAsyncCopy].operands.push(OperandId, "'Event'");
+
+ InstructionDesc[OpGroupWaitEvents].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupWaitEvents].operands.push(OperandId, "'Num Events'");
+ InstructionDesc[OpGroupWaitEvents].operands.push(OperandId, "'Events List'");
+
+ InstructionDesc[OpGroupAll].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupAll].operands.push(OperandId, "'Predicate'");
+
+ InstructionDesc[OpGroupAny].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupAny].operands.push(OperandId, "'Predicate'");
+
+ InstructionDesc[OpGroupBroadcast].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupBroadcast].operands.push(OperandId, "'Value'");
+ InstructionDesc[OpGroupBroadcast].operands.push(OperandId, "'LocalId'");
+
+ InstructionDesc[OpGroupIAdd].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupIAdd].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupIAdd].operands.push(OperandId, "'X'");
+
+ InstructionDesc[OpGroupFAdd].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupFAdd].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupFAdd].operands.push(OperandId, "'X'");
+
+ InstructionDesc[OpGroupUMin].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupUMin].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupUMin].operands.push(OperandId, "'X'");
+
+ InstructionDesc[OpGroupSMin].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupSMin].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupSMin].operands.push(OperandId, "X");
+
+ InstructionDesc[OpGroupFMin].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupFMin].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupFMin].operands.push(OperandId, "X");
+
+ InstructionDesc[OpGroupUMax].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupUMax].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupUMax].operands.push(OperandId, "X");
+
+ InstructionDesc[OpGroupSMax].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupSMax].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupSMax].operands.push(OperandId, "X");
+
+ InstructionDesc[OpGroupFMax].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupFMax].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupFMax].operands.push(OperandId, "X");
+
+ InstructionDesc[OpReadPipe].operands.push(OperandId, "'Pipe'");
+ InstructionDesc[OpReadPipe].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpReadPipe].operands.push(OperandId, "'Packet Size'");
+ InstructionDesc[OpReadPipe].operands.push(OperandId, "'Packet Alignment'");
+
+ InstructionDesc[OpWritePipe].operands.push(OperandId, "'Pipe'");
+ InstructionDesc[OpWritePipe].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpWritePipe].operands.push(OperandId, "'Packet Size'");
+ InstructionDesc[OpWritePipe].operands.push(OperandId, "'Packet Alignment'");
+
+ InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Pipe'");
+ InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Reserve Id'");
+ InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Index'");
+ InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Packet Size'");
+ InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Packet Alignment'");
+
+ InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Pipe'");
+ InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Reserve Id'");
+ InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Index'");
+ InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Packet Size'");
+ InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Packet Alignment'");
+
+ InstructionDesc[OpReserveReadPipePackets].operands.push(OperandId, "'Pipe'");
+ InstructionDesc[OpReserveReadPipePackets].operands.push(OperandId, "'Num Packets'");
+ InstructionDesc[OpReserveReadPipePackets].operands.push(OperandId, "'Packet Size'");
+ InstructionDesc[OpReserveReadPipePackets].operands.push(OperandId, "'Packet Alignment'");
+
+ InstructionDesc[OpReserveWritePipePackets].operands.push(OperandId, "'Pipe'");
+ InstructionDesc[OpReserveWritePipePackets].operands.push(OperandId, "'Num Packets'");
+ InstructionDesc[OpReserveWritePipePackets].operands.push(OperandId, "'Packet Size'");
+ InstructionDesc[OpReserveWritePipePackets].operands.push(OperandId, "'Packet Alignment'");
+
+ InstructionDesc[OpCommitReadPipe].operands.push(OperandId, "'Pipe'");
+ InstructionDesc[OpCommitReadPipe].operands.push(OperandId, "'Reserve Id'");
+ InstructionDesc[OpCommitReadPipe].operands.push(OperandId, "'Packet Size'");
+ InstructionDesc[OpCommitReadPipe].operands.push(OperandId, "'Packet Alignment'");
+
+ InstructionDesc[OpCommitWritePipe].operands.push(OperandId, "'Pipe'");
+ InstructionDesc[OpCommitWritePipe].operands.push(OperandId, "'Reserve Id'");
+ InstructionDesc[OpCommitWritePipe].operands.push(OperandId, "'Packet Size'");
+ InstructionDesc[OpCommitWritePipe].operands.push(OperandId, "'Packet Alignment'");
+
+ InstructionDesc[OpIsValidReserveId].operands.push(OperandId, "'Reserve Id'");
+
+ InstructionDesc[OpGetNumPipePackets].operands.push(OperandId, "'Pipe'");
+ InstructionDesc[OpGetNumPipePackets].operands.push(OperandId, "'Packet Size'");
+ InstructionDesc[OpGetNumPipePackets].operands.push(OperandId, "'Packet Alignment'");
+
+ InstructionDesc[OpGetMaxPipePackets].operands.push(OperandId, "'Pipe'");
+ InstructionDesc[OpGetMaxPipePackets].operands.push(OperandId, "'Packet Size'");
+ InstructionDesc[OpGetMaxPipePackets].operands.push(OperandId, "'Packet Alignment'");
+
+ InstructionDesc[OpGroupReserveReadPipePackets].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupReserveReadPipePackets].operands.push(OperandId, "'Pipe'");
+ InstructionDesc[OpGroupReserveReadPipePackets].operands.push(OperandId, "'Num Packets'");
+ InstructionDesc[OpGroupReserveReadPipePackets].operands.push(OperandId, "'Packet Size'");
+ InstructionDesc[OpGroupReserveReadPipePackets].operands.push(OperandId, "'Packet Alignment'");
+
+ InstructionDesc[OpGroupReserveWritePipePackets].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupReserveWritePipePackets].operands.push(OperandId, "'Pipe'");
+ InstructionDesc[OpGroupReserveWritePipePackets].operands.push(OperandId, "'Num Packets'");
+ InstructionDesc[OpGroupReserveWritePipePackets].operands.push(OperandId, "'Packet Size'");
+ InstructionDesc[OpGroupReserveWritePipePackets].operands.push(OperandId, "'Packet Alignment'");
+
+ InstructionDesc[OpGroupCommitReadPipe].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupCommitReadPipe].operands.push(OperandId, "'Pipe'");
+ InstructionDesc[OpGroupCommitReadPipe].operands.push(OperandId, "'Reserve Id'");
+ InstructionDesc[OpGroupCommitReadPipe].operands.push(OperandId, "'Packet Size'");
+ InstructionDesc[OpGroupCommitReadPipe].operands.push(OperandId, "'Packet Alignment'");
+
+ InstructionDesc[OpGroupCommitWritePipe].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupCommitWritePipe].operands.push(OperandId, "'Pipe'");
+ InstructionDesc[OpGroupCommitWritePipe].operands.push(OperandId, "'Reserve Id'");
+ InstructionDesc[OpGroupCommitWritePipe].operands.push(OperandId, "'Packet Size'");
+ InstructionDesc[OpGroupCommitWritePipe].operands.push(OperandId, "'Packet Alignment'");
+
+ InstructionDesc[OpBuildNDRange].operands.push(OperandId, "'GlobalWorkSize'");
+ InstructionDesc[OpBuildNDRange].operands.push(OperandId, "'LocalWorkSize'");
+ InstructionDesc[OpBuildNDRange].operands.push(OperandId, "'GlobalWorkOffset'");
+
+ InstructionDesc[OpCaptureEventProfilingInfo].operands.push(OperandId, "'Event'");
+ InstructionDesc[OpCaptureEventProfilingInfo].operands.push(OperandId, "'Profiling Info'");
+ InstructionDesc[OpCaptureEventProfilingInfo].operands.push(OperandId, "'Value'");
+
+ InstructionDesc[OpSetUserEventStatus].operands.push(OperandId, "'Event'");
+ InstructionDesc[OpSetUserEventStatus].operands.push(OperandId, "'Status'");
+
+ InstructionDesc[OpIsValidEvent].operands.push(OperandId, "'Event'");
+
+ InstructionDesc[OpRetainEvent].operands.push(OperandId, "'Event'");
+
+ InstructionDesc[OpReleaseEvent].operands.push(OperandId, "'Event'");
+
+ InstructionDesc[OpGetKernelWorkGroupSize].operands.push(OperandId, "'Invoke'");
+ InstructionDesc[OpGetKernelWorkGroupSize].operands.push(OperandId, "'Param'");
+ InstructionDesc[OpGetKernelWorkGroupSize].operands.push(OperandId, "'Param Size'");
+ InstructionDesc[OpGetKernelWorkGroupSize].operands.push(OperandId, "'Param Align'");
+
+ InstructionDesc[OpGetKernelPreferredWorkGroupSizeMultiple].operands.push(OperandId, "'Invoke'");
+ InstructionDesc[OpGetKernelPreferredWorkGroupSizeMultiple].operands.push(OperandId, "'Param'");
+ InstructionDesc[OpGetKernelPreferredWorkGroupSizeMultiple].operands.push(OperandId, "'Param Size'");
+ InstructionDesc[OpGetKernelPreferredWorkGroupSizeMultiple].operands.push(OperandId, "'Param Align'");
+
+ InstructionDesc[OpGetKernelNDrangeSubGroupCount].operands.push(OperandId, "'ND Range'");
+ InstructionDesc[OpGetKernelNDrangeSubGroupCount].operands.push(OperandId, "'Invoke'");
+ InstructionDesc[OpGetKernelNDrangeSubGroupCount].operands.push(OperandId, "'Param'");
+ InstructionDesc[OpGetKernelNDrangeSubGroupCount].operands.push(OperandId, "'Param Size'");
+ InstructionDesc[OpGetKernelNDrangeSubGroupCount].operands.push(OperandId, "'Param Align'");
+
+ InstructionDesc[OpGetKernelNDrangeMaxSubGroupSize].operands.push(OperandId, "'ND Range'");
+ InstructionDesc[OpGetKernelNDrangeMaxSubGroupSize].operands.push(OperandId, "'Invoke'");
+ InstructionDesc[OpGetKernelNDrangeMaxSubGroupSize].operands.push(OperandId, "'Param'");
+ InstructionDesc[OpGetKernelNDrangeMaxSubGroupSize].operands.push(OperandId, "'Param Size'");
+ InstructionDesc[OpGetKernelNDrangeMaxSubGroupSize].operands.push(OperandId, "'Param Align'");
+
+ InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Queue'");
+ InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Flags'");
+ InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'ND Range'");
+ InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Num Events'");
+ InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Wait Events'");
+ InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Ret Event'");
+ InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Invoke'");
+ InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Param'");
+ InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Param Size'");
+ InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Param Align'");
+ InstructionDesc[OpEnqueueKernel].operands.push(OperandVariableIds, "'Local Size'");
+
+ InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Queue'");
+ InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Num Events'");
+ InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Wait Events'");
+ InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Ret Event'");
+
+ InstructionDesc[OpGroupNonUniformElect].operands.push(OperandScope, "'Execution'");
+
+ InstructionDesc[OpGroupNonUniformAll].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformAll].operands.push(OperandId, "X");
+
+ InstructionDesc[OpGroupNonUniformAny].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformAny].operands.push(OperandId, "X");
+
+ InstructionDesc[OpGroupNonUniformAllEqual].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformAllEqual].operands.push(OperandId, "X");
+
+ InstructionDesc[OpGroupNonUniformBroadcast].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformBroadcast].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformBroadcast].operands.push(OperandId, "ID");
+
+ InstructionDesc[OpGroupNonUniformBroadcastFirst].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformBroadcastFirst].operands.push(OperandId, "X");
+
+ InstructionDesc[OpGroupNonUniformBallot].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformBallot].operands.push(OperandId, "X");
+
+ InstructionDesc[OpGroupNonUniformInverseBallot].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformInverseBallot].operands.push(OperandId, "X");
+
+ InstructionDesc[OpGroupNonUniformBallotBitExtract].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformBallotBitExtract].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformBallotBitExtract].operands.push(OperandId, "Bit");
+
+ InstructionDesc[OpGroupNonUniformBallotBitCount].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformBallotBitCount].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupNonUniformBallotBitCount].operands.push(OperandId, "X");
+
+ InstructionDesc[OpGroupNonUniformBallotFindLSB].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformBallotFindLSB].operands.push(OperandId, "X");
+
+ InstructionDesc[OpGroupNonUniformBallotFindMSB].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformBallotFindMSB].operands.push(OperandId, "X");
+
+ InstructionDesc[OpGroupNonUniformShuffle].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformShuffle].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformShuffle].operands.push(OperandId, "'Id'");
+
+ InstructionDesc[OpGroupNonUniformShuffleXor].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformShuffleXor].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformShuffleXor].operands.push(OperandId, "Mask");
+
+ InstructionDesc[OpGroupNonUniformShuffleUp].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformShuffleUp].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformShuffleUp].operands.push(OperandId, "Offset");
+
+ InstructionDesc[OpGroupNonUniformShuffleDown].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformShuffleDown].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformShuffleDown].operands.push(OperandId, "Offset");
+
+ InstructionDesc[OpGroupNonUniformIAdd].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformIAdd].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupNonUniformIAdd].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformIAdd].operands.push(OperandId, "'ClusterSize'", true);
+
+ InstructionDesc[OpGroupNonUniformFAdd].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformFAdd].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupNonUniformFAdd].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformFAdd].operands.push(OperandId, "'ClusterSize'", true);
+
+ InstructionDesc[OpGroupNonUniformIMul].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformIMul].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupNonUniformIMul].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformIMul].operands.push(OperandId, "'ClusterSize'", true);
+
+ InstructionDesc[OpGroupNonUniformFMul].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformFMul].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupNonUniformFMul].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformFMul].operands.push(OperandId, "'ClusterSize'", true);
+
+ InstructionDesc[OpGroupNonUniformSMin].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformSMin].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupNonUniformSMin].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformSMin].operands.push(OperandId, "'ClusterSize'", true);
+
+ InstructionDesc[OpGroupNonUniformUMin].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformUMin].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupNonUniformUMin].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformUMin].operands.push(OperandId, "'ClusterSize'", true);
+
+ InstructionDesc[OpGroupNonUniformFMin].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformFMin].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupNonUniformFMin].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformFMin].operands.push(OperandId, "'ClusterSize'", true);
+
+ InstructionDesc[OpGroupNonUniformSMax].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformSMax].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupNonUniformSMax].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformSMax].operands.push(OperandId, "'ClusterSize'", true);
+
+ InstructionDesc[OpGroupNonUniformUMax].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformUMax].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupNonUniformUMax].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformUMax].operands.push(OperandId, "'ClusterSize'", true);
+
+ InstructionDesc[OpGroupNonUniformFMax].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformFMax].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupNonUniformFMax].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformFMax].operands.push(OperandId, "'ClusterSize'", true);
+
+ InstructionDesc[OpGroupNonUniformBitwiseAnd].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformBitwiseAnd].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupNonUniformBitwiseAnd].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformBitwiseAnd].operands.push(OperandId, "'ClusterSize'", true);
+
+ InstructionDesc[OpGroupNonUniformBitwiseOr].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformBitwiseOr].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupNonUniformBitwiseOr].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformBitwiseOr].operands.push(OperandId, "'ClusterSize'", true);
+
+ InstructionDesc[OpGroupNonUniformBitwiseXor].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformBitwiseXor].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupNonUniformBitwiseXor].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformBitwiseXor].operands.push(OperandId, "'ClusterSize'", true);
+
+ InstructionDesc[OpGroupNonUniformLogicalAnd].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformLogicalAnd].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupNonUniformLogicalAnd].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformLogicalAnd].operands.push(OperandId, "'ClusterSize'", true);
+
+ InstructionDesc[OpGroupNonUniformLogicalOr].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformLogicalOr].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupNonUniformLogicalOr].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformLogicalOr].operands.push(OperandId, "'ClusterSize'", true);
+
+ InstructionDesc[OpGroupNonUniformLogicalXor].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformLogicalXor].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupNonUniformLogicalXor].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformLogicalXor].operands.push(OperandId, "'ClusterSize'", true);
+
+ InstructionDesc[OpGroupNonUniformQuadBroadcast].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformQuadBroadcast].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformQuadBroadcast].operands.push(OperandId, "'Id'");
+
+ InstructionDesc[OpGroupNonUniformQuadSwap].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupNonUniformQuadSwap].operands.push(OperandId, "X");
+ InstructionDesc[OpGroupNonUniformQuadSwap].operands.push(OperandLiteralNumber, "'Direction'");
+
+ InstructionDesc[OpSubgroupBallotKHR].operands.push(OperandId, "'Predicate'");
+
+ InstructionDesc[OpSubgroupFirstInvocationKHR].operands.push(OperandId, "'Value'");
+
+ InstructionDesc[OpSubgroupAnyKHR].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpSubgroupAnyKHR].operands.push(OperandId, "'Predicate'");
+
+ InstructionDesc[OpSubgroupAllKHR].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpSubgroupAllKHR].operands.push(OperandId, "'Predicate'");
+
+ InstructionDesc[OpSubgroupAllEqualKHR].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpSubgroupAllEqualKHR].operands.push(OperandId, "'Predicate'");
+
+ InstructionDesc[OpSubgroupReadInvocationKHR].operands.push(OperandId, "'Value'");
+ InstructionDesc[OpSubgroupReadInvocationKHR].operands.push(OperandId, "'Index'");
+
+ InstructionDesc[OpModuleProcessed].operands.push(OperandLiteralString, "'process'");
+
+#ifdef AMD_EXTENSIONS
+ InstructionDesc[OpGroupIAddNonUniformAMD].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupIAddNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupIAddNonUniformAMD].operands.push(OperandId, "'X'");
+
+ InstructionDesc[OpGroupFAddNonUniformAMD].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupFAddNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupFAddNonUniformAMD].operands.push(OperandId, "'X'");
+
+ InstructionDesc[OpGroupUMinNonUniformAMD].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupUMinNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupUMinNonUniformAMD].operands.push(OperandId, "'X'");
+
+ InstructionDesc[OpGroupSMinNonUniformAMD].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupSMinNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupSMinNonUniformAMD].operands.push(OperandId, "X");
+
+ InstructionDesc[OpGroupFMinNonUniformAMD].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupFMinNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupFMinNonUniformAMD].operands.push(OperandId, "X");
+
+ InstructionDesc[OpGroupUMaxNonUniformAMD].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupUMaxNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupUMaxNonUniformAMD].operands.push(OperandId, "X");
+
+ InstructionDesc[OpGroupSMaxNonUniformAMD].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupSMaxNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupSMaxNonUniformAMD].operands.push(OperandId, "X");
+
+ InstructionDesc[OpGroupFMaxNonUniformAMD].operands.push(OperandScope, "'Execution'");
+ InstructionDesc[OpGroupFMaxNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'");
+ InstructionDesc[OpGroupFMaxNonUniformAMD].operands.push(OperandId, "X");
+
+ InstructionDesc[OpFragmentMaskFetchAMD].operands.push(OperandId, "'Image'");
+ InstructionDesc[OpFragmentMaskFetchAMD].operands.push(OperandId, "'Coordinate'");
+
+ InstructionDesc[OpFragmentFetchAMD].operands.push(OperandId, "'Image'");
+ InstructionDesc[OpFragmentFetchAMD].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpFragmentFetchAMD].operands.push(OperandId, "'Fragment Index'");
+#endif
+
+#ifdef NV_EXTENSIONS
+ InstructionDesc[OpGroupNonUniformPartitionNV].operands.push(OperandId, "X");
+
+ InstructionDesc[OpTypeAccelerationStructureNV].setResultAndType(true, false);
+
+ InstructionDesc[OpTraceNV].operands.push(OperandId, "'NV Acceleration Structure'");
+ InstructionDesc[OpTraceNV].operands.push(OperandId, "'Ray Flags'");
+ InstructionDesc[OpTraceNV].operands.push(OperandId, "'Cull Mask'");
+ InstructionDesc[OpTraceNV].operands.push(OperandId, "'SBT Record Offset'");
+ InstructionDesc[OpTraceNV].operands.push(OperandId, "'SBT Record Stride'");
+ InstructionDesc[OpTraceNV].operands.push(OperandId, "'Miss Index'");
+ InstructionDesc[OpTraceNV].operands.push(OperandId, "'Ray Origin'");
+ InstructionDesc[OpTraceNV].operands.push(OperandId, "'TMin'");
+ InstructionDesc[OpTraceNV].operands.push(OperandId, "'Ray Direction'");
+ InstructionDesc[OpTraceNV].operands.push(OperandId, "'TMax'");
+ InstructionDesc[OpTraceNV].operands.push(OperandId, "'Payload'");
+ InstructionDesc[OpTraceNV].setResultAndType(false, false);
+
+ InstructionDesc[OpReportIntersectionNV].operands.push(OperandId, "'Hit Parameter'");
+ InstructionDesc[OpReportIntersectionNV].operands.push(OperandId, "'Hit Kind'");
+
+ InstructionDesc[OpIgnoreIntersectionNV].setResultAndType(false, false);
+
+ InstructionDesc[OpTerminateRayNV].setResultAndType(false, false);
+
+ InstructionDesc[OpExecuteCallableNV].operands.push(OperandId, "SBT Record Index");
+ InstructionDesc[OpExecuteCallableNV].operands.push(OperandId, "CallableData ID");
+ InstructionDesc[OpExecuteCallableNV].setResultAndType(false, false);
+
+ InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandId, "'Sampled Image'");
+ InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandId, "'Coordinate'");
+ InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandId, "'Granularity'");
+ InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandId, "'Coarse'");
+ InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandImageOperands, "", true);
+ InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandVariableIds, "", true);
+
+ InstructionDesc[OpWritePackedPrimitiveIndices4x8NV].operands.push(OperandId, "'Index Offset'");
+ InstructionDesc[OpWritePackedPrimitiveIndices4x8NV].operands.push(OperandId, "'Packed Indices'");
+#endif
+
+ InstructionDesc[OpTypeCooperativeMatrixNV].operands.push(OperandId, "'Component Type'");
+ InstructionDesc[OpTypeCooperativeMatrixNV].operands.push(OperandId, "'Scope'");
+ InstructionDesc[OpTypeCooperativeMatrixNV].operands.push(OperandId, "'Rows'");
+ InstructionDesc[OpTypeCooperativeMatrixNV].operands.push(OperandId, "'Columns'");
+
+ InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandId, "'Stride'");
+ InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandId, "'Column Major'");
+ InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandMemoryAccess, "'Memory Access'");
+ InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandLiteralNumber, "", true);
+ InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandId, "", true);
+
+ InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandId, "'Pointer'");
+ InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandId, "'Object'");
+ InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandId, "'Stride'");
+ InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandId, "'Column Major'");
+ InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandMemoryAccess, "'Memory Access'");
+ InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandLiteralNumber, "", true);
+ InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandId, "", true);
+
+ InstructionDesc[OpCooperativeMatrixMulAddNV].operands.push(OperandId, "'A'");
+ InstructionDesc[OpCooperativeMatrixMulAddNV].operands.push(OperandId, "'B'");
+ InstructionDesc[OpCooperativeMatrixMulAddNV].operands.push(OperandId, "'C'");
+
+ InstructionDesc[OpCooperativeMatrixLengthNV].operands.push(OperandId, "'Type'");
+}
+
+}; // end spv namespace
diff --git a/src/3rdparty/glslang/SPIRV/doc.h b/src/3rdparty/glslang/SPIRV/doc.h
new file mode 100644
index 0000000..293256a
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/doc.h
@@ -0,0 +1,258 @@
+//
+// Copyright (C) 2014-2015 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+//
+// Parameterize the SPIR-V enumerants.
+//
+
+#pragma once
+
+#include "spirv.hpp"
+
+#include <vector>
+
+namespace spv {
+
+// Fill in all the parameters
+void Parameterize();
+
+// Return the English names of all the enums.
+const char* SourceString(int);
+const char* AddressingString(int);
+const char* MemoryString(int);
+const char* ExecutionModelString(int);
+const char* ExecutionModeString(int);
+const char* StorageClassString(int);
+const char* DecorationString(int);
+const char* BuiltInString(int);
+const char* DimensionString(int);
+const char* SelectControlString(int);
+const char* LoopControlString(int);
+const char* FunctionControlString(int);
+const char* SamplerAddressingModeString(int);
+const char* SamplerFilterModeString(int);
+const char* ImageFormatString(int);
+const char* ImageChannelOrderString(int);
+const char* ImageChannelTypeString(int);
+const char* ImageChannelDataTypeString(int type);
+const char* ImageOperandsString(int format);
+const char* ImageOperands(int);
+const char* FPFastMathString(int);
+const char* FPRoundingModeString(int);
+const char* LinkageTypeString(int);
+const char* FuncParamAttrString(int);
+const char* AccessQualifierString(int);
+const char* MemorySemanticsString(int);
+const char* MemoryAccessString(int);
+const char* ExecutionScopeString(int);
+const char* GroupOperationString(int);
+const char* KernelEnqueueFlagsString(int);
+const char* KernelProfilingInfoString(int);
+const char* CapabilityString(int);
+const char* OpcodeString(int);
+const char* ScopeString(int mem);
+
+// For grouping opcodes into subsections
+enum OpcodeClass {
+ OpClassMisc,
+ OpClassDebug,
+ OpClassAnnotate,
+ OpClassExtension,
+ OpClassMode,
+ OpClassType,
+ OpClassConstant,
+ OpClassMemory,
+ OpClassFunction,
+ OpClassImage,
+ OpClassConvert,
+ OpClassComposite,
+ OpClassArithmetic,
+ OpClassBit,
+ OpClassRelationalLogical,
+ OpClassDerivative,
+ OpClassFlowControl,
+ OpClassAtomic,
+ OpClassPrimitive,
+ OpClassBarrier,
+ OpClassGroup,
+ OpClassDeviceSideEnqueue,
+ OpClassPipe,
+
+ OpClassCount,
+ OpClassMissing // all instructions start out as missing
+};
+
+// For parameterizing operands.
+enum OperandClass {
+ OperandNone,
+ OperandId,
+ OperandVariableIds,
+ OperandOptionalLiteral,
+ OperandOptionalLiteralString,
+ OperandVariableLiterals,
+ OperandVariableIdLiteral,
+ OperandVariableLiteralId,
+ OperandLiteralNumber,
+ OperandLiteralString,
+ OperandSource,
+ OperandExecutionModel,
+ OperandAddressing,
+ OperandMemory,
+ OperandExecutionMode,
+ OperandStorage,
+ OperandDimensionality,
+ OperandSamplerAddressingMode,
+ OperandSamplerFilterMode,
+ OperandSamplerImageFormat,
+ OperandImageChannelOrder,
+ OperandImageChannelDataType,
+ OperandImageOperands,
+ OperandFPFastMath,
+ OperandFPRoundingMode,
+ OperandLinkageType,
+ OperandAccessQualifier,
+ OperandFuncParamAttr,
+ OperandDecoration,
+ OperandBuiltIn,
+ OperandSelect,
+ OperandLoop,
+ OperandFunction,
+ OperandMemorySemantics,
+ OperandMemoryAccess,
+ OperandScope,
+ OperandGroupOperation,
+ OperandKernelEnqueueFlags,
+ OperandKernelProfilingInfo,
+ OperandCapability,
+
+ OperandOpcode,
+
+ OperandCount
+};
+
+// Any specific enum can have a set of capabilities that allow it:
+typedef std::vector<Capability> EnumCaps;
+
+// Parameterize a set of operands with their OperandClass(es) and descriptions.
+class OperandParameters {
+public:
+ OperandParameters() { }
+ void push(OperandClass oc, const char* d, bool opt = false)
+ {
+ opClass.push_back(oc);
+ desc.push_back(d);
+ optional.push_back(opt);
+ }
+ void setOptional();
+ OperandClass getClass(int op) const { return opClass[op]; }
+ const char* getDesc(int op) const { return desc[op]; }
+ bool isOptional(int op) const { return optional[op]; }
+ int getNum() const { return (int)opClass.size(); }
+
+protected:
+ std::vector<OperandClass> opClass;
+ std::vector<const char*> desc;
+ std::vector<bool> optional;
+};
+
+// Parameterize an enumerant
+class EnumParameters {
+public:
+ EnumParameters() : desc(0) { }
+ const char* desc;
+};
+
+// Parameterize a set of enumerants that form an enum
+class EnumDefinition : public EnumParameters {
+public:
+ EnumDefinition() :
+ ceiling(0), bitmask(false), getName(0), enumParams(0), operandParams(0) { }
+ void set(int ceil, const char* (*name)(int), EnumParameters* ep, bool mask = false)
+ {
+ ceiling = ceil;
+ getName = name;
+ bitmask = mask;
+ enumParams = ep;
+ }
+ void setOperands(OperandParameters* op) { operandParams = op; }
+ int ceiling; // ceiling of enumerants
+ bool bitmask; // true if these enumerants combine into a bitmask
+ const char* (*getName)(int); // a function that returns the name for each enumerant value (or shift)
+ EnumParameters* enumParams; // parameters for each individual enumerant
+ OperandParameters* operandParams; // sets of operands
+};
+
+// Parameterize an instruction's logical format, including its known set of operands,
+// per OperandParameters above.
+class InstructionParameters {
+public:
+ InstructionParameters() :
+ opDesc("TBD"),
+ opClass(OpClassMissing),
+ typePresent(true), // most normal, only exceptions have to be spelled out
+ resultPresent(true) // most normal, only exceptions have to be spelled out
+ { }
+
+ void setResultAndType(bool r, bool t)
+ {
+ resultPresent = r;
+ typePresent = t;
+ }
+
+ bool hasResult() const { return resultPresent != 0; }
+ bool hasType() const { return typePresent != 0; }
+
+ const char* opDesc;
+ OpcodeClass opClass;
+ OperandParameters operands;
+
+protected:
+ int typePresent : 1;
+ int resultPresent : 1;
+};
+
+// The set of objects that hold all the instruction/operand
+// parameterization information.
+extern InstructionParameters InstructionDesc[];
+
+// These hold definitions of the enumerants used for operands
+extern EnumDefinition OperandClassParams[];
+
+const char* GetOperandDesc(OperandClass operand);
+void PrintImmediateRow(int imm, const char* name, const EnumParameters* enumParams, bool caps, bool hex = false);
+const char* AccessQualifierString(int attr);
+
+void PrintOperands(const OperandParameters& operands, int reservedOperands);
+
+} // end namespace spv
diff --git a/src/3rdparty/glslang/SPIRV/hex_float.h b/src/3rdparty/glslang/SPIRV/hex_float.h
new file mode 100644
index 0000000..905b21a
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/hex_float.h
@@ -0,0 +1,1078 @@
+// Copyright (c) 2015-2016 The Khronos Group Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef LIBSPIRV_UTIL_HEX_FLOAT_H_
+#define LIBSPIRV_UTIL_HEX_FLOAT_H_
+
+#include <cassert>
+#include <cctype>
+#include <cmath>
+#include <cstdint>
+#include <iomanip>
+#include <limits>
+#include <sstream>
+
+#if defined(_MSC_VER) && _MSC_VER < 1800
+namespace std {
+bool isnan(double f)
+{
+ return ::_isnan(f) != 0;
+}
+bool isinf(double f)
+{
+ return ::_finite(f) == 0;
+}
+}
+#endif
+
+#include "bitutils.h"
+
+namespace spvutils {
+
+class Float16 {
+ public:
+ Float16(uint16_t v) : val(v) {}
+ Float16() {}
+ static bool isNan(const Float16& val) {
+ return ((val.val & 0x7C00) == 0x7C00) && ((val.val & 0x3FF) != 0);
+ }
+ // Returns true if the given value is any kind of infinity.
+ static bool isInfinity(const Float16& val) {
+ return ((val.val & 0x7C00) == 0x7C00) && ((val.val & 0x3FF) == 0);
+ }
+ Float16(const Float16& other) { val = other.val; }
+ uint16_t get_value() const { return val; }
+
+ // Returns the maximum normal value.
+ static Float16 max() { return Float16(0x7bff); }
+ // Returns the lowest normal value.
+ static Float16 lowest() { return Float16(0xfbff); }
+
+ private:
+ uint16_t val;
+};
+
+// To specialize this type, you must override uint_type to define
+// an unsigned integer that can fit your floating point type.
+// You must also add a isNan function that returns true if
+// a value is Nan.
+template <typename T>
+struct FloatProxyTraits {
+ typedef void uint_type;
+};
+
+template <>
+struct FloatProxyTraits<float> {
+ typedef uint32_t uint_type;
+ static bool isNan(float f) { return std::isnan(f); }
+ // Returns true if the given value is any kind of infinity.
+ static bool isInfinity(float f) { return std::isinf(f); }
+ // Returns the maximum normal value.
+ static float max() { return std::numeric_limits<float>::max(); }
+ // Returns the lowest normal value.
+ static float lowest() { return std::numeric_limits<float>::lowest(); }
+};
+
+template <>
+struct FloatProxyTraits<double> {
+ typedef uint64_t uint_type;
+ static bool isNan(double f) { return std::isnan(f); }
+ // Returns true if the given value is any kind of infinity.
+ static bool isInfinity(double f) { return std::isinf(f); }
+ // Returns the maximum normal value.
+ static double max() { return std::numeric_limits<double>::max(); }
+ // Returns the lowest normal value.
+ static double lowest() { return std::numeric_limits<double>::lowest(); }
+};
+
+template <>
+struct FloatProxyTraits<Float16> {
+ typedef uint16_t uint_type;
+ static bool isNan(Float16 f) { return Float16::isNan(f); }
+ // Returns true if the given value is any kind of infinity.
+ static bool isInfinity(Float16 f) { return Float16::isInfinity(f); }
+ // Returns the maximum normal value.
+ static Float16 max() { return Float16::max(); }
+ // Returns the lowest normal value.
+ static Float16 lowest() { return Float16::lowest(); }
+};
+
+// Since copying a floating point number (especially if it is NaN)
+// does not guarantee that bits are preserved, this class lets us
+// store the type and use it as a float when necessary.
+template <typename T>
+class FloatProxy {
+ public:
+ typedef typename FloatProxyTraits<T>::uint_type uint_type;
+
+ // Since this is to act similar to the normal floats,
+ // do not initialize the data by default.
+ FloatProxy() {}
+
+ // Intentionally non-explicit. This is a proxy type so
+ // implicit conversions allow us to use it more transparently.
+ FloatProxy(T val) { data_ = BitwiseCast<uint_type>(val); }
+
+ // Intentionally non-explicit. This is a proxy type so
+ // implicit conversions allow us to use it more transparently.
+ FloatProxy(uint_type val) { data_ = val; }
+
+ // This is helpful to have and is guaranteed not to stomp bits.
+ FloatProxy<T> operator-() const {
+ return static_cast<uint_type>(data_ ^
+ (uint_type(0x1) << (sizeof(T) * 8 - 1)));
+ }
+
+ // Returns the data as a floating point value.
+ T getAsFloat() const { return BitwiseCast<T>(data_); }
+
+ // Returns the raw data.
+ uint_type data() const { return data_; }
+
+ // Returns true if the value represents any type of NaN.
+ bool isNan() { return FloatProxyTraits<T>::isNan(getAsFloat()); }
+ // Returns true if the value represents any type of infinity.
+ bool isInfinity() { return FloatProxyTraits<T>::isInfinity(getAsFloat()); }
+
+ // Returns the maximum normal value.
+ static FloatProxy<T> max() {
+ return FloatProxy<T>(FloatProxyTraits<T>::max());
+ }
+ // Returns the lowest normal value.
+ static FloatProxy<T> lowest() {
+ return FloatProxy<T>(FloatProxyTraits<T>::lowest());
+ }
+
+ private:
+ uint_type data_;
+};
+
+template <typename T>
+bool operator==(const FloatProxy<T>& first, const FloatProxy<T>& second) {
+ return first.data() == second.data();
+}
+
+// Reads a FloatProxy value as a normal float from a stream.
+template <typename T>
+std::istream& operator>>(std::istream& is, FloatProxy<T>& value) {
+ T float_val;
+ is >> float_val;
+ value = FloatProxy<T>(float_val);
+ return is;
+}
+
+// This is an example traits. It is not meant to be used in practice, but will
+// be the default for any non-specialized type.
+template <typename T>
+struct HexFloatTraits {
+ // Integer type that can store this hex-float.
+ typedef void uint_type;
+ // Signed integer type that can store this hex-float.
+ typedef void int_type;
+ // The numerical type that this HexFloat represents.
+ typedef void underlying_type;
+ // The type needed to construct the underlying type.
+ typedef void native_type;
+ // The number of bits that are actually relevant in the uint_type.
+ // This allows us to deal with, for example, 24-bit values in a 32-bit
+ // integer.
+ static const uint32_t num_used_bits = 0;
+ // Number of bits that represent the exponent.
+ static const uint32_t num_exponent_bits = 0;
+ // Number of bits that represent the fractional part.
+ static const uint32_t num_fraction_bits = 0;
+ // The bias of the exponent. (How much we need to subtract from the stored
+ // value to get the correct value.)
+ static const uint32_t exponent_bias = 0;
+};
+
+// Traits for IEEE float.
+// 1 sign bit, 8 exponent bits, 23 fractional bits.
+template <>
+struct HexFloatTraits<FloatProxy<float>> {
+ typedef uint32_t uint_type;
+ typedef int32_t int_type;
+ typedef FloatProxy<float> underlying_type;
+ typedef float native_type;
+ static const uint_type num_used_bits = 32;
+ static const uint_type num_exponent_bits = 8;
+ static const uint_type num_fraction_bits = 23;
+ static const uint_type exponent_bias = 127;
+};
+
+// Traits for IEEE double.
+// 1 sign bit, 11 exponent bits, 52 fractional bits.
+template <>
+struct HexFloatTraits<FloatProxy<double>> {
+ typedef uint64_t uint_type;
+ typedef int64_t int_type;
+ typedef FloatProxy<double> underlying_type;
+ typedef double native_type;
+ static const uint_type num_used_bits = 64;
+ static const uint_type num_exponent_bits = 11;
+ static const uint_type num_fraction_bits = 52;
+ static const uint_type exponent_bias = 1023;
+};
+
+// Traits for IEEE half.
+// 1 sign bit, 5 exponent bits, 10 fractional bits.
+template <>
+struct HexFloatTraits<FloatProxy<Float16>> {
+ typedef uint16_t uint_type;
+ typedef int16_t int_type;
+ typedef uint16_t underlying_type;
+ typedef uint16_t native_type;
+ static const uint_type num_used_bits = 16;
+ static const uint_type num_exponent_bits = 5;
+ static const uint_type num_fraction_bits = 10;
+ static const uint_type exponent_bias = 15;
+};
+
+enum round_direction {
+ kRoundToZero,
+ kRoundToNearestEven,
+ kRoundToPositiveInfinity,
+ kRoundToNegativeInfinity
+};
+
+// Template class that houses a floating pointer number.
+// It exposes a number of constants based on the provided traits to
+// assist in interpreting the bits of the value.
+template <typename T, typename Traits = HexFloatTraits<T>>
+class HexFloat {
+ public:
+ typedef typename Traits::uint_type uint_type;
+ typedef typename Traits::int_type int_type;
+ typedef typename Traits::underlying_type underlying_type;
+ typedef typename Traits::native_type native_type;
+
+ explicit HexFloat(T f) : value_(f) {}
+
+ T value() const { return value_; }
+ void set_value(T f) { value_ = f; }
+
+ // These are all written like this because it is convenient to have
+ // compile-time constants for all of these values.
+
+ // Pass-through values to save typing.
+ static const uint32_t num_used_bits = Traits::num_used_bits;
+ static const uint32_t exponent_bias = Traits::exponent_bias;
+ static const uint32_t num_exponent_bits = Traits::num_exponent_bits;
+ static const uint32_t num_fraction_bits = Traits::num_fraction_bits;
+
+ // Number of bits to shift left to set the highest relevant bit.
+ static const uint32_t top_bit_left_shift = num_used_bits - 1;
+ // How many nibbles (hex characters) the fractional part takes up.
+ static const uint32_t fraction_nibbles = (num_fraction_bits + 3) / 4;
+ // If the fractional part does not fit evenly into a hex character (4-bits)
+ // then we have to left-shift to get rid of leading 0s. This is the amount
+ // we have to shift (might be 0).
+ static const uint32_t num_overflow_bits =
+ fraction_nibbles * 4 - num_fraction_bits;
+
+ // The representation of the fraction, not the actual bits. This
+ // includes the leading bit that is usually implicit.
+ static const uint_type fraction_represent_mask =
+ spvutils::SetBits<uint_type, 0,
+ num_fraction_bits + num_overflow_bits>::get;
+
+ // The topmost bit in the nibble-aligned fraction.
+ static const uint_type fraction_top_bit =
+ uint_type(1) << (num_fraction_bits + num_overflow_bits - 1);
+
+ // The least significant bit in the exponent, which is also the bit
+ // immediately to the left of the significand.
+ static const uint_type first_exponent_bit = uint_type(1)
+ << (num_fraction_bits);
+
+ // The mask for the encoded fraction. It does not include the
+ // implicit bit.
+ static const uint_type fraction_encode_mask =
+ spvutils::SetBits<uint_type, 0, num_fraction_bits>::get;
+
+ // The bit that is used as a sign.
+ static const uint_type sign_mask = uint_type(1) << top_bit_left_shift;
+
+ // The bits that represent the exponent.
+ static const uint_type exponent_mask =
+ spvutils::SetBits<uint_type, num_fraction_bits, num_exponent_bits>::get;
+
+ // How far left the exponent is shifted.
+ static const uint32_t exponent_left_shift = num_fraction_bits;
+
+ // How far from the right edge the fraction is shifted.
+ static const uint32_t fraction_right_shift =
+ static_cast<uint32_t>(sizeof(uint_type) * 8) - num_fraction_bits;
+
+ // The maximum representable unbiased exponent.
+ static const int_type max_exponent =
+ (exponent_mask >> num_fraction_bits) - exponent_bias;
+ // The minimum representable exponent for normalized numbers.
+ static const int_type min_exponent = -static_cast<int_type>(exponent_bias);
+
+ // Returns the bits associated with the value.
+ uint_type getBits() const { return spvutils::BitwiseCast<uint_type>(value_); }
+
+ // Returns the bits associated with the value, without the leading sign bit.
+ uint_type getUnsignedBits() const {
+ return static_cast<uint_type>(spvutils::BitwiseCast<uint_type>(value_) &
+ ~sign_mask);
+ }
+
+ // Returns the bits associated with the exponent, shifted to start at the
+ // lsb of the type.
+ const uint_type getExponentBits() const {
+ return static_cast<uint_type>((getBits() & exponent_mask) >>
+ num_fraction_bits);
+ }
+
+ // Returns the exponent in unbiased form. This is the exponent in the
+ // human-friendly form.
+ const int_type getUnbiasedExponent() const {
+ return static_cast<int_type>(getExponentBits() - exponent_bias);
+ }
+
+ // Returns just the significand bits from the value.
+ const uint_type getSignificandBits() const {
+ return getBits() & fraction_encode_mask;
+ }
+
+ // If the number was normalized, returns the unbiased exponent.
+ // If the number was denormal, normalize the exponent first.
+ const int_type getUnbiasedNormalizedExponent() const {
+ if ((getBits() & ~sign_mask) == 0) { // special case if everything is 0
+ return 0;
+ }
+ int_type exp = getUnbiasedExponent();
+ if (exp == min_exponent) { // We are in denorm land.
+ uint_type significand_bits = getSignificandBits();
+ while ((significand_bits & (first_exponent_bit >> 1)) == 0) {
+ significand_bits = static_cast<uint_type>(significand_bits << 1);
+ exp = static_cast<int_type>(exp - 1);
+ }
+ significand_bits &= fraction_encode_mask;
+ }
+ return exp;
+ }
+
+ // Returns the signficand after it has been normalized.
+ const uint_type getNormalizedSignificand() const {
+ int_type unbiased_exponent = getUnbiasedNormalizedExponent();
+ uint_type significand = getSignificandBits();
+ for (int_type i = unbiased_exponent; i <= min_exponent; ++i) {
+ significand = static_cast<uint_type>(significand << 1);
+ }
+ significand &= fraction_encode_mask;
+ return significand;
+ }
+
+ // Returns true if this number represents a negative value.
+ bool isNegative() const { return (getBits() & sign_mask) != 0; }
+
+ // Sets this HexFloat from the individual components.
+ // Note this assumes EVERY significand is normalized, and has an implicit
+ // leading one. This means that the only way that this method will set 0,
+ // is if you set a number so denormalized that it underflows.
+ // Do not use this method with raw bits extracted from a subnormal number,
+ // since subnormals do not have an implicit leading 1 in the significand.
+ // The significand is also expected to be in the
+ // lowest-most num_fraction_bits of the uint_type.
+ // The exponent is expected to be unbiased, meaning an exponent of
+ // 0 actually means 0.
+ // If underflow_round_up is set, then on underflow, if a number is non-0
+ // and would underflow, we round up to the smallest denorm.
+ void setFromSignUnbiasedExponentAndNormalizedSignificand(
+ bool negative, int_type exponent, uint_type significand,
+ bool round_denorm_up) {
+ bool significand_is_zero = significand == 0;
+
+ if (exponent <= min_exponent) {
+ // If this was denormalized, then we have to shift the bit on, meaning
+ // the significand is not zero.
+ significand_is_zero = false;
+ significand |= first_exponent_bit;
+ significand = static_cast<uint_type>(significand >> 1);
+ }
+
+ while (exponent < min_exponent) {
+ significand = static_cast<uint_type>(significand >> 1);
+ ++exponent;
+ }
+
+ if (exponent == min_exponent) {
+ if (significand == 0 && !significand_is_zero && round_denorm_up) {
+ significand = static_cast<uint_type>(0x1);
+ }
+ }
+
+ uint_type new_value = 0;
+ if (negative) {
+ new_value = static_cast<uint_type>(new_value | sign_mask);
+ }
+ exponent = static_cast<int_type>(exponent + exponent_bias);
+ assert(exponent >= 0);
+
+ // put it all together
+ exponent = static_cast<uint_type>((exponent << exponent_left_shift) &
+ exponent_mask);
+ significand = static_cast<uint_type>(significand & fraction_encode_mask);
+ new_value = static_cast<uint_type>(new_value | (exponent | significand));
+ value_ = BitwiseCast<T>(new_value);
+ }
+
+ // Increments the significand of this number by the given amount.
+ // If this would spill the significand into the implicit bit,
+ // carry is set to true and the significand is shifted to fit into
+ // the correct location, otherwise carry is set to false.
+ // All significands and to_increment are assumed to be within the bounds
+ // for a valid significand.
+ static uint_type incrementSignificand(uint_type significand,
+ uint_type to_increment, bool* carry) {
+ significand = static_cast<uint_type>(significand + to_increment);
+ *carry = false;
+ if (significand & first_exponent_bit) {
+ *carry = true;
+ // The implicit 1-bit will have carried, so we should zero-out the
+ // top bit and shift back.
+ significand = static_cast<uint_type>(significand & ~first_exponent_bit);
+ significand = static_cast<uint_type>(significand >> 1);
+ }
+ return significand;
+ }
+
+ // These exist because MSVC throws warnings on negative right-shifts
+ // even if they are not going to be executed. Eg:
+ // constant_number < 0? 0: constant_number
+ // These convert the negative left-shifts into right shifts.
+
+ template <typename int_type>
+ uint_type negatable_left_shift(int_type N, uint_type val)
+ {
+ if(N >= 0)
+ return val << N;
+
+ return val >> -N;
+ }
+
+ template <typename int_type>
+ uint_type negatable_right_shift(int_type N, uint_type val)
+ {
+ if(N >= 0)
+ return val >> N;
+
+ return val << -N;
+ }
+
+ // Returns the significand, rounded to fit in a significand in
+ // other_T. This is shifted so that the most significant
+ // bit of the rounded number lines up with the most significant bit
+ // of the returned significand.
+ template <typename other_T>
+ typename other_T::uint_type getRoundedNormalizedSignificand(
+ round_direction dir, bool* carry_bit) {
+ typedef typename other_T::uint_type other_uint_type;
+ static const int_type num_throwaway_bits =
+ static_cast<int_type>(num_fraction_bits) -
+ static_cast<int_type>(other_T::num_fraction_bits);
+
+ static const uint_type last_significant_bit =
+ (num_throwaway_bits < 0)
+ ? 0
+ : negatable_left_shift(num_throwaway_bits, 1u);
+ static const uint_type first_rounded_bit =
+ (num_throwaway_bits < 1)
+ ? 0
+ : negatable_left_shift(num_throwaway_bits - 1, 1u);
+
+ static const uint_type throwaway_mask_bits =
+ num_throwaway_bits > 0 ? num_throwaway_bits : 0;
+ static const uint_type throwaway_mask =
+ spvutils::SetBits<uint_type, 0, throwaway_mask_bits>::get;
+
+ *carry_bit = false;
+ other_uint_type out_val = 0;
+ uint_type significand = getNormalizedSignificand();
+ // If we are up-casting, then we just have to shift to the right location.
+ if (num_throwaway_bits <= 0) {
+ out_val = static_cast<other_uint_type>(significand);
+ uint_type shift_amount = static_cast<uint_type>(-num_throwaway_bits);
+ out_val = static_cast<other_uint_type>(out_val << shift_amount);
+ return out_val;
+ }
+
+ // If every non-representable bit is 0, then we don't have any casting to
+ // do.
+ if ((significand & throwaway_mask) == 0) {
+ return static_cast<other_uint_type>(
+ negatable_right_shift(num_throwaway_bits, significand));
+ }
+
+ bool round_away_from_zero = false;
+ // We actually have to narrow the significand here, so we have to follow the
+ // rounding rules.
+ switch (dir) {
+ case kRoundToZero:
+ break;
+ case kRoundToPositiveInfinity:
+ round_away_from_zero = !isNegative();
+ break;
+ case kRoundToNegativeInfinity:
+ round_away_from_zero = isNegative();
+ break;
+ case kRoundToNearestEven:
+ // Have to round down, round bit is 0
+ if ((first_rounded_bit & significand) == 0) {
+ break;
+ }
+ if (((significand & throwaway_mask) & ~first_rounded_bit) != 0) {
+ // If any subsequent bit of the rounded portion is non-0 then we round
+ // up.
+ round_away_from_zero = true;
+ break;
+ }
+ // We are exactly half-way between 2 numbers, pick even.
+ if ((significand & last_significant_bit) != 0) {
+ // 1 for our last bit, round up.
+ round_away_from_zero = true;
+ break;
+ }
+ break;
+ }
+
+ if (round_away_from_zero) {
+ return static_cast<other_uint_type>(
+ negatable_right_shift(num_throwaway_bits, incrementSignificand(
+ significand, last_significant_bit, carry_bit)));
+ } else {
+ return static_cast<other_uint_type>(
+ negatable_right_shift(num_throwaway_bits, significand));
+ }
+ }
+
+ // Casts this value to another HexFloat. If the cast is widening,
+ // then round_dir is ignored. If the cast is narrowing, then
+ // the result is rounded in the direction specified.
+ // This number will retain Nan and Inf values.
+ // It will also saturate to Inf if the number overflows, and
+ // underflow to (0 or min depending on rounding) if the number underflows.
+ template <typename other_T>
+ void castTo(other_T& other, round_direction round_dir) {
+ other = other_T(static_cast<typename other_T::native_type>(0));
+ bool negate = isNegative();
+ if (getUnsignedBits() == 0) {
+ if (negate) {
+ other.set_value(-other.value());
+ }
+ return;
+ }
+ uint_type significand = getSignificandBits();
+ bool carried = false;
+ typename other_T::uint_type rounded_significand =
+ getRoundedNormalizedSignificand<other_T>(round_dir, &carried);
+
+ int_type exponent = getUnbiasedExponent();
+ if (exponent == min_exponent) {
+ // If we are denormal, normalize the exponent, so that we can encode
+ // easily.
+ exponent = static_cast<int_type>(exponent + 1);
+ for (uint_type check_bit = first_exponent_bit >> 1; check_bit != 0;
+ check_bit = static_cast<uint_type>(check_bit >> 1)) {
+ exponent = static_cast<int_type>(exponent - 1);
+ if (check_bit & significand) break;
+ }
+ }
+
+ bool is_nan =
+ (getBits() & exponent_mask) == exponent_mask && significand != 0;
+ bool is_inf =
+ !is_nan &&
+ ((exponent + carried) > static_cast<int_type>(other_T::exponent_bias) ||
+ (significand == 0 && (getBits() & exponent_mask) == exponent_mask));
+
+ // If we are Nan or Inf we should pass that through.
+ if (is_inf) {
+ other.set_value(BitwiseCast<typename other_T::underlying_type>(
+ static_cast<typename other_T::uint_type>(
+ (negate ? other_T::sign_mask : 0) | other_T::exponent_mask)));
+ return;
+ }
+ if (is_nan) {
+ typename other_T::uint_type shifted_significand;
+ shifted_significand = static_cast<typename other_T::uint_type>(
+ negatable_left_shift(
+ static_cast<int_type>(other_T::num_fraction_bits) -
+ static_cast<int_type>(num_fraction_bits), significand));
+
+ // We are some sort of Nan. We try to keep the bit-pattern of the Nan
+ // as close as possible. If we had to shift off bits so we are 0, then we
+ // just set the last bit.
+ other.set_value(BitwiseCast<typename other_T::underlying_type>(
+ static_cast<typename other_T::uint_type>(
+ (negate ? other_T::sign_mask : 0) | other_T::exponent_mask |
+ (shifted_significand == 0 ? 0x1 : shifted_significand))));
+ return;
+ }
+
+ bool round_underflow_up =
+ isNegative() ? round_dir == kRoundToNegativeInfinity
+ : round_dir == kRoundToPositiveInfinity;
+ typedef typename other_T::int_type other_int_type;
+ // setFromSignUnbiasedExponentAndNormalizedSignificand will
+ // zero out any underflowing value (but retain the sign).
+ other.setFromSignUnbiasedExponentAndNormalizedSignificand(
+ negate, static_cast<other_int_type>(exponent), rounded_significand,
+ round_underflow_up);
+ return;
+ }
+
+ private:
+ T value_;
+
+ static_assert(num_used_bits ==
+ Traits::num_exponent_bits + Traits::num_fraction_bits + 1,
+ "The number of bits do not fit");
+ static_assert(sizeof(T) == sizeof(uint_type), "The type sizes do not match");
+};
+
+// Returns 4 bits represented by the hex character.
+inline uint8_t get_nibble_from_character(int character) {
+ const char* dec = "0123456789";
+ const char* lower = "abcdef";
+ const char* upper = "ABCDEF";
+ const char* p = nullptr;
+ if ((p = strchr(dec, character))) {
+ return static_cast<uint8_t>(p - dec);
+ } else if ((p = strchr(lower, character))) {
+ return static_cast<uint8_t>(p - lower + 0xa);
+ } else if ((p = strchr(upper, character))) {
+ return static_cast<uint8_t>(p - upper + 0xa);
+ }
+
+ assert(false && "This was called with a non-hex character");
+ return 0;
+}
+
+// Outputs the given HexFloat to the stream.
+template <typename T, typename Traits>
+std::ostream& operator<<(std::ostream& os, const HexFloat<T, Traits>& value) {
+ typedef HexFloat<T, Traits> HF;
+ typedef typename HF::uint_type uint_type;
+ typedef typename HF::int_type int_type;
+
+ static_assert(HF::num_used_bits != 0,
+ "num_used_bits must be non-zero for a valid float");
+ static_assert(HF::num_exponent_bits != 0,
+ "num_exponent_bits must be non-zero for a valid float");
+ static_assert(HF::num_fraction_bits != 0,
+ "num_fractin_bits must be non-zero for a valid float");
+
+ const uint_type bits = spvutils::BitwiseCast<uint_type>(value.value());
+ const char* const sign = (bits & HF::sign_mask) ? "-" : "";
+ const uint_type exponent = static_cast<uint_type>(
+ (bits & HF::exponent_mask) >> HF::num_fraction_bits);
+
+ uint_type fraction = static_cast<uint_type>((bits & HF::fraction_encode_mask)
+ << HF::num_overflow_bits);
+
+ const bool is_zero = exponent == 0 && fraction == 0;
+ const bool is_denorm = exponent == 0 && !is_zero;
+
+ // exponent contains the biased exponent we have to convert it back into
+ // the normal range.
+ int_type int_exponent = static_cast<int_type>(exponent - HF::exponent_bias);
+ // If the number is all zeros, then we actually have to NOT shift the
+ // exponent.
+ int_exponent = is_zero ? 0 : int_exponent;
+
+ // If we are denorm, then start shifting, and decreasing the exponent until
+ // our leading bit is 1.
+
+ if (is_denorm) {
+ while ((fraction & HF::fraction_top_bit) == 0) {
+ fraction = static_cast<uint_type>(fraction << 1);
+ int_exponent = static_cast<int_type>(int_exponent - 1);
+ }
+ // Since this is denormalized, we have to consume the leading 1 since it
+ // will end up being implicit.
+ fraction = static_cast<uint_type>(fraction << 1); // eat the leading 1
+ fraction &= HF::fraction_represent_mask;
+ }
+
+ uint_type fraction_nibbles = HF::fraction_nibbles;
+ // We do not have to display any trailing 0s, since this represents the
+ // fractional part.
+ while (fraction_nibbles > 0 && (fraction & 0xF) == 0) {
+ // Shift off any trailing values;
+ fraction = static_cast<uint_type>(fraction >> 4);
+ --fraction_nibbles;
+ }
+
+ const auto saved_flags = os.flags();
+ const auto saved_fill = os.fill();
+
+ os << sign << "0x" << (is_zero ? '0' : '1');
+ if (fraction_nibbles) {
+ // Make sure to keep the leading 0s in place, since this is the fractional
+ // part.
+ os << "." << std::setw(static_cast<int>(fraction_nibbles))
+ << std::setfill('0') << std::hex << fraction;
+ }
+ os << "p" << std::dec << (int_exponent >= 0 ? "+" : "") << int_exponent;
+
+ os.flags(saved_flags);
+ os.fill(saved_fill);
+
+ return os;
+}
+
+// Returns true if negate_value is true and the next character on the
+// input stream is a plus or minus sign. In that case we also set the fail bit
+// on the stream and set the value to the zero value for its type.
+template <typename T, typename Traits>
+inline bool RejectParseDueToLeadingSign(std::istream& is, bool negate_value,
+ HexFloat<T, Traits>& value) {
+ if (negate_value) {
+ auto next_char = is.peek();
+ if (next_char == '-' || next_char == '+') {
+ // Fail the parse. Emulate standard behaviour by setting the value to
+ // the zero value, and set the fail bit on the stream.
+ value = HexFloat<T, Traits>(typename HexFloat<T, Traits>::uint_type(0));
+ is.setstate(std::ios_base::failbit);
+ return true;
+ }
+ }
+ return false;
+}
+
+// Parses a floating point number from the given stream and stores it into the
+// value parameter.
+// If negate_value is true then the number may not have a leading minus or
+// plus, and if it successfully parses, then the number is negated before
+// being stored into the value parameter.
+// If the value cannot be correctly parsed or overflows the target floating
+// point type, then set the fail bit on the stream.
+// TODO(dneto): Promise C++11 standard behavior in how the value is set in
+// the error case, but only after all target platforms implement it correctly.
+// In particular, the Microsoft C++ runtime appears to be out of spec.
+template <typename T, typename Traits>
+inline std::istream& ParseNormalFloat(std::istream& is, bool negate_value,
+ HexFloat<T, Traits>& value) {
+ if (RejectParseDueToLeadingSign(is, negate_value, value)) {
+ return is;
+ }
+ T val;
+ is >> val;
+ if (negate_value) {
+ val = -val;
+ }
+ value.set_value(val);
+ // In the failure case, map -0.0 to 0.0.
+ if (is.fail() && value.getUnsignedBits() == 0u) {
+ value = HexFloat<T, Traits>(typename HexFloat<T, Traits>::uint_type(0));
+ }
+ if (val.isInfinity()) {
+ // Fail the parse. Emulate standard behaviour by setting the value to
+ // the closest normal value, and set the fail bit on the stream.
+ value.set_value((value.isNegative() | negate_value) ? T::lowest()
+ : T::max());
+ is.setstate(std::ios_base::failbit);
+ }
+ return is;
+}
+
+// Specialization of ParseNormalFloat for FloatProxy<Float16> values.
+// This will parse the float as it were a 32-bit floating point number,
+// and then round it down to fit into a Float16 value.
+// The number is rounded towards zero.
+// If negate_value is true then the number may not have a leading minus or
+// plus, and if it successfully parses, then the number is negated before
+// being stored into the value parameter.
+// If the value cannot be correctly parsed or overflows the target floating
+// point type, then set the fail bit on the stream.
+// TODO(dneto): Promise C++11 standard behavior in how the value is set in
+// the error case, but only after all target platforms implement it correctly.
+// In particular, the Microsoft C++ runtime appears to be out of spec.
+template <>
+inline std::istream&
+ParseNormalFloat<FloatProxy<Float16>, HexFloatTraits<FloatProxy<Float16>>>(
+ std::istream& is, bool negate_value,
+ HexFloat<FloatProxy<Float16>, HexFloatTraits<FloatProxy<Float16>>>& value) {
+ // First parse as a 32-bit float.
+ HexFloat<FloatProxy<float>> float_val(0.0f);
+ ParseNormalFloat(is, negate_value, float_val);
+
+ // Then convert to 16-bit float, saturating at infinities, and
+ // rounding toward zero.
+ float_val.castTo(value, kRoundToZero);
+
+ // Overflow on 16-bit behaves the same as for 32- and 64-bit: set the
+ // fail bit and set the lowest or highest value.
+ if (Float16::isInfinity(value.value().getAsFloat())) {
+ value.set_value(value.isNegative() ? Float16::lowest() : Float16::max());
+ is.setstate(std::ios_base::failbit);
+ }
+ return is;
+}
+
+// Reads a HexFloat from the given stream.
+// If the float is not encoded as a hex-float then it will be parsed
+// as a regular float.
+// This may fail if your stream does not support at least one unget.
+// Nan values can be encoded with "0x1.<not zero>p+exponent_bias".
+// This would normally overflow a float and round to
+// infinity but this special pattern is the exact representation for a NaN,
+// and therefore is actually encoded as the correct NaN. To encode inf,
+// either 0x0p+exponent_bias can be specified or any exponent greater than
+// exponent_bias.
+// Examples using IEEE 32-bit float encoding.
+// 0x1.0p+128 (+inf)
+// -0x1.0p-128 (-inf)
+//
+// 0x1.1p+128 (+Nan)
+// -0x1.1p+128 (-Nan)
+//
+// 0x1p+129 (+inf)
+// -0x1p+129 (-inf)
+template <typename T, typename Traits>
+std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
+ using HF = HexFloat<T, Traits>;
+ using uint_type = typename HF::uint_type;
+ using int_type = typename HF::int_type;
+
+ value.set_value(static_cast<typename HF::native_type>(0.f));
+
+ if (is.flags() & std::ios::skipws) {
+ // If the user wants to skip whitespace , then we should obey that.
+ while (std::isspace(is.peek())) {
+ is.get();
+ }
+ }
+
+ auto next_char = is.peek();
+ bool negate_value = false;
+
+ if (next_char != '-' && next_char != '0') {
+ return ParseNormalFloat(is, negate_value, value);
+ }
+
+ if (next_char == '-') {
+ negate_value = true;
+ is.get();
+ next_char = is.peek();
+ }
+
+ if (next_char == '0') {
+ is.get(); // We may have to unget this.
+ auto maybe_hex_start = is.peek();
+ if (maybe_hex_start != 'x' && maybe_hex_start != 'X') {
+ is.unget();
+ return ParseNormalFloat(is, negate_value, value);
+ } else {
+ is.get(); // Throw away the 'x';
+ }
+ } else {
+ return ParseNormalFloat(is, negate_value, value);
+ }
+
+ // This "looks" like a hex-float so treat it as one.
+ bool seen_p = false;
+ bool seen_dot = false;
+ uint_type fraction_index = 0;
+
+ uint_type fraction = 0;
+ int_type exponent = HF::exponent_bias;
+
+ // Strip off leading zeros so we don't have to special-case them later.
+ while ((next_char = is.peek()) == '0') {
+ is.get();
+ }
+
+ bool is_denorm =
+ true; // Assume denorm "representation" until we hear otherwise.
+ // NB: This does not mean the value is actually denorm,
+ // it just means that it was written 0.
+ bool bits_written = false; // Stays false until we write a bit.
+ while (!seen_p && !seen_dot) {
+ // Handle characters that are left of the fractional part.
+ if (next_char == '.') {
+ seen_dot = true;
+ } else if (next_char == 'p') {
+ seen_p = true;
+ } else if (::isxdigit(next_char)) {
+ // We know this is not denormalized since we have stripped all leading
+ // zeroes and we are not a ".".
+ is_denorm = false;
+ int number = get_nibble_from_character(next_char);
+ for (int i = 0; i < 4; ++i, number <<= 1) {
+ uint_type write_bit = (number & 0x8) ? 0x1 : 0x0;
+ if (bits_written) {
+ // If we are here the bits represented belong in the fractional
+ // part of the float, and we have to adjust the exponent accordingly.
+ fraction = static_cast<uint_type>(
+ fraction |
+ static_cast<uint_type>(
+ write_bit << (HF::top_bit_left_shift - fraction_index++)));
+ exponent = static_cast<int_type>(exponent + 1);
+ }
+ bits_written |= write_bit != 0;
+ }
+ } else {
+ // We have not found our exponent yet, so we have to fail.
+ is.setstate(std::ios::failbit);
+ return is;
+ }
+ is.get();
+ next_char = is.peek();
+ }
+ bits_written = false;
+ while (seen_dot && !seen_p) {
+ // Handle only fractional parts now.
+ if (next_char == 'p') {
+ seen_p = true;
+ } else if (::isxdigit(next_char)) {
+ int number = get_nibble_from_character(next_char);
+ for (int i = 0; i < 4; ++i, number <<= 1) {
+ uint_type write_bit = (number & 0x8) ? 0x01 : 0x00;
+ bits_written |= write_bit != 0;
+ if (is_denorm && !bits_written) {
+ // Handle modifying the exponent here this way we can handle
+ // an arbitrary number of hex values without overflowing our
+ // integer.
+ exponent = static_cast<int_type>(exponent - 1);
+ } else {
+ fraction = static_cast<uint_type>(
+ fraction |
+ static_cast<uint_type>(
+ write_bit << (HF::top_bit_left_shift - fraction_index++)));
+ }
+ }
+ } else {
+ // We still have not found our 'p' exponent yet, so this is not a valid
+ // hex-float.
+ is.setstate(std::ios::failbit);
+ return is;
+ }
+ is.get();
+ next_char = is.peek();
+ }
+
+ bool seen_sign = false;
+ int8_t exponent_sign = 1;
+ int_type written_exponent = 0;
+ while (true) {
+ if ((next_char == '-' || next_char == '+')) {
+ if (seen_sign) {
+ is.setstate(std::ios::failbit);
+ return is;
+ }
+ seen_sign = true;
+ exponent_sign = (next_char == '-') ? -1 : 1;
+ } else if (::isdigit(next_char)) {
+ // Hex-floats express their exponent as decimal.
+ written_exponent = static_cast<int_type>(written_exponent * 10);
+ written_exponent =
+ static_cast<int_type>(written_exponent + (next_char - '0'));
+ } else {
+ break;
+ }
+ is.get();
+ next_char = is.peek();
+ }
+
+ written_exponent = static_cast<int_type>(written_exponent * exponent_sign);
+ exponent = static_cast<int_type>(exponent + written_exponent);
+
+ bool is_zero = is_denorm && (fraction == 0);
+ if (is_denorm && !is_zero) {
+ fraction = static_cast<uint_type>(fraction << 1);
+ exponent = static_cast<int_type>(exponent - 1);
+ } else if (is_zero) {
+ exponent = 0;
+ }
+
+ if (exponent <= 0 && !is_zero) {
+ fraction = static_cast<uint_type>(fraction >> 1);
+ fraction |= static_cast<uint_type>(1) << HF::top_bit_left_shift;
+ }
+
+ fraction = (fraction >> HF::fraction_right_shift) & HF::fraction_encode_mask;
+
+ const int_type max_exponent =
+ SetBits<uint_type, 0, HF::num_exponent_bits>::get;
+
+ // Handle actual denorm numbers
+ while (exponent < 0 && !is_zero) {
+ fraction = static_cast<uint_type>(fraction >> 1);
+ exponent = static_cast<int_type>(exponent + 1);
+
+ fraction &= HF::fraction_encode_mask;
+ if (fraction == 0) {
+ // We have underflowed our fraction. We should clamp to zero.
+ is_zero = true;
+ exponent = 0;
+ }
+ }
+
+ // We have overflowed so we should be inf/-inf.
+ if (exponent > max_exponent) {
+ exponent = max_exponent;
+ fraction = 0;
+ }
+
+ uint_type output_bits = static_cast<uint_type>(
+ static_cast<uint_type>(negate_value ? 1 : 0) << HF::top_bit_left_shift);
+ output_bits |= fraction;
+
+ uint_type shifted_exponent = static_cast<uint_type>(
+ static_cast<uint_type>(exponent << HF::exponent_left_shift) &
+ HF::exponent_mask);
+ output_bits |= shifted_exponent;
+
+ T output_float = spvutils::BitwiseCast<T>(output_bits);
+ value.set_value(output_float);
+
+ return is;
+}
+
+// Writes a FloatProxy value to a stream.
+// Zero and normal numbers are printed in the usual notation, but with
+// enough digits to fully reproduce the value. Other values (subnormal,
+// NaN, and infinity) are printed as a hex float.
+template <typename T>
+std::ostream& operator<<(std::ostream& os, const FloatProxy<T>& value) {
+ auto float_val = value.getAsFloat();
+ switch (std::fpclassify(float_val)) {
+ case FP_ZERO:
+ case FP_NORMAL: {
+ auto saved_precision = os.precision();
+ os.precision(std::numeric_limits<T>::digits10);
+ os << float_val;
+ os.precision(saved_precision);
+ } break;
+ default:
+ os << HexFloat<FloatProxy<T>>(value);
+ break;
+ }
+ return os;
+}
+
+template <>
+inline std::ostream& operator<<<Float16>(std::ostream& os,
+ const FloatProxy<Float16>& value) {
+ os << HexFloat<FloatProxy<Float16>>(value);
+ return os;
+}
+}
+
+#endif // LIBSPIRV_UTIL_HEX_FLOAT_H_
diff --git a/src/3rdparty/glslang/SPIRV/spirv.hpp b/src/3rdparty/glslang/SPIRV/spirv.hpp
new file mode 100644
index 0000000..62c2dc9
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/spirv.hpp
@@ -0,0 +1,1343 @@
+// Copyright (c) 2014-2019 The Khronos Group Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and/or associated documentation files (the "Materials"),
+// to deal in the Materials without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Materials, and to permit persons to whom the
+// Materials are furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
+// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
+// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
+// IN THE MATERIALS.
+
+// This header is automatically generated by the same tool that creates
+// the Binary Section of the SPIR-V specification.
+
+// Enumeration tokens for SPIR-V, in various styles:
+// C, C++, C++11, JSON, Lua, Python, C#, D
+//
+// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL
+// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL
+// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL
+// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL
+// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']
+// - C# will use enum classes in the Specification class located in the "Spv" namespace,
+// e.g.: Spv.Specification.SourceLanguage.GLSL
+// - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL
+//
+// Some tokens act like mask values, which can be OR'd together,
+// while others are mutually exclusive. The mask-like ones have
+// "Mask" in their name, and a parallel enum that has the shift
+// amount (1 << x) for each corresponding enumerant.
+
+#ifndef spirv_HPP
+#define spirv_HPP
+
+namespace spv {
+
+typedef unsigned int Id;
+
+#define SPV_VERSION 0x10300
+#define SPV_REVISION 7
+
+static const unsigned int MagicNumber = 0x07230203;
+static const unsigned int Version = 0x00010300;
+static const unsigned int Revision = 7;
+static const unsigned int OpCodeMask = 0xffff;
+static const unsigned int WordCountShift = 16;
+
+enum SourceLanguage {
+ SourceLanguageUnknown = 0,
+ SourceLanguageESSL = 1,
+ SourceLanguageGLSL = 2,
+ SourceLanguageOpenCL_C = 3,
+ SourceLanguageOpenCL_CPP = 4,
+ SourceLanguageHLSL = 5,
+ SourceLanguageMax = 0x7fffffff,
+};
+
+enum ExecutionModel {
+ ExecutionModelVertex = 0,
+ ExecutionModelTessellationControl = 1,
+ ExecutionModelTessellationEvaluation = 2,
+ ExecutionModelGeometry = 3,
+ ExecutionModelFragment = 4,
+ ExecutionModelGLCompute = 5,
+ ExecutionModelKernel = 6,
+ ExecutionModelTaskNV = 5267,
+ ExecutionModelMeshNV = 5268,
+ ExecutionModelRayGenerationNV = 5313,
+ ExecutionModelIntersectionNV = 5314,
+ ExecutionModelAnyHitNV = 5315,
+ ExecutionModelClosestHitNV = 5316,
+ ExecutionModelMissNV = 5317,
+ ExecutionModelCallableNV = 5318,
+ ExecutionModelMax = 0x7fffffff,
+};
+
+enum AddressingModel {
+ AddressingModelLogical = 0,
+ AddressingModelPhysical32 = 1,
+ AddressingModelPhysical64 = 2,
+ AddressingModelPhysicalStorageBuffer64EXT = 5348,
+ AddressingModelMax = 0x7fffffff,
+};
+
+enum MemoryModel {
+ MemoryModelSimple = 0,
+ MemoryModelGLSL450 = 1,
+ MemoryModelOpenCL = 2,
+ MemoryModelVulkanKHR = 3,
+ MemoryModelMax = 0x7fffffff,
+};
+
+enum ExecutionMode {
+ ExecutionModeInvocations = 0,
+ ExecutionModeSpacingEqual = 1,
+ ExecutionModeSpacingFractionalEven = 2,
+ ExecutionModeSpacingFractionalOdd = 3,
+ ExecutionModeVertexOrderCw = 4,
+ ExecutionModeVertexOrderCcw = 5,
+ ExecutionModePixelCenterInteger = 6,
+ ExecutionModeOriginUpperLeft = 7,
+ ExecutionModeOriginLowerLeft = 8,
+ ExecutionModeEarlyFragmentTests = 9,
+ ExecutionModePointMode = 10,
+ ExecutionModeXfb = 11,
+ ExecutionModeDepthReplacing = 12,
+ ExecutionModeDepthGreater = 14,
+ ExecutionModeDepthLess = 15,
+ ExecutionModeDepthUnchanged = 16,
+ ExecutionModeLocalSize = 17,
+ ExecutionModeLocalSizeHint = 18,
+ ExecutionModeInputPoints = 19,
+ ExecutionModeInputLines = 20,
+ ExecutionModeInputLinesAdjacency = 21,
+ ExecutionModeTriangles = 22,
+ ExecutionModeInputTrianglesAdjacency = 23,
+ ExecutionModeQuads = 24,
+ ExecutionModeIsolines = 25,
+ ExecutionModeOutputVertices = 26,
+ ExecutionModeOutputPoints = 27,
+ ExecutionModeOutputLineStrip = 28,
+ ExecutionModeOutputTriangleStrip = 29,
+ ExecutionModeVecTypeHint = 30,
+ ExecutionModeContractionOff = 31,
+ ExecutionModeInitializer = 33,
+ ExecutionModeFinalizer = 34,
+ ExecutionModeSubgroupSize = 35,
+ ExecutionModeSubgroupsPerWorkgroup = 36,
+ ExecutionModeSubgroupsPerWorkgroupId = 37,
+ ExecutionModeLocalSizeId = 38,
+ ExecutionModeLocalSizeHintId = 39,
+ ExecutionModePostDepthCoverage = 4446,
+ ExecutionModeDenormPreserve = 4459,
+ ExecutionModeDenormFlushToZero = 4460,
+ ExecutionModeSignedZeroInfNanPreserve = 4461,
+ ExecutionModeRoundingModeRTE = 4462,
+ ExecutionModeRoundingModeRTZ = 4463,
+ ExecutionModeStencilRefReplacingEXT = 5027,
+ ExecutionModeOutputLinesNV = 5269,
+ ExecutionModeOutputPrimitivesNV = 5270,
+ ExecutionModeDerivativeGroupQuadsNV = 5289,
+ ExecutionModeDerivativeGroupLinearNV = 5290,
+ ExecutionModeOutputTrianglesNV = 5298,
+ ExecutionModeMax = 0x7fffffff,
+};
+
+enum StorageClass {
+ StorageClassUniformConstant = 0,
+ StorageClassInput = 1,
+ StorageClassUniform = 2,
+ StorageClassOutput = 3,
+ StorageClassWorkgroup = 4,
+ StorageClassCrossWorkgroup = 5,
+ StorageClassPrivate = 6,
+ StorageClassFunction = 7,
+ StorageClassGeneric = 8,
+ StorageClassPushConstant = 9,
+ StorageClassAtomicCounter = 10,
+ StorageClassImage = 11,
+ StorageClassStorageBuffer = 12,
+ StorageClassCallableDataNV = 5328,
+ StorageClassIncomingCallableDataNV = 5329,
+ StorageClassRayPayloadNV = 5338,
+ StorageClassHitAttributeNV = 5339,
+ StorageClassIncomingRayPayloadNV = 5342,
+ StorageClassShaderRecordBufferNV = 5343,
+ StorageClassPhysicalStorageBufferEXT = 5349,
+ StorageClassMax = 0x7fffffff,
+};
+
+enum Dim {
+ Dim1D = 0,
+ Dim2D = 1,
+ Dim3D = 2,
+ DimCube = 3,
+ DimRect = 4,
+ DimBuffer = 5,
+ DimSubpassData = 6,
+ DimMax = 0x7fffffff,
+};
+
+enum SamplerAddressingMode {
+ SamplerAddressingModeNone = 0,
+ SamplerAddressingModeClampToEdge = 1,
+ SamplerAddressingModeClamp = 2,
+ SamplerAddressingModeRepeat = 3,
+ SamplerAddressingModeRepeatMirrored = 4,
+ SamplerAddressingModeMax = 0x7fffffff,
+};
+
+enum SamplerFilterMode {
+ SamplerFilterModeNearest = 0,
+ SamplerFilterModeLinear = 1,
+ SamplerFilterModeMax = 0x7fffffff,
+};
+
+enum ImageFormat {
+ ImageFormatUnknown = 0,
+ ImageFormatRgba32f = 1,
+ ImageFormatRgba16f = 2,
+ ImageFormatR32f = 3,
+ ImageFormatRgba8 = 4,
+ ImageFormatRgba8Snorm = 5,
+ ImageFormatRg32f = 6,
+ ImageFormatRg16f = 7,
+ ImageFormatR11fG11fB10f = 8,
+ ImageFormatR16f = 9,
+ ImageFormatRgba16 = 10,
+ ImageFormatRgb10A2 = 11,
+ ImageFormatRg16 = 12,
+ ImageFormatRg8 = 13,
+ ImageFormatR16 = 14,
+ ImageFormatR8 = 15,
+ ImageFormatRgba16Snorm = 16,
+ ImageFormatRg16Snorm = 17,
+ ImageFormatRg8Snorm = 18,
+ ImageFormatR16Snorm = 19,
+ ImageFormatR8Snorm = 20,
+ ImageFormatRgba32i = 21,
+ ImageFormatRgba16i = 22,
+ ImageFormatRgba8i = 23,
+ ImageFormatR32i = 24,
+ ImageFormatRg32i = 25,
+ ImageFormatRg16i = 26,
+ ImageFormatRg8i = 27,
+ ImageFormatR16i = 28,
+ ImageFormatR8i = 29,
+ ImageFormatRgba32ui = 30,
+ ImageFormatRgba16ui = 31,
+ ImageFormatRgba8ui = 32,
+ ImageFormatR32ui = 33,
+ ImageFormatRgb10a2ui = 34,
+ ImageFormatRg32ui = 35,
+ ImageFormatRg16ui = 36,
+ ImageFormatRg8ui = 37,
+ ImageFormatR16ui = 38,
+ ImageFormatR8ui = 39,
+ ImageFormatMax = 0x7fffffff,
+};
+
+enum ImageChannelOrder {
+ ImageChannelOrderR = 0,
+ ImageChannelOrderA = 1,
+ ImageChannelOrderRG = 2,
+ ImageChannelOrderRA = 3,
+ ImageChannelOrderRGB = 4,
+ ImageChannelOrderRGBA = 5,
+ ImageChannelOrderBGRA = 6,
+ ImageChannelOrderARGB = 7,
+ ImageChannelOrderIntensity = 8,
+ ImageChannelOrderLuminance = 9,
+ ImageChannelOrderRx = 10,
+ ImageChannelOrderRGx = 11,
+ ImageChannelOrderRGBx = 12,
+ ImageChannelOrderDepth = 13,
+ ImageChannelOrderDepthStencil = 14,
+ ImageChannelOrdersRGB = 15,
+ ImageChannelOrdersRGBx = 16,
+ ImageChannelOrdersRGBA = 17,
+ ImageChannelOrdersBGRA = 18,
+ ImageChannelOrderABGR = 19,
+ ImageChannelOrderMax = 0x7fffffff,
+};
+
+enum ImageChannelDataType {
+ ImageChannelDataTypeSnormInt8 = 0,
+ ImageChannelDataTypeSnormInt16 = 1,
+ ImageChannelDataTypeUnormInt8 = 2,
+ ImageChannelDataTypeUnormInt16 = 3,
+ ImageChannelDataTypeUnormShort565 = 4,
+ ImageChannelDataTypeUnormShort555 = 5,
+ ImageChannelDataTypeUnormInt101010 = 6,
+ ImageChannelDataTypeSignedInt8 = 7,
+ ImageChannelDataTypeSignedInt16 = 8,
+ ImageChannelDataTypeSignedInt32 = 9,
+ ImageChannelDataTypeUnsignedInt8 = 10,
+ ImageChannelDataTypeUnsignedInt16 = 11,
+ ImageChannelDataTypeUnsignedInt32 = 12,
+ ImageChannelDataTypeHalfFloat = 13,
+ ImageChannelDataTypeFloat = 14,
+ ImageChannelDataTypeUnormInt24 = 15,
+ ImageChannelDataTypeUnormInt101010_2 = 16,
+ ImageChannelDataTypeMax = 0x7fffffff,
+};
+
+enum ImageOperandsShift {
+ ImageOperandsBiasShift = 0,
+ ImageOperandsLodShift = 1,
+ ImageOperandsGradShift = 2,
+ ImageOperandsConstOffsetShift = 3,
+ ImageOperandsOffsetShift = 4,
+ ImageOperandsConstOffsetsShift = 5,
+ ImageOperandsSampleShift = 6,
+ ImageOperandsMinLodShift = 7,
+ ImageOperandsMakeTexelAvailableKHRShift = 8,
+ ImageOperandsMakeTexelVisibleKHRShift = 9,
+ ImageOperandsNonPrivateTexelKHRShift = 10,
+ ImageOperandsVolatileTexelKHRShift = 11,
+ ImageOperandsMax = 0x7fffffff,
+};
+
+enum ImageOperandsMask {
+ ImageOperandsMaskNone = 0,
+ ImageOperandsBiasMask = 0x00000001,
+ ImageOperandsLodMask = 0x00000002,
+ ImageOperandsGradMask = 0x00000004,
+ ImageOperandsConstOffsetMask = 0x00000008,
+ ImageOperandsOffsetMask = 0x00000010,
+ ImageOperandsConstOffsetsMask = 0x00000020,
+ ImageOperandsSampleMask = 0x00000040,
+ ImageOperandsMinLodMask = 0x00000080,
+ ImageOperandsMakeTexelAvailableKHRMask = 0x00000100,
+ ImageOperandsMakeTexelVisibleKHRMask = 0x00000200,
+ ImageOperandsNonPrivateTexelKHRMask = 0x00000400,
+ ImageOperandsVolatileTexelKHRMask = 0x00000800,
+};
+
+enum FPFastMathModeShift {
+ FPFastMathModeNotNaNShift = 0,
+ FPFastMathModeNotInfShift = 1,
+ FPFastMathModeNSZShift = 2,
+ FPFastMathModeAllowRecipShift = 3,
+ FPFastMathModeFastShift = 4,
+ FPFastMathModeMax = 0x7fffffff,
+};
+
+enum FPFastMathModeMask {
+ FPFastMathModeMaskNone = 0,
+ FPFastMathModeNotNaNMask = 0x00000001,
+ FPFastMathModeNotInfMask = 0x00000002,
+ FPFastMathModeNSZMask = 0x00000004,
+ FPFastMathModeAllowRecipMask = 0x00000008,
+ FPFastMathModeFastMask = 0x00000010,
+};
+
+enum FPRoundingMode {
+ FPRoundingModeRTE = 0,
+ FPRoundingModeRTZ = 1,
+ FPRoundingModeRTP = 2,
+ FPRoundingModeRTN = 3,
+ FPRoundingModeMax = 0x7fffffff,
+};
+
+enum LinkageType {
+ LinkageTypeExport = 0,
+ LinkageTypeImport = 1,
+ LinkageTypeMax = 0x7fffffff,
+};
+
+enum AccessQualifier {
+ AccessQualifierReadOnly = 0,
+ AccessQualifierWriteOnly = 1,
+ AccessQualifierReadWrite = 2,
+ AccessQualifierMax = 0x7fffffff,
+};
+
+enum FunctionParameterAttribute {
+ FunctionParameterAttributeZext = 0,
+ FunctionParameterAttributeSext = 1,
+ FunctionParameterAttributeByVal = 2,
+ FunctionParameterAttributeSret = 3,
+ FunctionParameterAttributeNoAlias = 4,
+ FunctionParameterAttributeNoCapture = 5,
+ FunctionParameterAttributeNoWrite = 6,
+ FunctionParameterAttributeNoReadWrite = 7,
+ FunctionParameterAttributeMax = 0x7fffffff,
+};
+
+enum Decoration {
+ DecorationRelaxedPrecision = 0,
+ DecorationSpecId = 1,
+ DecorationBlock = 2,
+ DecorationBufferBlock = 3,
+ DecorationRowMajor = 4,
+ DecorationColMajor = 5,
+ DecorationArrayStride = 6,
+ DecorationMatrixStride = 7,
+ DecorationGLSLShared = 8,
+ DecorationGLSLPacked = 9,
+ DecorationCPacked = 10,
+ DecorationBuiltIn = 11,
+ DecorationNoPerspective = 13,
+ DecorationFlat = 14,
+ DecorationPatch = 15,
+ DecorationCentroid = 16,
+ DecorationSample = 17,
+ DecorationInvariant = 18,
+ DecorationRestrict = 19,
+ DecorationAliased = 20,
+ DecorationVolatile = 21,
+ DecorationConstant = 22,
+ DecorationCoherent = 23,
+ DecorationNonWritable = 24,
+ DecorationNonReadable = 25,
+ DecorationUniform = 26,
+ DecorationSaturatedConversion = 28,
+ DecorationStream = 29,
+ DecorationLocation = 30,
+ DecorationComponent = 31,
+ DecorationIndex = 32,
+ DecorationBinding = 33,
+ DecorationDescriptorSet = 34,
+ DecorationOffset = 35,
+ DecorationXfbBuffer = 36,
+ DecorationXfbStride = 37,
+ DecorationFuncParamAttr = 38,
+ DecorationFPRoundingMode = 39,
+ DecorationFPFastMathMode = 40,
+ DecorationLinkageAttributes = 41,
+ DecorationNoContraction = 42,
+ DecorationInputAttachmentIndex = 43,
+ DecorationAlignment = 44,
+ DecorationMaxByteOffset = 45,
+ DecorationAlignmentId = 46,
+ DecorationMaxByteOffsetId = 47,
+ DecorationNoSignedWrap = 4469,
+ DecorationNoUnsignedWrap = 4470,
+ DecorationExplicitInterpAMD = 4999,
+ DecorationOverrideCoverageNV = 5248,
+ DecorationPassthroughNV = 5250,
+ DecorationViewportRelativeNV = 5252,
+ DecorationSecondaryViewportRelativeNV = 5256,
+ DecorationPerPrimitiveNV = 5271,
+ DecorationPerViewNV = 5272,
+ DecorationPerTaskNV = 5273,
+ DecorationPerVertexNV = 5285,
+ DecorationNonUniformEXT = 5300,
+ DecorationRestrictPointerEXT = 5355,
+ DecorationAliasedPointerEXT = 5356,
+ DecorationHlslCounterBufferGOOGLE = 5634,
+ DecorationHlslSemanticGOOGLE = 5635,
+ DecorationMax = 0x7fffffff,
+};
+
+enum BuiltIn {
+ BuiltInPosition = 0,
+ BuiltInPointSize = 1,
+ BuiltInClipDistance = 3,
+ BuiltInCullDistance = 4,
+ BuiltInVertexId = 5,
+ BuiltInInstanceId = 6,
+ BuiltInPrimitiveId = 7,
+ BuiltInInvocationId = 8,
+ BuiltInLayer = 9,
+ BuiltInViewportIndex = 10,
+ BuiltInTessLevelOuter = 11,
+ BuiltInTessLevelInner = 12,
+ BuiltInTessCoord = 13,
+ BuiltInPatchVertices = 14,
+ BuiltInFragCoord = 15,
+ BuiltInPointCoord = 16,
+ BuiltInFrontFacing = 17,
+ BuiltInSampleId = 18,
+ BuiltInSamplePosition = 19,
+ BuiltInSampleMask = 20,
+ BuiltInFragDepth = 22,
+ BuiltInHelperInvocation = 23,
+ BuiltInNumWorkgroups = 24,
+ BuiltInWorkgroupSize = 25,
+ BuiltInWorkgroupId = 26,
+ BuiltInLocalInvocationId = 27,
+ BuiltInGlobalInvocationId = 28,
+ BuiltInLocalInvocationIndex = 29,
+ BuiltInWorkDim = 30,
+ BuiltInGlobalSize = 31,
+ BuiltInEnqueuedWorkgroupSize = 32,
+ BuiltInGlobalOffset = 33,
+ BuiltInGlobalLinearId = 34,
+ BuiltInSubgroupSize = 36,
+ BuiltInSubgroupMaxSize = 37,
+ BuiltInNumSubgroups = 38,
+ BuiltInNumEnqueuedSubgroups = 39,
+ BuiltInSubgroupId = 40,
+ BuiltInSubgroupLocalInvocationId = 41,
+ BuiltInVertexIndex = 42,
+ BuiltInInstanceIndex = 43,
+ BuiltInSubgroupEqMask = 4416,
+ BuiltInSubgroupEqMaskKHR = 4416,
+ BuiltInSubgroupGeMask = 4417,
+ BuiltInSubgroupGeMaskKHR = 4417,
+ BuiltInSubgroupGtMask = 4418,
+ BuiltInSubgroupGtMaskKHR = 4418,
+ BuiltInSubgroupLeMask = 4419,
+ BuiltInSubgroupLeMaskKHR = 4419,
+ BuiltInSubgroupLtMask = 4420,
+ BuiltInSubgroupLtMaskKHR = 4420,
+ BuiltInBaseVertex = 4424,
+ BuiltInBaseInstance = 4425,
+ BuiltInDrawIndex = 4426,
+ BuiltInDeviceIndex = 4438,
+ BuiltInViewIndex = 4440,
+ BuiltInBaryCoordNoPerspAMD = 4992,
+ BuiltInBaryCoordNoPerspCentroidAMD = 4993,
+ BuiltInBaryCoordNoPerspSampleAMD = 4994,
+ BuiltInBaryCoordSmoothAMD = 4995,
+ BuiltInBaryCoordSmoothCentroidAMD = 4996,
+ BuiltInBaryCoordSmoothSampleAMD = 4997,
+ BuiltInBaryCoordPullModelAMD = 4998,
+ BuiltInFragStencilRefEXT = 5014,
+ BuiltInViewportMaskNV = 5253,
+ BuiltInSecondaryPositionNV = 5257,
+ BuiltInSecondaryViewportMaskNV = 5258,
+ BuiltInPositionPerViewNV = 5261,
+ BuiltInViewportMaskPerViewNV = 5262,
+ BuiltInFullyCoveredEXT = 5264,
+ BuiltInTaskCountNV = 5274,
+ BuiltInPrimitiveCountNV = 5275,
+ BuiltInPrimitiveIndicesNV = 5276,
+ BuiltInClipDistancePerViewNV = 5277,
+ BuiltInCullDistancePerViewNV = 5278,
+ BuiltInLayerPerViewNV = 5279,
+ BuiltInMeshViewCountNV = 5280,
+ BuiltInMeshViewIndicesNV = 5281,
+ BuiltInBaryCoordNV = 5286,
+ BuiltInBaryCoordNoPerspNV = 5287,
+ BuiltInFragSizeEXT = 5292,
+ BuiltInFragmentSizeNV = 5292,
+ BuiltInFragInvocationCountEXT = 5293,
+ BuiltInInvocationsPerPixelNV = 5293,
+ BuiltInLaunchIdNV = 5319,
+ BuiltInLaunchSizeNV = 5320,
+ BuiltInWorldRayOriginNV = 5321,
+ BuiltInWorldRayDirectionNV = 5322,
+ BuiltInObjectRayOriginNV = 5323,
+ BuiltInObjectRayDirectionNV = 5324,
+ BuiltInRayTminNV = 5325,
+ BuiltInRayTmaxNV = 5326,
+ BuiltInInstanceCustomIndexNV = 5327,
+ BuiltInObjectToWorldNV = 5330,
+ BuiltInWorldToObjectNV = 5331,
+ BuiltInHitTNV = 5332,
+ BuiltInHitKindNV = 5333,
+ BuiltInIncomingRayFlagsNV = 5351,
+ BuiltInMax = 0x7fffffff,
+};
+
+enum SelectionControlShift {
+ SelectionControlFlattenShift = 0,
+ SelectionControlDontFlattenShift = 1,
+ SelectionControlMax = 0x7fffffff,
+};
+
+enum SelectionControlMask {
+ SelectionControlMaskNone = 0,
+ SelectionControlFlattenMask = 0x00000001,
+ SelectionControlDontFlattenMask = 0x00000002,
+};
+
+enum LoopControlShift {
+ LoopControlUnrollShift = 0,
+ LoopControlDontUnrollShift = 1,
+ LoopControlDependencyInfiniteShift = 2,
+ LoopControlDependencyLengthShift = 3,
+ LoopControlMax = 0x7fffffff,
+};
+
+enum LoopControlMask {
+ LoopControlMaskNone = 0,
+ LoopControlUnrollMask = 0x00000001,
+ LoopControlDontUnrollMask = 0x00000002,
+ LoopControlDependencyInfiniteMask = 0x00000004,
+ LoopControlDependencyLengthMask = 0x00000008,
+};
+
+enum FunctionControlShift {
+ FunctionControlInlineShift = 0,
+ FunctionControlDontInlineShift = 1,
+ FunctionControlPureShift = 2,
+ FunctionControlConstShift = 3,
+ FunctionControlMax = 0x7fffffff,
+};
+
+enum FunctionControlMask {
+ FunctionControlMaskNone = 0,
+ FunctionControlInlineMask = 0x00000001,
+ FunctionControlDontInlineMask = 0x00000002,
+ FunctionControlPureMask = 0x00000004,
+ FunctionControlConstMask = 0x00000008,
+};
+
+enum MemorySemanticsShift {
+ MemorySemanticsAcquireShift = 1,
+ MemorySemanticsReleaseShift = 2,
+ MemorySemanticsAcquireReleaseShift = 3,
+ MemorySemanticsSequentiallyConsistentShift = 4,
+ MemorySemanticsUniformMemoryShift = 6,
+ MemorySemanticsSubgroupMemoryShift = 7,
+ MemorySemanticsWorkgroupMemoryShift = 8,
+ MemorySemanticsCrossWorkgroupMemoryShift = 9,
+ MemorySemanticsAtomicCounterMemoryShift = 10,
+ MemorySemanticsImageMemoryShift = 11,
+ MemorySemanticsOutputMemoryKHRShift = 12,
+ MemorySemanticsMakeAvailableKHRShift = 13,
+ MemorySemanticsMakeVisibleKHRShift = 14,
+ MemorySemanticsMax = 0x7fffffff,
+};
+
+enum MemorySemanticsMask {
+ MemorySemanticsMaskNone = 0,
+ MemorySemanticsAcquireMask = 0x00000002,
+ MemorySemanticsReleaseMask = 0x00000004,
+ MemorySemanticsAcquireReleaseMask = 0x00000008,
+ MemorySemanticsSequentiallyConsistentMask = 0x00000010,
+ MemorySemanticsUniformMemoryMask = 0x00000040,
+ MemorySemanticsSubgroupMemoryMask = 0x00000080,
+ MemorySemanticsWorkgroupMemoryMask = 0x00000100,
+ MemorySemanticsCrossWorkgroupMemoryMask = 0x00000200,
+ MemorySemanticsAtomicCounterMemoryMask = 0x00000400,
+ MemorySemanticsImageMemoryMask = 0x00000800,
+ MemorySemanticsOutputMemoryKHRMask = 0x00001000,
+ MemorySemanticsMakeAvailableKHRMask = 0x00002000,
+ MemorySemanticsMakeVisibleKHRMask = 0x00004000,
+};
+
+enum MemoryAccessShift {
+ MemoryAccessVolatileShift = 0,
+ MemoryAccessAlignedShift = 1,
+ MemoryAccessNontemporalShift = 2,
+ MemoryAccessMakePointerAvailableKHRShift = 3,
+ MemoryAccessMakePointerVisibleKHRShift = 4,
+ MemoryAccessNonPrivatePointerKHRShift = 5,
+ MemoryAccessMax = 0x7fffffff,
+};
+
+enum MemoryAccessMask {
+ MemoryAccessMaskNone = 0,
+ MemoryAccessVolatileMask = 0x00000001,
+ MemoryAccessAlignedMask = 0x00000002,
+ MemoryAccessNontemporalMask = 0x00000004,
+ MemoryAccessMakePointerAvailableKHRMask = 0x00000008,
+ MemoryAccessMakePointerVisibleKHRMask = 0x00000010,
+ MemoryAccessNonPrivatePointerKHRMask = 0x00000020,
+};
+
+enum Scope {
+ ScopeCrossDevice = 0,
+ ScopeDevice = 1,
+ ScopeWorkgroup = 2,
+ ScopeSubgroup = 3,
+ ScopeInvocation = 4,
+ ScopeQueueFamilyKHR = 5,
+ ScopeMax = 0x7fffffff,
+};
+
+enum GroupOperation {
+ GroupOperationReduce = 0,
+ GroupOperationInclusiveScan = 1,
+ GroupOperationExclusiveScan = 2,
+ GroupOperationClusteredReduce = 3,
+ GroupOperationPartitionedReduceNV = 6,
+ GroupOperationPartitionedInclusiveScanNV = 7,
+ GroupOperationPartitionedExclusiveScanNV = 8,
+ GroupOperationMax = 0x7fffffff,
+};
+
+enum KernelEnqueueFlags {
+ KernelEnqueueFlagsNoWait = 0,
+ KernelEnqueueFlagsWaitKernel = 1,
+ KernelEnqueueFlagsWaitWorkGroup = 2,
+ KernelEnqueueFlagsMax = 0x7fffffff,
+};
+
+enum KernelProfilingInfoShift {
+ KernelProfilingInfoCmdExecTimeShift = 0,
+ KernelProfilingInfoMax = 0x7fffffff,
+};
+
+enum KernelProfilingInfoMask {
+ KernelProfilingInfoMaskNone = 0,
+ KernelProfilingInfoCmdExecTimeMask = 0x00000001,
+};
+
+enum Capability {
+ CapabilityMatrix = 0,
+ CapabilityShader = 1,
+ CapabilityGeometry = 2,
+ CapabilityTessellation = 3,
+ CapabilityAddresses = 4,
+ CapabilityLinkage = 5,
+ CapabilityKernel = 6,
+ CapabilityVector16 = 7,
+ CapabilityFloat16Buffer = 8,
+ CapabilityFloat16 = 9,
+ CapabilityFloat64 = 10,
+ CapabilityInt64 = 11,
+ CapabilityInt64Atomics = 12,
+ CapabilityImageBasic = 13,
+ CapabilityImageReadWrite = 14,
+ CapabilityImageMipmap = 15,
+ CapabilityPipes = 17,
+ CapabilityGroups = 18,
+ CapabilityDeviceEnqueue = 19,
+ CapabilityLiteralSampler = 20,
+ CapabilityAtomicStorage = 21,
+ CapabilityInt16 = 22,
+ CapabilityTessellationPointSize = 23,
+ CapabilityGeometryPointSize = 24,
+ CapabilityImageGatherExtended = 25,
+ CapabilityStorageImageMultisample = 27,
+ CapabilityUniformBufferArrayDynamicIndexing = 28,
+ CapabilitySampledImageArrayDynamicIndexing = 29,
+ CapabilityStorageBufferArrayDynamicIndexing = 30,
+ CapabilityStorageImageArrayDynamicIndexing = 31,
+ CapabilityClipDistance = 32,
+ CapabilityCullDistance = 33,
+ CapabilityImageCubeArray = 34,
+ CapabilitySampleRateShading = 35,
+ CapabilityImageRect = 36,
+ CapabilitySampledRect = 37,
+ CapabilityGenericPointer = 38,
+ CapabilityInt8 = 39,
+ CapabilityInputAttachment = 40,
+ CapabilitySparseResidency = 41,
+ CapabilityMinLod = 42,
+ CapabilitySampled1D = 43,
+ CapabilityImage1D = 44,
+ CapabilitySampledCubeArray = 45,
+ CapabilitySampledBuffer = 46,
+ CapabilityImageBuffer = 47,
+ CapabilityImageMSArray = 48,
+ CapabilityStorageImageExtendedFormats = 49,
+ CapabilityImageQuery = 50,
+ CapabilityDerivativeControl = 51,
+ CapabilityInterpolationFunction = 52,
+ CapabilityTransformFeedback = 53,
+ CapabilityGeometryStreams = 54,
+ CapabilityStorageImageReadWithoutFormat = 55,
+ CapabilityStorageImageWriteWithoutFormat = 56,
+ CapabilityMultiViewport = 57,
+ CapabilitySubgroupDispatch = 58,
+ CapabilityNamedBarrier = 59,
+ CapabilityPipeStorage = 60,
+ CapabilityGroupNonUniform = 61,
+ CapabilityGroupNonUniformVote = 62,
+ CapabilityGroupNonUniformArithmetic = 63,
+ CapabilityGroupNonUniformBallot = 64,
+ CapabilityGroupNonUniformShuffle = 65,
+ CapabilityGroupNonUniformShuffleRelative = 66,
+ CapabilityGroupNonUniformClustered = 67,
+ CapabilityGroupNonUniformQuad = 68,
+ CapabilitySubgroupBallotKHR = 4423,
+ CapabilityDrawParameters = 4427,
+ CapabilitySubgroupVoteKHR = 4431,
+ CapabilityStorageBuffer16BitAccess = 4433,
+ CapabilityStorageUniformBufferBlock16 = 4433,
+ CapabilityStorageUniform16 = 4434,
+ CapabilityUniformAndStorageBuffer16BitAccess = 4434,
+ CapabilityStoragePushConstant16 = 4435,
+ CapabilityStorageInputOutput16 = 4436,
+ CapabilityDeviceGroup = 4437,
+ CapabilityMultiView = 4439,
+ CapabilityVariablePointersStorageBuffer = 4441,
+ CapabilityVariablePointers = 4442,
+ CapabilityAtomicStorageOps = 4445,
+ CapabilitySampleMaskPostDepthCoverage = 4447,
+ CapabilityStorageBuffer8BitAccess = 4448,
+ CapabilityUniformAndStorageBuffer8BitAccess = 4449,
+ CapabilityStoragePushConstant8 = 4450,
+ CapabilityDenormPreserve = 4464,
+ CapabilityDenormFlushToZero = 4465,
+ CapabilitySignedZeroInfNanPreserve = 4466,
+ CapabilityRoundingModeRTE = 4467,
+ CapabilityRoundingModeRTZ = 4468,
+ CapabilityFloat16ImageAMD = 5008,
+ CapabilityImageGatherBiasLodAMD = 5009,
+ CapabilityFragmentMaskAMD = 5010,
+ CapabilityStencilExportEXT = 5013,
+ CapabilityImageReadWriteLodAMD = 5015,
+ CapabilitySampleMaskOverrideCoverageNV = 5249,
+ CapabilityGeometryShaderPassthroughNV = 5251,
+ CapabilityShaderViewportIndexLayerEXT = 5254,
+ CapabilityShaderViewportIndexLayerNV = 5254,
+ CapabilityShaderViewportMaskNV = 5255,
+ CapabilityShaderStereoViewNV = 5259,
+ CapabilityPerViewAttributesNV = 5260,
+ CapabilityFragmentFullyCoveredEXT = 5265,
+ CapabilityMeshShadingNV = 5266,
+ CapabilityImageFootprintNV = 5282,
+ CapabilityFragmentBarycentricNV = 5284,
+ CapabilityComputeDerivativeGroupQuadsNV = 5288,
+ CapabilityFragmentDensityEXT = 5291,
+ CapabilityShadingRateNV = 5291,
+ CapabilityGroupNonUniformPartitionedNV = 5297,
+ CapabilityShaderNonUniformEXT = 5301,
+ CapabilityRuntimeDescriptorArrayEXT = 5302,
+ CapabilityInputAttachmentArrayDynamicIndexingEXT = 5303,
+ CapabilityUniformTexelBufferArrayDynamicIndexingEXT = 5304,
+ CapabilityStorageTexelBufferArrayDynamicIndexingEXT = 5305,
+ CapabilityUniformBufferArrayNonUniformIndexingEXT = 5306,
+ CapabilitySampledImageArrayNonUniformIndexingEXT = 5307,
+ CapabilityStorageBufferArrayNonUniformIndexingEXT = 5308,
+ CapabilityStorageImageArrayNonUniformIndexingEXT = 5309,
+ CapabilityInputAttachmentArrayNonUniformIndexingEXT = 5310,
+ CapabilityUniformTexelBufferArrayNonUniformIndexingEXT = 5311,
+ CapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312,
+ CapabilityRayTracingNV = 5340,
+ CapabilityVulkanMemoryModelKHR = 5345,
+ CapabilityVulkanMemoryModelDeviceScopeKHR = 5346,
+ CapabilityPhysicalStorageBufferAddressesEXT = 5347,
+ CapabilityComputeDerivativeGroupLinearNV = 5350,
+ CapabilityCooperativeMatrixNV = 5357,
+ CapabilitySubgroupShuffleINTEL = 5568,
+ CapabilitySubgroupBufferBlockIOINTEL = 5569,
+ CapabilitySubgroupImageBlockIOINTEL = 5570,
+ CapabilitySubgroupImageMediaBlockIOINTEL = 5579,
+ CapabilitySubgroupAvcMotionEstimationINTEL = 5696,
+ CapabilitySubgroupAvcMotionEstimationIntraINTEL = 5697,
+ CapabilitySubgroupAvcMotionEstimationChromaINTEL = 5698,
+ CapabilityMax = 0x7fffffff,
+};
+
+enum Op {
+ OpNop = 0,
+ OpUndef = 1,
+ OpSourceContinued = 2,
+ OpSource = 3,
+ OpSourceExtension = 4,
+ OpName = 5,
+ OpMemberName = 6,
+ OpString = 7,
+ OpLine = 8,
+ OpExtension = 10,
+ OpExtInstImport = 11,
+ OpExtInst = 12,
+ OpMemoryModel = 14,
+ OpEntryPoint = 15,
+ OpExecutionMode = 16,
+ OpCapability = 17,
+ OpTypeVoid = 19,
+ OpTypeBool = 20,
+ OpTypeInt = 21,
+ OpTypeFloat = 22,
+ OpTypeVector = 23,
+ OpTypeMatrix = 24,
+ OpTypeImage = 25,
+ OpTypeSampler = 26,
+ OpTypeSampledImage = 27,
+ OpTypeArray = 28,
+ OpTypeRuntimeArray = 29,
+ OpTypeStruct = 30,
+ OpTypeOpaque = 31,
+ OpTypePointer = 32,
+ OpTypeFunction = 33,
+ OpTypeEvent = 34,
+ OpTypeDeviceEvent = 35,
+ OpTypeReserveId = 36,
+ OpTypeQueue = 37,
+ OpTypePipe = 38,
+ OpTypeForwardPointer = 39,
+ OpConstantTrue = 41,
+ OpConstantFalse = 42,
+ OpConstant = 43,
+ OpConstantComposite = 44,
+ OpConstantSampler = 45,
+ OpConstantNull = 46,
+ OpSpecConstantTrue = 48,
+ OpSpecConstantFalse = 49,
+ OpSpecConstant = 50,
+ OpSpecConstantComposite = 51,
+ OpSpecConstantOp = 52,
+ OpFunction = 54,
+ OpFunctionParameter = 55,
+ OpFunctionEnd = 56,
+ OpFunctionCall = 57,
+ OpVariable = 59,
+ OpImageTexelPointer = 60,
+ OpLoad = 61,
+ OpStore = 62,
+ OpCopyMemory = 63,
+ OpCopyMemorySized = 64,
+ OpAccessChain = 65,
+ OpInBoundsAccessChain = 66,
+ OpPtrAccessChain = 67,
+ OpArrayLength = 68,
+ OpGenericPtrMemSemantics = 69,
+ OpInBoundsPtrAccessChain = 70,
+ OpDecorate = 71,
+ OpMemberDecorate = 72,
+ OpDecorationGroup = 73,
+ OpGroupDecorate = 74,
+ OpGroupMemberDecorate = 75,
+ OpVectorExtractDynamic = 77,
+ OpVectorInsertDynamic = 78,
+ OpVectorShuffle = 79,
+ OpCompositeConstruct = 80,
+ OpCompositeExtract = 81,
+ OpCompositeInsert = 82,
+ OpCopyObject = 83,
+ OpTranspose = 84,
+ OpSampledImage = 86,
+ OpImageSampleImplicitLod = 87,
+ OpImageSampleExplicitLod = 88,
+ OpImageSampleDrefImplicitLod = 89,
+ OpImageSampleDrefExplicitLod = 90,
+ OpImageSampleProjImplicitLod = 91,
+ OpImageSampleProjExplicitLod = 92,
+ OpImageSampleProjDrefImplicitLod = 93,
+ OpImageSampleProjDrefExplicitLod = 94,
+ OpImageFetch = 95,
+ OpImageGather = 96,
+ OpImageDrefGather = 97,
+ OpImageRead = 98,
+ OpImageWrite = 99,
+ OpImage = 100,
+ OpImageQueryFormat = 101,
+ OpImageQueryOrder = 102,
+ OpImageQuerySizeLod = 103,
+ OpImageQuerySize = 104,
+ OpImageQueryLod = 105,
+ OpImageQueryLevels = 106,
+ OpImageQuerySamples = 107,
+ OpConvertFToU = 109,
+ OpConvertFToS = 110,
+ OpConvertSToF = 111,
+ OpConvertUToF = 112,
+ OpUConvert = 113,
+ OpSConvert = 114,
+ OpFConvert = 115,
+ OpQuantizeToF16 = 116,
+ OpConvertPtrToU = 117,
+ OpSatConvertSToU = 118,
+ OpSatConvertUToS = 119,
+ OpConvertUToPtr = 120,
+ OpPtrCastToGeneric = 121,
+ OpGenericCastToPtr = 122,
+ OpGenericCastToPtrExplicit = 123,
+ OpBitcast = 124,
+ OpSNegate = 126,
+ OpFNegate = 127,
+ OpIAdd = 128,
+ OpFAdd = 129,
+ OpISub = 130,
+ OpFSub = 131,
+ OpIMul = 132,
+ OpFMul = 133,
+ OpUDiv = 134,
+ OpSDiv = 135,
+ OpFDiv = 136,
+ OpUMod = 137,
+ OpSRem = 138,
+ OpSMod = 139,
+ OpFRem = 140,
+ OpFMod = 141,
+ OpVectorTimesScalar = 142,
+ OpMatrixTimesScalar = 143,
+ OpVectorTimesMatrix = 144,
+ OpMatrixTimesVector = 145,
+ OpMatrixTimesMatrix = 146,
+ OpOuterProduct = 147,
+ OpDot = 148,
+ OpIAddCarry = 149,
+ OpISubBorrow = 150,
+ OpUMulExtended = 151,
+ OpSMulExtended = 152,
+ OpAny = 154,
+ OpAll = 155,
+ OpIsNan = 156,
+ OpIsInf = 157,
+ OpIsFinite = 158,
+ OpIsNormal = 159,
+ OpSignBitSet = 160,
+ OpLessOrGreater = 161,
+ OpOrdered = 162,
+ OpUnordered = 163,
+ OpLogicalEqual = 164,
+ OpLogicalNotEqual = 165,
+ OpLogicalOr = 166,
+ OpLogicalAnd = 167,
+ OpLogicalNot = 168,
+ OpSelect = 169,
+ OpIEqual = 170,
+ OpINotEqual = 171,
+ OpUGreaterThan = 172,
+ OpSGreaterThan = 173,
+ OpUGreaterThanEqual = 174,
+ OpSGreaterThanEqual = 175,
+ OpULessThan = 176,
+ OpSLessThan = 177,
+ OpULessThanEqual = 178,
+ OpSLessThanEqual = 179,
+ OpFOrdEqual = 180,
+ OpFUnordEqual = 181,
+ OpFOrdNotEqual = 182,
+ OpFUnordNotEqual = 183,
+ OpFOrdLessThan = 184,
+ OpFUnordLessThan = 185,
+ OpFOrdGreaterThan = 186,
+ OpFUnordGreaterThan = 187,
+ OpFOrdLessThanEqual = 188,
+ OpFUnordLessThanEqual = 189,
+ OpFOrdGreaterThanEqual = 190,
+ OpFUnordGreaterThanEqual = 191,
+ OpShiftRightLogical = 194,
+ OpShiftRightArithmetic = 195,
+ OpShiftLeftLogical = 196,
+ OpBitwiseOr = 197,
+ OpBitwiseXor = 198,
+ OpBitwiseAnd = 199,
+ OpNot = 200,
+ OpBitFieldInsert = 201,
+ OpBitFieldSExtract = 202,
+ OpBitFieldUExtract = 203,
+ OpBitReverse = 204,
+ OpBitCount = 205,
+ OpDPdx = 207,
+ OpDPdy = 208,
+ OpFwidth = 209,
+ OpDPdxFine = 210,
+ OpDPdyFine = 211,
+ OpFwidthFine = 212,
+ OpDPdxCoarse = 213,
+ OpDPdyCoarse = 214,
+ OpFwidthCoarse = 215,
+ OpEmitVertex = 218,
+ OpEndPrimitive = 219,
+ OpEmitStreamVertex = 220,
+ OpEndStreamPrimitive = 221,
+ OpControlBarrier = 224,
+ OpMemoryBarrier = 225,
+ OpAtomicLoad = 227,
+ OpAtomicStore = 228,
+ OpAtomicExchange = 229,
+ OpAtomicCompareExchange = 230,
+ OpAtomicCompareExchangeWeak = 231,
+ OpAtomicIIncrement = 232,
+ OpAtomicIDecrement = 233,
+ OpAtomicIAdd = 234,
+ OpAtomicISub = 235,
+ OpAtomicSMin = 236,
+ OpAtomicUMin = 237,
+ OpAtomicSMax = 238,
+ OpAtomicUMax = 239,
+ OpAtomicAnd = 240,
+ OpAtomicOr = 241,
+ OpAtomicXor = 242,
+ OpPhi = 245,
+ OpLoopMerge = 246,
+ OpSelectionMerge = 247,
+ OpLabel = 248,
+ OpBranch = 249,
+ OpBranchConditional = 250,
+ OpSwitch = 251,
+ OpKill = 252,
+ OpReturn = 253,
+ OpReturnValue = 254,
+ OpUnreachable = 255,
+ OpLifetimeStart = 256,
+ OpLifetimeStop = 257,
+ OpGroupAsyncCopy = 259,
+ OpGroupWaitEvents = 260,
+ OpGroupAll = 261,
+ OpGroupAny = 262,
+ OpGroupBroadcast = 263,
+ OpGroupIAdd = 264,
+ OpGroupFAdd = 265,
+ OpGroupFMin = 266,
+ OpGroupUMin = 267,
+ OpGroupSMin = 268,
+ OpGroupFMax = 269,
+ OpGroupUMax = 270,
+ OpGroupSMax = 271,
+ OpReadPipe = 274,
+ OpWritePipe = 275,
+ OpReservedReadPipe = 276,
+ OpReservedWritePipe = 277,
+ OpReserveReadPipePackets = 278,
+ OpReserveWritePipePackets = 279,
+ OpCommitReadPipe = 280,
+ OpCommitWritePipe = 281,
+ OpIsValidReserveId = 282,
+ OpGetNumPipePackets = 283,
+ OpGetMaxPipePackets = 284,
+ OpGroupReserveReadPipePackets = 285,
+ OpGroupReserveWritePipePackets = 286,
+ OpGroupCommitReadPipe = 287,
+ OpGroupCommitWritePipe = 288,
+ OpEnqueueMarker = 291,
+ OpEnqueueKernel = 292,
+ OpGetKernelNDrangeSubGroupCount = 293,
+ OpGetKernelNDrangeMaxSubGroupSize = 294,
+ OpGetKernelWorkGroupSize = 295,
+ OpGetKernelPreferredWorkGroupSizeMultiple = 296,
+ OpRetainEvent = 297,
+ OpReleaseEvent = 298,
+ OpCreateUserEvent = 299,
+ OpIsValidEvent = 300,
+ OpSetUserEventStatus = 301,
+ OpCaptureEventProfilingInfo = 302,
+ OpGetDefaultQueue = 303,
+ OpBuildNDRange = 304,
+ OpImageSparseSampleImplicitLod = 305,
+ OpImageSparseSampleExplicitLod = 306,
+ OpImageSparseSampleDrefImplicitLod = 307,
+ OpImageSparseSampleDrefExplicitLod = 308,
+ OpImageSparseSampleProjImplicitLod = 309,
+ OpImageSparseSampleProjExplicitLod = 310,
+ OpImageSparseSampleProjDrefImplicitLod = 311,
+ OpImageSparseSampleProjDrefExplicitLod = 312,
+ OpImageSparseFetch = 313,
+ OpImageSparseGather = 314,
+ OpImageSparseDrefGather = 315,
+ OpImageSparseTexelsResident = 316,
+ OpNoLine = 317,
+ OpAtomicFlagTestAndSet = 318,
+ OpAtomicFlagClear = 319,
+ OpImageSparseRead = 320,
+ OpSizeOf = 321,
+ OpTypePipeStorage = 322,
+ OpConstantPipeStorage = 323,
+ OpCreatePipeFromPipeStorage = 324,
+ OpGetKernelLocalSizeForSubgroupCount = 325,
+ OpGetKernelMaxNumSubgroups = 326,
+ OpTypeNamedBarrier = 327,
+ OpNamedBarrierInitialize = 328,
+ OpMemoryNamedBarrier = 329,
+ OpModuleProcessed = 330,
+ OpExecutionModeId = 331,
+ OpDecorateId = 332,
+ OpGroupNonUniformElect = 333,
+ OpGroupNonUniformAll = 334,
+ OpGroupNonUniformAny = 335,
+ OpGroupNonUniformAllEqual = 336,
+ OpGroupNonUniformBroadcast = 337,
+ OpGroupNonUniformBroadcastFirst = 338,
+ OpGroupNonUniformBallot = 339,
+ OpGroupNonUniformInverseBallot = 340,
+ OpGroupNonUniformBallotBitExtract = 341,
+ OpGroupNonUniformBallotBitCount = 342,
+ OpGroupNonUniformBallotFindLSB = 343,
+ OpGroupNonUniformBallotFindMSB = 344,
+ OpGroupNonUniformShuffle = 345,
+ OpGroupNonUniformShuffleXor = 346,
+ OpGroupNonUniformShuffleUp = 347,
+ OpGroupNonUniformShuffleDown = 348,
+ OpGroupNonUniformIAdd = 349,
+ OpGroupNonUniformFAdd = 350,
+ OpGroupNonUniformIMul = 351,
+ OpGroupNonUniformFMul = 352,
+ OpGroupNonUniformSMin = 353,
+ OpGroupNonUniformUMin = 354,
+ OpGroupNonUniformFMin = 355,
+ OpGroupNonUniformSMax = 356,
+ OpGroupNonUniformUMax = 357,
+ OpGroupNonUniformFMax = 358,
+ OpGroupNonUniformBitwiseAnd = 359,
+ OpGroupNonUniformBitwiseOr = 360,
+ OpGroupNonUniformBitwiseXor = 361,
+ OpGroupNonUniformLogicalAnd = 362,
+ OpGroupNonUniformLogicalOr = 363,
+ OpGroupNonUniformLogicalXor = 364,
+ OpGroupNonUniformQuadBroadcast = 365,
+ OpGroupNonUniformQuadSwap = 366,
+ OpSubgroupBallotKHR = 4421,
+ OpSubgroupFirstInvocationKHR = 4422,
+ OpSubgroupAllKHR = 4428,
+ OpSubgroupAnyKHR = 4429,
+ OpSubgroupAllEqualKHR = 4430,
+ OpSubgroupReadInvocationKHR = 4432,
+ OpGroupIAddNonUniformAMD = 5000,
+ OpGroupFAddNonUniformAMD = 5001,
+ OpGroupFMinNonUniformAMD = 5002,
+ OpGroupUMinNonUniformAMD = 5003,
+ OpGroupSMinNonUniformAMD = 5004,
+ OpGroupFMaxNonUniformAMD = 5005,
+ OpGroupUMaxNonUniformAMD = 5006,
+ OpGroupSMaxNonUniformAMD = 5007,
+ OpFragmentMaskFetchAMD = 5011,
+ OpFragmentFetchAMD = 5012,
+ OpImageSampleFootprintNV = 5283,
+ OpGroupNonUniformPartitionNV = 5296,
+ OpWritePackedPrimitiveIndices4x8NV = 5299,
+ OpReportIntersectionNV = 5334,
+ OpIgnoreIntersectionNV = 5335,
+ OpTerminateRayNV = 5336,
+ OpTraceNV = 5337,
+ OpTypeAccelerationStructureNV = 5341,
+ OpExecuteCallableNV = 5344,
+ OpTypeCooperativeMatrixNV = 5358,
+ OpCooperativeMatrixLoadNV = 5359,
+ OpCooperativeMatrixStoreNV = 5360,
+ OpCooperativeMatrixMulAddNV = 5361,
+ OpCooperativeMatrixLengthNV = 5362,
+ OpSubgroupShuffleINTEL = 5571,
+ OpSubgroupShuffleDownINTEL = 5572,
+ OpSubgroupShuffleUpINTEL = 5573,
+ OpSubgroupShuffleXorINTEL = 5574,
+ OpSubgroupBlockReadINTEL = 5575,
+ OpSubgroupBlockWriteINTEL = 5576,
+ OpSubgroupImageBlockReadINTEL = 5577,
+ OpSubgroupImageBlockWriteINTEL = 5578,
+ OpSubgroupImageMediaBlockReadINTEL = 5580,
+ OpSubgroupImageMediaBlockWriteINTEL = 5581,
+ OpDecorateStringGOOGLE = 5632,
+ OpMemberDecorateStringGOOGLE = 5633,
+ OpVmeImageINTEL = 5699,
+ OpTypeVmeImageINTEL = 5700,
+ OpTypeAvcImePayloadINTEL = 5701,
+ OpTypeAvcRefPayloadINTEL = 5702,
+ OpTypeAvcSicPayloadINTEL = 5703,
+ OpTypeAvcMcePayloadINTEL = 5704,
+ OpTypeAvcMceResultINTEL = 5705,
+ OpTypeAvcImeResultINTEL = 5706,
+ OpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707,
+ OpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708,
+ OpTypeAvcImeSingleReferenceStreaminINTEL = 5709,
+ OpTypeAvcImeDualReferenceStreaminINTEL = 5710,
+ OpTypeAvcRefResultINTEL = 5711,
+ OpTypeAvcSicResultINTEL = 5712,
+ OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713,
+ OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714,
+ OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715,
+ OpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716,
+ OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717,
+ OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718,
+ OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719,
+ OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720,
+ OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721,
+ OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722,
+ OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723,
+ OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724,
+ OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725,
+ OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726,
+ OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727,
+ OpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728,
+ OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729,
+ OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730,
+ OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731,
+ OpSubgroupAvcMceConvertToImePayloadINTEL = 5732,
+ OpSubgroupAvcMceConvertToImeResultINTEL = 5733,
+ OpSubgroupAvcMceConvertToRefPayloadINTEL = 5734,
+ OpSubgroupAvcMceConvertToRefResultINTEL = 5735,
+ OpSubgroupAvcMceConvertToSicPayloadINTEL = 5736,
+ OpSubgroupAvcMceConvertToSicResultINTEL = 5737,
+ OpSubgroupAvcMceGetMotionVectorsINTEL = 5738,
+ OpSubgroupAvcMceGetInterDistortionsINTEL = 5739,
+ OpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740,
+ OpSubgroupAvcMceGetInterMajorShapeINTEL = 5741,
+ OpSubgroupAvcMceGetInterMinorShapeINTEL = 5742,
+ OpSubgroupAvcMceGetInterDirectionsINTEL = 5743,
+ OpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744,
+ OpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745,
+ OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746,
+ OpSubgroupAvcImeInitializeINTEL = 5747,
+ OpSubgroupAvcImeSetSingleReferenceINTEL = 5748,
+ OpSubgroupAvcImeSetDualReferenceINTEL = 5749,
+ OpSubgroupAvcImeRefWindowSizeINTEL = 5750,
+ OpSubgroupAvcImeAdjustRefOffsetINTEL = 5751,
+ OpSubgroupAvcImeConvertToMcePayloadINTEL = 5752,
+ OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753,
+ OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754,
+ OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755,
+ OpSubgroupAvcImeSetWeightedSadINTEL = 5756,
+ OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757,
+ OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758,
+ OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759,
+ OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760,
+ OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761,
+ OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762,
+ OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763,
+ OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764,
+ OpSubgroupAvcImeConvertToMceResultINTEL = 5765,
+ OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766,
+ OpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767,
+ OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768,
+ OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769,
+ OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770,
+ OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771,
+ OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772,
+ OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773,
+ OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774,
+ OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775,
+ OpSubgroupAvcImeGetBorderReachedINTEL = 5776,
+ OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777,
+ OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778,
+ OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779,
+ OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780,
+ OpSubgroupAvcFmeInitializeINTEL = 5781,
+ OpSubgroupAvcBmeInitializeINTEL = 5782,
+ OpSubgroupAvcRefConvertToMcePayloadINTEL = 5783,
+ OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784,
+ OpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785,
+ OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786,
+ OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787,
+ OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788,
+ OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789,
+ OpSubgroupAvcRefConvertToMceResultINTEL = 5790,
+ OpSubgroupAvcSicInitializeINTEL = 5791,
+ OpSubgroupAvcSicConfigureSkcINTEL = 5792,
+ OpSubgroupAvcSicConfigureIpeLumaINTEL = 5793,
+ OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794,
+ OpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795,
+ OpSubgroupAvcSicConvertToMcePayloadINTEL = 5796,
+ OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797,
+ OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798,
+ OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799,
+ OpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800,
+ OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801,
+ OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802,
+ OpSubgroupAvcSicEvaluateIpeINTEL = 5803,
+ OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804,
+ OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805,
+ OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806,
+ OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807,
+ OpSubgroupAvcSicConvertToMceResultINTEL = 5808,
+ OpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809,
+ OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810,
+ OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811,
+ OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812,
+ OpSubgroupAvcSicGetIpeChromaModeINTEL = 5813,
+ OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814,
+ OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815,
+ OpSubgroupAvcSicGetInterRawSadsINTEL = 5816,
+ OpMax = 0x7fffffff,
+};
+
+// Overload operator| for mask bit combining
+
+inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); }
+inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); }
+inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); }
+inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); }
+inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); }
+inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); }
+inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); }
+inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); }
+
+} // end namespace spv
+
+#endif // #ifndef spirv_HPP
+
diff --git a/src/3rdparty/glslang/SPIRV/spvIR.h b/src/3rdparty/glslang/SPIRV/spvIR.h
new file mode 100644
index 0000000..b3cd0b0
--- /dev/null
+++ b/src/3rdparty/glslang/SPIRV/spvIR.h
@@ -0,0 +1,441 @@
+//
+// Copyright (C) 2014 LunarG, Inc.
+// Copyright (C) 2015-2018 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+// SPIRV-IR
+//
+// Simple in-memory representation (IR) of SPIRV. Just for holding
+// Each function's CFG of blocks. Has this hierarchy:
+// - Module, which is a list of
+// - Function, which is a list of
+// - Block, which is a list of
+// - Instruction
+//
+
+#pragma once
+#ifndef spvIR_H
+#define spvIR_H
+
+#include "spirv.hpp"
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iostream>
+#include <memory>
+#include <vector>
+
+namespace spv {
+
+class Block;
+class Function;
+class Module;
+
+const Id NoResult = 0;
+const Id NoType = 0;
+
+const Decoration NoPrecision = DecorationMax;
+
+#ifdef __GNUC__
+# define POTENTIALLY_UNUSED __attribute__((unused))
+#else
+# define POTENTIALLY_UNUSED
+#endif
+
+POTENTIALLY_UNUSED
+const MemorySemanticsMask MemorySemanticsAllMemory =
+ (MemorySemanticsMask)(MemorySemanticsUniformMemoryMask |
+ MemorySemanticsWorkgroupMemoryMask |
+ MemorySemanticsAtomicCounterMemoryMask |
+ MemorySemanticsImageMemoryMask);
+
+struct IdImmediate {
+ bool isId; // true if word is an Id, false if word is an immediate
+ unsigned word;
+ IdImmediate(bool i, unsigned w) : isId(i), word(w) {}
+};
+
+//
+// SPIR-V IR instruction.
+//
+
+class Instruction {
+public:
+ Instruction(Id resultId, Id typeId, Op opCode) : resultId(resultId), typeId(typeId), opCode(opCode), block(nullptr) { }
+ explicit Instruction(Op opCode) : resultId(NoResult), typeId(NoType), opCode(opCode), block(nullptr) { }
+ virtual ~Instruction() {}
+ void addIdOperand(Id id) {
+ operands.push_back(id);
+ idOperand.push_back(true);
+ }
+ void addImmediateOperand(unsigned int immediate) {
+ operands.push_back(immediate);
+ idOperand.push_back(false);
+ }
+ void setImmediateOperand(unsigned idx, unsigned int immediate) {
+ assert(!idOperand[idx]);
+ operands[idx] = immediate;
+ }
+
+ void addStringOperand(const char* str)
+ {
+ unsigned int word;
+ char* wordString = (char*)&word;
+ char* wordPtr = wordString;
+ int charCount = 0;
+ char c;
+ do {
+ c = *(str++);
+ *(wordPtr++) = c;
+ ++charCount;
+ if (charCount == 4) {
+ addImmediateOperand(word);
+ wordPtr = wordString;
+ charCount = 0;
+ }
+ } while (c != 0);
+
+ // deal with partial last word
+ if (charCount > 0) {
+ // pad with 0s
+ for (; charCount < 4; ++charCount)
+ *(wordPtr++) = 0;
+ addImmediateOperand(word);
+ }
+ }
+ bool isIdOperand(int op) const { return idOperand[op]; }
+ void setBlock(Block* b) { block = b; }
+ Block* getBlock() const { return block; }
+ Op getOpCode() const { return opCode; }
+ int getNumOperands() const
+ {
+ assert(operands.size() == idOperand.size());
+ return (int)operands.size();
+ }
+ Id getResultId() const { return resultId; }
+ Id getTypeId() const { return typeId; }
+ Id getIdOperand(int op) const {
+ assert(idOperand[op]);
+ return operands[op];
+ }
+ unsigned int getImmediateOperand(int op) const {
+ assert(!idOperand[op]);
+ return operands[op];
+ }
+
+ // Write out the binary form.
+ void dump(std::vector<unsigned int>& out) const
+ {
+ // Compute the wordCount
+ unsigned int wordCount = 1;
+ if (typeId)
+ ++wordCount;
+ if (resultId)
+ ++wordCount;
+ wordCount += (unsigned int)operands.size();
+
+ // Write out the beginning of the instruction
+ out.push_back(((wordCount) << WordCountShift) | opCode);
+ if (typeId)
+ out.push_back(typeId);
+ if (resultId)
+ out.push_back(resultId);
+
+ // Write out the operands
+ for (int op = 0; op < (int)operands.size(); ++op)
+ out.push_back(operands[op]);
+ }
+
+protected:
+ Instruction(const Instruction&);
+ Id resultId;
+ Id typeId;
+ Op opCode;
+ std::vector<Id> operands; // operands, both <id> and immediates (both are unsigned int)
+ std::vector<bool> idOperand; // true for operands that are <id>, false for immediates
+ Block* block;
+};
+
+//
+// SPIR-V IR block.
+//
+
+class Block {
+public:
+ Block(Id id, Function& parent);
+ virtual ~Block()
+ {
+ }
+
+ Id getId() { return instructions.front()->getResultId(); }
+
+ Function& getParent() const { return parent; }
+ void addInstruction(std::unique_ptr<Instruction> inst);
+ void addPredecessor(Block* pred) { predecessors.push_back(pred); pred->successors.push_back(this);}
+ void addLocalVariable(std::unique_ptr<Instruction> inst) { localVariables.push_back(std::move(inst)); }
+ const std::vector<Block*>& getPredecessors() const { return predecessors; }
+ const std::vector<Block*>& getSuccessors() const { return successors; }
+ const std::vector<std::unique_ptr<Instruction> >& getInstructions() const {
+ return instructions;
+ }
+ const std::vector<std::unique_ptr<Instruction> >& getLocalVariables() const { return localVariables; }
+ void setUnreachable() { unreachable = true; }
+ bool isUnreachable() const { return unreachable; }
+ // Returns the block's merge instruction, if one exists (otherwise null).
+ const Instruction* getMergeInstruction() const {
+ if (instructions.size() < 2) return nullptr;
+ const Instruction* nextToLast = (instructions.cend() - 2)->get();
+ switch (nextToLast->getOpCode()) {
+ case OpSelectionMerge:
+ case OpLoopMerge:
+ return nextToLast;
+ default:
+ return nullptr;
+ }
+ return nullptr;
+ }
+
+ bool isTerminated() const
+ {
+ switch (instructions.back()->getOpCode()) {
+ case OpBranch:
+ case OpBranchConditional:
+ case OpSwitch:
+ case OpKill:
+ case OpReturn:
+ case OpReturnValue:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ void dump(std::vector<unsigned int>& out) const
+ {
+ instructions[0]->dump(out);
+ for (int i = 0; i < (int)localVariables.size(); ++i)
+ localVariables[i]->dump(out);
+ for (int i = 1; i < (int)instructions.size(); ++i)
+ instructions[i]->dump(out);
+ }
+
+protected:
+ Block(const Block&);
+ Block& operator=(Block&);
+
+ // To enforce keeping parent and ownership in sync:
+ friend Function;
+
+ std::vector<std::unique_ptr<Instruction> > instructions;
+ std::vector<Block*> predecessors, successors;
+ std::vector<std::unique_ptr<Instruction> > localVariables;
+ Function& parent;
+
+ // track whether this block is known to be uncreachable (not necessarily
+ // true for all unreachable blocks, but should be set at least
+ // for the extraneous ones introduced by the builder).
+ bool unreachable;
+};
+
+// Traverses the control-flow graph rooted at root in an order suited for
+// readable code generation. Invokes callback at every node in the traversal
+// order.
+void inReadableOrder(Block* root, std::function<void(Block*)> callback);
+
+//
+// SPIR-V IR Function.
+//
+
+class Function {
+public:
+ Function(Id id, Id resultType, Id functionType, Id firstParam, Module& parent);
+ virtual ~Function()
+ {
+ for (int i = 0; i < (int)parameterInstructions.size(); ++i)
+ delete parameterInstructions[i];
+
+ for (int i = 0; i < (int)blocks.size(); ++i)
+ delete blocks[i];
+ }
+ Id getId() const { return functionInstruction.getResultId(); }
+ Id getParamId(int p) const { return parameterInstructions[p]->getResultId(); }
+ Id getParamType(int p) const { return parameterInstructions[p]->getTypeId(); }
+
+ void addBlock(Block* block) { blocks.push_back(block); }
+ void removeBlock(Block* block)
+ {
+ auto found = find(blocks.begin(), blocks.end(), block);
+ assert(found != blocks.end());
+ blocks.erase(found);
+ delete block;
+ }
+
+ Module& getParent() const { return parent; }
+ Block* getEntryBlock() const { return blocks.front(); }
+ Block* getLastBlock() const { return blocks.back(); }
+ const std::vector<Block*>& getBlocks() const { return blocks; }
+ void addLocalVariable(std::unique_ptr<Instruction> inst);
+ Id getReturnType() const { return functionInstruction.getTypeId(); }
+
+ void setImplicitThis() { implicitThis = true; }
+ bool hasImplicitThis() const { return implicitThis; }
+
+ void dump(std::vector<unsigned int>& out) const
+ {
+ // OpFunction
+ functionInstruction.dump(out);
+
+ // OpFunctionParameter
+ for (int p = 0; p < (int)parameterInstructions.size(); ++p)
+ parameterInstructions[p]->dump(out);
+
+ // Blocks
+ inReadableOrder(blocks[0], [&out](const Block* b) { b->dump(out); });
+ Instruction end(0, 0, OpFunctionEnd);
+ end.dump(out);
+ }
+
+protected:
+ Function(const Function&);
+ Function& operator=(Function&);
+
+ Module& parent;
+ Instruction functionInstruction;
+ std::vector<Instruction*> parameterInstructions;
+ std::vector<Block*> blocks;
+ bool implicitThis; // true if this is a member function expecting to be passed a 'this' as the first argument
+};
+
+//
+// SPIR-V IR Module.
+//
+
+class Module {
+public:
+ Module() {}
+ virtual ~Module()
+ {
+ // TODO delete things
+ }
+
+ void addFunction(Function *fun) { functions.push_back(fun); }
+
+ void mapInstruction(Instruction *instruction)
+ {
+ spv::Id resultId = instruction->getResultId();
+ // map the instruction's result id
+ if (resultId >= idToInstruction.size())
+ idToInstruction.resize(resultId + 16);
+ idToInstruction[resultId] = instruction;
+ }
+
+ Instruction* getInstruction(Id id) const { return idToInstruction[id]; }
+ const std::vector<Function*>& getFunctions() const { return functions; }
+ spv::Id getTypeId(Id resultId) const {
+ return idToInstruction[resultId] == nullptr ? NoType : idToInstruction[resultId]->getTypeId();
+ }
+ StorageClass getStorageClass(Id typeId) const
+ {
+ assert(idToInstruction[typeId]->getOpCode() == spv::OpTypePointer);
+ return (StorageClass)idToInstruction[typeId]->getImmediateOperand(0);
+ }
+
+ void dump(std::vector<unsigned int>& out) const
+ {
+ for (int f = 0; f < (int)functions.size(); ++f)
+ functions[f]->dump(out);
+ }
+
+protected:
+ Module(const Module&);
+ std::vector<Function*> functions;
+
+ // map from result id to instruction having that result id
+ std::vector<Instruction*> idToInstruction;
+
+ // map from a result id to its type id
+};
+
+//
+// Implementation (it's here due to circular type definitions).
+//
+
+// Add both
+// - the OpFunction instruction
+// - all the OpFunctionParameter instructions
+__inline Function::Function(Id id, Id resultType, Id functionType, Id firstParamId, Module& parent)
+ : parent(parent), functionInstruction(id, resultType, OpFunction), implicitThis(false)
+{
+ // OpFunction
+ functionInstruction.addImmediateOperand(FunctionControlMaskNone);
+ functionInstruction.addIdOperand(functionType);
+ parent.mapInstruction(&functionInstruction);
+ parent.addFunction(this);
+
+ // OpFunctionParameter
+ Instruction* typeInst = parent.getInstruction(functionType);
+ int numParams = typeInst->getNumOperands() - 1;
+ for (int p = 0; p < numParams; ++p) {
+ Instruction* param = new Instruction(firstParamId + p, typeInst->getIdOperand(p + 1), OpFunctionParameter);
+ parent.mapInstruction(param);
+ parameterInstructions.push_back(param);
+ }
+}
+
+__inline void Function::addLocalVariable(std::unique_ptr<Instruction> inst)
+{
+ Instruction* raw_instruction = inst.get();
+ blocks[0]->addLocalVariable(std::move(inst));
+ parent.mapInstruction(raw_instruction);
+}
+
+__inline Block::Block(Id id, Function& parent) : parent(parent), unreachable(false)
+{
+ instructions.push_back(std::unique_ptr<Instruction>(new Instruction(id, NoType, OpLabel)));
+ instructions.back()->setBlock(this);
+ parent.getParent().mapInstruction(instructions.back().get());
+}
+
+__inline void Block::addInstruction(std::unique_ptr<Instruction> inst)
+{
+ Instruction* raw_instruction = inst.get();
+ instructions.push_back(std::move(inst));
+ raw_instruction->setBlock(this);
+ if (raw_instruction->getResultId())
+ parent.getParent().mapInstruction(raw_instruction);
+}
+
+}; // end spv namespace
+
+#endif // spvIR_H
diff --git a/src/3rdparty/glslang/glslang/GenericCodeGen/CodeGen.cpp b/src/3rdparty/glslang/glslang/GenericCodeGen/CodeGen.cpp
new file mode 100644
index 0000000..b3c7226
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/GenericCodeGen/CodeGen.cpp
@@ -0,0 +1,76 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "../Include/Common.h"
+#include "../Include/ShHandle.h"
+#include "../MachineIndependent/Versions.h"
+
+//
+// Here is where real machine specific high-level data would be defined.
+//
+class TGenericCompiler : public TCompiler {
+public:
+ TGenericCompiler(EShLanguage l, int dOptions) : TCompiler(l, infoSink), debugOptions(dOptions) { }
+ virtual bool compile(TIntermNode* root, int version = 0, EProfile profile = ENoProfile);
+ TInfoSink infoSink;
+ int debugOptions;
+};
+
+//
+// This function must be provided to create the actual
+// compile object used by higher level code. It returns
+// a subclass of TCompiler.
+//
+TCompiler* ConstructCompiler(EShLanguage language, int debugOptions)
+{
+ return new TGenericCompiler(language, debugOptions);
+}
+
+//
+// Delete the compiler made by ConstructCompiler
+//
+void DeleteCompiler(TCompiler* compiler)
+{
+ delete compiler;
+}
+
+//
+// Generate code from the given parse tree
+//
+bool TGenericCompiler::compile(TIntermNode* /*root*/, int /*version*/, EProfile /*profile*/)
+{
+ haveValidObjectCode = true;
+
+ return haveValidObjectCode;
+}
diff --git a/src/3rdparty/glslang/glslang/GenericCodeGen/Link.cpp b/src/3rdparty/glslang/glslang/GenericCodeGen/Link.cpp
new file mode 100644
index 0000000..c38db0f
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/GenericCodeGen/Link.cpp
@@ -0,0 +1,91 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+//
+// The top level algorithms for linking multiple
+// shaders together.
+//
+#include "../Include/Common.h"
+#include "../Include/ShHandle.h"
+
+//
+// Actual link object, derived from the shader handle base classes.
+//
+class TGenericLinker : public TLinker {
+public:
+ TGenericLinker(EShExecutable e, int dOptions) : TLinker(e, infoSink), debugOptions(dOptions) { }
+ bool link(TCompilerList&, TUniformMap*) { return true; }
+ void getAttributeBindings(ShBindingTable const **) const { }
+ TInfoSink infoSink;
+ int debugOptions;
+};
+
+//
+// The internal view of a uniform/float object exchanged with the driver.
+//
+class TUniformLinkedMap : public TUniformMap {
+public:
+ TUniformLinkedMap() { }
+ virtual int getLocation(const char*) { return 0; }
+};
+
+TShHandleBase* ConstructLinker(EShExecutable executable, int debugOptions)
+{
+ return new TGenericLinker(executable, debugOptions);
+}
+
+void DeleteLinker(TShHandleBase* linker)
+{
+ delete linker;
+}
+
+TUniformMap* ConstructUniformMap()
+{
+ return new TUniformLinkedMap();
+}
+
+void DeleteUniformMap(TUniformMap* map)
+{
+ delete map;
+}
+
+TShHandleBase* ConstructBindings()
+{
+ return 0;
+}
+
+void DeleteBindingList(TShHandleBase* bindingList)
+{
+ delete bindingList;
+}
diff --git a/src/3rdparty/glslang/glslang/Include/BaseTypes.h b/src/3rdparty/glslang/glslang/Include/BaseTypes.h
new file mode 100644
index 0000000..1827c49
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/Include/BaseTypes.h
@@ -0,0 +1,545 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2012-2013 LunarG, Inc.
+// Copyright (C) 2017 ARM Limited.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef _BASICTYPES_INCLUDED_
+#define _BASICTYPES_INCLUDED_
+
+namespace glslang {
+
+//
+// Basic type. Arrays, vectors, sampler details, etc., are orthogonal to this.
+//
+enum TBasicType {
+ EbtVoid,
+ EbtFloat,
+ EbtDouble,
+ EbtFloat16,
+ EbtInt8,
+ EbtUint8,
+ EbtInt16,
+ EbtUint16,
+ EbtInt,
+ EbtUint,
+ EbtInt64,
+ EbtUint64,
+ EbtBool,
+ EbtAtomicUint,
+ EbtSampler,
+ EbtStruct,
+ EbtBlock,
+
+#ifdef NV_EXTENSIONS
+ EbtAccStructNV,
+#endif
+
+ EbtReference,
+
+ // HLSL types that live only temporarily.
+ EbtString,
+
+ EbtNumTypes
+};
+
+//
+// Storage qualifiers. Should align with different kinds of storage or
+// resource or GLSL storage qualifier. Expansion is deprecated.
+//
+// N.B.: You probably DON'T want to add anything here, but rather just add it
+// to the built-in variables. See the comment above TBuiltInVariable.
+//
+// A new built-in variable will normally be an existing qualifier, like 'in', 'out', etc.
+// DO NOT follow the design pattern of, say EvqInstanceId, etc.
+//
+enum TStorageQualifier {
+ EvqTemporary, // For temporaries (within a function), read/write
+ EvqGlobal, // For globals read/write
+ EvqConst, // User-defined constant values, will be semantically constant and constant folded
+ EvqVaryingIn, // pipeline input, read only, also supercategory for all built-ins not included in this enum (see TBuiltInVariable)
+ EvqVaryingOut, // pipeline output, read/write, also supercategory for all built-ins not included in this enum (see TBuiltInVariable)
+ EvqUniform, // read only, shared with app
+ EvqBuffer, // read/write, shared with app
+ EvqShared, // compute shader's read/write 'shared' qualifier
+
+#ifdef NV_EXTENSIONS
+ EvqPayloadNV,
+ EvqPayloadInNV,
+ EvqHitAttrNV,
+ EvqCallableDataNV,
+ EvqCallableDataInNV,
+#endif
+
+ // parameters
+ EvqIn, // also, for 'in' in the grammar before we know if it's a pipeline input or an 'in' parameter
+ EvqOut, // also, for 'out' in the grammar before we know if it's a pipeline output or an 'out' parameter
+ EvqInOut,
+ EvqConstReadOnly, // input; also other read-only types having neither a constant value nor constant-value semantics
+
+ // built-ins read by vertex shader
+ EvqVertexId,
+ EvqInstanceId,
+
+ // built-ins written by vertex shader
+ EvqPosition,
+ EvqPointSize,
+ EvqClipVertex,
+
+ // built-ins read by fragment shader
+ EvqFace,
+ EvqFragCoord,
+ EvqPointCoord,
+
+ // built-ins written by fragment shader
+ EvqFragColor,
+ EvqFragDepth,
+
+ // end of list
+ EvqLast
+};
+
+//
+// Subcategories of the TStorageQualifier, simply to give a direct mapping
+// between built-in variable names and an numerical value (the enum).
+//
+// For backward compatibility, there is some redundancy between the
+// TStorageQualifier and these. Existing members should both be maintained accurately.
+// However, any new built-in variable (and any existing non-redundant one)
+// must follow the pattern that the specific built-in is here, and only its
+// general qualifier is in TStorageQualifier.
+//
+// Something like gl_Position, which is sometimes 'in' and sometimes 'out'
+// shows up as two different built-in variables in a single stage, but
+// only has a single enum in TBuiltInVariable, so both the
+// TStorageQualifier and the TBuitinVariable are needed to distinguish
+// between them.
+//
+enum TBuiltInVariable {
+ EbvNone,
+ EbvNumWorkGroups,
+ EbvWorkGroupSize,
+ EbvWorkGroupId,
+ EbvLocalInvocationId,
+ EbvGlobalInvocationId,
+ EbvLocalInvocationIndex,
+ EbvNumSubgroups,
+ EbvSubgroupID,
+ EbvSubGroupSize,
+ EbvSubGroupInvocation,
+ EbvSubGroupEqMask,
+ EbvSubGroupGeMask,
+ EbvSubGroupGtMask,
+ EbvSubGroupLeMask,
+ EbvSubGroupLtMask,
+ EbvSubgroupSize2,
+ EbvSubgroupInvocation2,
+ EbvSubgroupEqMask2,
+ EbvSubgroupGeMask2,
+ EbvSubgroupGtMask2,
+ EbvSubgroupLeMask2,
+ EbvSubgroupLtMask2,
+ EbvVertexId,
+ EbvInstanceId,
+ EbvVertexIndex,
+ EbvInstanceIndex,
+ EbvBaseVertex,
+ EbvBaseInstance,
+ EbvDrawId,
+ EbvPosition,
+ EbvPointSize,
+ EbvClipVertex,
+ EbvClipDistance,
+ EbvCullDistance,
+ EbvNormal,
+ EbvVertex,
+ EbvMultiTexCoord0,
+ EbvMultiTexCoord1,
+ EbvMultiTexCoord2,
+ EbvMultiTexCoord3,
+ EbvMultiTexCoord4,
+ EbvMultiTexCoord5,
+ EbvMultiTexCoord6,
+ EbvMultiTexCoord7,
+ EbvFrontColor,
+ EbvBackColor,
+ EbvFrontSecondaryColor,
+ EbvBackSecondaryColor,
+ EbvTexCoord,
+ EbvFogFragCoord,
+ EbvInvocationId,
+ EbvPrimitiveId,
+ EbvLayer,
+ EbvViewportIndex,
+ EbvPatchVertices,
+ EbvTessLevelOuter,
+ EbvTessLevelInner,
+ EbvBoundingBox,
+ EbvTessCoord,
+ EbvColor,
+ EbvSecondaryColor,
+ EbvFace,
+ EbvFragCoord,
+ EbvPointCoord,
+ EbvFragColor,
+ EbvFragData,
+ EbvFragDepth,
+ EbvFragStencilRef,
+ EbvSampleId,
+ EbvSamplePosition,
+ EbvSampleMask,
+ EbvHelperInvocation,
+
+#ifdef AMD_EXTENSIONS
+ EbvBaryCoordNoPersp,
+ EbvBaryCoordNoPerspCentroid,
+ EbvBaryCoordNoPerspSample,
+ EbvBaryCoordSmooth,
+ EbvBaryCoordSmoothCentroid,
+ EbvBaryCoordSmoothSample,
+ EbvBaryCoordPullModel,
+#endif
+
+ EbvViewIndex,
+ EbvDeviceIndex,
+
+ EbvFragSizeEXT,
+ EbvFragInvocationCountEXT,
+
+#ifdef NV_EXTENSIONS
+ EbvViewportMaskNV,
+ EbvSecondaryPositionNV,
+ EbvSecondaryViewportMaskNV,
+ EbvPositionPerViewNV,
+ EbvViewportMaskPerViewNV,
+ EbvFragFullyCoveredNV,
+ EbvFragmentSizeNV,
+ EbvInvocationsPerPixelNV,
+ // raytracing
+ EbvLaunchIdNV,
+ EbvLaunchSizeNV,
+ EbvInstanceCustomIndexNV,
+ EbvWorldRayOriginNV,
+ EbvWorldRayDirectionNV,
+ EbvObjectRayOriginNV,
+ EbvObjectRayDirectionNV,
+ EbvRayTminNV,
+ EbvRayTmaxNV,
+ EbvHitTNV,
+ EbvHitKindNV,
+ EbvObjectToWorldNV,
+ EbvWorldToObjectNV,
+ EbvIncomingRayFlagsNV,
+ EbvBaryCoordNV,
+ EbvBaryCoordNoPerspNV,
+ EbvTaskCountNV,
+ EbvPrimitiveCountNV,
+ EbvPrimitiveIndicesNV,
+ EbvClipDistancePerViewNV,
+ EbvCullDistancePerViewNV,
+ EbvLayerPerViewNV,
+ EbvMeshViewCountNV,
+ EbvMeshViewIndicesNV,
+#endif
+
+ // HLSL built-ins that live only temporarily, until they get remapped
+ // to one of the above.
+ EbvFragDepthGreater,
+ EbvFragDepthLesser,
+ EbvGsOutputStream,
+ EbvOutputPatch,
+ EbvInputPatch,
+
+ // structbuffer types
+ EbvAppendConsume, // no need to differentiate append and consume
+ EbvRWStructuredBuffer,
+ EbvStructuredBuffer,
+ EbvByteAddressBuffer,
+ EbvRWByteAddressBuffer,
+
+ EbvLast
+};
+
+// These will show up in error messages
+__inline const char* GetStorageQualifierString(TStorageQualifier q)
+{
+ switch (q) {
+ case EvqTemporary: return "temp"; break;
+ case EvqGlobal: return "global"; break;
+ case EvqConst: return "const"; break;
+ case EvqConstReadOnly: return "const (read only)"; break;
+ case EvqVaryingIn: return "in"; break;
+ case EvqVaryingOut: return "out"; break;
+ case EvqUniform: return "uniform"; break;
+ case EvqBuffer: return "buffer"; break;
+ case EvqShared: return "shared"; break;
+ case EvqIn: return "in"; break;
+ case EvqOut: return "out"; break;
+ case EvqInOut: return "inout"; break;
+ case EvqVertexId: return "gl_VertexId"; break;
+ case EvqInstanceId: return "gl_InstanceId"; break;
+ case EvqPosition: return "gl_Position"; break;
+ case EvqPointSize: return "gl_PointSize"; break;
+ case EvqClipVertex: return "gl_ClipVertex"; break;
+ case EvqFace: return "gl_FrontFacing"; break;
+ case EvqFragCoord: return "gl_FragCoord"; break;
+ case EvqPointCoord: return "gl_PointCoord"; break;
+ case EvqFragColor: return "fragColor"; break;
+ case EvqFragDepth: return "gl_FragDepth"; break;
+#ifdef NV_EXTENSIONS
+ case EvqPayloadNV: return "rayPayloadNV"; break;
+ case EvqPayloadInNV: return "rayPayloadInNV"; break;
+ case EvqHitAttrNV: return "hitAttributeNV"; break;
+ case EvqCallableDataNV: return "callableDataNV"; break;
+ case EvqCallableDataInNV: return "callableDataInNV"; break;
+#endif
+ default: return "unknown qualifier";
+ }
+}
+
+__inline const char* GetBuiltInVariableString(TBuiltInVariable v)
+{
+ switch (v) {
+ case EbvNone: return "";
+ case EbvNumWorkGroups: return "NumWorkGroups";
+ case EbvWorkGroupSize: return "WorkGroupSize";
+ case EbvWorkGroupId: return "WorkGroupID";
+ case EbvLocalInvocationId: return "LocalInvocationID";
+ case EbvGlobalInvocationId: return "GlobalInvocationID";
+ case EbvLocalInvocationIndex: return "LocalInvocationIndex";
+ case EbvSubGroupSize: return "SubGroupSize";
+ case EbvSubGroupInvocation: return "SubGroupInvocation";
+ case EbvSubGroupEqMask: return "SubGroupEqMask";
+ case EbvSubGroupGeMask: return "SubGroupGeMask";
+ case EbvSubGroupGtMask: return "SubGroupGtMask";
+ case EbvSubGroupLeMask: return "SubGroupLeMask";
+ case EbvSubGroupLtMask: return "SubGroupLtMask";
+ case EbvVertexId: return "VertexId";
+ case EbvInstanceId: return "InstanceId";
+ case EbvVertexIndex: return "VertexIndex";
+ case EbvInstanceIndex: return "InstanceIndex";
+ case EbvBaseVertex: return "BaseVertex";
+ case EbvBaseInstance: return "BaseInstance";
+ case EbvDrawId: return "DrawId";
+ case EbvPosition: return "Position";
+ case EbvPointSize: return "PointSize";
+ case EbvClipVertex: return "ClipVertex";
+ case EbvClipDistance: return "ClipDistance";
+ case EbvCullDistance: return "CullDistance";
+ case EbvNormal: return "Normal";
+ case EbvVertex: return "Vertex";
+ case EbvMultiTexCoord0: return "MultiTexCoord0";
+ case EbvMultiTexCoord1: return "MultiTexCoord1";
+ case EbvMultiTexCoord2: return "MultiTexCoord2";
+ case EbvMultiTexCoord3: return "MultiTexCoord3";
+ case EbvMultiTexCoord4: return "MultiTexCoord4";
+ case EbvMultiTexCoord5: return "MultiTexCoord5";
+ case EbvMultiTexCoord6: return "MultiTexCoord6";
+ case EbvMultiTexCoord7: return "MultiTexCoord7";
+ case EbvFrontColor: return "FrontColor";
+ case EbvBackColor: return "BackColor";
+ case EbvFrontSecondaryColor: return "FrontSecondaryColor";
+ case EbvBackSecondaryColor: return "BackSecondaryColor";
+ case EbvTexCoord: return "TexCoord";
+ case EbvFogFragCoord: return "FogFragCoord";
+ case EbvInvocationId: return "InvocationID";
+ case EbvPrimitiveId: return "PrimitiveID";
+ case EbvLayer: return "Layer";
+ case EbvViewportIndex: return "ViewportIndex";
+ case EbvPatchVertices: return "PatchVertices";
+ case EbvTessLevelOuter: return "TessLevelOuter";
+ case EbvTessLevelInner: return "TessLevelInner";
+ case EbvBoundingBox: return "BoundingBox";
+ case EbvTessCoord: return "TessCoord";
+ case EbvColor: return "Color";
+ case EbvSecondaryColor: return "SecondaryColor";
+ case EbvFace: return "Face";
+ case EbvFragCoord: return "FragCoord";
+ case EbvPointCoord: return "PointCoord";
+ case EbvFragColor: return "FragColor";
+ case EbvFragData: return "FragData";
+ case EbvFragDepth: return "FragDepth";
+ case EbvFragStencilRef: return "FragStencilRef";
+ case EbvSampleId: return "SampleId";
+ case EbvSamplePosition: return "SamplePosition";
+ case EbvSampleMask: return "SampleMaskIn";
+ case EbvHelperInvocation: return "HelperInvocation";
+
+#ifdef AMD_EXTENSIONS
+ case EbvBaryCoordNoPersp: return "BaryCoordNoPersp";
+ case EbvBaryCoordNoPerspCentroid: return "BaryCoordNoPerspCentroid";
+ case EbvBaryCoordNoPerspSample: return "BaryCoordNoPerspSample";
+ case EbvBaryCoordSmooth: return "BaryCoordSmooth";
+ case EbvBaryCoordSmoothCentroid: return "BaryCoordSmoothCentroid";
+ case EbvBaryCoordSmoothSample: return "BaryCoordSmoothSample";
+ case EbvBaryCoordPullModel: return "BaryCoordPullModel";
+#endif
+
+ case EbvViewIndex: return "ViewIndex";
+ case EbvDeviceIndex: return "DeviceIndex";
+
+ case EbvFragSizeEXT: return "FragSizeEXT";
+ case EbvFragInvocationCountEXT: return "FragInvocationCountEXT";
+
+#ifdef NV_EXTENSIONS
+ case EbvViewportMaskNV: return "ViewportMaskNV";
+ case EbvSecondaryPositionNV: return "SecondaryPositionNV";
+ case EbvSecondaryViewportMaskNV: return "SecondaryViewportMaskNV";
+ case EbvPositionPerViewNV: return "PositionPerViewNV";
+ case EbvViewportMaskPerViewNV: return "ViewportMaskPerViewNV";
+ case EbvFragFullyCoveredNV: return "FragFullyCoveredNV";
+ case EbvFragmentSizeNV: return "FragmentSizeNV";
+ case EbvInvocationsPerPixelNV: return "InvocationsPerPixelNV";
+ case EbvLaunchIdNV: return "LaunchIdNV";
+ case EbvLaunchSizeNV: return "LaunchSizeNV";
+ case EbvInstanceCustomIndexNV: return "InstanceCustomIndexNV";
+ case EbvWorldRayOriginNV: return "WorldRayOriginNV";
+ case EbvWorldRayDirectionNV: return "WorldRayDirectionNV";
+ case EbvObjectRayOriginNV: return "ObjectRayOriginNV";
+ case EbvObjectRayDirectionNV: return "ObjectRayDirectionNV";
+ case EbvRayTminNV: return "ObjectRayTminNV";
+ case EbvRayTmaxNV: return "ObjectRayTmaxNV";
+ case EbvHitTNV: return "HitTNV";
+ case EbvHitKindNV: return "HitKindNV";
+ case EbvIncomingRayFlagsNV: return "IncomingRayFlagsNV";
+ case EbvObjectToWorldNV: return "ObjectToWorldNV";
+ case EbvWorldToObjectNV: return "WorldToObjectNV";
+
+ case EbvBaryCoordNV: return "BaryCoordNV";
+ case EbvBaryCoordNoPerspNV: return "BaryCoordNoPerspNV";
+ case EbvTaskCountNV: return "TaskCountNV";
+ case EbvPrimitiveCountNV: return "PrimitiveCountNV";
+ case EbvPrimitiveIndicesNV: return "PrimitiveIndicesNV";
+ case EbvClipDistancePerViewNV: return "ClipDistancePerViewNV";
+ case EbvCullDistancePerViewNV: return "CullDistancePerViewNV";
+ case EbvLayerPerViewNV: return "LayerPerViewNV";
+ case EbvMeshViewCountNV: return "MeshViewCountNV";
+ case EbvMeshViewIndicesNV: return "MeshViewIndicesNV";
+#endif
+ default: return "unknown built-in variable";
+ }
+}
+
+// In this enum, order matters; users can assume higher precision is a bigger value
+// and EpqNone is 0.
+enum TPrecisionQualifier {
+ EpqNone = 0,
+ EpqLow,
+ EpqMedium,
+ EpqHigh
+};
+
+__inline const char* GetPrecisionQualifierString(TPrecisionQualifier p)
+{
+ switch (p) {
+ case EpqNone: return ""; break;
+ case EpqLow: return "lowp"; break;
+ case EpqMedium: return "mediump"; break;
+ case EpqHigh: return "highp"; break;
+ default: return "unknown precision qualifier";
+ }
+}
+
+__inline bool isTypeSignedInt(TBasicType type)
+{
+ switch (type) {
+ case EbtInt8:
+ case EbtInt16:
+ case EbtInt:
+ case EbtInt64:
+ return true;
+ default:
+ return false;
+ }
+}
+
+__inline bool isTypeUnsignedInt(TBasicType type)
+{
+ switch (type) {
+ case EbtUint8:
+ case EbtUint16:
+ case EbtUint:
+ case EbtUint64:
+ return true;
+ default:
+ return false;
+ }
+}
+
+__inline bool isTypeInt(TBasicType type)
+{
+ return isTypeSignedInt(type) || isTypeUnsignedInt(type);
+}
+
+__inline bool isTypeFloat(TBasicType type)
+{
+ switch (type) {
+ case EbtFloat:
+ case EbtDouble:
+ case EbtFloat16:
+ return true;
+ default:
+ return false;
+ }
+}
+
+__inline int getTypeRank(TBasicType type) {
+ int res = -1;
+ switch(type) {
+ case EbtInt8:
+ case EbtUint8:
+ res = 0;
+ break;
+ case EbtInt16:
+ case EbtUint16:
+ res = 1;
+ break;
+ case EbtInt:
+ case EbtUint:
+ res = 2;
+ break;
+ case EbtInt64:
+ case EbtUint64:
+ res = 3;
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ return res;
+}
+
+} // end namespace glslang
+
+#endif // _BASICTYPES_INCLUDED_
diff --git a/src/3rdparty/glslang/glslang/Include/Common.h b/src/3rdparty/glslang/glslang/Include/Common.h
new file mode 100644
index 0000000..98e5a1a
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/Include/Common.h
@@ -0,0 +1,291 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2012-2013 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef _COMMON_INCLUDED_
+#define _COMMON_INCLUDED_
+
+
+#if defined(__ANDROID__) || _MSC_VER < 1700
+#include <sstream>
+namespace std {
+template<typename T>
+std::string to_string(const T& val) {
+ std::ostringstream os;
+ os << val;
+ return os.str();
+}
+}
+#endif
+
+#if (defined(_MSC_VER) && _MSC_VER < 1900 /*vs2015*/) || defined MINGW_HAS_SECURE_API
+ #include <basetsd.h>
+ #ifndef snprintf
+ #define snprintf sprintf_s
+ #endif
+ #define safe_vsprintf(buf,max,format,args) vsnprintf_s((buf), (max), (max), (format), (args))
+#elif defined (solaris)
+ #define safe_vsprintf(buf,max,format,args) vsnprintf((buf), (max), (format), (args))
+ #include <sys/int_types.h>
+ #define UINT_PTR uintptr_t
+#else
+ #define safe_vsprintf(buf,max,format,args) vsnprintf((buf), (max), (format), (args))
+ #include <stdint.h>
+ #define UINT_PTR uintptr_t
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1800
+ #include <stdlib.h>
+ inline long long int strtoll (const char* str, char** endptr, int base)
+ {
+ return _strtoi64(str, endptr, base);
+ }
+ inline unsigned long long int strtoull (const char* str, char** endptr, int base)
+ {
+ return _strtoui64(str, endptr, base);
+ }
+ inline long long int atoll (const char* str)
+ {
+ return strtoll(str, NULL, 10);
+ }
+#endif
+
+#if defined(_MSC_VER)
+#define strdup _strdup
+#endif
+
+/* windows only pragma */
+#ifdef _MSC_VER
+ #pragma warning(disable : 4786) // Don't warn about too long identifiers
+ #pragma warning(disable : 4514) // unused inline method
+ #pragma warning(disable : 4201) // nameless union
+#endif
+
+#include <set>
+#include <unordered_set>
+#include <vector>
+#include <map>
+#include <unordered_map>
+#include <list>
+#include <algorithm>
+#include <string>
+#include <cstdio>
+#include <cassert>
+
+#include "PoolAlloc.h"
+
+//
+// Put POOL_ALLOCATOR_NEW_DELETE in base classes to make them use this scheme.
+//
+#define POOL_ALLOCATOR_NEW_DELETE(A) \
+ void* operator new(size_t s) { return (A).allocate(s); } \
+ void* operator new(size_t, void *_Where) { return (_Where); } \
+ void operator delete(void*) { } \
+ void operator delete(void *, void *) { } \
+ void* operator new[](size_t s) { return (A).allocate(s); } \
+ void* operator new[](size_t, void *_Where) { return (_Where); } \
+ void operator delete[](void*) { } \
+ void operator delete[](void *, void *) { }
+
+namespace glslang {
+
+ //
+ // Pool version of string.
+ //
+ typedef pool_allocator<char> TStringAllocator;
+ typedef std::basic_string <char, std::char_traits<char>, TStringAllocator> TString;
+
+} // end namespace glslang
+
+// Repackage the std::hash for use by unordered map/set with a TString key.
+namespace std {
+
+ template<> struct hash<glslang::TString> {
+ std::size_t operator()(const glslang::TString& s) const
+ {
+ const unsigned _FNV_offset_basis = 2166136261U;
+ const unsigned _FNV_prime = 16777619U;
+ unsigned _Val = _FNV_offset_basis;
+ size_t _Count = s.size();
+ const char* _First = s.c_str();
+ for (size_t _Next = 0; _Next < _Count; ++_Next)
+ {
+ _Val ^= (unsigned)_First[_Next];
+ _Val *= _FNV_prime;
+ }
+
+ return _Val;
+ }
+ };
+}
+
+namespace glslang {
+
+inline TString* NewPoolTString(const char* s)
+{
+ void* memory = GetThreadPoolAllocator().allocate(sizeof(TString));
+ return new(memory) TString(s);
+}
+
+template<class T> inline T* NewPoolObject(T*)
+{
+ return new(GetThreadPoolAllocator().allocate(sizeof(T))) T;
+}
+
+template<class T> inline T* NewPoolObject(T, int instances)
+{
+ return new(GetThreadPoolAllocator().allocate(instances * sizeof(T))) T[instances];
+}
+
+//
+// Pool allocator versions of vectors, lists, and maps
+//
+template <class T> class TVector : public std::vector<T, pool_allocator<T> > {
+public:
+ POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator())
+
+ typedef typename std::vector<T, pool_allocator<T> >::size_type size_type;
+ TVector() : std::vector<T, pool_allocator<T> >() {}
+ TVector(const pool_allocator<T>& a) : std::vector<T, pool_allocator<T> >(a) {}
+ TVector(size_type i) : std::vector<T, pool_allocator<T> >(i) {}
+ TVector(size_type i, const T& val) : std::vector<T, pool_allocator<T> >(i, val) {}
+};
+
+template <class T> class TList : public std::list<T, pool_allocator<T> > {
+};
+
+template <class K, class D, class CMP = std::less<K> >
+class TMap : public std::map<K, D, CMP, pool_allocator<std::pair<K const, D> > > {
+};
+
+template <class K, class D, class HASH = std::hash<K>, class PRED = std::equal_to<K> >
+class TUnorderedMap : public std::unordered_map<K, D, HASH, PRED, pool_allocator<std::pair<K const, D> > > {
+};
+
+//
+// Persistent string memory. Should only be used for strings that survive
+// across compiles/links.
+//
+typedef std::basic_string<char> TPersistString;
+
+//
+// templatized min and max functions.
+//
+template <class T> T Min(const T a, const T b) { return a < b ? a : b; }
+template <class T> T Max(const T a, const T b) { return a > b ? a : b; }
+
+//
+// Create a TString object from an integer.
+//
+#if defined _MSC_VER || defined MINGW_HAS_SECURE_API
+inline const TString String(const int i, const int base = 10)
+{
+ char text[16]; // 32 bit ints are at most 10 digits in base 10
+ _itoa_s(i, text, sizeof(text), base);
+ return text;
+}
+#else
+inline const TString String(const int i, const int /*base*/ = 10)
+{
+ char text[16]; // 32 bit ints are at most 10 digits in base 10
+
+ // we assume base 10 for all cases
+ snprintf(text, sizeof(text), "%d", i);
+
+ return text;
+}
+#endif
+
+struct TSourceLoc {
+ void init()
+ {
+ name = nullptr; string = 0; line = 0; column = 0;
+ }
+ void init(int stringNum) { init(); string = stringNum; }
+ // Returns the name if it exists. Otherwise, returns the string number.
+ std::string getStringNameOrNum(bool quoteStringName = true) const
+ {
+ if (name != nullptr) {
+ TString qstr = quoteStringName ? ("\"" + *name + "\"") : *name;
+ std::string ret_str(qstr.c_str());
+ return ret_str;
+ }
+ return std::to_string((long long)string);
+ }
+ const char* getFilename() const
+ {
+ if (name == nullptr)
+ return nullptr;
+ return name->c_str();
+ }
+ const char* getFilenameStr() const { return name == nullptr ? "" : name->c_str(); }
+ TString* name; // descriptive name for this string, when a textual name is available, otherwise nullptr
+ int string;
+ int line;
+ int column;
+};
+
+class TPragmaTable : public TMap<TString, TString> {
+public:
+ POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator())
+};
+
+const int MaxTokenLength = 1024;
+
+template <class T> bool IsPow2(T powerOf2)
+{
+ if (powerOf2 <= 0)
+ return false;
+
+ return (powerOf2 & (powerOf2 - 1)) == 0;
+}
+
+// Round number up to a multiple of the given powerOf2, which is not
+// a power, just a number that must be a power of 2.
+template <class T> void RoundToPow2(T& number, int powerOf2)
+{
+ assert(IsPow2(powerOf2));
+ number = (number + powerOf2 - 1) & ~(powerOf2 - 1);
+}
+
+template <class T> bool IsMultipleOfPow2(T number, int powerOf2)
+{
+ assert(IsPow2(powerOf2));
+ return ! (number & (powerOf2 - 1));
+}
+
+} // end namespace glslang
+
+#endif // _COMMON_INCLUDED_
diff --git a/src/3rdparty/glslang/glslang/Include/ConstantUnion.h b/src/3rdparty/glslang/glslang/Include/ConstantUnion.h
new file mode 100644
index 0000000..3e93340
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/Include/ConstantUnion.h
@@ -0,0 +1,938 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2013 LunarG, Inc.
+// Copyright (C) 2017 ARM Limited.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef _CONSTANT_UNION_INCLUDED_
+#define _CONSTANT_UNION_INCLUDED_
+
+#include "../Include/Common.h"
+#include "../Include/BaseTypes.h"
+
+namespace glslang {
+
+class TConstUnion {
+public:
+ POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator())
+
+ TConstUnion() : iConst(0), type(EbtInt) { }
+
+ void setI8Const(signed char i)
+ {
+ i8Const = i;
+ type = EbtInt8;
+ }
+
+ void setU8Const(unsigned char u)
+ {
+ u8Const = u;
+ type = EbtUint8;
+ }
+
+ void setI16Const(signed short i)
+ {
+ i16Const = i;
+ type = EbtInt16;
+ }
+
+ void setU16Const(unsigned short u)
+ {
+ u16Const = u;
+ type = EbtUint16;
+ }
+
+ void setIConst(int i)
+ {
+ iConst = i;
+ type = EbtInt;
+ }
+
+ void setUConst(unsigned int u)
+ {
+ uConst = u;
+ type = EbtUint;
+ }
+
+ void setI64Const(long long i64)
+ {
+ i64Const = i64;
+ type = EbtInt64;
+ }
+
+ void setU64Const(unsigned long long u64)
+ {
+ u64Const = u64;
+ type = EbtUint64;
+ }
+
+ void setDConst(double d)
+ {
+ dConst = d;
+ type = EbtDouble;
+ }
+
+ void setBConst(bool b)
+ {
+ bConst = b;
+ type = EbtBool;
+ }
+
+ void setSConst(const TString* s)
+ {
+ sConst = s;
+ type = EbtString;
+ }
+
+ signed char getI8Const() const { return i8Const; }
+ unsigned char getU8Const() const { return u8Const; }
+ signed short getI16Const() const { return i16Const; }
+ unsigned short getU16Const() const { return u16Const; }
+ int getIConst() const { return iConst; }
+ unsigned int getUConst() const { return uConst; }
+ long long getI64Const() const { return i64Const; }
+ unsigned long long getU64Const() const { return u64Const; }
+ double getDConst() const { return dConst; }
+ bool getBConst() const { return bConst; }
+ const TString* getSConst() const { return sConst; }
+
+ bool operator==(const signed char i) const
+ {
+ if (i == i8Const)
+ return true;
+
+ return false;
+ }
+
+ bool operator==(const unsigned char u) const
+ {
+ if (u == u8Const)
+ return true;
+
+ return false;
+ }
+
+ bool operator==(const signed short i) const
+ {
+ if (i == i16Const)
+ return true;
+
+ return false;
+ }
+
+ bool operator==(const unsigned short u) const
+ {
+ if (u == u16Const)
+ return true;
+
+ return false;
+ }
+
+ bool operator==(const int i) const
+ {
+ if (i == iConst)
+ return true;
+
+ return false;
+ }
+
+ bool operator==(const unsigned int u) const
+ {
+ if (u == uConst)
+ return true;
+
+ return false;
+ }
+
+ bool operator==(const long long i64) const
+ {
+ if (i64 == i64Const)
+ return true;
+
+ return false;
+ }
+
+ bool operator==(const unsigned long long u64) const
+ {
+ if (u64 == u64Const)
+ return true;
+
+ return false;
+ }
+
+ bool operator==(const double d) const
+ {
+ if (d == dConst)
+ return true;
+
+ return false;
+ }
+
+ bool operator==(const bool b) const
+ {
+ if (b == bConst)
+ return true;
+
+ return false;
+ }
+
+ bool operator==(const TConstUnion& constant) const
+ {
+ if (constant.type != type)
+ return false;
+
+ switch (type) {
+ case EbtInt16:
+ if (constant.i16Const == i16Const)
+ return true;
+
+ break;
+ case EbtUint16:
+ if (constant.u16Const == u16Const)
+ return true;
+
+ break;
+ case EbtInt8:
+ if (constant.i8Const == i8Const)
+ return true;
+
+ break;
+ case EbtUint8:
+ if (constant.u8Const == u8Const)
+ return true;
+
+ break;
+ case EbtInt:
+ if (constant.iConst == iConst)
+ return true;
+
+ break;
+ case EbtUint:
+ if (constant.uConst == uConst)
+ return true;
+
+ break;
+ case EbtInt64:
+ if (constant.i64Const == i64Const)
+ return true;
+
+ break;
+ case EbtUint64:
+ if (constant.u64Const == u64Const)
+ return true;
+
+ break;
+ case EbtDouble:
+ if (constant.dConst == dConst)
+ return true;
+
+ break;
+ case EbtBool:
+ if (constant.bConst == bConst)
+ return true;
+
+ break;
+ default:
+ assert(false && "Default missing");
+ }
+
+ return false;
+ }
+
+ bool operator!=(const signed char i) const
+ {
+ return !operator==(i);
+ }
+
+ bool operator!=(const unsigned char u) const
+ {
+ return !operator==(u);
+ }
+
+ bool operator!=(const signed short i) const
+ {
+ return !operator==(i);
+ }
+
+ bool operator!=(const unsigned short u) const
+ {
+ return !operator==(u);
+ }
+
+ bool operator!=(const int i) const
+ {
+ return !operator==(i);
+ }
+
+ bool operator!=(const unsigned int u) const
+ {
+ return !operator==(u);
+ }
+
+ bool operator!=(const long long i) const
+ {
+ return !operator==(i);
+ }
+
+ bool operator!=(const unsigned long long u) const
+ {
+ return !operator==(u);
+ }
+
+ bool operator!=(const float f) const
+ {
+ return !operator==(f);
+ }
+
+ bool operator!=(const bool b) const
+ {
+ return !operator==(b);
+ }
+
+ bool operator!=(const TConstUnion& constant) const
+ {
+ return !operator==(constant);
+ }
+
+ bool operator>(const TConstUnion& constant) const
+ {
+ assert(type == constant.type);
+ switch (type) {
+ case EbtInt8:
+ if (i8Const > constant.i8Const)
+ return true;
+
+ return false;
+ case EbtUint8:
+ if (u8Const > constant.u8Const)
+ return true;
+
+ return false;
+ case EbtInt16:
+ if (i16Const > constant.i16Const)
+ return true;
+
+ return false;
+ case EbtUint16:
+ if (u16Const > constant.u16Const)
+ return true;
+
+ return false;
+ case EbtInt:
+ if (iConst > constant.iConst)
+ return true;
+
+ return false;
+ case EbtUint:
+ if (uConst > constant.uConst)
+ return true;
+
+ return false;
+ case EbtInt64:
+ if (i64Const > constant.i64Const)
+ return true;
+
+ return false;
+ case EbtUint64:
+ if (u64Const > constant.u64Const)
+ return true;
+
+ return false;
+ case EbtDouble:
+ if (dConst > constant.dConst)
+ return true;
+
+ return false;
+ default:
+ assert(false && "Default missing");
+ return false;
+ }
+ }
+
+ bool operator<(const TConstUnion& constant) const
+ {
+ assert(type == constant.type);
+ switch (type) {
+ case EbtInt8:
+ if (i8Const < constant.i8Const)
+ return true;
+
+ return false;
+ case EbtUint8:
+ if (u8Const < constant.u8Const)
+ return true;
+
+ return false;
+ case EbtInt16:
+ if (i16Const < constant.i16Const)
+ return true;
+
+ return false;
+ case EbtUint16:
+ if (u16Const < constant.u16Const)
+ return true;
+
+ return false;
+ case EbtInt:
+ if (iConst < constant.iConst)
+ return true;
+
+ return false;
+ case EbtUint:
+ if (uConst < constant.uConst)
+ return true;
+
+ return false;
+ case EbtInt64:
+ if (i64Const < constant.i64Const)
+ return true;
+
+ return false;
+ case EbtUint64:
+ if (u64Const < constant.u64Const)
+ return true;
+
+ return false;
+ case EbtDouble:
+ if (dConst < constant.dConst)
+ return true;
+
+ return false;
+ default:
+ assert(false && "Default missing");
+ return false;
+ }
+ }
+
+ TConstUnion operator+(const TConstUnion& constant) const
+ {
+ TConstUnion returnValue;
+ assert(type == constant.type);
+ switch (type) {
+ case EbtInt8: returnValue.setI8Const(i8Const + constant.i8Const); break;
+ case EbtInt16: returnValue.setI16Const(i16Const + constant.i16Const); break;
+ case EbtInt: returnValue.setIConst(iConst + constant.iConst); break;
+ case EbtInt64: returnValue.setI64Const(i64Const + constant.i64Const); break;
+ case EbtUint8: returnValue.setU8Const(u8Const + constant.u8Const); break;
+ case EbtUint16: returnValue.setU16Const(u16Const + constant.u16Const); break;
+ case EbtUint: returnValue.setUConst(uConst + constant.uConst); break;
+ case EbtUint64: returnValue.setU64Const(u64Const + constant.u64Const); break;
+ case EbtDouble: returnValue.setDConst(dConst + constant.dConst); break;
+ default: assert(false && "Default missing");
+ }
+
+ return returnValue;
+ }
+
+ TConstUnion operator-(const TConstUnion& constant) const
+ {
+ TConstUnion returnValue;
+ assert(type == constant.type);
+ switch (type) {
+ case EbtInt8: returnValue.setI8Const(i8Const - constant.i8Const); break;
+ case EbtInt16: returnValue.setI16Const(i16Const - constant.i16Const); break;
+ case EbtInt: returnValue.setIConst(iConst - constant.iConst); break;
+ case EbtInt64: returnValue.setI64Const(i64Const - constant.i64Const); break;
+ case EbtUint8: returnValue.setU8Const(u8Const - constant.u8Const); break;
+ case EbtUint16: returnValue.setU16Const(u16Const - constant.u16Const); break;
+ case EbtUint: returnValue.setUConst(uConst - constant.uConst); break;
+ case EbtUint64: returnValue.setU64Const(u64Const - constant.u64Const); break;
+ case EbtDouble: returnValue.setDConst(dConst - constant.dConst); break;
+ default: assert(false && "Default missing");
+ }
+
+ return returnValue;
+ }
+
+ TConstUnion operator*(const TConstUnion& constant) const
+ {
+ TConstUnion returnValue;
+ assert(type == constant.type);
+ switch (type) {
+ case EbtInt8: returnValue.setI8Const(i8Const * constant.i8Const); break;
+ case EbtInt16: returnValue.setI16Const(i16Const * constant.i16Const); break;
+ case EbtInt: returnValue.setIConst(iConst * constant.iConst); break;
+ case EbtInt64: returnValue.setI64Const(i64Const * constant.i64Const); break;
+ case EbtUint8: returnValue.setU8Const(u8Const * constant.u8Const); break;
+ case EbtUint16: returnValue.setU16Const(u16Const * constant.u16Const); break;
+ case EbtUint: returnValue.setUConst(uConst * constant.uConst); break;
+ case EbtUint64: returnValue.setU64Const(u64Const * constant.u64Const); break;
+ case EbtDouble: returnValue.setDConst(dConst * constant.dConst); break;
+ default: assert(false && "Default missing");
+ }
+
+ return returnValue;
+ }
+
+ TConstUnion operator%(const TConstUnion& constant) const
+ {
+ TConstUnion returnValue;
+ assert(type == constant.type);
+ switch (type) {
+ case EbtInt8: returnValue.setI8Const(i8Const % constant.i8Const); break;
+ case EbtInt16: returnValue.setI8Const(i8Const % constant.i16Const); break;
+ case EbtInt: returnValue.setIConst(iConst % constant.iConst); break;
+ case EbtInt64: returnValue.setI64Const(i64Const % constant.i64Const); break;
+ case EbtUint8: returnValue.setU8Const(u8Const % constant.u8Const); break;
+ case EbtUint16: returnValue.setU16Const(u16Const % constant.u16Const); break;
+ case EbtUint: returnValue.setUConst(uConst % constant.uConst); break;
+ case EbtUint64: returnValue.setU64Const(u64Const % constant.u64Const); break;
+ default: assert(false && "Default missing");
+ }
+
+ return returnValue;
+ }
+
+ TConstUnion operator>>(const TConstUnion& constant) const
+ {
+ TConstUnion returnValue;
+ switch (type) {
+ case EbtInt8:
+ switch (constant.type) {
+ case EbtInt8: returnValue.setI8Const(i8Const >> constant.i8Const); break;
+ case EbtUint8: returnValue.setI8Const(i8Const >> constant.u8Const); break;
+ case EbtInt16: returnValue.setI8Const(i8Const >> constant.i16Const); break;
+ case EbtUint16: returnValue.setI8Const(i8Const >> constant.u16Const); break;
+ case EbtInt: returnValue.setI8Const(i8Const >> constant.iConst); break;
+ case EbtUint: returnValue.setI8Const(i8Const >> constant.uConst); break;
+ case EbtInt64: returnValue.setI8Const(i8Const >> constant.i64Const); break;
+ case EbtUint64: returnValue.setI8Const(i8Const >> constant.u64Const); break;
+ default: assert(false && "Default missing");
+ }
+ break;
+ case EbtUint8:
+ switch (constant.type) {
+ case EbtInt8: returnValue.setU8Const(u8Const >> constant.i8Const); break;
+ case EbtUint8: returnValue.setU8Const(u8Const >> constant.u8Const); break;
+ case EbtInt16: returnValue.setU8Const(u8Const >> constant.i16Const); break;
+ case EbtUint16: returnValue.setU8Const(u8Const >> constant.u16Const); break;
+ case EbtInt: returnValue.setU8Const(u8Const >> constant.iConst); break;
+ case EbtUint: returnValue.setU8Const(u8Const >> constant.uConst); break;
+ case EbtInt64: returnValue.setU8Const(u8Const >> constant.i64Const); break;
+ case EbtUint64: returnValue.setU8Const(u8Const >> constant.u64Const); break;
+ default: assert(false && "Default missing");
+ }
+ break;
+ case EbtInt16:
+ switch (constant.type) {
+ case EbtInt8: returnValue.setI16Const(i16Const >> constant.i8Const); break;
+ case EbtUint8: returnValue.setI16Const(i16Const >> constant.u8Const); break;
+ case EbtInt16: returnValue.setI16Const(i16Const >> constant.i16Const); break;
+ case EbtUint16: returnValue.setI16Const(i16Const >> constant.u16Const); break;
+ case EbtInt: returnValue.setI16Const(i16Const >> constant.iConst); break;
+ case EbtUint: returnValue.setI16Const(i16Const >> constant.uConst); break;
+ case EbtInt64: returnValue.setI16Const(i16Const >> constant.i64Const); break;
+ case EbtUint64: returnValue.setI16Const(i16Const >> constant.u64Const); break;
+ default: assert(false && "Default missing");
+ }
+ break;
+ case EbtUint16:
+ switch (constant.type) {
+ case EbtInt8: returnValue.setU16Const(u16Const >> constant.i8Const); break;
+ case EbtUint8: returnValue.setU16Const(u16Const >> constant.u8Const); break;
+ case EbtInt16: returnValue.setU16Const(u16Const >> constant.i16Const); break;
+ case EbtUint16: returnValue.setU16Const(u16Const >> constant.u16Const); break;
+ case EbtInt: returnValue.setU16Const(u16Const >> constant.iConst); break;
+ case EbtUint: returnValue.setU16Const(u16Const >> constant.uConst); break;
+ case EbtInt64: returnValue.setU16Const(u16Const >> constant.i64Const); break;
+ case EbtUint64: returnValue.setU16Const(u16Const >> constant.u64Const); break;
+ default: assert(false && "Default missing");
+ }
+ break;
+ case EbtInt:
+ switch (constant.type) {
+ case EbtInt8: returnValue.setIConst(iConst >> constant.i8Const); break;
+ case EbtUint8: returnValue.setIConst(iConst >> constant.u8Const); break;
+ case EbtInt16: returnValue.setIConst(iConst >> constant.i16Const); break;
+ case EbtUint16: returnValue.setIConst(iConst >> constant.u16Const); break;
+ case EbtInt: returnValue.setIConst(iConst >> constant.iConst); break;
+ case EbtUint: returnValue.setIConst(iConst >> constant.uConst); break;
+ case EbtInt64: returnValue.setIConst(iConst >> constant.i64Const); break;
+ case EbtUint64: returnValue.setIConst(iConst >> constant.u64Const); break;
+ default: assert(false && "Default missing");
+ }
+ break;
+ case EbtUint:
+ switch (constant.type) {
+ case EbtInt8: returnValue.setUConst(uConst >> constant.i8Const); break;
+ case EbtUint8: returnValue.setUConst(uConst >> constant.u8Const); break;
+ case EbtInt16: returnValue.setUConst(uConst >> constant.i16Const); break;
+ case EbtUint16: returnValue.setUConst(uConst >> constant.u16Const); break;
+ case EbtInt: returnValue.setUConst(uConst >> constant.iConst); break;
+ case EbtUint: returnValue.setUConst(uConst >> constant.uConst); break;
+ case EbtInt64: returnValue.setUConst(uConst >> constant.i64Const); break;
+ case EbtUint64: returnValue.setUConst(uConst >> constant.u64Const); break;
+ default: assert(false && "Default missing");
+ }
+ break;
+ case EbtInt64:
+ switch (constant.type) {
+ case EbtInt8: returnValue.setI64Const(i64Const >> constant.i8Const); break;
+ case EbtUint8: returnValue.setI64Const(i64Const >> constant.u8Const); break;
+ case EbtInt16: returnValue.setI64Const(i64Const >> constant.i16Const); break;
+ case EbtUint16: returnValue.setI64Const(i64Const >> constant.u16Const); break;
+ case EbtInt: returnValue.setI64Const(i64Const >> constant.iConst); break;
+ case EbtUint: returnValue.setI64Const(i64Const >> constant.uConst); break;
+ case EbtInt64: returnValue.setI64Const(i64Const >> constant.i64Const); break;
+ case EbtUint64: returnValue.setI64Const(i64Const >> constant.u64Const); break;
+ default: assert(false && "Default missing");
+ }
+ break;
+ case EbtUint64:
+ switch (constant.type) {
+ case EbtInt8: returnValue.setU64Const(u64Const >> constant.i8Const); break;
+ case EbtUint8: returnValue.setU64Const(u64Const >> constant.u8Const); break;
+ case EbtInt16: returnValue.setU64Const(u64Const >> constant.i16Const); break;
+ case EbtUint16: returnValue.setU64Const(u64Const >> constant.u16Const); break;
+ case EbtInt: returnValue.setU64Const(u64Const >> constant.iConst); break;
+ case EbtUint: returnValue.setU64Const(u64Const >> constant.uConst); break;
+ case EbtInt64: returnValue.setU64Const(u64Const >> constant.i64Const); break;
+ case EbtUint64: returnValue.setU64Const(u64Const >> constant.u64Const); break;
+ default: assert(false && "Default missing");
+ }
+ break;
+ default: assert(false && "Default missing");
+ }
+
+ return returnValue;
+ }
+
+ TConstUnion operator<<(const TConstUnion& constant) const
+ {
+ TConstUnion returnValue;
+ switch (type) {
+ case EbtInt8:
+ switch (constant.type) {
+ case EbtInt8: returnValue.setI8Const(i8Const << constant.i8Const); break;
+ case EbtUint8: returnValue.setI8Const(i8Const << constant.u8Const); break;
+ case EbtInt16: returnValue.setI8Const(i8Const << constant.i16Const); break;
+ case EbtUint16: returnValue.setI8Const(i8Const << constant.u16Const); break;
+ case EbtInt: returnValue.setI8Const(i8Const << constant.iConst); break;
+ case EbtUint: returnValue.setI8Const(i8Const << constant.uConst); break;
+ case EbtInt64: returnValue.setI8Const(i8Const << constant.i64Const); break;
+ case EbtUint64: returnValue.setI8Const(i8Const << constant.u64Const); break;
+ default: assert(false && "Default missing");
+ }
+ break;
+ case EbtUint8:
+ switch (constant.type) {
+ case EbtInt8: returnValue.setU8Const(u8Const << constant.i8Const); break;
+ case EbtUint8: returnValue.setU8Const(u8Const << constant.u8Const); break;
+ case EbtInt16: returnValue.setU8Const(u8Const << constant.i16Const); break;
+ case EbtUint16: returnValue.setU8Const(u8Const << constant.u16Const); break;
+ case EbtInt: returnValue.setU8Const(u8Const << constant.iConst); break;
+ case EbtUint: returnValue.setU8Const(u8Const << constant.uConst); break;
+ case EbtInt64: returnValue.setU8Const(u8Const << constant.i64Const); break;
+ case EbtUint64: returnValue.setU8Const(u8Const << constant.u64Const); break;
+ default: assert(false && "Default missing");
+ }
+ break;
+ case EbtInt16:
+ switch (constant.type) {
+ case EbtInt8: returnValue.setI16Const(i16Const << constant.i8Const); break;
+ case EbtUint8: returnValue.setI16Const(i16Const << constant.u8Const); break;
+ case EbtInt16: returnValue.setI16Const(i16Const << constant.i16Const); break;
+ case EbtUint16: returnValue.setI16Const(i16Const << constant.u16Const); break;
+ case EbtInt: returnValue.setI16Const(i16Const << constant.iConst); break;
+ case EbtUint: returnValue.setI16Const(i16Const << constant.uConst); break;
+ case EbtInt64: returnValue.setI16Const(i16Const << constant.i64Const); break;
+ case EbtUint64: returnValue.setI16Const(i16Const << constant.u64Const); break;
+ default: assert(false && "Default missing");
+ }
+ break;
+ case EbtUint16:
+ switch (constant.type) {
+ case EbtInt8: returnValue.setU16Const(u16Const << constant.i8Const); break;
+ case EbtUint8: returnValue.setU16Const(u16Const << constant.u8Const); break;
+ case EbtInt16: returnValue.setU16Const(u16Const << constant.i16Const); break;
+ case EbtUint16: returnValue.setU16Const(u16Const << constant.u16Const); break;
+ case EbtInt: returnValue.setU16Const(u16Const << constant.iConst); break;
+ case EbtUint: returnValue.setU16Const(u16Const << constant.uConst); break;
+ case EbtInt64: returnValue.setU16Const(u16Const << constant.i64Const); break;
+ case EbtUint64: returnValue.setU16Const(u16Const << constant.u64Const); break;
+ default: assert(false && "Default missing");
+ }
+ break;
+ case EbtInt:
+ switch (constant.type) {
+ case EbtInt8: returnValue.setIConst(iConst << constant.i8Const); break;
+ case EbtUint8: returnValue.setIConst(iConst << constant.u8Const); break;
+ case EbtInt16: returnValue.setIConst(iConst << constant.i16Const); break;
+ case EbtUint16: returnValue.setIConst(iConst << constant.u16Const); break;
+ case EbtInt: returnValue.setIConst(iConst << constant.iConst); break;
+ case EbtUint: returnValue.setIConst(iConst << constant.uConst); break;
+ case EbtInt64: returnValue.setIConst(iConst << constant.i64Const); break;
+ case EbtUint64: returnValue.setIConst(iConst << constant.u64Const); break;
+ default: assert(false && "Default missing");
+ }
+ break;
+ case EbtUint:
+ switch (constant.type) {
+ case EbtInt8: returnValue.setUConst(uConst << constant.i8Const); break;
+ case EbtUint8: returnValue.setUConst(uConst << constant.u8Const); break;
+ case EbtInt16: returnValue.setUConst(uConst << constant.i16Const); break;
+ case EbtUint16: returnValue.setUConst(uConst << constant.u16Const); break;
+ case EbtInt: returnValue.setUConst(uConst << constant.iConst); break;
+ case EbtUint: returnValue.setUConst(uConst << constant.uConst); break;
+ case EbtInt64: returnValue.setUConst(uConst << constant.i64Const); break;
+ case EbtUint64: returnValue.setUConst(uConst << constant.u64Const); break;
+ default: assert(false && "Default missing");
+ }
+ break;
+ case EbtInt64:
+ switch (constant.type) {
+ case EbtInt8: returnValue.setI64Const(i64Const << constant.i8Const); break;
+ case EbtUint8: returnValue.setI64Const(i64Const << constant.u8Const); break;
+ case EbtInt16: returnValue.setI64Const(i64Const << constant.i16Const); break;
+ case EbtUint16: returnValue.setI64Const(i64Const << constant.u16Const); break;
+ case EbtInt: returnValue.setI64Const(i64Const << constant.iConst); break;
+ case EbtUint: returnValue.setI64Const(i64Const << constant.uConst); break;
+ case EbtInt64: returnValue.setI64Const(i64Const << constant.i64Const); break;
+ case EbtUint64: returnValue.setI64Const(i64Const << constant.u64Const); break;
+ default: assert(false && "Default missing");
+ }
+ break;
+ case EbtUint64:
+ switch (constant.type) {
+ case EbtInt8: returnValue.setU64Const(u64Const << constant.i8Const); break;
+ case EbtUint8: returnValue.setU64Const(u64Const << constant.u8Const); break;
+ case EbtInt16: returnValue.setU64Const(u64Const << constant.i16Const); break;
+ case EbtUint16: returnValue.setU64Const(u64Const << constant.u16Const); break;
+ case EbtInt: returnValue.setU64Const(u64Const << constant.iConst); break;
+ case EbtUint: returnValue.setU64Const(u64Const << constant.uConst); break;
+ case EbtInt64: returnValue.setU64Const(u64Const << constant.i64Const); break;
+ case EbtUint64: returnValue.setU64Const(u64Const << constant.u64Const); break;
+ default: assert(false && "Default missing");
+ }
+ break;
+ default: assert(false && "Default missing");
+ }
+
+ return returnValue;
+ }
+
+ TConstUnion operator&(const TConstUnion& constant) const
+ {
+ TConstUnion returnValue;
+ assert(type == constant.type);
+ switch (type) {
+ case EbtInt8: returnValue.setI8Const(i8Const & constant.i8Const); break;
+ case EbtUint8: returnValue.setU8Const(u8Const & constant.u8Const); break;
+ case EbtInt16: returnValue.setI16Const(i16Const & constant.i16Const); break;
+ case EbtUint16: returnValue.setU16Const(u16Const & constant.u16Const); break;
+ case EbtInt: returnValue.setIConst(iConst & constant.iConst); break;
+ case EbtUint: returnValue.setUConst(uConst & constant.uConst); break;
+ case EbtInt64: returnValue.setI64Const(i64Const & constant.i64Const); break;
+ case EbtUint64: returnValue.setU64Const(u64Const & constant.u64Const); break;
+ default: assert(false && "Default missing");
+ }
+
+ return returnValue;
+ }
+
+ TConstUnion operator|(const TConstUnion& constant) const
+ {
+ TConstUnion returnValue;
+ assert(type == constant.type);
+ switch (type) {
+ case EbtInt8: returnValue.setI8Const(i8Const | constant.i8Const); break;
+ case EbtUint8: returnValue.setU8Const(u8Const | constant.u8Const); break;
+ case EbtInt16: returnValue.setI16Const(i16Const | constant.i16Const); break;
+ case EbtUint16: returnValue.setU16Const(u16Const | constant.u16Const); break;
+ case EbtInt: returnValue.setIConst(iConst | constant.iConst); break;
+ case EbtUint: returnValue.setUConst(uConst | constant.uConst); break;
+ case EbtInt64: returnValue.setI64Const(i64Const | constant.i64Const); break;
+ case EbtUint64: returnValue.setU64Const(u64Const | constant.u64Const); break;
+ default: assert(false && "Default missing");
+ }
+
+ return returnValue;
+ }
+
+ TConstUnion operator^(const TConstUnion& constant) const
+ {
+ TConstUnion returnValue;
+ assert(type == constant.type);
+ switch (type) {
+ case EbtInt8: returnValue.setI8Const(i8Const ^ constant.i8Const); break;
+ case EbtUint8: returnValue.setU8Const(u8Const ^ constant.u8Const); break;
+ case EbtInt16: returnValue.setI16Const(i16Const ^ constant.i16Const); break;
+ case EbtUint16: returnValue.setU16Const(u16Const ^ constant.u16Const); break;
+ case EbtInt: returnValue.setIConst(iConst ^ constant.iConst); break;
+ case EbtUint: returnValue.setUConst(uConst ^ constant.uConst); break;
+ case EbtInt64: returnValue.setI64Const(i64Const ^ constant.i64Const); break;
+ case EbtUint64: returnValue.setU64Const(u64Const ^ constant.u64Const); break;
+ default: assert(false && "Default missing");
+ }
+
+ return returnValue;
+ }
+
+ TConstUnion operator~() const
+ {
+ TConstUnion returnValue;
+ switch (type) {
+ case EbtInt8: returnValue.setI8Const(~i8Const); break;
+ case EbtUint8: returnValue.setU8Const(~u8Const); break;
+ case EbtInt16: returnValue.setI16Const(~i16Const); break;
+ case EbtUint16: returnValue.setU16Const(~u16Const); break;
+ case EbtInt: returnValue.setIConst(~iConst); break;
+ case EbtUint: returnValue.setUConst(~uConst); break;
+ case EbtInt64: returnValue.setI64Const(~i64Const); break;
+ case EbtUint64: returnValue.setU64Const(~u64Const); break;
+ default: assert(false && "Default missing");
+ }
+
+ return returnValue;
+ }
+
+ TConstUnion operator&&(const TConstUnion& constant) const
+ {
+ TConstUnion returnValue;
+ assert(type == constant.type);
+ switch (type) {
+ case EbtBool: returnValue.setBConst(bConst && constant.bConst); break;
+ default: assert(false && "Default missing");
+ }
+
+ return returnValue;
+ }
+
+ TConstUnion operator||(const TConstUnion& constant) const
+ {
+ TConstUnion returnValue;
+ assert(type == constant.type);
+ switch (type) {
+ case EbtBool: returnValue.setBConst(bConst || constant.bConst); break;
+ default: assert(false && "Default missing");
+ }
+
+ return returnValue;
+ }
+
+ TBasicType getType() const { return type; }
+
+private:
+ union {
+ signed char i8Const; // used for i8vec, scalar int8s
+ unsigned char u8Const; // used for u8vec, scalar uint8s
+ signed short i16Const; // used for i16vec, scalar int16s
+ unsigned short u16Const; // used for u16vec, scalar uint16s
+ int iConst; // used for ivec, scalar ints
+ unsigned int uConst; // used for uvec, scalar uints
+ long long i64Const; // used for i64vec, scalar int64s
+ unsigned long long u64Const; // used for u64vec, scalar uint64s
+ bool bConst; // used for bvec, scalar bools
+ double dConst; // used for vec, dvec, mat, dmat, scalar floats and doubles
+ const TString* sConst; // string constant
+ };
+
+ TBasicType type;
+};
+
+// Encapsulate having a pointer to an array of TConstUnion,
+// which only needs to be allocated if its size is going to be
+// bigger than 0.
+//
+// One convenience is being able to use [] to go inside the array, instead
+// of C++ assuming it as an array of pointers to vectors.
+//
+// General usage is that the size is known up front, and it is
+// created once with the proper size.
+//
+class TConstUnionArray {
+public:
+ POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator())
+
+ TConstUnionArray() : unionArray(nullptr) { }
+ virtual ~TConstUnionArray() { }
+
+ explicit TConstUnionArray(int size)
+ {
+ if (size == 0)
+ unionArray = nullptr;
+ else
+ unionArray = new TConstUnionVector(size);
+ }
+ TConstUnionArray(const TConstUnionArray& a) : unionArray(a.unionArray) { }
+ TConstUnionArray(const TConstUnionArray& a, int start, int size)
+ {
+ unionArray = new TConstUnionVector(size);
+ for (int i = 0; i < size; ++i)
+ (*unionArray)[i] = a[start + i];
+ }
+
+ // Use this constructor for a smear operation
+ TConstUnionArray(int size, const TConstUnion& val)
+ {
+ unionArray = new TConstUnionVector(size, val);
+ }
+
+ int size() const { return unionArray ? (int)unionArray->size() : 0; }
+ TConstUnion& operator[](size_t index) { return (*unionArray)[index]; }
+ const TConstUnion& operator[](size_t index) const { return (*unionArray)[index]; }
+ bool operator==(const TConstUnionArray& rhs) const
+ {
+ // this includes the case that both are unallocated
+ if (unionArray == rhs.unionArray)
+ return true;
+
+ if (! unionArray || ! rhs.unionArray)
+ return false;
+
+ return *unionArray == *rhs.unionArray;
+ }
+ bool operator!=(const TConstUnionArray& rhs) const { return ! operator==(rhs); }
+
+ double dot(const TConstUnionArray& rhs)
+ {
+ assert(rhs.unionArray->size() == unionArray->size());
+ double sum = 0.0;
+
+ for (size_t comp = 0; comp < unionArray->size(); ++comp)
+ sum += (*this)[comp].getDConst() * rhs[comp].getDConst();
+
+ return sum;
+ }
+
+ bool empty() const { return unionArray == nullptr; }
+
+protected:
+ typedef TVector<TConstUnion> TConstUnionVector;
+ TConstUnionVector* unionArray;
+};
+
+} // end namespace glslang
+
+#endif // _CONSTANT_UNION_INCLUDED_
diff --git a/src/3rdparty/glslang/glslang/Include/InfoSink.h b/src/3rdparty/glslang/glslang/Include/InfoSink.h
new file mode 100644
index 0000000..dceb603
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/Include/InfoSink.h
@@ -0,0 +1,144 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef _INFOSINK_INCLUDED_
+#define _INFOSINK_INCLUDED_
+
+#include "../Include/Common.h"
+#include <cmath>
+
+namespace glslang {
+
+//
+// TPrefixType is used to centralize how info log messages start.
+// See below.
+//
+enum TPrefixType {
+ EPrefixNone,
+ EPrefixWarning,
+ EPrefixError,
+ EPrefixInternalError,
+ EPrefixUnimplemented,
+ EPrefixNote
+};
+
+enum TOutputStream {
+ ENull = 0,
+ EDebugger = 0x01,
+ EStdOut = 0x02,
+ EString = 0x04,
+};
+//
+// Encapsulate info logs for all objects that have them.
+//
+// The methods are a general set of tools for getting a variety of
+// messages and types inserted into the log.
+//
+class TInfoSinkBase {
+public:
+ TInfoSinkBase() : outputStream(4) {}
+ void erase() { sink.erase(); }
+ TInfoSinkBase& operator<<(const TPersistString& t) { append(t); return *this; }
+ TInfoSinkBase& operator<<(char c) { append(1, c); return *this; }
+ TInfoSinkBase& operator<<(const char* s) { append(s); return *this; }
+ TInfoSinkBase& operator<<(int n) { append(String(n)); return *this; }
+ TInfoSinkBase& operator<<(unsigned int n) { append(String(n)); return *this; }
+ TInfoSinkBase& operator<<(float n) { const int size = 40; char buf[size];
+ snprintf(buf, size, (fabs(n) > 1e-8 && fabs(n) < 1e8) || n == 0.0f ? "%f" : "%g", n);
+ append(buf);
+ return *this; }
+ TInfoSinkBase& operator+(const TPersistString& t) { append(t); return *this; }
+ TInfoSinkBase& operator+(const TString& t) { append(t); return *this; }
+ TInfoSinkBase& operator<<(const TString& t) { append(t); return *this; }
+ TInfoSinkBase& operator+(const char* s) { append(s); return *this; }
+ const char* c_str() const { return sink.c_str(); }
+ void prefix(TPrefixType message) {
+ switch(message) {
+ case EPrefixNone: break;
+ case EPrefixWarning: append("WARNING: "); break;
+ case EPrefixError: append("ERROR: "); break;
+ case EPrefixInternalError: append("INTERNAL ERROR: "); break;
+ case EPrefixUnimplemented: append("UNIMPLEMENTED: "); break;
+ case EPrefixNote: append("NOTE: "); break;
+ default: append("UNKNOWN ERROR: "); break;
+ }
+ }
+ void location(const TSourceLoc& loc) {
+ const int maxSize = 24;
+ char locText[maxSize];
+ snprintf(locText, maxSize, ":%d", loc.line);
+ append(loc.getStringNameOrNum(false).c_str());
+ append(locText);
+ append(": ");
+ }
+ void message(TPrefixType message, const char* s) {
+ prefix(message);
+ append(s);
+ append("\n");
+ }
+ void message(TPrefixType message, const char* s, const TSourceLoc& loc) {
+ prefix(message);
+ location(loc);
+ append(s);
+ append("\n");
+ }
+
+ void setOutputStream(int output = 4)
+ {
+ outputStream = output;
+ }
+
+protected:
+ void append(const char* s);
+
+ void append(int count, char c);
+ void append(const TPersistString& t);
+ void append(const TString& t);
+
+ void checkMem(size_t growth) { if (sink.capacity() < sink.size() + growth + 2)
+ sink.reserve(sink.capacity() + sink.capacity() / 2); }
+ void appendToStream(const char* s);
+ TPersistString sink;
+ int outputStream;
+};
+
+} // end namespace glslang
+
+class TInfoSink {
+public:
+ glslang::TInfoSinkBase info;
+ glslang::TInfoSinkBase debug;
+};
+
+#endif // _INFOSINK_INCLUDED_
diff --git a/src/3rdparty/glslang/glslang/Include/InitializeGlobals.h b/src/3rdparty/glslang/glslang/Include/InitializeGlobals.h
new file mode 100644
index 0000000..95d0a40
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/Include/InitializeGlobals.h
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef __INITIALIZE_GLOBALS_INCLUDED_
+#define __INITIALIZE_GLOBALS_INCLUDED_
+
+namespace glslang {
+
+bool InitializePoolIndex();
+
+} // end namespace glslang
+
+#endif // __INITIALIZE_GLOBALS_INCLUDED_
diff --git a/src/3rdparty/glslang/glslang/Include/PoolAlloc.h b/src/3rdparty/glslang/glslang/Include/PoolAlloc.h
new file mode 100644
index 0000000..0e237a6
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/Include/PoolAlloc.h
@@ -0,0 +1,317 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2012-2013 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef _POOLALLOC_INCLUDED_
+#define _POOLALLOC_INCLUDED_
+
+#ifdef _DEBUG
+# define GUARD_BLOCKS // define to enable guard block sanity checking
+#endif
+
+//
+// This header defines an allocator that can be used to efficiently
+// allocate a large number of small requests for heap memory, with the
+// intention that they are not individually deallocated, but rather
+// collectively deallocated at one time.
+//
+// This simultaneously
+//
+// * Makes each individual allocation much more efficient; the
+// typical allocation is trivial.
+// * Completely avoids the cost of doing individual deallocation.
+// * Saves the trouble of tracking down and plugging a large class of leaks.
+//
+// Individual classes can use this allocator by supplying their own
+// new and delete methods.
+//
+// STL containers can use this allocator by using the pool_allocator
+// class as the allocator (second) template argument.
+//
+
+#include <cstddef>
+#include <cstring>
+#include <vector>
+
+namespace glslang {
+
+// If we are using guard blocks, we must track each individual
+// allocation. If we aren't using guard blocks, these
+// never get instantiated, so won't have any impact.
+//
+
+class TAllocation {
+public:
+ TAllocation(size_t size, unsigned char* mem, TAllocation* prev = 0) :
+ size(size), mem(mem), prevAlloc(prev) {
+ // Allocations are bracketed:
+ // [allocationHeader][initialGuardBlock][userData][finalGuardBlock]
+ // This would be cleaner with if (guardBlockSize)..., but that
+ // makes the compiler print warnings about 0 length memsets,
+ // even with the if() protecting them.
+# ifdef GUARD_BLOCKS
+ memset(preGuard(), guardBlockBeginVal, guardBlockSize);
+ memset(data(), userDataFill, size);
+ memset(postGuard(), guardBlockEndVal, guardBlockSize);
+# endif
+ }
+
+ void check() const {
+ checkGuardBlock(preGuard(), guardBlockBeginVal, "before");
+ checkGuardBlock(postGuard(), guardBlockEndVal, "after");
+ }
+
+ void checkAllocList() const;
+
+ // Return total size needed to accommodate user buffer of 'size',
+ // plus our tracking data.
+ inline static size_t allocationSize(size_t size) {
+ return size + 2 * guardBlockSize + headerSize();
+ }
+
+ // Offset from surrounding buffer to get to user data buffer.
+ inline static unsigned char* offsetAllocation(unsigned char* m) {
+ return m + guardBlockSize + headerSize();
+ }
+
+private:
+ void checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const;
+
+ // Find offsets to pre and post guard blocks, and user data buffer
+ unsigned char* preGuard() const { return mem + headerSize(); }
+ unsigned char* data() const { return preGuard() + guardBlockSize; }
+ unsigned char* postGuard() const { return data() + size; }
+
+ size_t size; // size of the user data area
+ unsigned char* mem; // beginning of our allocation (pts to header)
+ TAllocation* prevAlloc; // prior allocation in the chain
+
+ const static unsigned char guardBlockBeginVal;
+ const static unsigned char guardBlockEndVal;
+ const static unsigned char userDataFill;
+
+ const static size_t guardBlockSize;
+# ifdef GUARD_BLOCKS
+ inline static size_t headerSize() { return sizeof(TAllocation); }
+# else
+ inline static size_t headerSize() { return 0; }
+# endif
+};
+
+//
+// There are several stacks. One is to track the pushing and popping
+// of the user, and not yet implemented. The others are simply a
+// repositories of free pages or used pages.
+//
+// Page stacks are linked together with a simple header at the beginning
+// of each allocation obtained from the underlying OS. Multi-page allocations
+// are returned to the OS. Individual page allocations are kept for future
+// re-use.
+//
+// The "page size" used is not, nor must it match, the underlying OS
+// page size. But, having it be about that size or equal to a set of
+// pages is likely most optimal.
+//
+class TPoolAllocator {
+public:
+ TPoolAllocator(int growthIncrement = 8*1024, int allocationAlignment = 16);
+
+ //
+ // Don't call the destructor just to free up the memory, call pop()
+ //
+ ~TPoolAllocator();
+
+ //
+ // Call push() to establish a new place to pop memory too. Does not
+ // have to be called to get things started.
+ //
+ void push();
+
+ //
+ // Call pop() to free all memory allocated since the last call to push(),
+ // or if no last call to push, frees all memory since first allocation.
+ //
+ void pop();
+
+ //
+ // Call popAll() to free all memory allocated.
+ //
+ void popAll();
+
+ //
+ // Call allocate() to actually acquire memory. Returns 0 if no memory
+ // available, otherwise a properly aligned pointer to 'numBytes' of memory.
+ //
+ void* allocate(size_t numBytes);
+
+ //
+ // There is no deallocate. The point of this class is that
+ // deallocation can be skipped by the user of it, as the model
+ // of use is to simultaneously deallocate everything at once
+ // by calling pop(), and to not have to solve memory leak problems.
+ //
+
+protected:
+ friend struct tHeader;
+
+ struct tHeader {
+ tHeader(tHeader* nextPage, size_t pageCount) :
+#ifdef GUARD_BLOCKS
+ lastAllocation(0),
+#endif
+ nextPage(nextPage), pageCount(pageCount) { }
+
+ ~tHeader() {
+#ifdef GUARD_BLOCKS
+ if (lastAllocation)
+ lastAllocation->checkAllocList();
+#endif
+ }
+
+#ifdef GUARD_BLOCKS
+ TAllocation* lastAllocation;
+#endif
+ tHeader* nextPage;
+ size_t pageCount;
+ };
+
+ struct tAllocState {
+ size_t offset;
+ tHeader* page;
+ };
+ typedef std::vector<tAllocState> tAllocStack;
+
+ // Track allocations if and only if we're using guard blocks
+#ifndef GUARD_BLOCKS
+ void* initializeAllocation(tHeader*, unsigned char* memory, size_t) {
+#else
+ void* initializeAllocation(tHeader* block, unsigned char* memory, size_t numBytes) {
+ new(memory) TAllocation(numBytes, memory, block->lastAllocation);
+ block->lastAllocation = reinterpret_cast<TAllocation*>(memory);
+#endif
+
+ // This is optimized entirely away if GUARD_BLOCKS is not defined.
+ return TAllocation::offsetAllocation(memory);
+ }
+
+ size_t pageSize; // granularity of allocation from the OS
+ size_t alignment; // all returned allocations will be aligned at
+ // this granularity, which will be a power of 2
+ size_t alignmentMask;
+ size_t headerSkip; // amount of memory to skip to make room for the
+ // header (basically, size of header, rounded
+ // up to make it aligned
+ size_t currentPageOffset; // next offset in top of inUseList to allocate from
+ tHeader* freeList; // list of popped memory
+ tHeader* inUseList; // list of all memory currently being used
+ tAllocStack stack; // stack of where to allocate from, to partition pool
+
+ int numCalls; // just an interesting statistic
+ size_t totalBytes; // just an interesting statistic
+private:
+ TPoolAllocator& operator=(const TPoolAllocator&); // don't allow assignment operator
+ TPoolAllocator(const TPoolAllocator&); // don't allow default copy constructor
+};
+
+//
+// There could potentially be many pools with pops happening at
+// different times. But a simple use is to have a global pop
+// with everyone using the same global allocator.
+//
+extern TPoolAllocator& GetThreadPoolAllocator();
+void SetThreadPoolAllocator(TPoolAllocator* poolAllocator);
+
+//
+// This STL compatible allocator is intended to be used as the allocator
+// parameter to templatized STL containers, like vector and map.
+//
+// It will use the pools for allocation, and not
+// do any deallocation, but will still do destruction.
+//
+template<class T>
+class pool_allocator {
+public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef T *pointer;
+ typedef const T *const_pointer;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T value_type;
+ template<class Other>
+ struct rebind {
+ typedef pool_allocator<Other> other;
+ };
+ pointer address(reference x) const { return &x; }
+ const_pointer address(const_reference x) const { return &x; }
+
+ pool_allocator() : allocator(GetThreadPoolAllocator()) { }
+ pool_allocator(TPoolAllocator& a) : allocator(a) { }
+ pool_allocator(const pool_allocator<T>& p) : allocator(p.allocator) { }
+
+ template<class Other>
+ pool_allocator(const pool_allocator<Other>& p) : allocator(p.getAllocator()) { }
+
+ pointer allocate(size_type n) {
+ return reinterpret_cast<pointer>(getAllocator().allocate(n * sizeof(T))); }
+ pointer allocate(size_type n, const void*) {
+ return reinterpret_cast<pointer>(getAllocator().allocate(n * sizeof(T))); }
+
+ void deallocate(void*, size_type) { }
+ void deallocate(pointer, size_type) { }
+
+ pointer _Charalloc(size_t n) {
+ return reinterpret_cast<pointer>(getAllocator().allocate(n)); }
+
+ void construct(pointer p, const T& val) { new ((void *)p) T(val); }
+ void destroy(pointer p) { p->T::~T(); }
+
+ bool operator==(const pool_allocator& rhs) const { return &getAllocator() == &rhs.getAllocator(); }
+ bool operator!=(const pool_allocator& rhs) const { return &getAllocator() != &rhs.getAllocator(); }
+
+ size_type max_size() const { return static_cast<size_type>(-1) / sizeof(T); }
+ size_type max_size(int size) const { return static_cast<size_type>(-1) / size; }
+
+ void setAllocator(TPoolAllocator* a) { allocator = *a; }
+ TPoolAllocator& getAllocator() const { return allocator; }
+
+protected:
+ pool_allocator& operator=(const pool_allocator&) { return *this; }
+ TPoolAllocator& allocator;
+};
+
+} // end namespace glslang
+
+#endif // _POOLALLOC_INCLUDED_
diff --git a/src/3rdparty/glslang/glslang/Include/ResourceLimits.h b/src/3rdparty/glslang/glslang/Include/ResourceLimits.h
new file mode 100644
index 0000000..106b21d
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/Include/ResourceLimits.h
@@ -0,0 +1,149 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2013 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef _RESOURCE_LIMITS_INCLUDED_
+#define _RESOURCE_LIMITS_INCLUDED_
+
+struct TLimits {
+ bool nonInductiveForLoops;
+ bool whileLoops;
+ bool doWhileLoops;
+ bool generalUniformIndexing;
+ bool generalAttributeMatrixVectorIndexing;
+ bool generalVaryingIndexing;
+ bool generalSamplerIndexing;
+ bool generalVariableIndexing;
+ bool generalConstantMatrixVectorIndexing;
+};
+
+struct TBuiltInResource {
+ int maxLights;
+ int maxClipPlanes;
+ int maxTextureUnits;
+ int maxTextureCoords;
+ int maxVertexAttribs;
+ int maxVertexUniformComponents;
+ int maxVaryingFloats;
+ int maxVertexTextureImageUnits;
+ int maxCombinedTextureImageUnits;
+ int maxTextureImageUnits;
+ int maxFragmentUniformComponents;
+ int maxDrawBuffers;
+ int maxVertexUniformVectors;
+ int maxVaryingVectors;
+ int maxFragmentUniformVectors;
+ int maxVertexOutputVectors;
+ int maxFragmentInputVectors;
+ int minProgramTexelOffset;
+ int maxProgramTexelOffset;
+ int maxClipDistances;
+ int maxComputeWorkGroupCountX;
+ int maxComputeWorkGroupCountY;
+ int maxComputeWorkGroupCountZ;
+ int maxComputeWorkGroupSizeX;
+ int maxComputeWorkGroupSizeY;
+ int maxComputeWorkGroupSizeZ;
+ int maxComputeUniformComponents;
+ int maxComputeTextureImageUnits;
+ int maxComputeImageUniforms;
+ int maxComputeAtomicCounters;
+ int maxComputeAtomicCounterBuffers;
+ int maxVaryingComponents;
+ int maxVertexOutputComponents;
+ int maxGeometryInputComponents;
+ int maxGeometryOutputComponents;
+ int maxFragmentInputComponents;
+ int maxImageUnits;
+ int maxCombinedImageUnitsAndFragmentOutputs;
+ int maxCombinedShaderOutputResources;
+ int maxImageSamples;
+ int maxVertexImageUniforms;
+ int maxTessControlImageUniforms;
+ int maxTessEvaluationImageUniforms;
+ int maxGeometryImageUniforms;
+ int maxFragmentImageUniforms;
+ int maxCombinedImageUniforms;
+ int maxGeometryTextureImageUnits;
+ int maxGeometryOutputVertices;
+ int maxGeometryTotalOutputComponents;
+ int maxGeometryUniformComponents;
+ int maxGeometryVaryingComponents;
+ int maxTessControlInputComponents;
+ int maxTessControlOutputComponents;
+ int maxTessControlTextureImageUnits;
+ int maxTessControlUniformComponents;
+ int maxTessControlTotalOutputComponents;
+ int maxTessEvaluationInputComponents;
+ int maxTessEvaluationOutputComponents;
+ int maxTessEvaluationTextureImageUnits;
+ int maxTessEvaluationUniformComponents;
+ int maxTessPatchComponents;
+ int maxPatchVertices;
+ int maxTessGenLevel;
+ int maxViewports;
+ int maxVertexAtomicCounters;
+ int maxTessControlAtomicCounters;
+ int maxTessEvaluationAtomicCounters;
+ int maxGeometryAtomicCounters;
+ int maxFragmentAtomicCounters;
+ int maxCombinedAtomicCounters;
+ int maxAtomicCounterBindings;
+ int maxVertexAtomicCounterBuffers;
+ int maxTessControlAtomicCounterBuffers;
+ int maxTessEvaluationAtomicCounterBuffers;
+ int maxGeometryAtomicCounterBuffers;
+ int maxFragmentAtomicCounterBuffers;
+ int maxCombinedAtomicCounterBuffers;
+ int maxAtomicCounterBufferSize;
+ int maxTransformFeedbackBuffers;
+ int maxTransformFeedbackInterleavedComponents;
+ int maxCullDistances;
+ int maxCombinedClipAndCullDistances;
+ int maxSamples;
+ int maxMeshOutputVerticesNV;
+ int maxMeshOutputPrimitivesNV;
+ int maxMeshWorkGroupSizeX_NV;
+ int maxMeshWorkGroupSizeY_NV;
+ int maxMeshWorkGroupSizeZ_NV;
+ int maxTaskWorkGroupSizeX_NV;
+ int maxTaskWorkGroupSizeY_NV;
+ int maxTaskWorkGroupSizeZ_NV;
+ int maxMeshViewCountNV;
+
+ TLimits limits;
+};
+
+#endif // _RESOURCE_LIMITS_INCLUDED_
diff --git a/src/3rdparty/glslang/glslang/Include/ShHandle.h b/src/3rdparty/glslang/glslang/Include/ShHandle.h
new file mode 100644
index 0000000..df07bd8
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/Include/ShHandle.h
@@ -0,0 +1,176 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef _SHHANDLE_INCLUDED_
+#define _SHHANDLE_INCLUDED_
+
+//
+// Machine independent part of the compiler private objects
+// sent as ShHandle to the driver.
+//
+// This should not be included by driver code.
+//
+
+#define SH_EXPORTING
+#include "../Public/ShaderLang.h"
+#include "../MachineIndependent/Versions.h"
+#include "InfoSink.h"
+
+class TCompiler;
+class TLinker;
+class TUniformMap;
+
+//
+// The base class used to back handles returned to the driver.
+//
+class TShHandleBase {
+public:
+ TShHandleBase() { pool = new glslang::TPoolAllocator; }
+ virtual ~TShHandleBase() { delete pool; }
+ virtual TCompiler* getAsCompiler() { return 0; }
+ virtual TLinker* getAsLinker() { return 0; }
+ virtual TUniformMap* getAsUniformMap() { return 0; }
+ virtual glslang::TPoolAllocator* getPool() const { return pool; }
+private:
+ glslang::TPoolAllocator* pool;
+};
+
+//
+// The base class for the machine dependent linker to derive from
+// for managing where uniforms live.
+//
+class TUniformMap : public TShHandleBase {
+public:
+ TUniformMap() { }
+ virtual ~TUniformMap() { }
+ virtual TUniformMap* getAsUniformMap() { return this; }
+ virtual int getLocation(const char* name) = 0;
+ virtual TInfoSink& getInfoSink() { return infoSink; }
+ TInfoSink infoSink;
+};
+
+class TIntermNode;
+
+//
+// The base class for the machine dependent compiler to derive from
+// for managing object code from the compile.
+//
+class TCompiler : public TShHandleBase {
+public:
+ TCompiler(EShLanguage l, TInfoSink& sink) : infoSink(sink) , language(l), haveValidObjectCode(false) { }
+ virtual ~TCompiler() { }
+ EShLanguage getLanguage() { return language; }
+ virtual TInfoSink& getInfoSink() { return infoSink; }
+
+ virtual bool compile(TIntermNode* root, int version = 0, EProfile profile = ENoProfile) = 0;
+
+ virtual TCompiler* getAsCompiler() { return this; }
+ virtual bool linkable() { return haveValidObjectCode; }
+
+ TInfoSink& infoSink;
+protected:
+ TCompiler& operator=(TCompiler&);
+
+ EShLanguage language;
+ bool haveValidObjectCode;
+};
+
+//
+// Link operations are based on a list of compile results...
+//
+typedef glslang::TVector<TCompiler*> TCompilerList;
+typedef glslang::TVector<TShHandleBase*> THandleList;
+
+//
+// The base class for the machine dependent linker to derive from
+// to manage the resulting executable.
+//
+
+class TLinker : public TShHandleBase {
+public:
+ TLinker(EShExecutable e, TInfoSink& iSink) :
+ infoSink(iSink),
+ executable(e),
+ haveReturnableObjectCode(false),
+ appAttributeBindings(0),
+ fixedAttributeBindings(0),
+ excludedAttributes(0),
+ excludedCount(0),
+ uniformBindings(0) { }
+ virtual TLinker* getAsLinker() { return this; }
+ virtual ~TLinker() { }
+ virtual bool link(TCompilerList&, TUniformMap*) = 0;
+ virtual bool link(THandleList&) { return false; }
+ virtual void setAppAttributeBindings(const ShBindingTable* t) { appAttributeBindings = t; }
+ virtual void setFixedAttributeBindings(const ShBindingTable* t) { fixedAttributeBindings = t; }
+ virtual void getAttributeBindings(ShBindingTable const **t) const = 0;
+ virtual void setExcludedAttributes(const int* attributes, int count) { excludedAttributes = attributes; excludedCount = count; }
+ virtual ShBindingTable* getUniformBindings() const { return uniformBindings; }
+ virtual const void* getObjectCode() const { return 0; } // a real compiler would be returning object code here
+ virtual TInfoSink& getInfoSink() { return infoSink; }
+ TInfoSink& infoSink;
+protected:
+ TLinker& operator=(TLinker&);
+ EShExecutable executable;
+ bool haveReturnableObjectCode; // true when objectCode is acceptable to send to driver
+
+ const ShBindingTable* appAttributeBindings;
+ const ShBindingTable* fixedAttributeBindings;
+ const int* excludedAttributes;
+ int excludedCount;
+ ShBindingTable* uniformBindings; // created by the linker
+};
+
+//
+// This is the interface between the machine independent code
+// and the machine dependent code.
+//
+// The machine dependent code should derive from the classes
+// above. Then Construct*() and Delete*() will create and
+// destroy the machine dependent objects, which contain the
+// above machine independent information.
+//
+TCompiler* ConstructCompiler(EShLanguage, int);
+
+TShHandleBase* ConstructLinker(EShExecutable, int);
+TShHandleBase* ConstructBindings();
+void DeleteLinker(TShHandleBase*);
+void DeleteBindingList(TShHandleBase* bindingList);
+
+TUniformMap* ConstructUniformMap();
+void DeleteCompiler(TCompiler*);
+
+void DeleteUniformMap(TUniformMap*);
+
+#endif // _SHHANDLE_INCLUDED_
diff --git a/src/3rdparty/glslang/glslang/Include/Types.h b/src/3rdparty/glslang/glslang/Include/Types.h
new file mode 100644
index 0000000..d0d9b60
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/Include/Types.h
@@ -0,0 +1,2276 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2012-2016 LunarG, Inc.
+// Copyright (C) 2015-2016 Google, Inc.
+// Copyright (C) 2017 ARM Limited.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef _TYPES_INCLUDED
+#define _TYPES_INCLUDED
+
+#include "../Include/Common.h"
+#include "../Include/BaseTypes.h"
+#include "../Public/ShaderLang.h"
+#include "arrays.h"
+
+#include <algorithm>
+
+namespace glslang {
+
+const int GlslangMaxTypeLength = 200; // TODO: need to print block/struct one member per line, so this can stay bounded
+
+const char* const AnonymousPrefix = "anon@"; // for something like a block whose members can be directly accessed
+inline bool IsAnonymous(const TString& name)
+{
+ return name.compare(0, 5, AnonymousPrefix) == 0;
+}
+
+//
+// Details within a sampler type
+//
+enum TSamplerDim {
+ EsdNone,
+ Esd1D,
+ Esd2D,
+ Esd3D,
+ EsdCube,
+ EsdRect,
+ EsdBuffer,
+ EsdSubpass, // goes only with non-sampled image (image is true)
+ EsdNumDims
+};
+
+struct TSampler { // misnomer now; includes images, textures without sampler, and textures with sampler
+ TBasicType type : 8; // type returned by sampler
+ TSamplerDim dim : 8;
+ bool arrayed : 1;
+ bool shadow : 1;
+ bool ms : 1;
+ bool image : 1; // image, combined should be false
+ bool combined : 1; // true means texture is combined with a sampler, false means texture with no sampler
+ bool sampler : 1; // true means a pure sampler, other fields should be clear()
+ bool external : 1; // GL_OES_EGL_image_external
+ bool yuv : 1; // GL_EXT_YUV_target
+ unsigned int vectorSize : 3; // vector return type size.
+
+ // Some languages support structures as sample results. Storing the whole structure in the
+ // TSampler is too large, so there is an index to a separate table.
+ static const unsigned structReturnIndexBits = 4; // number of index bits to use.
+ static const unsigned structReturnSlots = (1<<structReturnIndexBits)-1; // number of valid values
+ static const unsigned noReturnStruct = structReturnSlots; // value if no return struct type.
+
+ // Index into a language specific table of texture return structures.
+ unsigned int structReturnIndex : structReturnIndexBits;
+
+ // Encapsulate getting members' vector sizes packed into the vectorSize bitfield.
+ unsigned int getVectorSize() const { return vectorSize; }
+
+ bool isImage() const { return image && dim != EsdSubpass; }
+ bool isSubpass() const { return dim == EsdSubpass; }
+ bool isCombined() const { return combined; }
+ bool isPureSampler() const { return sampler; }
+ bool isTexture() const { return !sampler && !image; }
+ bool isShadow() const { return shadow; }
+ bool isArrayed() const { return arrayed; }
+ bool isMultiSample() const { return ms; }
+ bool hasReturnStruct() const { return structReturnIndex != noReturnStruct; }
+
+ void clear()
+ {
+ type = EbtVoid;
+ dim = EsdNone;
+ arrayed = false;
+ shadow = false;
+ ms = false;
+ image = false;
+ combined = false;
+ sampler = false;
+ external = false;
+ yuv = false;
+ structReturnIndex = noReturnStruct;
+
+ // by default, returns a single vec4;
+ vectorSize = 4;
+ }
+
+ // make a combined sampler and texture
+ void set(TBasicType t, TSamplerDim d, bool a = false, bool s = false, bool m = false)
+ {
+ clear();
+ type = t;
+ dim = d;
+ arrayed = a;
+ shadow = s;
+ ms = m;
+ combined = true;
+ }
+
+ // make an image
+ void setImage(TBasicType t, TSamplerDim d, bool a = false, bool s = false, bool m = false)
+ {
+ clear();
+ type = t;
+ dim = d;
+ arrayed = a;
+ shadow = s;
+ ms = m;
+ image = true;
+ }
+
+ // make a texture with no sampler
+ void setTexture(TBasicType t, TSamplerDim d, bool a = false, bool s = false, bool m = false)
+ {
+ clear();
+ type = t;
+ dim = d;
+ arrayed = a;
+ shadow = s;
+ ms = m;
+ }
+
+ // make a subpass input attachment
+ void setSubpass(TBasicType t, bool m = false)
+ {
+ clear();
+ type = t;
+ image = true;
+ dim = EsdSubpass;
+ ms = m;
+ }
+
+ // make a pure sampler, no texture, no image, nothing combined, the 'sampler' keyword
+ void setPureSampler(bool s)
+ {
+ clear();
+ sampler = true;
+ shadow = s;
+ }
+
+ bool operator==(const TSampler& right) const
+ {
+ return type == right.type &&
+ dim == right.dim &&
+ arrayed == right.arrayed &&
+ shadow == right.shadow &&
+ ms == right.ms &&
+ image == right.image &&
+ combined == right.combined &&
+ sampler == right.sampler &&
+ external == right.external &&
+ yuv == right.yuv &&
+ vectorSize == right.vectorSize &&
+ structReturnIndex == right.structReturnIndex;
+ }
+
+ bool operator!=(const TSampler& right) const
+ {
+ return ! operator==(right);
+ }
+
+ TString getString() const
+ {
+ TString s;
+
+ if (sampler) {
+ s.append("sampler");
+ return s;
+ }
+
+ switch (type) {
+ case EbtFloat: break;
+#ifdef AMD_EXTENSIONS
+ case EbtFloat16: s.append("f16"); break;
+#endif
+ case EbtInt8: s.append("i8"); break;
+ case EbtUint16: s.append("u8"); break;
+ case EbtInt16: s.append("i16"); break;
+ case EbtUint8: s.append("u16"); break;
+ case EbtInt: s.append("i"); break;
+ case EbtUint: s.append("u"); break;
+ case EbtInt64: s.append("i64"); break;
+ case EbtUint64: s.append("u64"); break;
+ default: break; // some compilers want this
+ }
+ if (image) {
+ if (dim == EsdSubpass)
+ s.append("subpass");
+ else
+ s.append("image");
+ } else if (combined) {
+ s.append("sampler");
+ } else {
+ s.append("texture");
+ }
+ if (external) {
+ s.append("ExternalOES");
+ return s;
+ }
+ if (yuv) {
+ return "__" + s + "External2DY2YEXT";
+ }
+ switch (dim) {
+ case Esd1D: s.append("1D"); break;
+ case Esd2D: s.append("2D"); break;
+ case Esd3D: s.append("3D"); break;
+ case EsdCube: s.append("Cube"); break;
+ case EsdRect: s.append("2DRect"); break;
+ case EsdBuffer: s.append("Buffer"); break;
+ case EsdSubpass: s.append("Input"); break;
+ default: break; // some compilers want this
+ }
+ if (ms)
+ s.append("MS");
+ if (arrayed)
+ s.append("Array");
+ if (shadow)
+ s.append("Shadow");
+
+ return s;
+ }
+};
+
+//
+// Need to have association of line numbers to types in a list for building structs.
+//
+class TType;
+struct TTypeLoc {
+ TType* type;
+ TSourceLoc loc;
+};
+typedef TVector<TTypeLoc> TTypeList;
+
+typedef TVector<TString*> TIdentifierList;
+
+//
+// Following are a series of helper enums for managing layouts and qualifiers,
+// used for TPublicType, TType, others.
+//
+
+enum TLayoutPacking {
+ ElpNone,
+ ElpShared, // default, but different than saying nothing
+ ElpStd140,
+ ElpStd430,
+ ElpPacked,
+ ElpScalar,
+ ElpCount // If expanding, see bitfield width below
+};
+
+enum TLayoutMatrix {
+ ElmNone,
+ ElmRowMajor,
+ ElmColumnMajor, // default, but different than saying nothing
+ ElmCount // If expanding, see bitfield width below
+};
+
+// Union of geometry shader and tessellation shader geometry types.
+// They don't go into TType, but rather have current state per shader or
+// active parser type (TPublicType).
+enum TLayoutGeometry {
+ ElgNone,
+ ElgPoints,
+ ElgLines,
+ ElgLinesAdjacency,
+ ElgLineStrip,
+ ElgTriangles,
+ ElgTrianglesAdjacency,
+ ElgTriangleStrip,
+ ElgQuads,
+ ElgIsolines,
+};
+
+enum TVertexSpacing {
+ EvsNone,
+ EvsEqual,
+ EvsFractionalEven,
+ EvsFractionalOdd
+};
+
+enum TVertexOrder {
+ EvoNone,
+ EvoCw,
+ EvoCcw
+};
+
+// Note: order matters, as type of format is done by comparison.
+enum TLayoutFormat {
+ ElfNone,
+
+ // Float image
+ ElfRgba32f,
+ ElfRgba16f,
+ ElfR32f,
+ ElfRgba8,
+ ElfRgba8Snorm,
+
+ ElfEsFloatGuard, // to help with comparisons
+
+ ElfRg32f,
+ ElfRg16f,
+ ElfR11fG11fB10f,
+ ElfR16f,
+ ElfRgba16,
+ ElfRgb10A2,
+ ElfRg16,
+ ElfRg8,
+ ElfR16,
+ ElfR8,
+ ElfRgba16Snorm,
+ ElfRg16Snorm,
+ ElfRg8Snorm,
+ ElfR16Snorm,
+ ElfR8Snorm,
+
+ ElfFloatGuard, // to help with comparisons
+
+ // Int image
+ ElfRgba32i,
+ ElfRgba16i,
+ ElfRgba8i,
+ ElfR32i,
+
+ ElfEsIntGuard, // to help with comparisons
+
+ ElfRg32i,
+ ElfRg16i,
+ ElfRg8i,
+ ElfR16i,
+ ElfR8i,
+
+ ElfIntGuard, // to help with comparisons
+
+ // Uint image
+ ElfRgba32ui,
+ ElfRgba16ui,
+ ElfRgba8ui,
+ ElfR32ui,
+
+ ElfEsUintGuard, // to help with comparisons
+
+ ElfRg32ui,
+ ElfRg16ui,
+ ElfRgb10a2ui,
+ ElfRg8ui,
+ ElfR16ui,
+ ElfR8ui,
+
+ ElfCount
+};
+
+enum TLayoutDepth {
+ EldNone,
+ EldAny,
+ EldGreater,
+ EldLess,
+ EldUnchanged,
+
+ EldCount
+};
+
+enum TBlendEquationShift {
+ // No 'EBlendNone':
+ // These are used as bit-shift amounts. A mask of such shifts will have type 'int',
+ // and in that space, 0 means no bits set, or none. In this enum, 0 means (1 << 0), a bit is set.
+ EBlendMultiply,
+ EBlendScreen,
+ EBlendOverlay,
+ EBlendDarken,
+ EBlendLighten,
+ EBlendColordodge,
+ EBlendColorburn,
+ EBlendHardlight,
+ EBlendSoftlight,
+ EBlendDifference,
+ EBlendExclusion,
+ EBlendHslHue,
+ EBlendHslSaturation,
+ EBlendHslColor,
+ EBlendHslLuminosity,
+ EBlendAllEquations,
+
+ EBlendCount
+};
+
+class TQualifier {
+public:
+ static const int layoutNotSet = -1;
+
+ void clear()
+ {
+ precision = EpqNone;
+ invariant = false;
+ noContraction = false;
+ makeTemporary();
+ declaredBuiltIn = EbvNone;
+ }
+
+ // drop qualifiers that don't belong in a temporary variable
+ void makeTemporary()
+ {
+ semanticName = nullptr;
+ storage = EvqTemporary;
+ builtIn = EbvNone;
+ clearInterstage();
+ clearMemory();
+ specConstant = false;
+ nonUniform = false;
+ clearLayout();
+ }
+
+ void clearInterstage()
+ {
+ clearInterpolation();
+ patch = false;
+ sample = false;
+ }
+
+ void clearInterpolation()
+ {
+ centroid = false;
+ smooth = false;
+ flat = false;
+ nopersp = false;
+#ifdef AMD_EXTENSIONS
+ explicitInterp = false;
+#endif
+#ifdef NV_EXTENSIONS
+ pervertexNV = false;
+ perPrimitiveNV = false;
+ perViewNV = false;
+ perTaskNV = false;
+#endif
+ }
+
+ void clearMemory()
+ {
+ coherent = false;
+ devicecoherent = false;
+ queuefamilycoherent = false;
+ workgroupcoherent = false;
+ subgroupcoherent = false;
+ nonprivate = false;
+ volatil = false;
+ restrict = false;
+ readonly = false;
+ writeonly = false;
+ }
+
+ // Drop just the storage qualification, which perhaps should
+ // never be done, as it is fundamentally inconsistent, but need to
+ // explore what downstream consumers need.
+ // E.g., in a dereference, it is an inconsistency between:
+ // A) partially dereferenced resource is still in the storage class it started in
+ // B) partially dereferenced resource is a new temporary object
+ // If A, then nothing should change, if B, then everything should change, but this is half way.
+ void makePartialTemporary()
+ {
+ storage = EvqTemporary;
+ specConstant = false;
+ nonUniform = false;
+ }
+
+ const char* semanticName;
+ TStorageQualifier storage : 6;
+ TBuiltInVariable builtIn : 8;
+ TBuiltInVariable declaredBuiltIn : 8;
+ TPrecisionQualifier precision : 3;
+ bool invariant : 1; // require canonical treatment for cross-shader invariance
+ bool noContraction: 1; // prevent contraction and reassociation, e.g., for 'precise' keyword, and expressions it affects
+ bool centroid : 1;
+ bool smooth : 1;
+ bool flat : 1;
+ bool nopersp : 1;
+#ifdef AMD_EXTENSIONS
+ bool explicitInterp : 1;
+#endif
+#ifdef NV_EXTENSIONS
+ bool pervertexNV : 1;
+ bool perPrimitiveNV : 1;
+ bool perViewNV : 1;
+ bool perTaskNV : 1;
+#endif
+ bool patch : 1;
+ bool sample : 1;
+ bool coherent : 1;
+ bool devicecoherent : 1;
+ bool queuefamilycoherent : 1;
+ bool workgroupcoherent : 1;
+ bool subgroupcoherent : 1;
+ bool nonprivate : 1;
+ bool volatil : 1;
+ bool restrict : 1;
+ bool readonly : 1;
+ bool writeonly : 1;
+ bool specConstant : 1; // having a constant_id is not sufficient: expressions have no id, but are still specConstant
+ bool nonUniform : 1;
+
+ bool isMemory() const
+ {
+ return subgroupcoherent || workgroupcoherent || queuefamilycoherent || devicecoherent || coherent || volatil || restrict || readonly || writeonly || nonprivate;
+ }
+ bool isMemoryQualifierImageAndSSBOOnly() const
+ {
+ return subgroupcoherent || workgroupcoherent || queuefamilycoherent || devicecoherent || coherent || volatil || restrict || readonly || writeonly;
+ }
+ bool bufferReferenceNeedsVulkanMemoryModel() const
+ {
+ // include qualifiers that map to load/store availability/visibility/nonprivate memory access operands
+ return subgroupcoherent || workgroupcoherent || queuefamilycoherent || devicecoherent || coherent || nonprivate;
+ }
+
+ bool isInterpolation() const
+ {
+#ifdef AMD_EXTENSIONS
+ return flat || smooth || nopersp || explicitInterp;
+#else
+ return flat || smooth || nopersp;
+#endif
+ }
+
+#ifdef AMD_EXTENSIONS
+ bool isExplicitInterpolation() const
+ {
+ return explicitInterp;
+ }
+#endif
+
+ bool isAuxiliary() const
+ {
+#ifdef NV_EXTENSIONS
+ return centroid || patch || sample || pervertexNV;
+#else
+ return centroid || patch || sample;
+#endif
+ }
+
+ bool isPipeInput() const
+ {
+ switch (storage) {
+ case EvqVaryingIn:
+ case EvqFragCoord:
+ case EvqPointCoord:
+ case EvqFace:
+ case EvqVertexId:
+ case EvqInstanceId:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ bool isPipeOutput() const
+ {
+ switch (storage) {
+ case EvqPosition:
+ case EvqPointSize:
+ case EvqClipVertex:
+ case EvqVaryingOut:
+ case EvqFragColor:
+ case EvqFragDepth:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ bool isParamInput() const
+ {
+ switch (storage) {
+ case EvqIn:
+ case EvqInOut:
+ case EvqConstReadOnly:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ bool isParamOutput() const
+ {
+ switch (storage) {
+ case EvqOut:
+ case EvqInOut:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ bool isUniformOrBuffer() const
+ {
+ switch (storage) {
+ case EvqUniform:
+ case EvqBuffer:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ bool isPerPrimitive() const
+ {
+#ifdef NV_EXTENSIONS
+ return perPrimitiveNV;
+#else
+ return false;
+#endif
+ }
+
+ bool isPerView() const
+ {
+#ifdef NV_EXTENSIONS
+ return perViewNV;
+#else
+ return false;
+#endif
+ }
+
+ bool isTaskMemory() const
+ {
+#ifdef NV_EXTENSIONS
+ return perTaskNV;
+#else
+ return false;
+#endif
+ }
+
+ bool isIo() const
+ {
+ switch (storage) {
+ case EvqUniform:
+ case EvqBuffer:
+ case EvqVaryingIn:
+ case EvqFragCoord:
+ case EvqPointCoord:
+ case EvqFace:
+ case EvqVertexId:
+ case EvqInstanceId:
+ case EvqPosition:
+ case EvqPointSize:
+ case EvqClipVertex:
+ case EvqVaryingOut:
+ case EvqFragColor:
+ case EvqFragDepth:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ // non-built-in symbols that might link between compilation units
+ bool isLinkable() const
+ {
+ switch (storage) {
+ case EvqGlobal:
+ case EvqVaryingIn:
+ case EvqVaryingOut:
+ case EvqUniform:
+ case EvqBuffer:
+ case EvqShared:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ // True if this type of IO is supposed to be arrayed with extra level for per-vertex data
+ bool isArrayedIo(EShLanguage language) const
+ {
+ switch (language) {
+ case EShLangGeometry:
+ return isPipeInput();
+ case EShLangTessControl:
+ return ! patch && (isPipeInput() || isPipeOutput());
+ case EShLangTessEvaluation:
+ return ! patch && isPipeInput();
+#ifdef NV_EXTENSIONS
+ case EShLangFragment:
+ return pervertexNV && isPipeInput();
+ case EShLangMeshNV:
+ return ! perTaskNV && isPipeOutput();
+#endif
+
+ default:
+ return false;
+ }
+ }
+
+ // Implementing an embedded layout-qualifier class here, since C++ can't have a real class bitfield
+ void clearLayout() // all layout
+ {
+ clearUniformLayout();
+
+ layoutPushConstant = false;
+ layoutBufferReference = false;
+#ifdef NV_EXTENSIONS
+ layoutPassthrough = false;
+ layoutViewportRelative = false;
+ // -2048 as the default value indicating layoutSecondaryViewportRelative is not set
+ layoutSecondaryViewportRelativeOffset = -2048;
+ layoutShaderRecordNV = false;
+#endif
+
+ layoutBufferReferenceAlign = layoutBufferReferenceAlignEnd;
+
+ clearInterstageLayout();
+
+ layoutSpecConstantId = layoutSpecConstantIdEnd;
+
+ layoutFormat = ElfNone;
+ }
+ void clearInterstageLayout()
+ {
+ layoutLocation = layoutLocationEnd;
+ layoutComponent = layoutComponentEnd;
+ layoutIndex = layoutIndexEnd;
+ clearStreamLayout();
+ clearXfbLayout();
+ }
+ void clearStreamLayout()
+ {
+ layoutStream = layoutStreamEnd;
+ }
+ void clearXfbLayout()
+ {
+ layoutXfbBuffer = layoutXfbBufferEnd;
+ layoutXfbStride = layoutXfbStrideEnd;
+ layoutXfbOffset = layoutXfbOffsetEnd;
+ }
+
+ bool hasNonXfbLayout() const
+ {
+ return hasUniformLayout() ||
+ hasAnyLocation() ||
+ hasStream() ||
+ hasFormat() ||
+#ifdef NV_EXTENSIONS
+ layoutShaderRecordNV ||
+#endif
+ layoutPushConstant ||
+ layoutBufferReference;
+ }
+ bool hasLayout() const
+ {
+ return hasNonXfbLayout() ||
+ hasXfb();
+ }
+ TLayoutMatrix layoutMatrix : 3;
+ TLayoutPacking layoutPacking : 4;
+ int layoutOffset;
+ int layoutAlign;
+
+ unsigned int layoutLocation : 12;
+ static const unsigned int layoutLocationEnd = 0xFFF;
+
+ unsigned int layoutComponent : 3;
+ static const unsigned int layoutComponentEnd = 4;
+
+ unsigned int layoutSet : 7;
+ static const unsigned int layoutSetEnd = 0x3F;
+
+ unsigned int layoutBinding : 16;
+ static const unsigned int layoutBindingEnd = 0xFFFF;
+
+ unsigned int layoutIndex : 8;
+ static const unsigned int layoutIndexEnd = 0xFF;
+
+ unsigned int layoutStream : 8;
+ static const unsigned int layoutStreamEnd = 0xFF;
+
+ unsigned int layoutXfbBuffer : 4;
+ static const unsigned int layoutXfbBufferEnd = 0xF;
+
+ unsigned int layoutXfbStride : 14;
+ static const unsigned int layoutXfbStrideEnd = 0x3FFF;
+
+ unsigned int layoutXfbOffset : 13;
+ static const unsigned int layoutXfbOffsetEnd = 0x1FFF;
+
+ unsigned int layoutAttachment : 8; // for input_attachment_index
+ static const unsigned int layoutAttachmentEnd = 0XFF;
+
+ unsigned int layoutSpecConstantId : 11;
+ static const unsigned int layoutSpecConstantIdEnd = 0x7FF;
+
+ // stored as log2 of the actual alignment value
+ unsigned int layoutBufferReferenceAlign : 6;
+ static const unsigned int layoutBufferReferenceAlignEnd = 0x3F;
+
+ TLayoutFormat layoutFormat : 8;
+
+ bool layoutPushConstant;
+ bool layoutBufferReference;
+
+#ifdef NV_EXTENSIONS
+ bool layoutPassthrough;
+ bool layoutViewportRelative;
+ int layoutSecondaryViewportRelativeOffset;
+ bool layoutShaderRecordNV;
+#endif
+
+ bool hasUniformLayout() const
+ {
+ return hasMatrix() ||
+ hasPacking() ||
+ hasOffset() ||
+ hasBinding() ||
+ hasSet() ||
+ hasAlign();
+ }
+ void clearUniformLayout() // only uniform specific
+ {
+ layoutMatrix = ElmNone;
+ layoutPacking = ElpNone;
+ layoutOffset = layoutNotSet;
+ layoutAlign = layoutNotSet;
+
+ layoutSet = layoutSetEnd;
+ layoutBinding = layoutBindingEnd;
+ layoutAttachment = layoutAttachmentEnd;
+ }
+
+ bool hasMatrix() const
+ {
+ return layoutMatrix != ElmNone;
+ }
+ bool hasPacking() const
+ {
+ return layoutPacking != ElpNone;
+ }
+ bool hasOffset() const
+ {
+ return layoutOffset != layoutNotSet;
+ }
+ bool hasAlign() const
+ {
+ return layoutAlign != layoutNotSet;
+ }
+ bool hasAnyLocation() const
+ {
+ return hasLocation() ||
+ hasComponent() ||
+ hasIndex();
+ }
+ bool hasLocation() const
+ {
+ return layoutLocation != layoutLocationEnd;
+ }
+ bool hasComponent() const
+ {
+ return layoutComponent != layoutComponentEnd;
+ }
+ bool hasIndex() const
+ {
+ return layoutIndex != layoutIndexEnd;
+ }
+ bool hasSet() const
+ {
+ return layoutSet != layoutSetEnd;
+ }
+ bool hasBinding() const
+ {
+ return layoutBinding != layoutBindingEnd;
+ }
+ bool hasStream() const
+ {
+ return layoutStream != layoutStreamEnd;
+ }
+ bool hasFormat() const
+ {
+ return layoutFormat != ElfNone;
+ }
+ bool hasXfb() const
+ {
+ return hasXfbBuffer() ||
+ hasXfbStride() ||
+ hasXfbOffset();
+ }
+ bool hasXfbBuffer() const
+ {
+ return layoutXfbBuffer != layoutXfbBufferEnd;
+ }
+ bool hasXfbStride() const
+ {
+ return layoutXfbStride != layoutXfbStrideEnd;
+ }
+ bool hasXfbOffset() const
+ {
+ return layoutXfbOffset != layoutXfbOffsetEnd;
+ }
+ bool hasAttachment() const
+ {
+ return layoutAttachment != layoutAttachmentEnd;
+ }
+ bool hasSpecConstantId() const
+ {
+ // Not the same thing as being a specialization constant, this
+ // is just whether or not it was declared with an ID.
+ return layoutSpecConstantId != layoutSpecConstantIdEnd;
+ }
+ bool hasBufferReferenceAlign() const
+ {
+ return layoutBufferReferenceAlign != layoutBufferReferenceAlignEnd;
+ }
+ bool isSpecConstant() const
+ {
+ // True if type is a specialization constant, whether or not it
+ // had a specialization-constant ID, and false if it is not a
+ // true front-end constant.
+ return specConstant;
+ }
+ bool isNonUniform() const
+ {
+ return nonUniform;
+ }
+ bool isFrontEndConstant() const
+ {
+ // True if the front-end knows the final constant value.
+ // This allows front-end constant folding.
+ return storage == EvqConst && ! specConstant;
+ }
+ bool isConstant() const
+ {
+ // True if is either kind of constant; specialization or regular.
+ return isFrontEndConstant() || isSpecConstant();
+ }
+ void makeSpecConstant()
+ {
+ storage = EvqConst;
+ specConstant = true;
+ }
+ static const char* getLayoutPackingString(TLayoutPacking packing)
+ {
+ switch (packing) {
+ case ElpPacked: return "packed";
+ case ElpShared: return "shared";
+ case ElpStd140: return "std140";
+ case ElpStd430: return "std430";
+ case ElpScalar: return "scalar";
+ default: return "none";
+ }
+ }
+ static const char* getLayoutMatrixString(TLayoutMatrix m)
+ {
+ switch (m) {
+ case ElmColumnMajor: return "column_major";
+ case ElmRowMajor: return "row_major";
+ default: return "none";
+ }
+ }
+ static const char* getLayoutFormatString(TLayoutFormat f)
+ {
+ switch (f) {
+ case ElfRgba32f: return "rgba32f";
+ case ElfRgba16f: return "rgba16f";
+ case ElfRg32f: return "rg32f";
+ case ElfRg16f: return "rg16f";
+ case ElfR11fG11fB10f: return "r11f_g11f_b10f";
+ case ElfR32f: return "r32f";
+ case ElfR16f: return "r16f";
+ case ElfRgba16: return "rgba16";
+ case ElfRgb10A2: return "rgb10_a2";
+ case ElfRgba8: return "rgba8";
+ case ElfRg16: return "rg16";
+ case ElfRg8: return "rg8";
+ case ElfR16: return "r16";
+ case ElfR8: return "r8";
+ case ElfRgba16Snorm: return "rgba16_snorm";
+ case ElfRgba8Snorm: return "rgba8_snorm";
+ case ElfRg16Snorm: return "rg16_snorm";
+ case ElfRg8Snorm: return "rg8_snorm";
+ case ElfR16Snorm: return "r16_snorm";
+ case ElfR8Snorm: return "r8_snorm";
+
+ case ElfRgba32i: return "rgba32i";
+ case ElfRgba16i: return "rgba16i";
+ case ElfRgba8i: return "rgba8i";
+ case ElfRg32i: return "rg32i";
+ case ElfRg16i: return "rg16i";
+ case ElfRg8i: return "rg8i";
+ case ElfR32i: return "r32i";
+ case ElfR16i: return "r16i";
+ case ElfR8i: return "r8i";
+
+ case ElfRgba32ui: return "rgba32ui";
+ case ElfRgba16ui: return "rgba16ui";
+ case ElfRgba8ui: return "rgba8ui";
+ case ElfRg32ui: return "rg32ui";
+ case ElfRg16ui: return "rg16ui";
+ case ElfRgb10a2ui: return "rgb10_a2ui";
+ case ElfRg8ui: return "rg8ui";
+ case ElfR32ui: return "r32ui";
+ case ElfR16ui: return "r16ui";
+ case ElfR8ui: return "r8ui";
+ default: return "none";
+ }
+ }
+ static const char* getLayoutDepthString(TLayoutDepth d)
+ {
+ switch (d) {
+ case EldAny: return "depth_any";
+ case EldGreater: return "depth_greater";
+ case EldLess: return "depth_less";
+ case EldUnchanged: return "depth_unchanged";
+ default: return "none";
+ }
+ }
+ static const char* getBlendEquationString(TBlendEquationShift e)
+ {
+ switch (e) {
+ case EBlendMultiply: return "blend_support_multiply";
+ case EBlendScreen: return "blend_support_screen";
+ case EBlendOverlay: return "blend_support_overlay";
+ case EBlendDarken: return "blend_support_darken";
+ case EBlendLighten: return "blend_support_lighten";
+ case EBlendColordodge: return "blend_support_colordodge";
+ case EBlendColorburn: return "blend_support_colorburn";
+ case EBlendHardlight: return "blend_support_hardlight";
+ case EBlendSoftlight: return "blend_support_softlight";
+ case EBlendDifference: return "blend_support_difference";
+ case EBlendExclusion: return "blend_support_exclusion";
+ case EBlendHslHue: return "blend_support_hsl_hue";
+ case EBlendHslSaturation: return "blend_support_hsl_saturation";
+ case EBlendHslColor: return "blend_support_hsl_color";
+ case EBlendHslLuminosity: return "blend_support_hsl_luminosity";
+ case EBlendAllEquations: return "blend_support_all_equations";
+ default: return "unknown";
+ }
+ }
+ static const char* getGeometryString(TLayoutGeometry geometry)
+ {
+ switch (geometry) {
+ case ElgPoints: return "points";
+ case ElgLines: return "lines";
+ case ElgLinesAdjacency: return "lines_adjacency";
+ case ElgLineStrip: return "line_strip";
+ case ElgTriangles: return "triangles";
+ case ElgTrianglesAdjacency: return "triangles_adjacency";
+ case ElgTriangleStrip: return "triangle_strip";
+ case ElgQuads: return "quads";
+ case ElgIsolines: return "isolines";
+ default: return "none";
+ }
+ }
+ static const char* getVertexSpacingString(TVertexSpacing spacing)
+ {
+ switch (spacing) {
+ case EvsEqual: return "equal_spacing";
+ case EvsFractionalEven: return "fractional_even_spacing";
+ case EvsFractionalOdd: return "fractional_odd_spacing";
+ default: return "none";
+ }
+ }
+ static const char* getVertexOrderString(TVertexOrder order)
+ {
+ switch (order) {
+ case EvoCw: return "cw";
+ case EvoCcw: return "ccw";
+ default: return "none";
+ }
+ }
+ static int mapGeometryToSize(TLayoutGeometry geometry)
+ {
+ switch (geometry) {
+ case ElgPoints: return 1;
+ case ElgLines: return 2;
+ case ElgLinesAdjacency: return 4;
+ case ElgTriangles: return 3;
+ case ElgTrianglesAdjacency: return 6;
+ default: return 0;
+ }
+ }
+};
+
+// Qualifiers that don't need to be keep per object. They have shader scope, not object scope.
+// So, they will not be part of TType, TQualifier, etc.
+struct TShaderQualifiers {
+ TLayoutGeometry geometry; // geometry/tessellation shader in/out primitives
+ bool pixelCenterInteger; // fragment shader
+ bool originUpperLeft; // fragment shader
+ int invocations;
+ int vertices; // for tessellation "vertices", geometry & mesh "max_vertices"
+ TVertexSpacing spacing;
+ TVertexOrder order;
+ bool pointMode;
+ int localSize[3]; // compute shader
+ int localSizeSpecId[3]; // compute shader specialization id for gl_WorkGroupSize
+ bool earlyFragmentTests; // fragment input
+ bool postDepthCoverage; // fragment input
+ TLayoutDepth layoutDepth;
+ bool blendEquation; // true if any blend equation was specified
+ int numViews; // multiview extenstions
+
+#ifdef NV_EXTENSIONS
+ bool layoutOverrideCoverage; // true if layout override_coverage set
+ bool layoutDerivativeGroupQuads; // true if layout derivative_group_quadsNV set
+ bool layoutDerivativeGroupLinear; // true if layout derivative_group_linearNV set
+ int primitives; // mesh shader "max_primitives"DerivativeGroupLinear; // true if layout derivative_group_linearNV set
+#endif
+
+ void init()
+ {
+ geometry = ElgNone;
+ originUpperLeft = false;
+ pixelCenterInteger = false;
+ invocations = TQualifier::layoutNotSet;
+ vertices = TQualifier::layoutNotSet;
+ spacing = EvsNone;
+ order = EvoNone;
+ pointMode = false;
+ localSize[0] = 1;
+ localSize[1] = 1;
+ localSize[2] = 1;
+ localSizeSpecId[0] = TQualifier::layoutNotSet;
+ localSizeSpecId[1] = TQualifier::layoutNotSet;
+ localSizeSpecId[2] = TQualifier::layoutNotSet;
+ earlyFragmentTests = false;
+ postDepthCoverage = false;
+ layoutDepth = EldNone;
+ blendEquation = false;
+ numViews = TQualifier::layoutNotSet;
+#ifdef NV_EXTENSIONS
+ layoutOverrideCoverage = false;
+ layoutDerivativeGroupQuads = false;
+ layoutDerivativeGroupLinear = false;
+ primitives = TQualifier::layoutNotSet;
+#endif
+ }
+
+ // Merge in characteristics from the 'src' qualifier. They can override when
+ // set, but never erase when not set.
+ void merge(const TShaderQualifiers& src)
+ {
+ if (src.geometry != ElgNone)
+ geometry = src.geometry;
+ if (src.pixelCenterInteger)
+ pixelCenterInteger = src.pixelCenterInteger;
+ if (src.originUpperLeft)
+ originUpperLeft = src.originUpperLeft;
+ if (src.invocations != TQualifier::layoutNotSet)
+ invocations = src.invocations;
+ if (src.vertices != TQualifier::layoutNotSet)
+ vertices = src.vertices;
+ if (src.spacing != EvsNone)
+ spacing = src.spacing;
+ if (src.order != EvoNone)
+ order = src.order;
+ if (src.pointMode)
+ pointMode = true;
+ for (int i = 0; i < 3; ++i) {
+ if (src.localSize[i] > 1)
+ localSize[i] = src.localSize[i];
+ }
+ for (int i = 0; i < 3; ++i) {
+ if (src.localSizeSpecId[i] != TQualifier::layoutNotSet)
+ localSizeSpecId[i] = src.localSizeSpecId[i];
+ }
+ if (src.earlyFragmentTests)
+ earlyFragmentTests = true;
+ if (src.postDepthCoverage)
+ postDepthCoverage = true;
+ if (src.layoutDepth)
+ layoutDepth = src.layoutDepth;
+ if (src.blendEquation)
+ blendEquation = src.blendEquation;
+ if (src.numViews != TQualifier::layoutNotSet)
+ numViews = src.numViews;
+#ifdef NV_EXTENSIONS
+ if (src.layoutOverrideCoverage)
+ layoutOverrideCoverage = src.layoutOverrideCoverage;
+ if (src.layoutDerivativeGroupQuads)
+ layoutDerivativeGroupQuads = src.layoutDerivativeGroupQuads;
+ if (src.layoutDerivativeGroupLinear)
+ layoutDerivativeGroupLinear = src.layoutDerivativeGroupLinear;
+ if (src.primitives != TQualifier::layoutNotSet)
+ primitives = src.primitives;
+#endif
+ }
+};
+
+//
+// TPublicType is just temporarily used while parsing and not quite the same
+// information kept per node in TType. Due to the bison stack, it can't have
+// types that it thinks have non-trivial constructors. It should
+// just be used while recognizing the grammar, not anything else.
+// Once enough is known about the situation, the proper information
+// moved into a TType, or the parse context, etc.
+//
+class TPublicType {
+public:
+ TBasicType basicType;
+ TSampler sampler;
+ TQualifier qualifier;
+ TShaderQualifiers shaderQualifiers;
+ int vectorSize : 4;
+ int matrixCols : 4;
+ int matrixRows : 4;
+ bool coopmat : 1;
+ TArraySizes* arraySizes;
+ const TType* userDef;
+ TSourceLoc loc;
+ TArraySizes* typeParameters;
+
+ void initType(const TSourceLoc& l)
+ {
+ basicType = EbtVoid;
+ vectorSize = 1;
+ matrixRows = 0;
+ matrixCols = 0;
+ arraySizes = nullptr;
+ userDef = nullptr;
+ loc = l;
+ typeParameters = nullptr;
+ coopmat = false;
+ }
+
+ void initQualifiers(bool global = false)
+ {
+ qualifier.clear();
+ if (global)
+ qualifier.storage = EvqGlobal;
+ }
+
+ void init(const TSourceLoc& l, bool global = false)
+ {
+ initType(l);
+ sampler.clear();
+ initQualifiers(global);
+ shaderQualifiers.init();
+ }
+
+ void setVector(int s)
+ {
+ matrixRows = 0;
+ matrixCols = 0;
+ vectorSize = s;
+ }
+
+ void setMatrix(int c, int r)
+ {
+ matrixRows = r;
+ matrixCols = c;
+ vectorSize = 0;
+ }
+
+ bool isScalar() const
+ {
+ return matrixCols == 0 && vectorSize == 1 && arraySizes == nullptr && userDef == nullptr;
+ }
+
+ // "Image" is a superset of "Subpass"
+ bool isImage() const { return basicType == EbtSampler && sampler.isImage(); }
+ bool isSubpass() const { return basicType == EbtSampler && sampler.isSubpass(); }
+};
+
+//
+// Base class for things that have a type.
+//
+class TType {
+public:
+ POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator())
+
+ // for "empty" type (no args) or simple scalar/vector/matrix
+ explicit TType(TBasicType t = EbtVoid, TStorageQualifier q = EvqTemporary, int vs = 1, int mc = 0, int mr = 0,
+ bool isVector = false) :
+ basicType(t), vectorSize(vs), matrixCols(mc), matrixRows(mr), vector1(isVector && vs == 1), coopmat(false),
+ arraySizes(nullptr), structure(nullptr), fieldName(nullptr), typeName(nullptr), typeParameters(nullptr)
+ {
+ sampler.clear();
+ qualifier.clear();
+ qualifier.storage = q;
+ assert(!(isMatrix() && vectorSize != 0)); // prevent vectorSize != 0 on matrices
+ }
+ // for explicit precision qualifier
+ TType(TBasicType t, TStorageQualifier q, TPrecisionQualifier p, int vs = 1, int mc = 0, int mr = 0,
+ bool isVector = false) :
+ basicType(t), vectorSize(vs), matrixCols(mc), matrixRows(mr), vector1(isVector && vs == 1), coopmat(false),
+ arraySizes(nullptr), structure(nullptr), fieldName(nullptr), typeName(nullptr), typeParameters(nullptr)
+ {
+ sampler.clear();
+ qualifier.clear();
+ qualifier.storage = q;
+ qualifier.precision = p;
+ assert(p >= EpqNone && p <= EpqHigh);
+ assert(!(isMatrix() && vectorSize != 0)); // prevent vectorSize != 0 on matrices
+ }
+ // for turning a TPublicType into a TType, using a shallow copy
+ explicit TType(const TPublicType& p) :
+ basicType(p.basicType),
+ vectorSize(p.vectorSize), matrixCols(p.matrixCols), matrixRows(p.matrixRows), vector1(false), coopmat(p.coopmat),
+ arraySizes(p.arraySizes), structure(nullptr), fieldName(nullptr), typeName(nullptr), typeParameters(p.typeParameters)
+ {
+ if (basicType == EbtSampler)
+ sampler = p.sampler;
+ else
+ sampler.clear();
+ qualifier = p.qualifier;
+ if (p.userDef) {
+ if (p.userDef->basicType == EbtReference) {
+ basicType = EbtReference;
+ referentType = p.userDef->referentType;
+ } else {
+ structure = p.userDef->getWritableStruct(); // public type is short-lived; there are no sharing issues
+ }
+ typeName = NewPoolTString(p.userDef->getTypeName().c_str());
+ }
+ if (p.coopmat && p.basicType == EbtFloat &&
+ p.typeParameters && p.typeParameters->getNumDims() > 0 &&
+ p.typeParameters->getDimSize(0) == 16) {
+ basicType = EbtFloat16;
+ qualifier.precision = EpqNone;
+ }
+ }
+ // for construction of sampler types
+ TType(const TSampler& sampler, TStorageQualifier q = EvqUniform, TArraySizes* as = nullptr) :
+ basicType(EbtSampler), vectorSize(1), matrixCols(0), matrixRows(0), vector1(false), coopmat(false),
+ arraySizes(as), structure(nullptr), fieldName(nullptr), typeName(nullptr),
+ sampler(sampler), typeParameters(nullptr)
+ {
+ qualifier.clear();
+ qualifier.storage = q;
+ }
+ // to efficiently make a dereferenced type
+ // without ever duplicating the outer structure that will be thrown away
+ // and using only shallow copy
+ TType(const TType& type, int derefIndex, bool rowMajor = false)
+ {
+ if (type.isArray()) {
+ shallowCopy(type);
+ if (type.getArraySizes()->getNumDims() == 1) {
+ arraySizes = nullptr;
+ } else {
+ // want our own copy of the array, so we can edit it
+ arraySizes = new TArraySizes;
+ arraySizes->copyDereferenced(*type.arraySizes);
+ }
+ } else if (type.basicType == EbtStruct || type.basicType == EbtBlock) {
+ // do a structure dereference
+ const TTypeList& memberList = *type.getStruct();
+ shallowCopy(*memberList[derefIndex].type);
+ return;
+ } else {
+ // do a vector/matrix dereference
+ shallowCopy(type);
+ if (matrixCols > 0) {
+ // dereference from matrix to vector
+ if (rowMajor)
+ vectorSize = matrixCols;
+ else
+ vectorSize = matrixRows;
+ matrixCols = 0;
+ matrixRows = 0;
+ if (vectorSize == 1)
+ vector1 = true;
+ } else if (isVector()) {
+ // dereference from vector to scalar
+ vectorSize = 1;
+ vector1 = false;
+ } else if (isCoopMat()) {
+ coopmat = false;
+ typeParameters = nullptr;
+ }
+ }
+ }
+ // for making structures, ...
+ TType(TTypeList* userDef, const TString& n) :
+ basicType(EbtStruct), vectorSize(1), matrixCols(0), matrixRows(0), vector1(false), coopmat(false),
+ arraySizes(nullptr), structure(userDef), fieldName(nullptr), typeParameters(nullptr)
+ {
+ sampler.clear();
+ qualifier.clear();
+ typeName = NewPoolTString(n.c_str());
+ }
+ // For interface blocks
+ TType(TTypeList* userDef, const TString& n, const TQualifier& q) :
+ basicType(EbtBlock), vectorSize(1), matrixCols(0), matrixRows(0), vector1(false), coopmat(false),
+ qualifier(q), arraySizes(nullptr), structure(userDef), fieldName(nullptr), typeParameters(nullptr)
+ {
+ sampler.clear();
+ typeName = NewPoolTString(n.c_str());
+ }
+ // for block reference (first parameter must be EbtReference)
+ explicit TType(TBasicType t, const TType &p, const TString& n) :
+ basicType(t), vectorSize(1), matrixCols(0), matrixRows(0), vector1(false),
+ arraySizes(nullptr), structure(nullptr), fieldName(nullptr), typeName(nullptr)
+ {
+ assert(t == EbtReference);
+ typeName = NewPoolTString(n.c_str());
+ qualifier.clear();
+ qualifier.storage = p.qualifier.storage;
+ referentType = p.clone();
+ }
+ virtual ~TType() {}
+
+ // Not for use across pool pops; it will cause multiple instances of TType to point to the same information.
+ // This only works if that information (like a structure's list of types) does not change and
+ // the instances are sharing the same pool.
+ void shallowCopy(const TType& copyOf)
+ {
+ basicType = copyOf.basicType;
+ sampler = copyOf.sampler;
+ qualifier = copyOf.qualifier;
+ vectorSize = copyOf.vectorSize;
+ matrixCols = copyOf.matrixCols;
+ matrixRows = copyOf.matrixRows;
+ vector1 = copyOf.vector1;
+ arraySizes = copyOf.arraySizes; // copying the pointer only, not the contents
+ fieldName = copyOf.fieldName;
+ typeName = copyOf.typeName;
+ if (isStruct()) {
+ structure = copyOf.structure;
+ } else {
+ referentType = copyOf.referentType;
+ }
+ typeParameters = copyOf.typeParameters;
+ coopmat = copyOf.coopmat;
+ }
+
+ // Make complete copy of the whole type graph rooted at 'copyOf'.
+ void deepCopy(const TType& copyOf)
+ {
+ TMap<TTypeList*,TTypeList*> copied; // to enable copying a type graph as a graph, not a tree
+ deepCopy(copyOf, copied);
+ }
+
+ // Recursively make temporary
+ void makeTemporary()
+ {
+ getQualifier().makeTemporary();
+
+ if (isStruct())
+ for (unsigned int i = 0; i < structure->size(); ++i)
+ (*structure)[i].type->makeTemporary();
+ }
+
+ TType* clone() const
+ {
+ TType *newType = new TType();
+ newType->deepCopy(*this);
+
+ return newType;
+ }
+
+ void makeVector() { vector1 = true; }
+
+ virtual void hideMember() { basicType = EbtVoid; vectorSize = 1; }
+ virtual bool hiddenMember() const { return basicType == EbtVoid; }
+
+ virtual void setFieldName(const TString& n) { fieldName = NewPoolTString(n.c_str()); }
+ virtual const TString& getTypeName() const
+ {
+ assert(typeName);
+ return *typeName;
+ }
+
+ virtual const TString& getFieldName() const
+ {
+ assert(fieldName);
+ return *fieldName;
+ }
+
+ virtual TBasicType getBasicType() const { return basicType; }
+ virtual const TSampler& getSampler() const { return sampler; }
+ virtual TSampler& getSampler() { return sampler; }
+
+ virtual TQualifier& getQualifier() { return qualifier; }
+ virtual const TQualifier& getQualifier() const { return qualifier; }
+
+ virtual int getVectorSize() const { return vectorSize; } // returns 1 for either scalar or vector of size 1, valid for both
+ virtual int getMatrixCols() const { return matrixCols; }
+ virtual int getMatrixRows() const { return matrixRows; }
+ virtual int getOuterArraySize() const { return arraySizes->getOuterSize(); }
+ virtual TIntermTyped* getOuterArrayNode() const { return arraySizes->getOuterNode(); }
+ virtual int getCumulativeArraySize() const { return arraySizes->getCumulativeSize(); }
+ virtual bool isArrayOfArrays() const { return arraySizes != nullptr && arraySizes->getNumDims() > 1; }
+ virtual int getImplicitArraySize() const { return arraySizes->getImplicitSize(); }
+ virtual const TArraySizes* getArraySizes() const { return arraySizes; }
+ virtual TArraySizes* getArraySizes() { return arraySizes; }
+ virtual TType* getReferentType() const { return referentType; }
+ virtual const TArraySizes* getTypeParameters() const { return typeParameters; }
+ virtual TArraySizes* getTypeParameters() { return typeParameters; }
+
+ virtual bool isScalar() const { return ! isVector() && ! isMatrix() && ! isStruct() && ! isArray(); }
+ virtual bool isScalarOrVec1() const { return isScalar() || vector1; }
+ virtual bool isVector() const { return vectorSize > 1 || vector1; }
+ virtual bool isMatrix() const { return matrixCols ? true : false; }
+ virtual bool isArray() const { return arraySizes != nullptr; }
+ virtual bool isSizedArray() const { return isArray() && arraySizes->isSized(); }
+ virtual bool isUnsizedArray() const { return isArray() && !arraySizes->isSized(); }
+ virtual bool isArrayVariablyIndexed() const { assert(isArray()); return arraySizes->isVariablyIndexed(); }
+ virtual void setArrayVariablyIndexed() { assert(isArray()); arraySizes->setVariablyIndexed(); }
+ virtual void updateImplicitArraySize(int size) { assert(isArray()); arraySizes->updateImplicitSize(size); }
+ virtual bool isStruct() const { return basicType == EbtStruct || basicType == EbtBlock; }
+ virtual bool isFloatingDomain() const { return basicType == EbtFloat || basicType == EbtDouble || basicType == EbtFloat16; }
+ virtual bool isIntegerDomain() const
+ {
+ switch (basicType) {
+ case EbtInt8:
+ case EbtUint8:
+ case EbtInt16:
+ case EbtUint16:
+ case EbtInt:
+ case EbtUint:
+ case EbtInt64:
+ case EbtUint64:
+ case EbtAtomicUint:
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }
+ virtual bool isOpaque() const { return basicType == EbtSampler || basicType == EbtAtomicUint
+#ifdef NV_EXTENSIONS
+ || basicType == EbtAccStructNV
+#endif
+ ; }
+ virtual bool isBuiltIn() const { return getQualifier().builtIn != EbvNone; }
+
+ // "Image" is a superset of "Subpass"
+ virtual bool isImage() const { return basicType == EbtSampler && getSampler().isImage(); }
+ virtual bool isSubpass() const { return basicType == EbtSampler && getSampler().isSubpass(); }
+ virtual bool isTexture() const { return basicType == EbtSampler && getSampler().isTexture(); }
+ virtual bool isParameterized() const { return typeParameters != nullptr; }
+ virtual bool isCoopMat() const { return coopmat; }
+
+ // return true if this type contains any subtype which satisfies the given predicate.
+ template <typename P>
+ bool contains(P predicate) const
+ {
+ if (predicate(this))
+ return true;
+
+ const auto hasa = [predicate](const TTypeLoc& tl) { return tl.type->contains(predicate); };
+
+ return isStruct() && std::any_of(structure->begin(), structure->end(), hasa);
+ }
+
+ // Recursively checks if the type contains the given basic type
+ virtual bool containsBasicType(TBasicType checkType) const
+ {
+ return contains([checkType](const TType* t) { return t->basicType == checkType; } );
+ }
+
+ // Recursively check the structure for any arrays, needed for some error checks
+ virtual bool containsArray() const
+ {
+ return contains([](const TType* t) { return t->isArray(); } );
+ }
+
+ // Check the structure for any structures, needed for some error checks
+ virtual bool containsStructure() const
+ {
+ return contains([this](const TType* t) { return t != this && t->isStruct(); } );
+ }
+
+ // Recursively check the structure for any unsized arrays, needed for triggering a copyUp().
+ virtual bool containsUnsizedArray() const
+ {
+ return contains([](const TType* t) { return t->isUnsizedArray(); } );
+ }
+
+ virtual bool containsOpaque() const
+ {
+ return contains([](const TType* t) { return t->isOpaque(); } );
+ }
+
+ // Recursively checks if the type contains a built-in variable
+ virtual bool containsBuiltIn() const
+ {
+ return contains([](const TType* t) { return t->isBuiltIn(); } );
+ }
+
+ virtual bool containsNonOpaque() const
+ {
+ const auto nonOpaque = [](const TType* t) {
+ switch (t->basicType) {
+ case EbtVoid:
+ case EbtFloat:
+ case EbtDouble:
+ case EbtFloat16:
+ case EbtInt8:
+ case EbtUint8:
+ case EbtInt16:
+ case EbtUint16:
+ case EbtInt:
+ case EbtUint:
+ case EbtInt64:
+ case EbtUint64:
+ case EbtBool:
+ case EbtReference:
+ return true;
+ default:
+ return false;
+ }
+ };
+
+ return contains(nonOpaque);
+ }
+
+ virtual bool containsSpecializationSize() const
+ {
+ return contains([](const TType* t) { return t->isArray() && t->arraySizes->isOuterSpecialization(); } );
+ }
+
+ virtual bool contains16BitInt() const
+ {
+ return containsBasicType(EbtInt16) || containsBasicType(EbtUint16);
+ }
+
+ virtual bool contains8BitInt() const
+ {
+ return containsBasicType(EbtInt8) || containsBasicType(EbtUint8);
+ }
+
+ virtual bool containsCoopMat() const
+ {
+ return contains([](const TType* t) { return t->coopmat; } );
+ }
+
+ // Array editing methods. Array descriptors can be shared across
+ // type instances. This allows all uses of the same array
+ // to be updated at once. E.g., all nodes can be explicitly sized
+ // by tracking and correcting one implicit size. Or, all nodes
+ // can get the explicit size on a redeclaration that gives size.
+ //
+ // N.B.: Don't share with the shared symbol tables (symbols are
+ // marked as isReadOnly(). Such symbols with arrays that will be
+ // edited need to copyUp() on first use, so that
+ // A) the edits don't effect the shared symbol table, and
+ // B) the edits are shared across all users.
+ void updateArraySizes(const TType& type)
+ {
+ // For when we may already be sharing existing array descriptors,
+ // keeping the pointers the same, just updating the contents.
+ assert(arraySizes != nullptr);
+ assert(type.arraySizes != nullptr);
+ *arraySizes = *type.arraySizes;
+ }
+ void copyArraySizes(const TArraySizes& s)
+ {
+ // For setting a fresh new set of array sizes, not yet worrying about sharing.
+ arraySizes = new TArraySizes;
+ *arraySizes = s;
+ }
+ void transferArraySizes(TArraySizes* s)
+ {
+ // For setting an already allocated set of sizes that this type can use
+ // (no copy made).
+ arraySizes = s;
+ }
+ void clearArraySizes()
+ {
+ arraySizes = nullptr;
+ }
+
+ // Add inner array sizes, to any existing sizes, via copy; the
+ // sizes passed in can still be reused for other purposes.
+ void copyArrayInnerSizes(const TArraySizes* s)
+ {
+ if (s != nullptr) {
+ if (arraySizes == nullptr)
+ copyArraySizes(*s);
+ else
+ arraySizes->addInnerSizes(*s);
+ }
+ }
+ void changeOuterArraySize(int s) { arraySizes->changeOuterSize(s); }
+
+ // Recursively make the implicit array size the explicit array size.
+ // Expicit arrays are compile-time or link-time sized, never run-time sized.
+ // Sometimes, policy calls for an array to be run-time sized even if it was
+ // never variably indexed: Don't turn a 'skipNonvariablyIndexed' array into
+ // an explicit array.
+ void adoptImplicitArraySizes(bool skipNonvariablyIndexed)
+ {
+ if (isUnsizedArray() && !(skipNonvariablyIndexed || isArrayVariablyIndexed()))
+ changeOuterArraySize(getImplicitArraySize());
+#ifdef NV_EXTENSIONS
+ // For multi-dim per-view arrays, set unsized inner dimension size to 1
+ if (qualifier.isPerView() && arraySizes && arraySizes->isInnerUnsized())
+ arraySizes->clearInnerUnsized();
+#endif
+ if (isStruct() && structure->size() > 0) {
+ int lastMember = (int)structure->size() - 1;
+ for (int i = 0; i < lastMember; ++i)
+ (*structure)[i].type->adoptImplicitArraySizes(false);
+ // implement the "last member of an SSBO" policy
+ (*structure)[lastMember].type->adoptImplicitArraySizes(getQualifier().storage == EvqBuffer);
+ }
+ }
+
+
+ void updateTypeParameters(const TType& type)
+ {
+ // For when we may already be sharing existing array descriptors,
+ // keeping the pointers the same, just updating the contents.
+ assert(typeParameters != nullptr);
+ assert(type.typeParameters != nullptr);
+ *typeParameters = *type.typeParameters;
+ }
+ void copyTypeParameters(const TArraySizes& s)
+ {
+ // For setting a fresh new set of type parameters, not yet worrying about sharing.
+ typeParameters = new TArraySizes;
+ *typeParameters = s;
+ }
+ void transferTypeParameters(TArraySizes* s)
+ {
+ // For setting an already allocated set of sizes that this type can use
+ // (no copy made).
+ typeParameters = s;
+ }
+ void clearTypeParameters()
+ {
+ typeParameters = nullptr;
+ }
+
+ // Add inner array sizes, to any existing sizes, via copy; the
+ // sizes passed in can still be reused for other purposes.
+ void copyTypeParametersInnerSizes(const TArraySizes* s)
+ {
+ if (s != nullptr) {
+ if (typeParameters == nullptr)
+ copyTypeParameters(*s);
+ else
+ typeParameters->addInnerSizes(*s);
+ }
+ }
+
+
+
+ const char* getBasicString() const
+ {
+ return TType::getBasicString(basicType);
+ }
+
+ static const char* getBasicString(TBasicType t)
+ {
+ switch (t) {
+ case EbtVoid: return "void";
+ case EbtFloat: return "float";
+ case EbtDouble: return "double";
+ case EbtFloat16: return "float16_t";
+ case EbtInt8: return "int8_t";
+ case EbtUint8: return "uint8_t";
+ case EbtInt16: return "int16_t";
+ case EbtUint16: return "uint16_t";
+ case EbtInt: return "int";
+ case EbtUint: return "uint";
+ case EbtInt64: return "int64_t";
+ case EbtUint64: return "uint64_t";
+ case EbtBool: return "bool";
+ case EbtAtomicUint: return "atomic_uint";
+ case EbtSampler: return "sampler/image";
+ case EbtStruct: return "structure";
+ case EbtBlock: return "block";
+#ifdef NV_EXTENSIONS
+ case EbtAccStructNV: return "accelerationStructureNV";
+#endif
+ case EbtReference: return "reference";
+ default: return "unknown type";
+ }
+ }
+
+ TString getCompleteString() const
+ {
+ TString typeString;
+
+ const auto appendStr = [&](const char* s) { typeString.append(s); };
+ const auto appendUint = [&](unsigned int u) { typeString.append(std::to_string(u).c_str()); };
+ const auto appendInt = [&](int i) { typeString.append(std::to_string(i).c_str()); };
+
+ if (qualifier.hasLayout()) {
+ // To reduce noise, skip this if the only layout is an xfb_buffer
+ // with no triggering xfb_offset.
+ TQualifier noXfbBuffer = qualifier;
+ noXfbBuffer.layoutXfbBuffer = TQualifier::layoutXfbBufferEnd;
+ if (noXfbBuffer.hasLayout()) {
+ appendStr("layout(");
+ if (qualifier.hasAnyLocation()) {
+ appendStr(" location=");
+ appendUint(qualifier.layoutLocation);
+ if (qualifier.hasComponent()) {
+ appendStr(" component=");
+ appendUint(qualifier.layoutComponent);
+ }
+ if (qualifier.hasIndex()) {
+ appendStr(" index=");
+ appendUint(qualifier.layoutIndex);
+ }
+ }
+ if (qualifier.hasSet()) {
+ appendStr(" set=");
+ appendUint(qualifier.layoutSet);
+ }
+ if (qualifier.hasBinding()) {
+ appendStr(" binding=");
+ appendUint(qualifier.layoutBinding);
+ }
+ if (qualifier.hasStream()) {
+ appendStr(" stream=");
+ appendUint(qualifier.layoutStream);
+ }
+ if (qualifier.hasMatrix()) {
+ appendStr(" ");
+ appendStr(TQualifier::getLayoutMatrixString(qualifier.layoutMatrix));
+ }
+ if (qualifier.hasPacking()) {
+ appendStr(" ");
+ appendStr(TQualifier::getLayoutPackingString(qualifier.layoutPacking));
+ }
+ if (qualifier.hasOffset()) {
+ appendStr(" offset=");
+ appendInt(qualifier.layoutOffset);
+ }
+ if (qualifier.hasAlign()) {
+ appendStr(" align=");
+ appendInt(qualifier.layoutAlign);
+ }
+ if (qualifier.hasFormat()) {
+ appendStr(" ");
+ appendStr(TQualifier::getLayoutFormatString(qualifier.layoutFormat));
+ }
+ if (qualifier.hasXfbBuffer() && qualifier.hasXfbOffset()) {
+ appendStr(" xfb_buffer=");
+ appendUint(qualifier.layoutXfbBuffer);
+ }
+ if (qualifier.hasXfbOffset()) {
+ appendStr(" xfb_offset=");
+ appendUint(qualifier.layoutXfbOffset);
+ }
+ if (qualifier.hasXfbStride()) {
+ appendStr(" xfb_stride=");
+ appendUint(qualifier.layoutXfbStride);
+ }
+ if (qualifier.hasAttachment()) {
+ appendStr(" input_attachment_index=");
+ appendUint(qualifier.layoutAttachment);
+ }
+ if (qualifier.hasSpecConstantId()) {
+ appendStr(" constant_id=");
+ appendUint(qualifier.layoutSpecConstantId);
+ }
+ if (qualifier.layoutPushConstant)
+ appendStr(" push_constant");
+ if (qualifier.layoutBufferReference)
+ appendStr(" buffer_reference");
+ if (qualifier.hasBufferReferenceAlign()) {
+ appendStr(" buffer_reference_align=");
+ appendUint(1u << qualifier.layoutBufferReferenceAlign);
+ }
+
+#ifdef NV_EXTENSIONS
+ if (qualifier.layoutPassthrough)
+ appendStr(" passthrough");
+ if (qualifier.layoutViewportRelative)
+ appendStr(" layoutViewportRelative");
+ if (qualifier.layoutSecondaryViewportRelativeOffset != -2048) {
+ appendStr(" layoutSecondaryViewportRelativeOffset=");
+ appendInt(qualifier.layoutSecondaryViewportRelativeOffset);
+ }
+ if (qualifier.layoutShaderRecordNV)
+ appendStr(" shaderRecordNV");
+#endif
+
+ appendStr(")");
+ }
+ }
+
+ if (qualifier.invariant)
+ appendStr(" invariant");
+ if (qualifier.noContraction)
+ appendStr(" noContraction");
+ if (qualifier.centroid)
+ appendStr(" centroid");
+ if (qualifier.smooth)
+ appendStr(" smooth");
+ if (qualifier.flat)
+ appendStr(" flat");
+ if (qualifier.nopersp)
+ appendStr(" noperspective");
+#ifdef AMD_EXTENSIONS
+ if (qualifier.explicitInterp)
+ appendStr(" __explicitInterpAMD");
+#endif
+#ifdef NV_EXTENSIONS
+ if (qualifier.pervertexNV)
+ appendStr(" pervertexNV");
+ if (qualifier.perPrimitiveNV)
+ appendStr(" perprimitiveNV");
+ if (qualifier.perViewNV)
+ appendStr(" perviewNV");
+ if (qualifier.perTaskNV)
+ appendStr(" taskNV");
+#endif
+ if (qualifier.patch)
+ appendStr(" patch");
+ if (qualifier.sample)
+ appendStr(" sample");
+ if (qualifier.coherent)
+ appendStr(" coherent");
+ if (qualifier.devicecoherent)
+ appendStr(" devicecoherent");
+ if (qualifier.queuefamilycoherent)
+ appendStr(" queuefamilycoherent");
+ if (qualifier.workgroupcoherent)
+ appendStr(" workgroupcoherent");
+ if (qualifier.subgroupcoherent)
+ appendStr(" subgroupcoherent");
+ if (qualifier.nonprivate)
+ appendStr(" nonprivate");
+ if (qualifier.volatil)
+ appendStr(" volatile");
+ if (qualifier.restrict)
+ appendStr(" restrict");
+ if (qualifier.readonly)
+ appendStr(" readonly");
+ if (qualifier.writeonly)
+ appendStr(" writeonly");
+ if (qualifier.specConstant)
+ appendStr(" specialization-constant");
+ if (qualifier.nonUniform)
+ appendStr(" nonuniform");
+ appendStr(" ");
+ appendStr(getStorageQualifierString());
+ if (isArray()) {
+ for(int i = 0; i < (int)arraySizes->getNumDims(); ++i) {
+ int size = arraySizes->getDimSize(i);
+ if (size == UnsizedArraySize && i == 0 && arraySizes->isVariablyIndexed())
+ appendStr(" runtime-sized array of");
+ else {
+ if (size == UnsizedArraySize) {
+ appendStr(" unsized");
+ if (i == 0) {
+ appendStr(" ");
+ appendInt(arraySizes->getImplicitSize());
+ }
+ } else {
+ appendStr(" ");
+ appendInt(arraySizes->getDimSize(i));
+ }
+ appendStr("-element array of");
+ }
+ }
+ }
+ if (isParameterized()) {
+ appendStr("<");
+ for(int i = 0; i < (int)typeParameters->getNumDims(); ++i) {
+ appendInt(typeParameters->getDimSize(i));
+ if (i != (int)typeParameters->getNumDims() - 1)
+ appendStr(", ");
+ }
+ appendStr(">");
+ }
+ if (qualifier.precision != EpqNone) {
+ appendStr(" ");
+ appendStr(getPrecisionQualifierString());
+ }
+ if (isMatrix()) {
+ appendStr(" ");
+ appendInt(matrixCols);
+ appendStr("X");
+ appendInt(matrixRows);
+ appendStr(" matrix of");
+ } else if (isVector()) {
+ appendStr(" ");
+ appendInt(vectorSize);
+ appendStr("-component vector of");
+ }
+
+ appendStr(" ");
+ typeString.append(getBasicTypeString());
+
+ if (qualifier.builtIn != EbvNone) {
+ appendStr(" ");
+ appendStr(getBuiltInVariableString());
+ }
+
+ // Add struct/block members
+ if (isStruct()) {
+ appendStr("{");
+ for (size_t i = 0; i < structure->size(); ++i) {
+ if (! (*structure)[i].type->hiddenMember()) {
+ typeString.append((*structure)[i].type->getCompleteString());
+ typeString.append(" ");
+ typeString.append((*structure)[i].type->getFieldName());
+ if (i < structure->size() - 1)
+ appendStr(", ");
+ }
+ }
+ appendStr("}");
+ }
+
+ return typeString;
+ }
+
+ TString getBasicTypeString() const
+ {
+ if (basicType == EbtSampler)
+ return sampler.getString();
+ else
+ return getBasicString();
+ }
+
+ const char* getStorageQualifierString() const { return GetStorageQualifierString(qualifier.storage); }
+ const char* getBuiltInVariableString() const { return GetBuiltInVariableString(qualifier.builtIn); }
+ const char* getPrecisionQualifierString() const { return GetPrecisionQualifierString(qualifier.precision); }
+ const TTypeList* getStruct() const { assert(isStruct()); return structure; }
+ void setStruct(TTypeList* s) { assert(isStruct()); structure = s; }
+ TTypeList* getWritableStruct() const { assert(isStruct()); return structure; } // This should only be used when known to not be sharing with other threads
+
+ int computeNumComponents() const
+ {
+ int components = 0;
+
+ if (getBasicType() == EbtStruct || getBasicType() == EbtBlock) {
+ for (TTypeList::const_iterator tl = getStruct()->begin(); tl != getStruct()->end(); tl++)
+ components += ((*tl).type)->computeNumComponents();
+ } else if (matrixCols)
+ components = matrixCols * matrixRows;
+ else
+ components = vectorSize;
+
+ if (arraySizes != nullptr) {
+ components *= arraySizes->getCumulativeSize();
+ }
+
+ return components;
+ }
+
+ // append this type's mangled name to the passed in 'name'
+ void appendMangledName(TString& name) const
+ {
+ buildMangledName(name);
+ name += ';' ;
+ }
+
+ // Do two structure types match? They could be declared independently,
+ // in different places, but still might satisfy the definition of matching.
+ // From the spec:
+ //
+ // "Structures must have the same name, sequence of type names, and
+ // type definitions, and member names to be considered the same type.
+ // This rule applies recursively for nested or embedded types."
+ //
+ bool sameStructType(const TType& right) const
+ {
+ // Most commonly, they are both nullptr, or the same pointer to the same actual structure
+ if ((!isStruct() && !right.isStruct()) ||
+ (isStruct() && right.isStruct() && structure == right.structure))
+ return true;
+
+ // Both being nullptr was caught above, now they both have to be structures of the same number of elements
+ if (!isStruct() || !right.isStruct() ||
+ structure->size() != right.structure->size())
+ return false;
+
+ // Structure names have to match
+ if (*typeName != *right.typeName)
+ return false;
+
+ // Compare the names and types of all the members, which have to match
+ for (unsigned int i = 0; i < structure->size(); ++i) {
+ if ((*structure)[i].type->getFieldName() != (*right.structure)[i].type->getFieldName())
+ return false;
+
+ if (*(*structure)[i].type != *(*right.structure)[i].type)
+ return false;
+ }
+
+ return true;
+ }
+
+ bool sameReferenceType(const TType& right) const
+ {
+ if ((basicType == EbtReference) != (right.basicType == EbtReference))
+ return false;
+
+ if ((basicType != EbtReference) && (right.basicType != EbtReference))
+ return true;
+
+ assert(referentType != nullptr);
+ assert(right.referentType != nullptr);
+
+ if (referentType == right.referentType)
+ return true;
+
+ return *referentType == *right.referentType;
+ }
+
+ // See if two types match, in all aspects except arrayness
+ bool sameElementType(const TType& right) const
+ {
+ return basicType == right.basicType && sameElementShape(right);
+ }
+
+ // See if two type's arrayness match
+ bool sameArrayness(const TType& right) const
+ {
+ return ((arraySizes == nullptr && right.arraySizes == nullptr) ||
+ (arraySizes != nullptr && right.arraySizes != nullptr && *arraySizes == *right.arraySizes));
+ }
+
+ // See if two type's arrayness match in everything except their outer dimension
+ bool sameInnerArrayness(const TType& right) const
+ {
+ assert(arraySizes != nullptr && right.arraySizes != nullptr);
+ return arraySizes->sameInnerArrayness(*right.arraySizes);
+ }
+
+ // See if two type's parameters match
+ bool sameTypeParameters(const TType& right) const
+ {
+ return ((typeParameters == nullptr && right.typeParameters == nullptr) ||
+ (typeParameters != nullptr && right.typeParameters != nullptr && *typeParameters == *right.typeParameters));
+ }
+
+ // See if two type's elements match in all ways except basic type
+ bool sameElementShape(const TType& right) const
+ {
+ return sampler == right.sampler &&
+ vectorSize == right.vectorSize &&
+ matrixCols == right.matrixCols &&
+ matrixRows == right.matrixRows &&
+ vector1 == right.vector1 &&
+ coopmat == right.coopmat &&
+ sameStructType(right) &&
+ sameReferenceType(right);
+ }
+
+ // See if a cooperative matrix type parameter with unspecified parameters is
+ // an OK function parameter
+ bool coopMatParameterOK(const TType& right) const
+ {
+ return coopmat && right.coopmat &&
+ typeParameters == nullptr && right.typeParameters != nullptr;
+ }
+
+ // See if two types match in all ways (just the actual type, not qualification)
+ bool operator==(const TType& right) const
+ {
+ return sameElementType(right) && sameArrayness(right) && sameTypeParameters(right);
+ }
+
+ bool operator!=(const TType& right) const
+ {
+ return ! operator==(right);
+ }
+
+ unsigned int getBufferReferenceAlignment() const
+ {
+ if (getBasicType() == glslang::EbtReference) {
+ return getReferentType()->getQualifier().hasBufferReferenceAlign() ?
+ (1u << getReferentType()->getQualifier().layoutBufferReferenceAlign) : 16u;
+ } else {
+ return 0;
+ }
+ }
+
+protected:
+ // Require consumer to pick between deep copy and shallow copy.
+ TType(const TType& type);
+ TType& operator=(const TType& type);
+
+ // Recursively copy a type graph, while preserving the graph-like
+ // quality. That is, don't make more than one copy of a structure that
+ // gets reused multiple times in the type graph.
+ void deepCopy(const TType& copyOf, TMap<TTypeList*,TTypeList*>& copiedMap)
+ {
+ shallowCopy(copyOf);
+
+ if (copyOf.arraySizes) {
+ arraySizes = new TArraySizes;
+ *arraySizes = *copyOf.arraySizes;
+ }
+
+ if (copyOf.typeParameters) {
+ typeParameters = new TArraySizes;
+ *typeParameters = *copyOf.typeParameters;
+ }
+
+ if (copyOf.isStruct() && copyOf.structure) {
+ auto prevCopy = copiedMap.find(copyOf.structure);
+ if (prevCopy != copiedMap.end())
+ structure = prevCopy->second;
+ else {
+ structure = new TTypeList;
+ copiedMap[copyOf.structure] = structure;
+ for (unsigned int i = 0; i < copyOf.structure->size(); ++i) {
+ TTypeLoc typeLoc;
+ typeLoc.loc = (*copyOf.structure)[i].loc;
+ typeLoc.type = new TType();
+ typeLoc.type->deepCopy(*(*copyOf.structure)[i].type, copiedMap);
+ structure->push_back(typeLoc);
+ }
+ }
+ }
+
+ if (copyOf.fieldName)
+ fieldName = NewPoolTString(copyOf.fieldName->c_str());
+ if (copyOf.typeName)
+ typeName = NewPoolTString(copyOf.typeName->c_str());
+ }
+
+
+ void buildMangledName(TString&) const;
+
+ TBasicType basicType : 8;
+ int vectorSize : 4; // 1 means either scalar or 1-component vector; see vector1 to disambiguate.
+ int matrixCols : 4;
+ int matrixRows : 4;
+ bool vector1 : 1; // Backward-compatible tracking of a 1-component vector distinguished from a scalar.
+ // GLSL 4.5 never has a 1-component vector; so this will always be false until such
+ // functionality is added.
+ // HLSL does have a 1-component vectors, so this will be true to disambiguate
+ // from a scalar.
+ bool coopmat : 1;
+ TQualifier qualifier;
+
+ TArraySizes* arraySizes; // nullptr unless an array; can be shared across types
+ // A type can't be both a structure (EbtStruct/EbtBlock) and a reference (EbtReference), so
+ // conserve space by making these a union
+ union {
+ TTypeList* structure; // invalid unless this is a struct; can be shared across types
+ TType *referentType; // invalid unless this is an EbtReference
+ };
+ TString *fieldName; // for structure field names
+ TString *typeName; // for structure type name
+ TSampler sampler;
+ TArraySizes* typeParameters;// nullptr unless a parameterized type; can be shared across types
+};
+
+} // end namespace glslang
+
+#endif // _TYPES_INCLUDED_
diff --git a/src/3rdparty/glslang/glslang/Include/arrays.h b/src/3rdparty/glslang/glslang/Include/arrays.h
new file mode 100644
index 0000000..7f047d9
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/Include/arrays.h
@@ -0,0 +1,341 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2012-2013 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+//
+// Implement types for tracking GLSL arrays, arrays of arrays, etc.
+//
+
+#ifndef _ARRAYS_INCLUDED
+#define _ARRAYS_INCLUDED
+
+#include <algorithm>
+
+namespace glslang {
+
+// This is used to mean there is no size yet (unsized), it is waiting to get a size from somewhere else.
+const int UnsizedArraySize = 0;
+
+class TIntermTyped;
+extern bool SameSpecializationConstants(TIntermTyped*, TIntermTyped*);
+
+// Specialization constants need both a nominal size and a node that defines
+// the specialization constant being used. Array types are the same when their
+// size and specialization constant nodes are the same.
+struct TArraySize {
+ unsigned int size;
+ TIntermTyped* node; // nullptr means no specialization constant node
+ bool operator==(const TArraySize& rhs) const
+ {
+ if (size != rhs.size)
+ return false;
+ if (node == nullptr || rhs.node == nullptr)
+ return node == rhs.node;
+
+ return SameSpecializationConstants(node, rhs.node);
+ }
+};
+
+//
+// TSmallArrayVector is used as the container for the set of sizes in TArraySizes.
+// It has generic-container semantics, while TArraySizes has array-of-array semantics.
+// That is, TSmallArrayVector should be more focused on mechanism and TArraySizes on policy.
+//
+struct TSmallArrayVector {
+ //
+ // TODO: memory: TSmallArrayVector is intended to be smaller.
+ // Almost all arrays could be handled by two sizes each fitting
+ // in 16 bits, needing a real vector only in the cases where there
+ // are more than 3 sizes or a size needing more than 16 bits.
+ //
+ POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator())
+
+ TSmallArrayVector() : sizes(nullptr) { }
+ virtual ~TSmallArrayVector() { dealloc(); }
+
+ // For breaking into two non-shared copies, independently modifiable.
+ TSmallArrayVector& operator=(const TSmallArrayVector& from)
+ {
+ if (from.sizes == nullptr)
+ sizes = nullptr;
+ else {
+ alloc();
+ *sizes = *from.sizes;
+ }
+
+ return *this;
+ }
+
+ int size() const
+ {
+ if (sizes == nullptr)
+ return 0;
+ return (int)sizes->size();
+ }
+
+ unsigned int frontSize() const
+ {
+ assert(sizes != nullptr && sizes->size() > 0);
+ return sizes->front().size;
+ }
+
+ TIntermTyped* frontNode() const
+ {
+ assert(sizes != nullptr && sizes->size() > 0);
+ return sizes->front().node;
+ }
+
+ void changeFront(unsigned int s)
+ {
+ assert(sizes != nullptr);
+ // this should only happen for implicitly sized arrays, not specialization constants
+ assert(sizes->front().node == nullptr);
+ sizes->front().size = s;
+ }
+
+ void push_back(unsigned int e, TIntermTyped* n)
+ {
+ alloc();
+ TArraySize pair = { e, n };
+ sizes->push_back(pair);
+ }
+
+ void push_back(const TSmallArrayVector& newDims)
+ {
+ alloc();
+ sizes->insert(sizes->end(), newDims.sizes->begin(), newDims.sizes->end());
+ }
+
+ void pop_front()
+ {
+ assert(sizes != nullptr && sizes->size() > 0);
+ if (sizes->size() == 1)
+ dealloc();
+ else
+ sizes->erase(sizes->begin());
+ }
+
+ // 'this' should currently not be holding anything, and copyNonFront
+ // will make it hold a copy of all but the first element of rhs.
+ // (This would be useful for making a type that is dereferenced by
+ // one dimension.)
+ void copyNonFront(const TSmallArrayVector& rhs)
+ {
+ assert(sizes == nullptr);
+ if (rhs.size() > 1) {
+ alloc();
+ sizes->insert(sizes->begin(), rhs.sizes->begin() + 1, rhs.sizes->end());
+ }
+ }
+
+ unsigned int getDimSize(int i) const
+ {
+ assert(sizes != nullptr && (int)sizes->size() > i);
+ return (*sizes)[i].size;
+ }
+
+ void setDimSize(int i, unsigned int size) const
+ {
+ assert(sizes != nullptr && (int)sizes->size() > i);
+ assert((*sizes)[i].node == nullptr);
+ (*sizes)[i].size = size;
+ }
+
+ TIntermTyped* getDimNode(int i) const
+ {
+ assert(sizes != nullptr && (int)sizes->size() > i);
+ return (*sizes)[i].node;
+ }
+
+ bool operator==(const TSmallArrayVector& rhs) const
+ {
+ if (sizes == nullptr && rhs.sizes == nullptr)
+ return true;
+ if (sizes == nullptr || rhs.sizes == nullptr)
+ return false;
+ return *sizes == *rhs.sizes;
+ }
+ bool operator!=(const TSmallArrayVector& rhs) const { return ! operator==(rhs); }
+
+protected:
+ TSmallArrayVector(const TSmallArrayVector&);
+
+ void alloc()
+ {
+ if (sizes == nullptr)
+ sizes = new TVector<TArraySize>;
+ }
+ void dealloc()
+ {
+ delete sizes;
+ sizes = nullptr;
+ }
+
+ TVector<TArraySize>* sizes; // will either hold such a pointer, or in the future, hold the two array sizes
+};
+
+//
+// Represent an array, or array of arrays, to arbitrary depth. This is not
+// done through a hierarchy of types in a type tree, rather all contiguous arrayness
+// in the type hierarchy is localized into this single cumulative object.
+//
+// The arrayness in TTtype is a pointer, so that it can be non-allocated and zero
+// for the vast majority of types that are non-array types.
+//
+// Order Policy: these are all identical:
+// - left to right order within a contiguous set of ...[..][..][..]... in the source language
+// - index order 0, 1, 2, ... within the 'sizes' member below
+// - outer-most to inner-most
+//
+struct TArraySizes {
+ POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator())
+
+ TArraySizes() : implicitArraySize(1), variablyIndexed(false) { }
+
+ // For breaking into two non-shared copies, independently modifiable.
+ TArraySizes& operator=(const TArraySizes& from)
+ {
+ implicitArraySize = from.implicitArraySize;
+ variablyIndexed = from.variablyIndexed;
+ sizes = from.sizes;
+
+ return *this;
+ }
+
+ // translate from array-of-array semantics to container semantics
+ int getNumDims() const { return sizes.size(); }
+ int getDimSize(int dim) const { return sizes.getDimSize(dim); }
+ TIntermTyped* getDimNode(int dim) const { return sizes.getDimNode(dim); }
+ void setDimSize(int dim, int size) { sizes.setDimSize(dim, size); }
+ int getOuterSize() const { return sizes.frontSize(); }
+ TIntermTyped* getOuterNode() const { return sizes.frontNode(); }
+ int getCumulativeSize() const
+ {
+ int size = 1;
+ for (int d = 0; d < sizes.size(); ++d) {
+ // this only makes sense in paths that have a known array size
+ assert(sizes.getDimSize(d) != UnsizedArraySize);
+ size *= sizes.getDimSize(d);
+ }
+ return size;
+ }
+ void addInnerSize() { addInnerSize((unsigned)UnsizedArraySize); }
+ void addInnerSize(int s) { addInnerSize((unsigned)s, nullptr); }
+ void addInnerSize(int s, TIntermTyped* n) { sizes.push_back((unsigned)s, n); }
+ void addInnerSize(TArraySize pair) {
+ sizes.push_back(pair.size, pair.node);
+ }
+ void addInnerSizes(const TArraySizes& s) { sizes.push_back(s.sizes); }
+ void changeOuterSize(int s) { sizes.changeFront((unsigned)s); }
+ int getImplicitSize() const { return implicitArraySize; }
+ void updateImplicitSize(int s) { implicitArraySize = std::max(implicitArraySize, s); }
+ bool isInnerUnsized() const
+ {
+ for (int d = 1; d < sizes.size(); ++d) {
+ if (sizes.getDimSize(d) == (unsigned)UnsizedArraySize)
+ return true;
+ }
+
+ return false;
+ }
+ bool clearInnerUnsized()
+ {
+ for (int d = 1; d < sizes.size(); ++d) {
+ if (sizes.getDimSize(d) == (unsigned)UnsizedArraySize)
+ setDimSize(d, 1);
+ }
+
+ return false;
+ }
+ bool isInnerSpecialization() const
+ {
+ for (int d = 1; d < sizes.size(); ++d) {
+ if (sizes.getDimNode(d) != nullptr)
+ return true;
+ }
+
+ return false;
+ }
+ bool isOuterSpecialization()
+ {
+ return sizes.getDimNode(0) != nullptr;
+ }
+
+ bool hasUnsized() const { return getOuterSize() == UnsizedArraySize || isInnerUnsized(); }
+ bool isSized() const { return getOuterSize() != UnsizedArraySize; }
+ void dereference() { sizes.pop_front(); }
+ void copyDereferenced(const TArraySizes& rhs)
+ {
+ assert(sizes.size() == 0);
+ if (rhs.sizes.size() > 1)
+ sizes.copyNonFront(rhs.sizes);
+ }
+
+ bool sameInnerArrayness(const TArraySizes& rhs) const
+ {
+ if (sizes.size() != rhs.sizes.size())
+ return false;
+
+ for (int d = 1; d < sizes.size(); ++d) {
+ if (sizes.getDimSize(d) != rhs.sizes.getDimSize(d) ||
+ sizes.getDimNode(d) != rhs.sizes.getDimNode(d))
+ return false;
+ }
+
+ return true;
+ }
+
+ void setVariablyIndexed() { variablyIndexed = true; }
+ bool isVariablyIndexed() const { return variablyIndexed; }
+
+ bool operator==(const TArraySizes& rhs) const { return sizes == rhs.sizes; }
+ bool operator!=(const TArraySizes& rhs) const { return sizes != rhs.sizes; }
+
+protected:
+ TSmallArrayVector sizes;
+
+ TArraySizes(const TArraySizes&);
+
+ // For tracking maximum referenced compile-time constant index.
+ // Applies only to the outer-most dimension. Potentially becomes
+ // the implicit size of the array, if not variably indexed and
+ // otherwise legal.
+ int implicitArraySize;
+ bool variablyIndexed; // true if array is indexed with a non compile-time constant
+};
+
+} // end namespace glslang
+
+#endif // _ARRAYS_INCLUDED_
diff --git a/src/3rdparty/glslang/glslang/Include/intermediate.h b/src/3rdparty/glslang/glslang/Include/intermediate.h
new file mode 100644
index 0000000..32e684c
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/Include/intermediate.h
@@ -0,0 +1,1730 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2012-2016 LunarG, Inc.
+// Copyright (C) 2017 ARM Limited.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+//
+// Definition of the in-memory high-level intermediate representation
+// of shaders. This is a tree that parser creates.
+//
+// Nodes in the tree are defined as a hierarchy of classes derived from
+// TIntermNode. Each is a node in a tree. There is no preset branching factor;
+// each node can have it's own type of list of children.
+//
+
+#ifndef __INTERMEDIATE_H
+#define __INTERMEDIATE_H
+
+#if defined(_MSC_VER) && _MSC_VER >= 1900
+ #pragma warning(disable : 4464) // relative include path contains '..'
+ #pragma warning(disable : 5026) // 'glslang::TIntermUnary': move constructor was implicitly defined as deleted
+#endif
+
+#include "../Include/Common.h"
+#include "../Include/Types.h"
+#include "../Include/ConstantUnion.h"
+
+namespace glslang {
+
+class TIntermediate;
+
+//
+// Operators used by the high-level (parse tree) representation.
+//
+enum TOperator {
+ EOpNull, // if in a node, should only mean a node is still being built
+ EOpSequence, // denotes a list of statements, or parameters, etc.
+ EOpLinkerObjects, // for aggregate node of objects the linker may need, if not reference by the rest of the AST
+ EOpFunctionCall,
+ EOpFunction, // For function definition
+ EOpParameters, // an aggregate listing the parameters to a function
+
+ //
+ // Unary operators
+ //
+
+ EOpNegative,
+ EOpLogicalNot,
+ EOpVectorLogicalNot,
+ EOpBitwiseNot,
+
+ EOpPostIncrement,
+ EOpPostDecrement,
+ EOpPreIncrement,
+ EOpPreDecrement,
+
+ // (u)int* -> bool
+ EOpConvInt8ToBool,
+ EOpConvUint8ToBool,
+ EOpConvInt16ToBool,
+ EOpConvUint16ToBool,
+ EOpConvIntToBool,
+ EOpConvUintToBool,
+ EOpConvInt64ToBool,
+ EOpConvUint64ToBool,
+
+ // float* -> bool
+ EOpConvFloat16ToBool,
+ EOpConvFloatToBool,
+ EOpConvDoubleToBool,
+
+ // bool -> (u)int*
+ EOpConvBoolToInt8,
+ EOpConvBoolToUint8,
+ EOpConvBoolToInt16,
+ EOpConvBoolToUint16,
+ EOpConvBoolToInt,
+ EOpConvBoolToUint,
+ EOpConvBoolToInt64,
+ EOpConvBoolToUint64,
+
+ // bool -> float*
+ EOpConvBoolToFloat16,
+ EOpConvBoolToFloat,
+ EOpConvBoolToDouble,
+
+ // int8_t -> (u)int*
+ EOpConvInt8ToInt16,
+ EOpConvInt8ToInt,
+ EOpConvInt8ToInt64,
+ EOpConvInt8ToUint8,
+ EOpConvInt8ToUint16,
+ EOpConvInt8ToUint,
+ EOpConvInt8ToUint64,
+
+ // uint8_t -> (u)int*
+ EOpConvUint8ToInt8,
+ EOpConvUint8ToInt16,
+ EOpConvUint8ToInt,
+ EOpConvUint8ToInt64,
+ EOpConvUint8ToUint16,
+ EOpConvUint8ToUint,
+ EOpConvUint8ToUint64,
+
+ // int8_t -> float*
+ EOpConvInt8ToFloat16,
+ EOpConvInt8ToFloat,
+ EOpConvInt8ToDouble,
+
+ // uint8_t -> float*
+ EOpConvUint8ToFloat16,
+ EOpConvUint8ToFloat,
+ EOpConvUint8ToDouble,
+
+ // int16_t -> (u)int*
+ EOpConvInt16ToInt8,
+ EOpConvInt16ToInt,
+ EOpConvInt16ToInt64,
+ EOpConvInt16ToUint8,
+ EOpConvInt16ToUint16,
+ EOpConvInt16ToUint,
+ EOpConvInt16ToUint64,
+
+ // uint16_t -> (u)int*
+ EOpConvUint16ToInt8,
+ EOpConvUint16ToInt16,
+ EOpConvUint16ToInt,
+ EOpConvUint16ToInt64,
+ EOpConvUint16ToUint8,
+ EOpConvUint16ToUint,
+ EOpConvUint16ToUint64,
+
+ // int16_t -> float*
+ EOpConvInt16ToFloat16,
+ EOpConvInt16ToFloat,
+ EOpConvInt16ToDouble,
+
+ // uint16_t -> float*
+ EOpConvUint16ToFloat16,
+ EOpConvUint16ToFloat,
+ EOpConvUint16ToDouble,
+
+ // int32_t -> (u)int*
+ EOpConvIntToInt8,
+ EOpConvIntToInt16,
+ EOpConvIntToInt64,
+ EOpConvIntToUint8,
+ EOpConvIntToUint16,
+ EOpConvIntToUint,
+ EOpConvIntToUint64,
+
+ // uint32_t -> (u)int*
+ EOpConvUintToInt8,
+ EOpConvUintToInt16,
+ EOpConvUintToInt,
+ EOpConvUintToInt64,
+ EOpConvUintToUint8,
+ EOpConvUintToUint16,
+ EOpConvUintToUint64,
+
+ // int32_t -> float*
+ EOpConvIntToFloat16,
+ EOpConvIntToFloat,
+ EOpConvIntToDouble,
+
+ // uint32_t -> float*
+ EOpConvUintToFloat16,
+ EOpConvUintToFloat,
+ EOpConvUintToDouble,
+
+ // int64_t -> (u)int*
+ EOpConvInt64ToInt8,
+ EOpConvInt64ToInt16,
+ EOpConvInt64ToInt,
+ EOpConvInt64ToUint8,
+ EOpConvInt64ToUint16,
+ EOpConvInt64ToUint,
+ EOpConvInt64ToUint64,
+
+ // uint64_t -> (u)int*
+ EOpConvUint64ToInt8,
+ EOpConvUint64ToInt16,
+ EOpConvUint64ToInt,
+ EOpConvUint64ToInt64,
+ EOpConvUint64ToUint8,
+ EOpConvUint64ToUint16,
+ EOpConvUint64ToUint,
+
+ // int64_t -> float*
+ EOpConvInt64ToFloat16,
+ EOpConvInt64ToFloat,
+ EOpConvInt64ToDouble,
+
+ // uint64_t -> float*
+ EOpConvUint64ToFloat16,
+ EOpConvUint64ToFloat,
+ EOpConvUint64ToDouble,
+
+ // float16_t -> (u)int*
+ EOpConvFloat16ToInt8,
+ EOpConvFloat16ToInt16,
+ EOpConvFloat16ToInt,
+ EOpConvFloat16ToInt64,
+ EOpConvFloat16ToUint8,
+ EOpConvFloat16ToUint16,
+ EOpConvFloat16ToUint,
+ EOpConvFloat16ToUint64,
+
+ // float16_t -> float*
+ EOpConvFloat16ToFloat,
+ EOpConvFloat16ToDouble,
+
+ // float -> (u)int*
+ EOpConvFloatToInt8,
+ EOpConvFloatToInt16,
+ EOpConvFloatToInt,
+ EOpConvFloatToInt64,
+ EOpConvFloatToUint8,
+ EOpConvFloatToUint16,
+ EOpConvFloatToUint,
+ EOpConvFloatToUint64,
+
+ // float -> float*
+ EOpConvFloatToFloat16,
+ EOpConvFloatToDouble,
+
+ // float64 _t-> (u)int*
+ EOpConvDoubleToInt8,
+ EOpConvDoubleToInt16,
+ EOpConvDoubleToInt,
+ EOpConvDoubleToInt64,
+ EOpConvDoubleToUint8,
+ EOpConvDoubleToUint16,
+ EOpConvDoubleToUint,
+ EOpConvDoubleToUint64,
+
+ // float64_t -> float*
+ EOpConvDoubleToFloat16,
+ EOpConvDoubleToFloat,
+
+ // uint64_t <-> pointer
+ EOpConvUint64ToPtr,
+ EOpConvPtrToUint64,
+
+ //
+ // binary operations
+ //
+
+ EOpAdd,
+ EOpSub,
+ EOpMul,
+ EOpDiv,
+ EOpMod,
+ EOpRightShift,
+ EOpLeftShift,
+ EOpAnd,
+ EOpInclusiveOr,
+ EOpExclusiveOr,
+ EOpEqual,
+ EOpNotEqual,
+ EOpVectorEqual,
+ EOpVectorNotEqual,
+ EOpLessThan,
+ EOpGreaterThan,
+ EOpLessThanEqual,
+ EOpGreaterThanEqual,
+ EOpComma,
+
+ EOpVectorTimesScalar,
+ EOpVectorTimesMatrix,
+ EOpMatrixTimesVector,
+ EOpMatrixTimesScalar,
+
+ EOpLogicalOr,
+ EOpLogicalXor,
+ EOpLogicalAnd,
+
+ EOpIndexDirect,
+ EOpIndexIndirect,
+ EOpIndexDirectStruct,
+
+ EOpVectorSwizzle,
+
+ EOpMethod,
+ EOpScoping,
+
+ //
+ // Built-in functions mapped to operators
+ //
+
+ EOpRadians,
+ EOpDegrees,
+ EOpSin,
+ EOpCos,
+ EOpTan,
+ EOpAsin,
+ EOpAcos,
+ EOpAtan,
+ EOpSinh,
+ EOpCosh,
+ EOpTanh,
+ EOpAsinh,
+ EOpAcosh,
+ EOpAtanh,
+
+ EOpPow,
+ EOpExp,
+ EOpLog,
+ EOpExp2,
+ EOpLog2,
+ EOpSqrt,
+ EOpInverseSqrt,
+
+ EOpAbs,
+ EOpSign,
+ EOpFloor,
+ EOpTrunc,
+ EOpRound,
+ EOpRoundEven,
+ EOpCeil,
+ EOpFract,
+ EOpModf,
+ EOpMin,
+ EOpMax,
+ EOpClamp,
+ EOpMix,
+ EOpStep,
+ EOpSmoothStep,
+
+ EOpIsNan,
+ EOpIsInf,
+
+ EOpFma,
+
+ EOpFrexp,
+ EOpLdexp,
+
+ EOpFloatBitsToInt,
+ EOpFloatBitsToUint,
+ EOpIntBitsToFloat,
+ EOpUintBitsToFloat,
+ EOpDoubleBitsToInt64,
+ EOpDoubleBitsToUint64,
+ EOpInt64BitsToDouble,
+ EOpUint64BitsToDouble,
+ EOpFloat16BitsToInt16,
+ EOpFloat16BitsToUint16,
+ EOpInt16BitsToFloat16,
+ EOpUint16BitsToFloat16,
+ EOpPackSnorm2x16,
+ EOpUnpackSnorm2x16,
+ EOpPackUnorm2x16,
+ EOpUnpackUnorm2x16,
+ EOpPackSnorm4x8,
+ EOpUnpackSnorm4x8,
+ EOpPackUnorm4x8,
+ EOpUnpackUnorm4x8,
+ EOpPackHalf2x16,
+ EOpUnpackHalf2x16,
+ EOpPackDouble2x32,
+ EOpUnpackDouble2x32,
+ EOpPackInt2x32,
+ EOpUnpackInt2x32,
+ EOpPackUint2x32,
+ EOpUnpackUint2x32,
+ EOpPackFloat2x16,
+ EOpUnpackFloat2x16,
+ EOpPackInt2x16,
+ EOpUnpackInt2x16,
+ EOpPackUint2x16,
+ EOpUnpackUint2x16,
+ EOpPackInt4x16,
+ EOpUnpackInt4x16,
+ EOpPackUint4x16,
+ EOpUnpackUint4x16,
+ EOpPack16,
+ EOpPack32,
+ EOpPack64,
+ EOpUnpack32,
+ EOpUnpack16,
+ EOpUnpack8,
+
+ EOpLength,
+ EOpDistance,
+ EOpDot,
+ EOpCross,
+ EOpNormalize,
+ EOpFaceForward,
+ EOpReflect,
+ EOpRefract,
+
+#ifdef AMD_EXTENSIONS
+ EOpMin3,
+ EOpMax3,
+ EOpMid3,
+#endif
+
+ EOpDPdx, // Fragment only
+ EOpDPdy, // Fragment only
+ EOpFwidth, // Fragment only
+ EOpDPdxFine, // Fragment only
+ EOpDPdyFine, // Fragment only
+ EOpFwidthFine, // Fragment only
+ EOpDPdxCoarse, // Fragment only
+ EOpDPdyCoarse, // Fragment only
+ EOpFwidthCoarse, // Fragment only
+
+ EOpInterpolateAtCentroid, // Fragment only
+ EOpInterpolateAtSample, // Fragment only
+ EOpInterpolateAtOffset, // Fragment only
+
+#ifdef AMD_EXTENSIONS
+ EOpInterpolateAtVertex,
+#endif
+
+ EOpMatrixTimesMatrix,
+ EOpOuterProduct,
+ EOpDeterminant,
+ EOpMatrixInverse,
+ EOpTranspose,
+
+ EOpFtransform,
+
+ EOpNoise,
+
+ EOpEmitVertex, // geometry only
+ EOpEndPrimitive, // geometry only
+ EOpEmitStreamVertex, // geometry only
+ EOpEndStreamPrimitive, // geometry only
+
+ EOpBarrier,
+ EOpMemoryBarrier,
+ EOpMemoryBarrierAtomicCounter,
+ EOpMemoryBarrierBuffer,
+ EOpMemoryBarrierImage,
+ EOpMemoryBarrierShared, // compute only
+ EOpGroupMemoryBarrier, // compute only
+
+ EOpBallot,
+ EOpReadInvocation,
+ EOpReadFirstInvocation,
+
+ EOpAnyInvocation,
+ EOpAllInvocations,
+ EOpAllInvocationsEqual,
+
+ EOpSubgroupGuardStart,
+ EOpSubgroupBarrier,
+ EOpSubgroupMemoryBarrier,
+ EOpSubgroupMemoryBarrierBuffer,
+ EOpSubgroupMemoryBarrierImage,
+ EOpSubgroupMemoryBarrierShared, // compute only
+ EOpSubgroupElect,
+ EOpSubgroupAll,
+ EOpSubgroupAny,
+ EOpSubgroupAllEqual,
+ EOpSubgroupBroadcast,
+ EOpSubgroupBroadcastFirst,
+ EOpSubgroupBallot,
+ EOpSubgroupInverseBallot,
+ EOpSubgroupBallotBitExtract,
+ EOpSubgroupBallotBitCount,
+ EOpSubgroupBallotInclusiveBitCount,
+ EOpSubgroupBallotExclusiveBitCount,
+ EOpSubgroupBallotFindLSB,
+ EOpSubgroupBallotFindMSB,
+ EOpSubgroupShuffle,
+ EOpSubgroupShuffleXor,
+ EOpSubgroupShuffleUp,
+ EOpSubgroupShuffleDown,
+ EOpSubgroupAdd,
+ EOpSubgroupMul,
+ EOpSubgroupMin,
+ EOpSubgroupMax,
+ EOpSubgroupAnd,
+ EOpSubgroupOr,
+ EOpSubgroupXor,
+ EOpSubgroupInclusiveAdd,
+ EOpSubgroupInclusiveMul,
+ EOpSubgroupInclusiveMin,
+ EOpSubgroupInclusiveMax,
+ EOpSubgroupInclusiveAnd,
+ EOpSubgroupInclusiveOr,
+ EOpSubgroupInclusiveXor,
+ EOpSubgroupExclusiveAdd,
+ EOpSubgroupExclusiveMul,
+ EOpSubgroupExclusiveMin,
+ EOpSubgroupExclusiveMax,
+ EOpSubgroupExclusiveAnd,
+ EOpSubgroupExclusiveOr,
+ EOpSubgroupExclusiveXor,
+ EOpSubgroupClusteredAdd,
+ EOpSubgroupClusteredMul,
+ EOpSubgroupClusteredMin,
+ EOpSubgroupClusteredMax,
+ EOpSubgroupClusteredAnd,
+ EOpSubgroupClusteredOr,
+ EOpSubgroupClusteredXor,
+ EOpSubgroupQuadBroadcast,
+ EOpSubgroupQuadSwapHorizontal,
+ EOpSubgroupQuadSwapVertical,
+ EOpSubgroupQuadSwapDiagonal,
+
+#ifdef NV_EXTENSIONS
+ EOpSubgroupPartition,
+ EOpSubgroupPartitionedAdd,
+ EOpSubgroupPartitionedMul,
+ EOpSubgroupPartitionedMin,
+ EOpSubgroupPartitionedMax,
+ EOpSubgroupPartitionedAnd,
+ EOpSubgroupPartitionedOr,
+ EOpSubgroupPartitionedXor,
+ EOpSubgroupPartitionedInclusiveAdd,
+ EOpSubgroupPartitionedInclusiveMul,
+ EOpSubgroupPartitionedInclusiveMin,
+ EOpSubgroupPartitionedInclusiveMax,
+ EOpSubgroupPartitionedInclusiveAnd,
+ EOpSubgroupPartitionedInclusiveOr,
+ EOpSubgroupPartitionedInclusiveXor,
+ EOpSubgroupPartitionedExclusiveAdd,
+ EOpSubgroupPartitionedExclusiveMul,
+ EOpSubgroupPartitionedExclusiveMin,
+ EOpSubgroupPartitionedExclusiveMax,
+ EOpSubgroupPartitionedExclusiveAnd,
+ EOpSubgroupPartitionedExclusiveOr,
+ EOpSubgroupPartitionedExclusiveXor,
+#endif
+
+ EOpSubgroupGuardStop,
+
+#ifdef AMD_EXTENSIONS
+ EOpMinInvocations,
+ EOpMaxInvocations,
+ EOpAddInvocations,
+ EOpMinInvocationsNonUniform,
+ EOpMaxInvocationsNonUniform,
+ EOpAddInvocationsNonUniform,
+ EOpMinInvocationsInclusiveScan,
+ EOpMaxInvocationsInclusiveScan,
+ EOpAddInvocationsInclusiveScan,
+ EOpMinInvocationsInclusiveScanNonUniform,
+ EOpMaxInvocationsInclusiveScanNonUniform,
+ EOpAddInvocationsInclusiveScanNonUniform,
+ EOpMinInvocationsExclusiveScan,
+ EOpMaxInvocationsExclusiveScan,
+ EOpAddInvocationsExclusiveScan,
+ EOpMinInvocationsExclusiveScanNonUniform,
+ EOpMaxInvocationsExclusiveScanNonUniform,
+ EOpAddInvocationsExclusiveScanNonUniform,
+ EOpSwizzleInvocations,
+ EOpSwizzleInvocationsMasked,
+ EOpWriteInvocation,
+ EOpMbcnt,
+
+ EOpCubeFaceIndex,
+ EOpCubeFaceCoord,
+ EOpTime,
+#endif
+
+ EOpAtomicAdd,
+ EOpAtomicMin,
+ EOpAtomicMax,
+ EOpAtomicAnd,
+ EOpAtomicOr,
+ EOpAtomicXor,
+ EOpAtomicExchange,
+ EOpAtomicCompSwap,
+ EOpAtomicLoad,
+ EOpAtomicStore,
+
+ EOpAtomicCounterIncrement, // results in pre-increment value
+ EOpAtomicCounterDecrement, // results in post-decrement value
+ EOpAtomicCounter,
+ EOpAtomicCounterAdd,
+ EOpAtomicCounterSubtract,
+ EOpAtomicCounterMin,
+ EOpAtomicCounterMax,
+ EOpAtomicCounterAnd,
+ EOpAtomicCounterOr,
+ EOpAtomicCounterXor,
+ EOpAtomicCounterExchange,
+ EOpAtomicCounterCompSwap,
+
+ EOpAny,
+ EOpAll,
+
+ EOpCooperativeMatrixLoad,
+ EOpCooperativeMatrixStore,
+ EOpCooperativeMatrixMulAdd,
+
+ //
+ // Branch
+ //
+
+ EOpKill, // Fragment only
+ EOpReturn,
+ EOpBreak,
+ EOpContinue,
+ EOpCase,
+ EOpDefault,
+
+ //
+ // Constructors
+ //
+
+ EOpConstructGuardStart,
+ EOpConstructInt, // these first scalar forms also identify what implicit conversion is needed
+ EOpConstructUint,
+ EOpConstructInt8,
+ EOpConstructUint8,
+ EOpConstructInt16,
+ EOpConstructUint16,
+ EOpConstructInt64,
+ EOpConstructUint64,
+ EOpConstructBool,
+ EOpConstructFloat,
+ EOpConstructDouble,
+ EOpConstructVec2,
+ EOpConstructVec3,
+ EOpConstructVec4,
+ EOpConstructDVec2,
+ EOpConstructDVec3,
+ EOpConstructDVec4,
+ EOpConstructBVec2,
+ EOpConstructBVec3,
+ EOpConstructBVec4,
+ EOpConstructI8Vec2,
+ EOpConstructI8Vec3,
+ EOpConstructI8Vec4,
+ EOpConstructU8Vec2,
+ EOpConstructU8Vec3,
+ EOpConstructU8Vec4,
+ EOpConstructI16Vec2,
+ EOpConstructI16Vec3,
+ EOpConstructI16Vec4,
+ EOpConstructU16Vec2,
+ EOpConstructU16Vec3,
+ EOpConstructU16Vec4,
+ EOpConstructIVec2,
+ EOpConstructIVec3,
+ EOpConstructIVec4,
+ EOpConstructUVec2,
+ EOpConstructUVec3,
+ EOpConstructUVec4,
+ EOpConstructI64Vec2,
+ EOpConstructI64Vec3,
+ EOpConstructI64Vec4,
+ EOpConstructU64Vec2,
+ EOpConstructU64Vec3,
+ EOpConstructU64Vec4,
+ EOpConstructMat2x2,
+ EOpConstructMat2x3,
+ EOpConstructMat2x4,
+ EOpConstructMat3x2,
+ EOpConstructMat3x3,
+ EOpConstructMat3x4,
+ EOpConstructMat4x2,
+ EOpConstructMat4x3,
+ EOpConstructMat4x4,
+ EOpConstructDMat2x2,
+ EOpConstructDMat2x3,
+ EOpConstructDMat2x4,
+ EOpConstructDMat3x2,
+ EOpConstructDMat3x3,
+ EOpConstructDMat3x4,
+ EOpConstructDMat4x2,
+ EOpConstructDMat4x3,
+ EOpConstructDMat4x4,
+ EOpConstructIMat2x2,
+ EOpConstructIMat2x3,
+ EOpConstructIMat2x4,
+ EOpConstructIMat3x2,
+ EOpConstructIMat3x3,
+ EOpConstructIMat3x4,
+ EOpConstructIMat4x2,
+ EOpConstructIMat4x3,
+ EOpConstructIMat4x4,
+ EOpConstructUMat2x2,
+ EOpConstructUMat2x3,
+ EOpConstructUMat2x4,
+ EOpConstructUMat3x2,
+ EOpConstructUMat3x3,
+ EOpConstructUMat3x4,
+ EOpConstructUMat4x2,
+ EOpConstructUMat4x3,
+ EOpConstructUMat4x4,
+ EOpConstructBMat2x2,
+ EOpConstructBMat2x3,
+ EOpConstructBMat2x4,
+ EOpConstructBMat3x2,
+ EOpConstructBMat3x3,
+ EOpConstructBMat3x4,
+ EOpConstructBMat4x2,
+ EOpConstructBMat4x3,
+ EOpConstructBMat4x4,
+ EOpConstructFloat16,
+ EOpConstructF16Vec2,
+ EOpConstructF16Vec3,
+ EOpConstructF16Vec4,
+ EOpConstructF16Mat2x2,
+ EOpConstructF16Mat2x3,
+ EOpConstructF16Mat2x4,
+ EOpConstructF16Mat3x2,
+ EOpConstructF16Mat3x3,
+ EOpConstructF16Mat3x4,
+ EOpConstructF16Mat4x2,
+ EOpConstructF16Mat4x3,
+ EOpConstructF16Mat4x4,
+ EOpConstructStruct,
+ EOpConstructTextureSampler,
+ EOpConstructNonuniform, // expected to be transformed away, not present in final AST
+ EOpConstructReference,
+ EOpConstructCooperativeMatrix,
+ EOpConstructGuardEnd,
+
+ //
+ // moves
+ //
+
+ EOpAssign,
+ EOpAddAssign,
+ EOpSubAssign,
+ EOpMulAssign,
+ EOpVectorTimesMatrixAssign,
+ EOpVectorTimesScalarAssign,
+ EOpMatrixTimesScalarAssign,
+ EOpMatrixTimesMatrixAssign,
+ EOpDivAssign,
+ EOpModAssign,
+ EOpAndAssign,
+ EOpInclusiveOrAssign,
+ EOpExclusiveOrAssign,
+ EOpLeftShiftAssign,
+ EOpRightShiftAssign,
+
+ //
+ // Array operators
+ //
+
+ // Can apply to arrays, vectors, or matrices.
+ // Can be decomposed to a constant at compile time, but this does not always happen,
+ // due to link-time effects. So, consumer can expect either a link-time sized or
+ // run-time sized array.
+ EOpArrayLength,
+
+ //
+ // Image operations
+ //
+
+ EOpImageGuardBegin,
+
+ EOpImageQuerySize,
+ EOpImageQuerySamples,
+ EOpImageLoad,
+ EOpImageStore,
+#ifdef AMD_EXTENSIONS
+ EOpImageLoadLod,
+ EOpImageStoreLod,
+#endif
+ EOpImageAtomicAdd,
+ EOpImageAtomicMin,
+ EOpImageAtomicMax,
+ EOpImageAtomicAnd,
+ EOpImageAtomicOr,
+ EOpImageAtomicXor,
+ EOpImageAtomicExchange,
+ EOpImageAtomicCompSwap,
+ EOpImageAtomicLoad,
+ EOpImageAtomicStore,
+
+ EOpSubpassLoad,
+ EOpSubpassLoadMS,
+ EOpSparseImageLoad,
+#ifdef AMD_EXTENSIONS
+ EOpSparseImageLoadLod,
+#endif
+
+ EOpImageGuardEnd,
+
+ //
+ // Texture operations
+ //
+
+ EOpTextureGuardBegin,
+
+ EOpTextureQuerySize,
+ EOpTextureQueryLod,
+ EOpTextureQueryLevels,
+ EOpTextureQuerySamples,
+
+ EOpSamplingGuardBegin,
+
+ EOpTexture,
+ EOpTextureProj,
+ EOpTextureLod,
+ EOpTextureOffset,
+ EOpTextureFetch,
+ EOpTextureFetchOffset,
+ EOpTextureProjOffset,
+ EOpTextureLodOffset,
+ EOpTextureProjLod,
+ EOpTextureProjLodOffset,
+ EOpTextureGrad,
+ EOpTextureGradOffset,
+ EOpTextureProjGrad,
+ EOpTextureProjGradOffset,
+ EOpTextureGather,
+ EOpTextureGatherOffset,
+ EOpTextureGatherOffsets,
+ EOpTextureClamp,
+ EOpTextureOffsetClamp,
+ EOpTextureGradClamp,
+ EOpTextureGradOffsetClamp,
+#ifdef AMD_EXTENSIONS
+ EOpTextureGatherLod,
+ EOpTextureGatherLodOffset,
+ EOpTextureGatherLodOffsets,
+ EOpFragmentMaskFetch,
+ EOpFragmentFetch,
+#endif
+
+ EOpSparseTextureGuardBegin,
+
+ EOpSparseTexture,
+ EOpSparseTextureLod,
+ EOpSparseTextureOffset,
+ EOpSparseTextureFetch,
+ EOpSparseTextureFetchOffset,
+ EOpSparseTextureLodOffset,
+ EOpSparseTextureGrad,
+ EOpSparseTextureGradOffset,
+ EOpSparseTextureGather,
+ EOpSparseTextureGatherOffset,
+ EOpSparseTextureGatherOffsets,
+ EOpSparseTexelsResident,
+ EOpSparseTextureClamp,
+ EOpSparseTextureOffsetClamp,
+ EOpSparseTextureGradClamp,
+ EOpSparseTextureGradOffsetClamp,
+#ifdef AMD_EXTENSIONS
+ EOpSparseTextureGatherLod,
+ EOpSparseTextureGatherLodOffset,
+ EOpSparseTextureGatherLodOffsets,
+#endif
+
+ EOpSparseTextureGuardEnd,
+
+#ifdef NV_EXTENSIONS
+ EOpImageFootprintGuardBegin,
+ EOpImageSampleFootprintNV,
+ EOpImageSampleFootprintClampNV,
+ EOpImageSampleFootprintLodNV,
+ EOpImageSampleFootprintGradNV,
+ EOpImageSampleFootprintGradClampNV,
+ EOpImageFootprintGuardEnd,
+#endif
+ EOpSamplingGuardEnd,
+ EOpTextureGuardEnd,
+
+ //
+ // Integer operations
+ //
+
+ EOpAddCarry,
+ EOpSubBorrow,
+ EOpUMulExtended,
+ EOpIMulExtended,
+ EOpBitfieldExtract,
+ EOpBitfieldInsert,
+ EOpBitFieldReverse,
+ EOpBitCount,
+ EOpFindLSB,
+ EOpFindMSB,
+
+#ifdef NV_EXTENSIONS
+ EOpTraceNV,
+ EOpReportIntersectionNV,
+ EOpIgnoreIntersectionNV,
+ EOpTerminateRayNV,
+ EOpExecuteCallableNV,
+ EOpWritePackedPrimitiveIndices4x8NV,
+#endif
+ //
+ // HLSL operations
+ //
+
+ EOpClip, // discard if input value < 0
+ EOpIsFinite,
+ EOpLog10, // base 10 log
+ EOpRcp, // 1/x
+ EOpSaturate, // clamp from 0 to 1
+ EOpSinCos, // sin and cos in out parameters
+ EOpGenMul, // mul(x,y) on any of mat/vec/scalars
+ EOpDst, // x = 1, y=src0.y * src1.y, z=src0.z, w=src1.w
+ EOpInterlockedAdd, // atomic ops, but uses [optional] out arg instead of return
+ EOpInterlockedAnd, // ...
+ EOpInterlockedCompareExchange, // ...
+ EOpInterlockedCompareStore, // ...
+ EOpInterlockedExchange, // ...
+ EOpInterlockedMax, // ...
+ EOpInterlockedMin, // ...
+ EOpInterlockedOr, // ...
+ EOpInterlockedXor, // ...
+ EOpAllMemoryBarrierWithGroupSync, // memory barriers without non-hlsl AST equivalents
+ EOpDeviceMemoryBarrier, // ...
+ EOpDeviceMemoryBarrierWithGroupSync, // ...
+ EOpWorkgroupMemoryBarrier, // ...
+ EOpWorkgroupMemoryBarrierWithGroupSync, // ...
+ EOpEvaluateAttributeSnapped, // InterpolateAtOffset with int position on 16x16 grid
+ EOpF32tof16, // HLSL conversion: half of a PackHalf2x16
+ EOpF16tof32, // HLSL conversion: half of an UnpackHalf2x16
+ EOpLit, // HLSL lighting coefficient vector
+ EOpTextureBias, // HLSL texture bias: will be lowered to EOpTexture
+ EOpAsDouble, // slightly different from EOpUint64BitsToDouble
+ EOpD3DCOLORtoUBYTE4, // convert and swizzle 4-component color to UBYTE4 range
+
+ EOpMethodSample, // Texture object methods. These are translated to existing
+ EOpMethodSampleBias, // AST methods, and exist to represent HLSL semantics until that
+ EOpMethodSampleCmp, // translation is performed. See HlslParseContext::decomposeSampleMethods().
+ EOpMethodSampleCmpLevelZero, // ...
+ EOpMethodSampleGrad, // ...
+ EOpMethodSampleLevel, // ...
+ EOpMethodLoad, // ...
+ EOpMethodGetDimensions, // ...
+ EOpMethodGetSamplePosition, // ...
+ EOpMethodGather, // ...
+ EOpMethodCalculateLevelOfDetail, // ...
+ EOpMethodCalculateLevelOfDetailUnclamped, // ...
+
+ // Load already defined above for textures
+ EOpMethodLoad2, // Structure buffer object methods. These are translated to existing
+ EOpMethodLoad3, // AST methods, and exist to represent HLSL semantics until that
+ EOpMethodLoad4, // translation is performed. See HlslParseContext::decomposeSampleMethods().
+ EOpMethodStore, // ...
+ EOpMethodStore2, // ...
+ EOpMethodStore3, // ...
+ EOpMethodStore4, // ...
+ EOpMethodIncrementCounter, // ...
+ EOpMethodDecrementCounter, // ...
+ // EOpMethodAppend is defined for geo shaders below
+ EOpMethodConsume,
+
+ // SM5 texture methods
+ EOpMethodGatherRed, // These are covered under the above EOpMethodSample comment about
+ EOpMethodGatherGreen, // translation to existing AST opcodes. They exist temporarily
+ EOpMethodGatherBlue, // because HLSL arguments are slightly different.
+ EOpMethodGatherAlpha, // ...
+ EOpMethodGatherCmp, // ...
+ EOpMethodGatherCmpRed, // ...
+ EOpMethodGatherCmpGreen, // ...
+ EOpMethodGatherCmpBlue, // ...
+ EOpMethodGatherCmpAlpha, // ...
+
+ // geometry methods
+ EOpMethodAppend, // Geometry shader methods
+ EOpMethodRestartStrip, // ...
+
+ // matrix
+ EOpMatrixSwizzle, // select multiple matrix components (non-column)
+
+ // SM6 wave ops
+ EOpWaveGetLaneCount, // Will decompose to gl_SubgroupSize.
+ EOpWaveGetLaneIndex, // Will decompose to gl_SubgroupInvocationID.
+ EOpWaveActiveCountBits, // Will decompose to subgroupBallotBitCount(subgroupBallot()).
+ EOpWavePrefixCountBits, // Will decompose to subgroupBallotInclusiveBitCount(subgroupBallot()).
+};
+
+class TIntermTraverser;
+class TIntermOperator;
+class TIntermAggregate;
+class TIntermUnary;
+class TIntermBinary;
+class TIntermConstantUnion;
+class TIntermSelection;
+class TIntermSwitch;
+class TIntermBranch;
+class TIntermTyped;
+class TIntermMethod;
+class TIntermSymbol;
+class TIntermLoop;
+
+} // end namespace glslang
+
+//
+// Base class for the tree nodes
+//
+// (Put outside the glslang namespace, as it's used as part of the external interface.)
+//
+class TIntermNode {
+public:
+ POOL_ALLOCATOR_NEW_DELETE(glslang::GetThreadPoolAllocator())
+
+ TIntermNode() { loc.init(); }
+ virtual const glslang::TSourceLoc& getLoc() const { return loc; }
+ virtual void setLoc(const glslang::TSourceLoc& l) { loc = l; }
+ virtual void traverse(glslang::TIntermTraverser*) = 0;
+ virtual glslang::TIntermTyped* getAsTyped() { return 0; }
+ virtual glslang::TIntermOperator* getAsOperator() { return 0; }
+ virtual glslang::TIntermConstantUnion* getAsConstantUnion() { return 0; }
+ virtual glslang::TIntermAggregate* getAsAggregate() { return 0; }
+ virtual glslang::TIntermUnary* getAsUnaryNode() { return 0; }
+ virtual glslang::TIntermBinary* getAsBinaryNode() { return 0; }
+ virtual glslang::TIntermSelection* getAsSelectionNode() { return 0; }
+ virtual glslang::TIntermSwitch* getAsSwitchNode() { return 0; }
+ virtual glslang::TIntermMethod* getAsMethodNode() { return 0; }
+ virtual glslang::TIntermSymbol* getAsSymbolNode() { return 0; }
+ virtual glslang::TIntermBranch* getAsBranchNode() { return 0; }
+ virtual glslang::TIntermLoop* getAsLoopNode() { return 0; }
+
+ virtual const glslang::TIntermTyped* getAsTyped() const { return 0; }
+ virtual const glslang::TIntermOperator* getAsOperator() const { return 0; }
+ virtual const glslang::TIntermConstantUnion* getAsConstantUnion() const { return 0; }
+ virtual const glslang::TIntermAggregate* getAsAggregate() const { return 0; }
+ virtual const glslang::TIntermUnary* getAsUnaryNode() const { return 0; }
+ virtual const glslang::TIntermBinary* getAsBinaryNode() const { return 0; }
+ virtual const glslang::TIntermSelection* getAsSelectionNode() const { return 0; }
+ virtual const glslang::TIntermSwitch* getAsSwitchNode() const { return 0; }
+ virtual const glslang::TIntermMethod* getAsMethodNode() const { return 0; }
+ virtual const glslang::TIntermSymbol* getAsSymbolNode() const { return 0; }
+ virtual const glslang::TIntermBranch* getAsBranchNode() const { return 0; }
+ virtual const glslang::TIntermLoop* getAsLoopNode() const { return 0; }
+ virtual ~TIntermNode() { }
+
+protected:
+ TIntermNode(const TIntermNode&);
+ TIntermNode& operator=(const TIntermNode&);
+ glslang::TSourceLoc loc;
+};
+
+namespace glslang {
+
+//
+// This is just to help yacc.
+//
+struct TIntermNodePair {
+ TIntermNode* node1;
+ TIntermNode* node2;
+};
+
+//
+// Intermediate class for nodes that have a type.
+//
+class TIntermTyped : public TIntermNode {
+public:
+ TIntermTyped(const TType& t) { type.shallowCopy(t); }
+ TIntermTyped(TBasicType basicType) { TType bt(basicType); type.shallowCopy(bt); }
+ virtual TIntermTyped* getAsTyped() { return this; }
+ virtual const TIntermTyped* getAsTyped() const { return this; }
+ virtual void setType(const TType& t) { type.shallowCopy(t); }
+ virtual const TType& getType() const { return type; }
+ virtual TType& getWritableType() { return type; }
+
+ virtual TBasicType getBasicType() const { return type.getBasicType(); }
+ virtual TQualifier& getQualifier() { return type.getQualifier(); }
+ virtual const TQualifier& getQualifier() const { return type.getQualifier(); }
+ virtual void propagatePrecision(TPrecisionQualifier);
+ virtual int getVectorSize() const { return type.getVectorSize(); }
+ virtual int getMatrixCols() const { return type.getMatrixCols(); }
+ virtual int getMatrixRows() const { return type.getMatrixRows(); }
+ virtual bool isMatrix() const { return type.isMatrix(); }
+ virtual bool isArray() const { return type.isArray(); }
+ virtual bool isVector() const { return type.isVector(); }
+ virtual bool isScalar() const { return type.isScalar(); }
+ virtual bool isStruct() const { return type.isStruct(); }
+ virtual bool isFloatingDomain() const { return type.isFloatingDomain(); }
+ virtual bool isIntegerDomain() const { return type.isIntegerDomain(); }
+ TString getCompleteString() const { return type.getCompleteString(); }
+
+protected:
+ TIntermTyped& operator=(const TIntermTyped&);
+ TType type;
+};
+
+//
+// Handle for, do-while, and while loops.
+//
+class TIntermLoop : public TIntermNode {
+public:
+ TIntermLoop(TIntermNode* aBody, TIntermTyped* aTest, TIntermTyped* aTerminal, bool testFirst) :
+ body(aBody),
+ test(aTest),
+ terminal(aTerminal),
+ first(testFirst),
+ unroll(false),
+ dontUnroll(false),
+ dependency(0)
+ { }
+
+ virtual TIntermLoop* getAsLoopNode() { return this; }
+ virtual const TIntermLoop* getAsLoopNode() const { return this; }
+ virtual void traverse(TIntermTraverser*);
+ TIntermNode* getBody() const { return body; }
+ TIntermTyped* getTest() const { return test; }
+ TIntermTyped* getTerminal() const { return terminal; }
+ bool testFirst() const { return first; }
+
+ void setUnroll() { unroll = true; }
+ void setDontUnroll() { dontUnroll = true; }
+ bool getUnroll() const { return unroll; }
+ bool getDontUnroll() const { return dontUnroll; }
+
+ static const unsigned int dependencyInfinite = 0xFFFFFFFF;
+ void setLoopDependency(int d) { dependency = d; }
+ int getLoopDependency() const { return dependency; }
+
+protected:
+ TIntermNode* body; // code to loop over
+ TIntermTyped* test; // exit condition associated with loop, could be 0 for 'for' loops
+ TIntermTyped* terminal; // exists for for-loops
+ bool first; // true for while and for, not for do-while
+ bool unroll; // true if unroll requested
+ bool dontUnroll; // true if request to not unroll
+ unsigned int dependency; // loop dependency hint; 0 means not set or unknown
+};
+
+//
+// Handle case, break, continue, return, and kill.
+//
+class TIntermBranch : public TIntermNode {
+public:
+ TIntermBranch(TOperator op, TIntermTyped* e) :
+ flowOp(op),
+ expression(e) { }
+ virtual TIntermBranch* getAsBranchNode() { return this; }
+ virtual const TIntermBranch* getAsBranchNode() const { return this; }
+ virtual void traverse(TIntermTraverser*);
+ TOperator getFlowOp() const { return flowOp; }
+ TIntermTyped* getExpression() const { return expression; }
+protected:
+ TOperator flowOp;
+ TIntermTyped* expression;
+};
+
+//
+// Represent method names before seeing their calling signature
+// or resolving them to operations. Just an expression as the base object
+// and a textural name.
+//
+class TIntermMethod : public TIntermTyped {
+public:
+ TIntermMethod(TIntermTyped* o, const TType& t, const TString& m) : TIntermTyped(t), object(o), method(m) { }
+ virtual TIntermMethod* getAsMethodNode() { return this; }
+ virtual const TIntermMethod* getAsMethodNode() const { return this; }
+ virtual const TString& getMethodName() const { return method; }
+ virtual TIntermTyped* getObject() const { return object; }
+ virtual void traverse(TIntermTraverser*);
+protected:
+ TIntermTyped* object;
+ TString method;
+};
+
+//
+// Nodes that correspond to symbols or constants in the source code.
+//
+class TIntermSymbol : public TIntermTyped {
+public:
+ // if symbol is initialized as symbol(sym), the memory comes from the pool allocator of sym. If sym comes from
+ // per process threadPoolAllocator, then it causes increased memory usage per compile
+ // it is essential to use "symbol = sym" to assign to symbol
+ TIntermSymbol(int i, const TString& n, const TType& t)
+ : TIntermTyped(t), id(i),
+#ifdef ENABLE_HLSL
+ flattenSubset(-1),
+#endif
+ constSubtree(nullptr)
+ { name = n; }
+ virtual int getId() const { return id; }
+ virtual void changeId(int i) { id = i; }
+ virtual const TString& getName() const { return name; }
+ virtual void traverse(TIntermTraverser*);
+ virtual TIntermSymbol* getAsSymbolNode() { return this; }
+ virtual const TIntermSymbol* getAsSymbolNode() const { return this; }
+ void setConstArray(const TConstUnionArray& c) { constArray = c; }
+ const TConstUnionArray& getConstArray() const { return constArray; }
+ void setConstSubtree(TIntermTyped* subtree) { constSubtree = subtree; }
+ TIntermTyped* getConstSubtree() const { return constSubtree; }
+#ifdef ENABLE_HLSL
+ void setFlattenSubset(int subset) { flattenSubset = subset; }
+ int getFlattenSubset() const { return flattenSubset; } // -1 means full object
+#endif
+
+ // This is meant for cases where a node has already been constructed, and
+ // later on, it becomes necessary to switch to a different symbol.
+ virtual void switchId(int newId) { id = newId; }
+
+protected:
+ int id; // the unique id of the symbol this node represents
+#ifdef ENABLE_HLSL
+ int flattenSubset; // how deeply the flattened object rooted at id has been dereferenced
+#endif
+ TString name; // the name of the symbol this node represents
+ TConstUnionArray constArray; // if the symbol is a front-end compile-time constant, this is its value
+ TIntermTyped* constSubtree;
+};
+
+class TIntermConstantUnion : public TIntermTyped {
+public:
+ TIntermConstantUnion(const TConstUnionArray& ua, const TType& t) : TIntermTyped(t), constArray(ua), literal(false) { }
+ const TConstUnionArray& getConstArray() const { return constArray; }
+ virtual TIntermConstantUnion* getAsConstantUnion() { return this; }
+ virtual const TIntermConstantUnion* getAsConstantUnion() const { return this; }
+ virtual void traverse(TIntermTraverser*);
+ virtual TIntermTyped* fold(TOperator, const TIntermTyped*) const;
+ virtual TIntermTyped* fold(TOperator, const TType&) const;
+ void setLiteral() { literal = true; }
+ void setExpression() { literal = false; }
+ bool isLiteral() const { return literal; }
+
+protected:
+ TIntermConstantUnion& operator=(const TIntermConstantUnion&);
+
+ const TConstUnionArray constArray;
+ bool literal; // true if node represents a literal in the source code
+};
+
+// Represent the independent aspects of a texturing TOperator
+struct TCrackedTextureOp {
+ bool query;
+ bool proj;
+ bool lod;
+ bool fetch;
+ bool offset;
+ bool offsets;
+ bool gather;
+ bool grad;
+ bool subpass;
+ bool lodClamp;
+#ifdef AMD_EXTENSIONS
+ bool fragMask;
+#endif
+};
+
+//
+// Intermediate class for node types that hold operators.
+//
+class TIntermOperator : public TIntermTyped {
+public:
+ virtual TIntermOperator* getAsOperator() { return this; }
+ virtual const TIntermOperator* getAsOperator() const { return this; }
+ TOperator getOp() const { return op; }
+ void setOp(TOperator newOp) { op = newOp; }
+ bool modifiesState() const;
+ bool isConstructor() const;
+ bool isTexture() const { return op > EOpTextureGuardBegin && op < EOpTextureGuardEnd; }
+ bool isSampling() const { return op > EOpSamplingGuardBegin && op < EOpSamplingGuardEnd; }
+ bool isImage() const { return op > EOpImageGuardBegin && op < EOpImageGuardEnd; }
+ bool isSparseTexture() const { return op > EOpSparseTextureGuardBegin && op < EOpSparseTextureGuardEnd; }
+#ifdef NV_EXTENSIONS
+ bool isImageFootprint() const { return op > EOpImageFootprintGuardBegin && op < EOpImageFootprintGuardEnd; }
+#endif
+ bool isSparseImage() const { return op == EOpSparseImageLoad; }
+
+ void setOperationPrecision(TPrecisionQualifier p) { operationPrecision = p; }
+ TPrecisionQualifier getOperationPrecision() const { return operationPrecision != EpqNone ?
+ operationPrecision :
+ type.getQualifier().precision; }
+ TString getCompleteString() const
+ {
+ TString cs = type.getCompleteString();
+ if (getOperationPrecision() != type.getQualifier().precision) {
+ cs += ", operation at ";
+ cs += GetPrecisionQualifierString(getOperationPrecision());
+ }
+
+ return cs;
+ }
+
+ // Crack the op into the individual dimensions of texturing operation.
+ void crackTexture(TSampler sampler, TCrackedTextureOp& cracked) const
+ {
+ cracked.query = false;
+ cracked.proj = false;
+ cracked.lod = false;
+ cracked.fetch = false;
+ cracked.offset = false;
+ cracked.offsets = false;
+ cracked.gather = false;
+ cracked.grad = false;
+ cracked.subpass = false;
+ cracked.lodClamp = false;
+#ifdef AMD_EXTENSIONS
+ cracked.fragMask = false;
+#endif
+
+ switch (op) {
+ case EOpImageQuerySize:
+ case EOpImageQuerySamples:
+ case EOpTextureQuerySize:
+ case EOpTextureQueryLod:
+ case EOpTextureQueryLevels:
+ case EOpTextureQuerySamples:
+ case EOpSparseTexelsResident:
+ cracked.query = true;
+ break;
+ case EOpTexture:
+ case EOpSparseTexture:
+ break;
+ case EOpTextureClamp:
+ case EOpSparseTextureClamp:
+ cracked.lodClamp = true;
+ break;
+ case EOpTextureProj:
+ cracked.proj = true;
+ break;
+ case EOpTextureLod:
+ case EOpSparseTextureLod:
+ cracked.lod = true;
+ break;
+ case EOpTextureOffset:
+ case EOpSparseTextureOffset:
+ cracked.offset = true;
+ break;
+ case EOpTextureOffsetClamp:
+ case EOpSparseTextureOffsetClamp:
+ cracked.offset = true;
+ cracked.lodClamp = true;
+ break;
+ case EOpTextureFetch:
+ case EOpSparseTextureFetch:
+ cracked.fetch = true;
+ if (sampler.dim == Esd1D || (sampler.dim == Esd2D && ! sampler.ms) || sampler.dim == Esd3D)
+ cracked.lod = true;
+ break;
+ case EOpTextureFetchOffset:
+ case EOpSparseTextureFetchOffset:
+ cracked.fetch = true;
+ cracked.offset = true;
+ if (sampler.dim == Esd1D || (sampler.dim == Esd2D && ! sampler.ms) || sampler.dim == Esd3D)
+ cracked.lod = true;
+ break;
+ case EOpTextureProjOffset:
+ cracked.offset = true;
+ cracked.proj = true;
+ break;
+ case EOpTextureLodOffset:
+ case EOpSparseTextureLodOffset:
+ cracked.offset = true;
+ cracked.lod = true;
+ break;
+ case EOpTextureProjLod:
+ cracked.lod = true;
+ cracked.proj = true;
+ break;
+ case EOpTextureProjLodOffset:
+ cracked.offset = true;
+ cracked.lod = true;
+ cracked.proj = true;
+ break;
+ case EOpTextureGrad:
+ case EOpSparseTextureGrad:
+ cracked.grad = true;
+ break;
+ case EOpTextureGradClamp:
+ case EOpSparseTextureGradClamp:
+ cracked.grad = true;
+ cracked.lodClamp = true;
+ break;
+ case EOpTextureGradOffset:
+ case EOpSparseTextureGradOffset:
+ cracked.grad = true;
+ cracked.offset = true;
+ break;
+ case EOpTextureProjGrad:
+ cracked.grad = true;
+ cracked.proj = true;
+ break;
+ case EOpTextureProjGradOffset:
+ cracked.grad = true;
+ cracked.offset = true;
+ cracked.proj = true;
+ break;
+ case EOpTextureGradOffsetClamp:
+ case EOpSparseTextureGradOffsetClamp:
+ cracked.grad = true;
+ cracked.offset = true;
+ cracked.lodClamp = true;
+ break;
+ case EOpTextureGather:
+ case EOpSparseTextureGather:
+ cracked.gather = true;
+ break;
+ case EOpTextureGatherOffset:
+ case EOpSparseTextureGatherOffset:
+ cracked.gather = true;
+ cracked.offset = true;
+ break;
+ case EOpTextureGatherOffsets:
+ case EOpSparseTextureGatherOffsets:
+ cracked.gather = true;
+ cracked.offsets = true;
+ break;
+#ifdef AMD_EXTENSIONS
+ case EOpTextureGatherLod:
+ case EOpSparseTextureGatherLod:
+ cracked.gather = true;
+ cracked.lod = true;
+ break;
+ case EOpTextureGatherLodOffset:
+ case EOpSparseTextureGatherLodOffset:
+ cracked.gather = true;
+ cracked.offset = true;
+ cracked.lod = true;
+ break;
+ case EOpTextureGatherLodOffsets:
+ case EOpSparseTextureGatherLodOffsets:
+ cracked.gather = true;
+ cracked.offsets = true;
+ cracked.lod = true;
+ break;
+ case EOpImageLoadLod:
+ case EOpImageStoreLod:
+ case EOpSparseImageLoadLod:
+ cracked.lod = true;
+ break;
+ case EOpFragmentMaskFetch:
+ cracked.subpass = sampler.dim == EsdSubpass;
+ cracked.fragMask = true;
+ break;
+ case EOpFragmentFetch:
+ cracked.subpass = sampler.dim == EsdSubpass;
+ cracked.fragMask = true;
+ break;
+#endif
+#ifdef NV_EXTENSIONS
+ case EOpImageSampleFootprintNV:
+ break;
+ case EOpImageSampleFootprintClampNV:
+ cracked.lodClamp = true;
+ break;
+ case EOpImageSampleFootprintLodNV:
+ cracked.lod = true;
+ break;
+ case EOpImageSampleFootprintGradNV:
+ cracked.grad = true;
+ break;
+ case EOpImageSampleFootprintGradClampNV:
+ cracked.lodClamp = true;
+ cracked.grad = true;
+ break;
+#endif
+ case EOpSubpassLoad:
+ case EOpSubpassLoadMS:
+ cracked.subpass = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+protected:
+ TIntermOperator(TOperator o) : TIntermTyped(EbtFloat), op(o), operationPrecision(EpqNone) {}
+ TIntermOperator(TOperator o, TType& t) : TIntermTyped(t), op(o), operationPrecision(EpqNone) {}
+ TOperator op;
+ // The result precision is in the inherited TType, and is usually meant to be both
+ // the operation precision and the result precision. However, some more complex things,
+ // like built-in function calls, distinguish between the two, in which case non-EqpNone
+ // 'operationPrecision' overrides the result precision as far as operation precision
+ // is concerned.
+ TPrecisionQualifier operationPrecision;
+};
+
+//
+// Nodes for all the basic binary math operators.
+//
+class TIntermBinary : public TIntermOperator {
+public:
+ TIntermBinary(TOperator o) : TIntermOperator(o) {}
+ virtual void traverse(TIntermTraverser*);
+ virtual void setLeft(TIntermTyped* n) { left = n; }
+ virtual void setRight(TIntermTyped* n) { right = n; }
+ virtual TIntermTyped* getLeft() const { return left; }
+ virtual TIntermTyped* getRight() const { return right; }
+ virtual TIntermBinary* getAsBinaryNode() { return this; }
+ virtual const TIntermBinary* getAsBinaryNode() const { return this; }
+ virtual void updatePrecision();
+protected:
+ TIntermTyped* left;
+ TIntermTyped* right;
+};
+
+//
+// Nodes for unary math operators.
+//
+class TIntermUnary : public TIntermOperator {
+public:
+ TIntermUnary(TOperator o, TType& t) : TIntermOperator(o, t), operand(0) {}
+ TIntermUnary(TOperator o) : TIntermOperator(o), operand(0) {}
+ virtual void traverse(TIntermTraverser*);
+ virtual void setOperand(TIntermTyped* o) { operand = o; }
+ virtual TIntermTyped* getOperand() { return operand; }
+ virtual const TIntermTyped* getOperand() const { return operand; }
+ virtual TIntermUnary* getAsUnaryNode() { return this; }
+ virtual const TIntermUnary* getAsUnaryNode() const { return this; }
+ virtual void updatePrecision();
+protected:
+ TIntermTyped* operand;
+};
+
+typedef TVector<TIntermNode*> TIntermSequence;
+typedef TVector<TStorageQualifier> TQualifierList;
+//
+// Nodes that operate on an arbitrary sized set of children.
+//
+class TIntermAggregate : public TIntermOperator {
+public:
+ TIntermAggregate() : TIntermOperator(EOpNull), userDefined(false), pragmaTable(nullptr) { }
+ TIntermAggregate(TOperator o) : TIntermOperator(o), pragmaTable(nullptr) { }
+ ~TIntermAggregate() { delete pragmaTable; }
+ virtual TIntermAggregate* getAsAggregate() { return this; }
+ virtual const TIntermAggregate* getAsAggregate() const { return this; }
+ virtual void setOperator(TOperator o) { op = o; }
+ virtual TIntermSequence& getSequence() { return sequence; }
+ virtual const TIntermSequence& getSequence() const { return sequence; }
+ virtual void setName(const TString& n) { name = n; }
+ virtual const TString& getName() const { return name; }
+ virtual void traverse(TIntermTraverser*);
+ virtual void setUserDefined() { userDefined = true; }
+ virtual bool isUserDefined() { return userDefined; }
+ virtual TQualifierList& getQualifierList() { return qualifier; }
+ virtual const TQualifierList& getQualifierList() const { return qualifier; }
+ void setOptimize(bool o) { optimize = o; }
+ void setDebug(bool d) { debug = d; }
+ bool getOptimize() const { return optimize; }
+ bool getDebug() const { return debug; }
+ void setPragmaTable(const TPragmaTable& pTable);
+ const TPragmaTable& getPragmaTable() const { return *pragmaTable; }
+protected:
+ TIntermAggregate(const TIntermAggregate&); // disallow copy constructor
+ TIntermAggregate& operator=(const TIntermAggregate&); // disallow assignment operator
+ TIntermSequence sequence;
+ TQualifierList qualifier;
+ TString name;
+ bool userDefined; // used for user defined function names
+ bool optimize;
+ bool debug;
+ TPragmaTable* pragmaTable;
+};
+
+//
+// For if tests.
+//
+class TIntermSelection : public TIntermTyped {
+public:
+ TIntermSelection(TIntermTyped* cond, TIntermNode* trueB, TIntermNode* falseB) :
+ TIntermTyped(EbtVoid), condition(cond), trueBlock(trueB), falseBlock(falseB),
+ shortCircuit(true),
+ flatten(false), dontFlatten(false) {}
+ TIntermSelection(TIntermTyped* cond, TIntermNode* trueB, TIntermNode* falseB, const TType& type) :
+ TIntermTyped(type), condition(cond), trueBlock(trueB), falseBlock(falseB),
+ shortCircuit(true),
+ flatten(false), dontFlatten(false) {}
+ virtual void traverse(TIntermTraverser*);
+ virtual TIntermTyped* getCondition() const { return condition; }
+ virtual TIntermNode* getTrueBlock() const { return trueBlock; }
+ virtual TIntermNode* getFalseBlock() const { return falseBlock; }
+ virtual TIntermSelection* getAsSelectionNode() { return this; }
+ virtual const TIntermSelection* getAsSelectionNode() const { return this; }
+
+ void setNoShortCircuit() { shortCircuit = false; }
+ bool getShortCircuit() const { return shortCircuit; }
+
+ void setFlatten() { flatten = true; }
+ void setDontFlatten() { dontFlatten = true; }
+ bool getFlatten() const { return flatten; }
+ bool getDontFlatten() const { return dontFlatten; }
+
+protected:
+ TIntermTyped* condition;
+ TIntermNode* trueBlock;
+ TIntermNode* falseBlock;
+ bool shortCircuit; // normally all if-then-else and all GLSL ?: short-circuit, but HLSL ?: does not
+ bool flatten; // true if flatten requested
+ bool dontFlatten; // true if requested to not flatten
+};
+
+//
+// For switch statements. Designed use is that a switch will have sequence of nodes
+// that are either case/default nodes or a *single* node that represents all the code
+// in between (if any) consecutive case/defaults. So, a traversal need only deal with
+// 0 or 1 nodes per case/default statement.
+//
+class TIntermSwitch : public TIntermNode {
+public:
+ TIntermSwitch(TIntermTyped* cond, TIntermAggregate* b) : condition(cond), body(b),
+ flatten(false), dontFlatten(false) {}
+ virtual void traverse(TIntermTraverser*);
+ virtual TIntermNode* getCondition() const { return condition; }
+ virtual TIntermAggregate* getBody() const { return body; }
+ virtual TIntermSwitch* getAsSwitchNode() { return this; }
+ virtual const TIntermSwitch* getAsSwitchNode() const { return this; }
+
+ void setFlatten() { flatten = true; }
+ void setDontFlatten() { dontFlatten = true; }
+ bool getFlatten() const { return flatten; }
+ bool getDontFlatten() const { return dontFlatten; }
+
+protected:
+ TIntermTyped* condition;
+ TIntermAggregate* body;
+ bool flatten; // true if flatten requested
+ bool dontFlatten; // true if requested to not flatten
+};
+
+enum TVisit
+{
+ EvPreVisit,
+ EvInVisit,
+ EvPostVisit
+};
+
+//
+// For traversing the tree. User should derive from this,
+// put their traversal specific data in it, and then pass
+// it to a Traverse method.
+//
+// When using this, just fill in the methods for nodes you want visited.
+// Return false from a pre-visit to skip visiting that node's subtree.
+//
+// Explicitly set postVisit to true if you want post visiting, otherwise,
+// filled in methods will only be called at pre-visit time (before processing
+// the subtree). Similarly for inVisit for in-order visiting of nodes with
+// multiple children.
+//
+// If you only want post-visits, explicitly turn off preVisit (and inVisit)
+// and turn on postVisit.
+//
+// In general, for the visit*() methods, return true from interior nodes
+// to have the traversal continue on to children.
+//
+// If you process children yourself, or don't want them processed, return false.
+//
+class TIntermTraverser {
+public:
+ POOL_ALLOCATOR_NEW_DELETE(glslang::GetThreadPoolAllocator())
+ TIntermTraverser(bool preVisit = true, bool inVisit = false, bool postVisit = false, bool rightToLeft = false) :
+ preVisit(preVisit),
+ inVisit(inVisit),
+ postVisit(postVisit),
+ rightToLeft(rightToLeft),
+ depth(0),
+ maxDepth(0) { }
+ virtual ~TIntermTraverser() { }
+
+ virtual void visitSymbol(TIntermSymbol*) { }
+ virtual void visitConstantUnion(TIntermConstantUnion*) { }
+ virtual bool visitBinary(TVisit, TIntermBinary*) { return true; }
+ virtual bool visitUnary(TVisit, TIntermUnary*) { return true; }
+ virtual bool visitSelection(TVisit, TIntermSelection*) { return true; }
+ virtual bool visitAggregate(TVisit, TIntermAggregate*) { return true; }
+ virtual bool visitLoop(TVisit, TIntermLoop*) { return true; }
+ virtual bool visitBranch(TVisit, TIntermBranch*) { return true; }
+ virtual bool visitSwitch(TVisit, TIntermSwitch*) { return true; }
+
+ int getMaxDepth() const { return maxDepth; }
+
+ void incrementDepth(TIntermNode *current)
+ {
+ depth++;
+ maxDepth = (std::max)(maxDepth, depth);
+ path.push_back(current);
+ }
+
+ void decrementDepth()
+ {
+ depth--;
+ path.pop_back();
+ }
+
+ TIntermNode *getParentNode()
+ {
+ return path.size() == 0 ? NULL : path.back();
+ }
+
+ const bool preVisit;
+ const bool inVisit;
+ const bool postVisit;
+ const bool rightToLeft;
+
+protected:
+ TIntermTraverser& operator=(TIntermTraverser&);
+
+ int depth;
+ int maxDepth;
+
+ // All the nodes from root to the current node's parent during traversing.
+ TVector<TIntermNode *> path;
+};
+
+// KHR_vulkan_glsl says "Two arrays sized with specialization constants are the same type only if
+// sized with the same symbol, involving no operations"
+inline bool SameSpecializationConstants(TIntermTyped* node1, TIntermTyped* node2)
+{
+ return node1->getAsSymbolNode() && node2->getAsSymbolNode() &&
+ node1->getAsSymbolNode()->getId() == node2->getAsSymbolNode()->getId();
+}
+
+} // end namespace glslang
+
+#endif // __INTERMEDIATE_H
diff --git a/src/3rdparty/glslang/glslang/Include/revision.h b/src/3rdparty/glslang/glslang/Include/revision.h
new file mode 100644
index 0000000..f810a33
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/Include/revision.h
@@ -0,0 +1,3 @@
+// This header is generated by the make-revision script.
+
+#define GLSLANG_PATCH_LEVEL 3170
diff --git a/src/3rdparty/glslang/glslang/Include/revision.template b/src/3rdparty/glslang/glslang/Include/revision.template
new file mode 100644
index 0000000..4a16bee
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/Include/revision.template
@@ -0,0 +1,13 @@
+// The file revision.h should be updated to the latest version, somehow, on
+// check-in, if glslang has changed.
+//
+// revision.template is the source for revision.h when using SubWCRev as the
+// method of updating revision.h. You don't have to do it this way, the
+// requirement is only that revision.h gets updated.
+//
+// revision.h is under source control so that not all consumers of glslang
+// source have to figure out how to create revision.h just to get a build
+// going. However, if it is not updated, it can be a version behind.
+
+#define GLSLANG_REVISION "$WCREV$"
+#define GLSLANG_DATE "$WCDATE$"
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/Constant.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/Constant.cpp
new file mode 100644
index 0000000..b75e3ef
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/Constant.cpp
@@ -0,0 +1,1405 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2012-2013 LunarG, Inc.
+// Copyright (C) 2017 ARM Limited.
+// Copyright (C) 2018 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "localintermediate.h"
+#include <cmath>
+#include <cfloat>
+#include <cstdlib>
+#include <climits>
+
+namespace {
+
+using namespace glslang;
+
+typedef union {
+ double d;
+ int i[2];
+} DoubleIntUnion;
+
+// Some helper functions
+
+bool isNan(double x)
+{
+ DoubleIntUnion u;
+ // tough to find a platform independent library function, do it directly
+ u.d = x;
+ int bitPatternL = u.i[0];
+ int bitPatternH = u.i[1];
+ return (bitPatternH & 0x7ff80000) == 0x7ff80000 &&
+ ((bitPatternH & 0xFFFFF) != 0 || bitPatternL != 0);
+}
+
+bool isInf(double x)
+{
+ DoubleIntUnion u;
+ // tough to find a platform independent library function, do it directly
+ u.d = x;
+ int bitPatternL = u.i[0];
+ int bitPatternH = u.i[1];
+ return (bitPatternH & 0x7ff00000) == 0x7ff00000 &&
+ (bitPatternH & 0xFFFFF) == 0 && bitPatternL == 0;
+}
+
+const double pi = 3.1415926535897932384626433832795;
+
+} // end anonymous namespace
+
+
+namespace glslang {
+
+//
+// The fold functions see if an operation on a constant can be done in place,
+// without generating run-time code.
+//
+// Returns the node to keep using, which may or may not be the node passed in.
+//
+// Note: As of version 1.2, all constant operations must be folded. It is
+// not opportunistic, but rather a semantic requirement.
+//
+
+//
+// Do folding between a pair of nodes.
+// 'this' is the left-hand operand and 'rightConstantNode' is the right-hand operand.
+//
+// Returns a new node representing the result.
+//
+TIntermTyped* TIntermConstantUnion::fold(TOperator op, const TIntermTyped* rightConstantNode) const
+{
+ // For most cases, the return type matches the argument type, so set that
+ // up and just code to exceptions below.
+ TType returnType;
+ returnType.shallowCopy(getType());
+
+ //
+ // A pair of nodes is to be folded together
+ //
+
+ const TIntermConstantUnion *rightNode = rightConstantNode->getAsConstantUnion();
+ TConstUnionArray leftUnionArray = getConstArray();
+ TConstUnionArray rightUnionArray = rightNode->getConstArray();
+
+ // Figure out the size of the result
+ int newComps;
+ int constComps;
+ switch(op) {
+ case EOpMatrixTimesMatrix:
+ newComps = rightNode->getMatrixCols() * getMatrixRows();
+ break;
+ case EOpMatrixTimesVector:
+ newComps = getMatrixRows();
+ break;
+ case EOpVectorTimesMatrix:
+ newComps = rightNode->getMatrixCols();
+ break;
+ default:
+ newComps = getType().computeNumComponents();
+ constComps = rightConstantNode->getType().computeNumComponents();
+ if (constComps == 1 && newComps > 1) {
+ // for a case like vec4 f = vec4(2,3,4,5) + 1.2;
+ TConstUnionArray smearedArray(newComps, rightNode->getConstArray()[0]);
+ rightUnionArray = smearedArray;
+ } else if (constComps > 1 && newComps == 1) {
+ // for a case like vec4 f = 1.2 + vec4(2,3,4,5);
+ newComps = constComps;
+ rightUnionArray = rightNode->getConstArray();
+ TConstUnionArray smearedArray(newComps, getConstArray()[0]);
+ leftUnionArray = smearedArray;
+ returnType.shallowCopy(rightNode->getType());
+ }
+ break;
+ }
+
+ TConstUnionArray newConstArray(newComps);
+ TType constBool(EbtBool, EvqConst);
+
+ switch(op) {
+ case EOpAdd:
+ for (int i = 0; i < newComps; i++)
+ newConstArray[i] = leftUnionArray[i] + rightUnionArray[i];
+ break;
+ case EOpSub:
+ for (int i = 0; i < newComps; i++)
+ newConstArray[i] = leftUnionArray[i] - rightUnionArray[i];
+ break;
+
+ case EOpMul:
+ case EOpVectorTimesScalar:
+ case EOpMatrixTimesScalar:
+ for (int i = 0; i < newComps; i++)
+ newConstArray[i] = leftUnionArray[i] * rightUnionArray[i];
+ break;
+ case EOpMatrixTimesMatrix:
+ for (int row = 0; row < getMatrixRows(); row++) {
+ for (int column = 0; column < rightNode->getMatrixCols(); column++) {
+ double sum = 0.0f;
+ for (int i = 0; i < rightNode->getMatrixRows(); i++)
+ sum += leftUnionArray[i * getMatrixRows() + row].getDConst() * rightUnionArray[column * rightNode->getMatrixRows() + i].getDConst();
+ newConstArray[column * getMatrixRows() + row].setDConst(sum);
+ }
+ }
+ returnType.shallowCopy(TType(getType().getBasicType(), EvqConst, 0, rightNode->getMatrixCols(), getMatrixRows()));
+ break;
+ case EOpDiv:
+ for (int i = 0; i < newComps; i++) {
+ switch (getType().getBasicType()) {
+ case EbtDouble:
+ case EbtFloat:
+ case EbtFloat16:
+ if (rightUnionArray[i].getDConst() != 0.0)
+ newConstArray[i].setDConst(leftUnionArray[i].getDConst() / rightUnionArray[i].getDConst());
+ else if (leftUnionArray[i].getDConst() > 0.0)
+ newConstArray[i].setDConst((double)INFINITY);
+ else if (leftUnionArray[i].getDConst() < 0.0)
+ newConstArray[i].setDConst(-(double)INFINITY);
+ else
+ newConstArray[i].setDConst((double)NAN);
+ break;
+ case EbtInt8:
+ if (rightUnionArray[i] == (signed char)0)
+ newConstArray[i].setI8Const((signed char)0x7F);
+ else if (rightUnionArray[i].getI8Const() == (signed char)-1 && leftUnionArray[i].getI8Const() == (signed char)-0x80)
+ newConstArray[i].setI8Const((signed char)-0x80);
+ else
+ newConstArray[i].setI8Const(leftUnionArray[i].getI8Const() / rightUnionArray[i].getI8Const());
+ break;
+
+ case EbtUint8:
+ if (rightUnionArray[i] == (unsigned char)0u)
+ newConstArray[i].setU8Const((unsigned char)0xFFu);
+ else
+ newConstArray[i].setU8Const(leftUnionArray[i].getU8Const() / rightUnionArray[i].getU8Const());
+ break;
+
+ case EbtInt16:
+ if (rightUnionArray[i] == (signed short)0)
+ newConstArray[i].setI16Const((signed short)0x7FFF);
+ else if (rightUnionArray[i].getI16Const() == (signed short)-1 && leftUnionArray[i].getI16Const() == (signed short)-0x8000)
+ newConstArray[i].setI16Const((signed short)-0x8000);
+ else
+ newConstArray[i].setI16Const(leftUnionArray[i].getI16Const() / rightUnionArray[i].getI16Const());
+ break;
+
+ case EbtUint16:
+ if (rightUnionArray[i] == (unsigned short)0u)
+ newConstArray[i].setU16Const((unsigned short)0xFFFFu);
+ else
+ newConstArray[i].setU16Const(leftUnionArray[i].getU16Const() / rightUnionArray[i].getU16Const());
+ break;
+
+ case EbtInt:
+ if (rightUnionArray[i] == 0)
+ newConstArray[i].setIConst(0x7FFFFFFF);
+ else if (rightUnionArray[i].getIConst() == -1 && leftUnionArray[i].getIConst() == (int)-0x80000000ll)
+ newConstArray[i].setIConst((int)-0x80000000ll);
+ else
+ newConstArray[i].setIConst(leftUnionArray[i].getIConst() / rightUnionArray[i].getIConst());
+ break;
+
+ case EbtUint:
+ if (rightUnionArray[i] == 0u)
+ newConstArray[i].setUConst(0xFFFFFFFFu);
+ else
+ newConstArray[i].setUConst(leftUnionArray[i].getUConst() / rightUnionArray[i].getUConst());
+ break;
+
+ case EbtInt64:
+ if (rightUnionArray[i] == 0ll)
+ newConstArray[i].setI64Const(0x7FFFFFFFFFFFFFFFll);
+ else if (rightUnionArray[i].getI64Const() == -1 && leftUnionArray[i].getI64Const() == (long long)-0x8000000000000000ll)
+ newConstArray[i].setI64Const((long long)-0x8000000000000000ll);
+ else
+ newConstArray[i].setI64Const(leftUnionArray[i].getI64Const() / rightUnionArray[i].getI64Const());
+ break;
+
+ case EbtUint64:
+ if (rightUnionArray[i] == 0ull)
+ newConstArray[i].setU64Const(0xFFFFFFFFFFFFFFFFull);
+ else
+ newConstArray[i].setU64Const(leftUnionArray[i].getU64Const() / rightUnionArray[i].getU64Const());
+ break;
+ default:
+ return 0;
+ }
+ }
+ break;
+
+ case EOpMatrixTimesVector:
+ for (int i = 0; i < getMatrixRows(); i++) {
+ double sum = 0.0f;
+ for (int j = 0; j < rightNode->getVectorSize(); j++) {
+ sum += leftUnionArray[j*getMatrixRows() + i].getDConst() * rightUnionArray[j].getDConst();
+ }
+ newConstArray[i].setDConst(sum);
+ }
+
+ returnType.shallowCopy(TType(getBasicType(), EvqConst, getMatrixRows()));
+ break;
+
+ case EOpVectorTimesMatrix:
+ for (int i = 0; i < rightNode->getMatrixCols(); i++) {
+ double sum = 0.0f;
+ for (int j = 0; j < getVectorSize(); j++)
+ sum += leftUnionArray[j].getDConst() * rightUnionArray[i*rightNode->getMatrixRows() + j].getDConst();
+ newConstArray[i].setDConst(sum);
+ }
+
+ returnType.shallowCopy(TType(getBasicType(), EvqConst, rightNode->getMatrixCols()));
+ break;
+
+ case EOpMod:
+ for (int i = 0; i < newComps; i++) {
+ if (rightUnionArray[i] == 0)
+ newConstArray[i] = leftUnionArray[i];
+ else {
+ switch (getType().getBasicType()) {
+ case EbtInt:
+ if (rightUnionArray[i].getIConst() == -1 && leftUnionArray[i].getIConst() == INT_MIN) {
+ newConstArray[i].setIConst(0);
+ break;
+ } else goto modulo_default;
+
+ case EbtInt64:
+ if (rightUnionArray[i].getI64Const() == -1 && leftUnionArray[i].getI64Const() == LLONG_MIN) {
+ newConstArray[i].setI64Const(0);
+ break;
+ } else goto modulo_default;
+#ifdef AMD_EXTENSIONS
+ case EbtInt16:
+ if (rightUnionArray[i].getIConst() == -1 && leftUnionArray[i].getIConst() == SHRT_MIN) {
+ newConstArray[i].setIConst(0);
+ break;
+ } else goto modulo_default;
+#endif
+ default:
+ modulo_default:
+ newConstArray[i] = leftUnionArray[i] % rightUnionArray[i];
+ }
+ }
+ }
+ break;
+
+ case EOpRightShift:
+ for (int i = 0; i < newComps; i++)
+ newConstArray[i] = leftUnionArray[i] >> rightUnionArray[i];
+ break;
+
+ case EOpLeftShift:
+ for (int i = 0; i < newComps; i++)
+ newConstArray[i] = leftUnionArray[i] << rightUnionArray[i];
+ break;
+
+ case EOpAnd:
+ for (int i = 0; i < newComps; i++)
+ newConstArray[i] = leftUnionArray[i] & rightUnionArray[i];
+ break;
+ case EOpInclusiveOr:
+ for (int i = 0; i < newComps; i++)
+ newConstArray[i] = leftUnionArray[i] | rightUnionArray[i];
+ break;
+ case EOpExclusiveOr:
+ for (int i = 0; i < newComps; i++)
+ newConstArray[i] = leftUnionArray[i] ^ rightUnionArray[i];
+ break;
+
+ case EOpLogicalAnd: // this code is written for possible future use, will not get executed currently
+ for (int i = 0; i < newComps; i++)
+ newConstArray[i] = leftUnionArray[i] && rightUnionArray[i];
+ break;
+
+ case EOpLogicalOr: // this code is written for possible future use, will not get executed currently
+ for (int i = 0; i < newComps; i++)
+ newConstArray[i] = leftUnionArray[i] || rightUnionArray[i];
+ break;
+
+ case EOpLogicalXor:
+ for (int i = 0; i < newComps; i++) {
+ switch (getType().getBasicType()) {
+ case EbtBool: newConstArray[i].setBConst((leftUnionArray[i] == rightUnionArray[i]) ? false : true); break;
+ default: assert(false && "Default missing");
+ }
+ }
+ break;
+
+ case EOpLessThan:
+ newConstArray[0].setBConst(leftUnionArray[0] < rightUnionArray[0]);
+ returnType.shallowCopy(constBool);
+ break;
+ case EOpGreaterThan:
+ newConstArray[0].setBConst(leftUnionArray[0] > rightUnionArray[0]);
+ returnType.shallowCopy(constBool);
+ break;
+ case EOpLessThanEqual:
+ newConstArray[0].setBConst(! (leftUnionArray[0] > rightUnionArray[0]));
+ returnType.shallowCopy(constBool);
+ break;
+ case EOpGreaterThanEqual:
+ newConstArray[0].setBConst(! (leftUnionArray[0] < rightUnionArray[0]));
+ returnType.shallowCopy(constBool);
+ break;
+ case EOpEqual:
+ newConstArray[0].setBConst(rightNode->getConstArray() == leftUnionArray);
+ returnType.shallowCopy(constBool);
+ break;
+ case EOpNotEqual:
+ newConstArray[0].setBConst(rightNode->getConstArray() != leftUnionArray);
+ returnType.shallowCopy(constBool);
+ break;
+
+ default:
+ return 0;
+ }
+
+ TIntermConstantUnion *newNode = new TIntermConstantUnion(newConstArray, returnType);
+ newNode->setLoc(getLoc());
+
+ return newNode;
+}
+
+//
+// Do single unary node folding
+//
+// Returns a new node representing the result.
+//
+TIntermTyped* TIntermConstantUnion::fold(TOperator op, const TType& returnType) const
+{
+ // First, size the result, which is mostly the same as the argument's size,
+ // but not always, and classify what is componentwise.
+ // Also, eliminate cases that can't be compile-time constant.
+ int resultSize;
+ bool componentWise = true;
+
+ int objectSize = getType().computeNumComponents();
+ switch (op) {
+ case EOpDeterminant:
+ case EOpAny:
+ case EOpAll:
+ case EOpLength:
+ componentWise = false;
+ resultSize = 1;
+ break;
+
+ case EOpEmitStreamVertex:
+ case EOpEndStreamPrimitive:
+ // These don't actually fold
+ return 0;
+
+ case EOpPackSnorm2x16:
+ case EOpPackUnorm2x16:
+ case EOpPackHalf2x16:
+ componentWise = false;
+ resultSize = 1;
+ break;
+
+ case EOpUnpackSnorm2x16:
+ case EOpUnpackUnorm2x16:
+ case EOpUnpackHalf2x16:
+ componentWise = false;
+ resultSize = 2;
+ break;
+
+ case EOpPack16:
+ case EOpPack32:
+ case EOpPack64:
+ case EOpUnpack32:
+ case EOpUnpack16:
+ case EOpUnpack8:
+ case EOpNormalize:
+ componentWise = false;
+ resultSize = objectSize;
+ break;
+
+ default:
+ resultSize = objectSize;
+ break;
+ }
+
+ // Set up for processing
+ TConstUnionArray newConstArray(resultSize);
+ const TConstUnionArray& unionArray = getConstArray();
+
+ // Process non-component-wise operations
+ switch (op) {
+ case EOpLength:
+ case EOpNormalize:
+ {
+ double sum = 0;
+ for (int i = 0; i < objectSize; i++)
+ sum += unionArray[i].getDConst() * unionArray[i].getDConst();
+ double length = sqrt(sum);
+ if (op == EOpLength)
+ newConstArray[0].setDConst(length);
+ else {
+ for (int i = 0; i < objectSize; i++)
+ newConstArray[i].setDConst(unionArray[i].getDConst() / length);
+ }
+ break;
+ }
+
+ case EOpAny:
+ {
+ bool result = false;
+ for (int i = 0; i < objectSize; i++) {
+ if (unionArray[i].getBConst())
+ result = true;
+ }
+ newConstArray[0].setBConst(result);
+ break;
+ }
+ case EOpAll:
+ {
+ bool result = true;
+ for (int i = 0; i < objectSize; i++) {
+ if (! unionArray[i].getBConst())
+ result = false;
+ }
+ newConstArray[0].setBConst(result);
+ break;
+ }
+
+ // TODO: 3.0 Functionality: unary constant folding: the rest of the ops have to be fleshed out
+
+ case EOpPackSnorm2x16:
+ case EOpPackUnorm2x16:
+ case EOpPackHalf2x16:
+ case EOpPack16:
+ case EOpPack32:
+ case EOpPack64:
+ case EOpUnpack32:
+ case EOpUnpack16:
+ case EOpUnpack8:
+
+ case EOpUnpackSnorm2x16:
+ case EOpUnpackUnorm2x16:
+ case EOpUnpackHalf2x16:
+
+ case EOpDeterminant:
+ case EOpMatrixInverse:
+ case EOpTranspose:
+ return 0;
+
+ default:
+ assert(componentWise);
+ break;
+ }
+
+ // Turn off the componentwise loop
+ if (! componentWise)
+ objectSize = 0;
+
+ // Process component-wise operations
+ for (int i = 0; i < objectSize; i++) {
+ switch (op) {
+ case EOpNegative:
+ switch (getType().getBasicType()) {
+ case EbtDouble:
+ case EbtFloat16:
+ case EbtFloat: newConstArray[i].setDConst(-unionArray[i].getDConst()); break;
+ case EbtInt8: newConstArray[i].setI8Const(-unionArray[i].getI8Const()); break;
+ case EbtUint8: newConstArray[i].setU8Const(static_cast<unsigned int>(-static_cast<signed int>(unionArray[i].getU8Const()))); break;
+ case EbtInt16: newConstArray[i].setI16Const(-unionArray[i].getI16Const()); break;
+ case EbtUint16:newConstArray[i].setU16Const(static_cast<unsigned int>(-static_cast<signed int>(unionArray[i].getU16Const()))); break;
+ case EbtInt: newConstArray[i].setIConst(-unionArray[i].getIConst()); break;
+ case EbtUint: newConstArray[i].setUConst(static_cast<unsigned int>(-static_cast<int>(unionArray[i].getUConst()))); break;
+ case EbtInt64: newConstArray[i].setI64Const(-unionArray[i].getI64Const()); break;
+ case EbtUint64: newConstArray[i].setU64Const(static_cast<unsigned long long>(-static_cast<long long>(unionArray[i].getU64Const()))); break;
+ default:
+ return 0;
+ }
+ break;
+ case EOpLogicalNot:
+ case EOpVectorLogicalNot:
+ switch (getType().getBasicType()) {
+ case EbtBool: newConstArray[i].setBConst(!unionArray[i].getBConst()); break;
+ default:
+ return 0;
+ }
+ break;
+ case EOpBitwiseNot:
+ newConstArray[i] = ~unionArray[i];
+ break;
+ case EOpRadians:
+ newConstArray[i].setDConst(unionArray[i].getDConst() * pi / 180.0);
+ break;
+ case EOpDegrees:
+ newConstArray[i].setDConst(unionArray[i].getDConst() * 180.0 / pi);
+ break;
+ case EOpSin:
+ newConstArray[i].setDConst(sin(unionArray[i].getDConst()));
+ break;
+ case EOpCos:
+ newConstArray[i].setDConst(cos(unionArray[i].getDConst()));
+ break;
+ case EOpTan:
+ newConstArray[i].setDConst(tan(unionArray[i].getDConst()));
+ break;
+ case EOpAsin:
+ newConstArray[i].setDConst(asin(unionArray[i].getDConst()));
+ break;
+ case EOpAcos:
+ newConstArray[i].setDConst(acos(unionArray[i].getDConst()));
+ break;
+ case EOpAtan:
+ newConstArray[i].setDConst(atan(unionArray[i].getDConst()));
+ break;
+
+ case EOpDPdx:
+ case EOpDPdy:
+ case EOpFwidth:
+ case EOpDPdxFine:
+ case EOpDPdyFine:
+ case EOpFwidthFine:
+ case EOpDPdxCoarse:
+ case EOpDPdyCoarse:
+ case EOpFwidthCoarse:
+ // The derivatives are all mandated to create a constant 0.
+ newConstArray[i].setDConst(0.0);
+ break;
+
+ case EOpExp:
+ newConstArray[i].setDConst(exp(unionArray[i].getDConst()));
+ break;
+ case EOpLog:
+ newConstArray[i].setDConst(log(unionArray[i].getDConst()));
+ break;
+ case EOpExp2:
+ {
+ const double inv_log2_e = 0.69314718055994530941723212145818;
+ newConstArray[i].setDConst(exp(unionArray[i].getDConst() * inv_log2_e));
+ break;
+ }
+ case EOpLog2:
+ {
+ const double log2_e = 1.4426950408889634073599246810019;
+ newConstArray[i].setDConst(log2_e * log(unionArray[i].getDConst()));
+ break;
+ }
+ case EOpSqrt:
+ newConstArray[i].setDConst(sqrt(unionArray[i].getDConst()));
+ break;
+ case EOpInverseSqrt:
+ newConstArray[i].setDConst(1.0 / sqrt(unionArray[i].getDConst()));
+ break;
+
+ case EOpAbs:
+ if (unionArray[i].getType() == EbtDouble)
+ newConstArray[i].setDConst(fabs(unionArray[i].getDConst()));
+ else if (unionArray[i].getType() == EbtInt)
+ newConstArray[i].setIConst(abs(unionArray[i].getIConst()));
+ else
+ newConstArray[i] = unionArray[i];
+ break;
+ case EOpSign:
+ #define SIGN(X) (X == 0 ? 0 : (X < 0 ? -1 : 1))
+ if (unionArray[i].getType() == EbtDouble)
+ newConstArray[i].setDConst(SIGN(unionArray[i].getDConst()));
+ else
+ newConstArray[i].setIConst(SIGN(unionArray[i].getIConst()));
+ break;
+ case EOpFloor:
+ newConstArray[i].setDConst(floor(unionArray[i].getDConst()));
+ break;
+ case EOpTrunc:
+ if (unionArray[i].getDConst() > 0)
+ newConstArray[i].setDConst(floor(unionArray[i].getDConst()));
+ else
+ newConstArray[i].setDConst(ceil(unionArray[i].getDConst()));
+ break;
+ case EOpRound:
+ newConstArray[i].setDConst(floor(0.5 + unionArray[i].getDConst()));
+ break;
+ case EOpRoundEven:
+ {
+ double flr = floor(unionArray[i].getDConst());
+ bool even = flr / 2.0 == floor(flr / 2.0);
+ double rounded = even ? ceil(unionArray[i].getDConst() - 0.5) : floor(unionArray[i].getDConst() + 0.5);
+ newConstArray[i].setDConst(rounded);
+ break;
+ }
+ case EOpCeil:
+ newConstArray[i].setDConst(ceil(unionArray[i].getDConst()));
+ break;
+ case EOpFract:
+ {
+ double x = unionArray[i].getDConst();
+ newConstArray[i].setDConst(x - floor(x));
+ break;
+ }
+
+ case EOpIsNan:
+ {
+ newConstArray[i].setBConst(isNan(unionArray[i].getDConst()));
+ break;
+ }
+ case EOpIsInf:
+ {
+ newConstArray[i].setBConst(isInf(unionArray[i].getDConst()));
+ break;
+ }
+
+ case EOpConvInt8ToBool:
+ newConstArray[i].setBConst(unionArray[i].getI8Const() != 0); break;
+ case EOpConvUint8ToBool:
+ newConstArray[i].setBConst(unionArray[i].getU8Const() != 0); break;
+ case EOpConvInt16ToBool:
+ newConstArray[i].setBConst(unionArray[i].getI16Const() != 0); break;
+ case EOpConvUint16ToBool:
+ newConstArray[i].setBConst(unionArray[i].getU16Const() != 0); break;
+ case EOpConvIntToBool:
+ newConstArray[i].setBConst(unionArray[i].getIConst() != 0); break;
+ case EOpConvUintToBool:
+ newConstArray[i].setBConst(unionArray[i].getUConst() != 0); break;
+ case EOpConvInt64ToBool:
+ newConstArray[i].setBConst(unionArray[i].getI64Const() != 0); break;
+ case EOpConvUint64ToBool:
+ newConstArray[i].setBConst(unionArray[i].getI64Const() != 0); break;
+ case EOpConvFloat16ToBool:
+ newConstArray[i].setBConst(unionArray[i].getDConst() != 0); break;
+ case EOpConvFloatToBool:
+ newConstArray[i].setBConst(unionArray[i].getDConst() != 0); break;
+ case EOpConvDoubleToBool:
+ newConstArray[i].setBConst(unionArray[i].getDConst() != 0); break;
+
+ case EOpConvBoolToInt8:
+ newConstArray[i].setI8Const(unionArray[i].getBConst()); break;
+ case EOpConvBoolToUint8:
+ newConstArray[i].setU8Const(unionArray[i].getBConst()); break;
+ case EOpConvBoolToInt16:
+ newConstArray[i].setI16Const(unionArray[i].getBConst()); break;
+ case EOpConvBoolToUint16:
+ newConstArray[i].setU16Const(unionArray[i].getBConst()); break;
+ case EOpConvBoolToInt:
+ newConstArray[i].setIConst(unionArray[i].getBConst()); break;
+ case EOpConvBoolToUint:
+ newConstArray[i].setUConst(unionArray[i].getBConst()); break;
+ case EOpConvBoolToInt64:
+ newConstArray[i].setI64Const(unionArray[i].getBConst()); break;
+ case EOpConvBoolToUint64:
+ newConstArray[i].setU64Const(unionArray[i].getBConst()); break;
+ case EOpConvBoolToFloat16:
+ newConstArray[i].setDConst(unionArray[i].getBConst()); break;
+ case EOpConvBoolToFloat:
+ newConstArray[i].setDConst(unionArray[i].getBConst()); break;
+ case EOpConvBoolToDouble:
+ newConstArray[i].setDConst(unionArray[i].getBConst()); break;
+
+ case EOpConvInt8ToInt16:
+ newConstArray[i].setI16Const(unionArray[i].getI8Const()); break;
+ case EOpConvInt8ToInt:
+ newConstArray[i].setIConst(unionArray[i].getI8Const()); break;
+ case EOpConvInt8ToInt64:
+ newConstArray[i].setI64Const(unionArray[i].getI8Const()); break;
+ case EOpConvInt8ToUint8:
+ newConstArray[i].setU8Const(unionArray[i].getI8Const()); break;
+ case EOpConvInt8ToUint16:
+ newConstArray[i].setU16Const(unionArray[i].getI8Const()); break;
+ case EOpConvInt8ToUint:
+ newConstArray[i].setUConst(unionArray[i].getI8Const()); break;
+ case EOpConvInt8ToUint64:
+ newConstArray[i].setU64Const(unionArray[i].getI8Const()); break;
+ case EOpConvUint8ToInt8:
+ newConstArray[i].setI8Const(unionArray[i].getU8Const()); break;
+ case EOpConvUint8ToInt16:
+ newConstArray[i].setI16Const(unionArray[i].getU8Const()); break;
+ case EOpConvUint8ToInt:
+ newConstArray[i].setIConst(unionArray[i].getU8Const()); break;
+ case EOpConvUint8ToInt64:
+ newConstArray[i].setI64Const(unionArray[i].getU8Const()); break;
+ case EOpConvUint8ToUint16:
+ newConstArray[i].setU16Const(unionArray[i].getU8Const()); break;
+ case EOpConvUint8ToUint:
+ newConstArray[i].setUConst(unionArray[i].getU8Const()); break;
+ case EOpConvUint8ToUint64:
+ newConstArray[i].setU64Const(unionArray[i].getU8Const()); break;
+ case EOpConvInt8ToFloat16:
+ newConstArray[i].setDConst(unionArray[i].getI8Const()); break;
+ case EOpConvInt8ToFloat:
+ newConstArray[i].setDConst(unionArray[i].getI8Const()); break;
+ case EOpConvInt8ToDouble:
+ newConstArray[i].setDConst(unionArray[i].getI8Const()); break;
+ case EOpConvUint8ToFloat16:
+ newConstArray[i].setDConst(unionArray[i].getU8Const()); break;
+ case EOpConvUint8ToFloat:
+ newConstArray[i].setDConst(unionArray[i].getU8Const()); break;
+ case EOpConvUint8ToDouble:
+ newConstArray[i].setDConst(unionArray[i].getU8Const()); break;
+
+ case EOpConvInt16ToInt8:
+ newConstArray[i].setI8Const(static_cast<signed char>(unionArray[i].getI16Const())); break;
+ case EOpConvInt16ToInt:
+ newConstArray[i].setIConst(unionArray[i].getI16Const()); break;
+ case EOpConvInt16ToInt64:
+ newConstArray[i].setI64Const(unionArray[i].getI16Const()); break;
+ case EOpConvInt16ToUint8:
+ newConstArray[i].setU8Const(static_cast<unsigned char>(unionArray[i].getI16Const())); break;
+ case EOpConvInt16ToUint16:
+ newConstArray[i].setU16Const(unionArray[i].getI16Const()); break;
+ case EOpConvInt16ToUint:
+ newConstArray[i].setUConst(unionArray[i].getI16Const()); break;
+ case EOpConvInt16ToUint64:
+ newConstArray[i].setU64Const(unionArray[i].getI16Const()); break;
+ case EOpConvUint16ToInt8:
+ newConstArray[i].setI8Const(static_cast<signed char>(unionArray[i].getU16Const())); break;
+ case EOpConvUint16ToInt16:
+ newConstArray[i].setI16Const(unionArray[i].getU16Const()); break;
+ case EOpConvUint16ToInt:
+ newConstArray[i].setIConst(unionArray[i].getU16Const()); break;
+ case EOpConvUint16ToInt64:
+ newConstArray[i].setI64Const(unionArray[i].getU16Const()); break;
+ case EOpConvUint16ToUint8:
+ newConstArray[i].setU8Const(static_cast<unsigned char>(unionArray[i].getU16Const())); break;
+
+ case EOpConvUint16ToUint:
+ newConstArray[i].setUConst(unionArray[i].getU16Const()); break;
+ case EOpConvUint16ToUint64:
+ newConstArray[i].setU64Const(unionArray[i].getU16Const()); break;
+ case EOpConvInt16ToFloat16:
+ newConstArray[i].setDConst(unionArray[i].getI16Const()); break;
+ case EOpConvInt16ToFloat:
+ newConstArray[i].setDConst(unionArray[i].getI16Const()); break;
+ case EOpConvInt16ToDouble:
+ newConstArray[i].setDConst(unionArray[i].getI16Const()); break;
+ case EOpConvUint16ToFloat16:
+ newConstArray[i].setDConst(unionArray[i].getU16Const()); break;
+ case EOpConvUint16ToFloat:
+ newConstArray[i].setDConst(unionArray[i].getU16Const()); break;
+ case EOpConvUint16ToDouble:
+ newConstArray[i].setDConst(unionArray[i].getU16Const()); break;
+
+ case EOpConvIntToInt8:
+ newConstArray[i].setI8Const((signed char)unionArray[i].getIConst()); break;
+ case EOpConvIntToInt16:
+ newConstArray[i].setI16Const((signed short)unionArray[i].getIConst()); break;
+ case EOpConvIntToInt64:
+ newConstArray[i].setI64Const(unionArray[i].getIConst()); break;
+ case EOpConvIntToUint8:
+ newConstArray[i].setU8Const((unsigned char)unionArray[i].getIConst()); break;
+ case EOpConvIntToUint16:
+ newConstArray[i].setU16Const((unsigned char)unionArray[i].getIConst()); break;
+ case EOpConvIntToUint:
+ newConstArray[i].setUConst(unionArray[i].getIConst()); break;
+ case EOpConvIntToUint64:
+ newConstArray[i].setU64Const(unionArray[i].getIConst()); break;
+
+ case EOpConvUintToInt8:
+ newConstArray[i].setI8Const((signed char)unionArray[i].getUConst()); break;
+ case EOpConvUintToInt16:
+ newConstArray[i].setI16Const((signed short)unionArray[i].getUConst()); break;
+ case EOpConvUintToInt:
+ newConstArray[i].setIConst(unionArray[i].getUConst()); break;
+ case EOpConvUintToInt64:
+ newConstArray[i].setI64Const(unionArray[i].getUConst()); break;
+ case EOpConvUintToUint8:
+ newConstArray[i].setU8Const((unsigned char)unionArray[i].getUConst()); break;
+ case EOpConvUintToUint16:
+ newConstArray[i].setU16Const((unsigned short)unionArray[i].getUConst()); break;
+ case EOpConvUintToUint64:
+ newConstArray[i].setU64Const(unionArray[i].getUConst()); break;
+ case EOpConvIntToFloat16:
+ newConstArray[i].setDConst(unionArray[i].getIConst()); break;
+ case EOpConvIntToFloat:
+ newConstArray[i].setDConst(unionArray[i].getIConst()); break;
+ case EOpConvIntToDouble:
+ newConstArray[i].setDConst(unionArray[i].getIConst()); break;
+ case EOpConvUintToFloat16:
+ newConstArray[i].setDConst(unionArray[i].getUConst()); break;
+ case EOpConvUintToFloat:
+ newConstArray[i].setDConst(unionArray[i].getUConst()); break;
+ case EOpConvUintToDouble:
+ newConstArray[i].setDConst(unionArray[i].getUConst()); break;
+ case EOpConvInt64ToInt8:
+ newConstArray[i].setI8Const(static_cast<signed char>(unionArray[i].getI64Const())); break;
+ case EOpConvInt64ToInt16:
+ newConstArray[i].setI16Const(static_cast<signed short>(unionArray[i].getI64Const())); break;
+ case EOpConvInt64ToInt:
+ newConstArray[i].setIConst(static_cast<int>(unionArray[i].getI64Const())); break;
+ case EOpConvInt64ToUint8:
+ newConstArray[i].setU8Const(static_cast<unsigned char>(unionArray[i].getI64Const())); break;
+ case EOpConvInt64ToUint16:
+ newConstArray[i].setU16Const(static_cast<unsigned short>(unionArray[i].getI64Const())); break;
+ case EOpConvInt64ToUint:
+ newConstArray[i].setUConst(static_cast<unsigned int>(unionArray[i].getI64Const())); break;
+ case EOpConvInt64ToUint64:
+ newConstArray[i].setU64Const(unionArray[i].getI64Const()); break;
+ case EOpConvUint64ToInt8:
+ newConstArray[i].setI8Const(static_cast<signed char>(unionArray[i].getU64Const())); break;
+ case EOpConvUint64ToInt16:
+ newConstArray[i].setI16Const(static_cast<signed short>(unionArray[i].getU64Const())); break;
+ case EOpConvUint64ToInt:
+ newConstArray[i].setIConst(static_cast<int>(unionArray[i].getU64Const())); break;
+ case EOpConvUint64ToInt64:
+ newConstArray[i].setI64Const(unionArray[i].getU64Const()); break;
+ case EOpConvUint64ToUint8:
+ newConstArray[i].setU8Const(static_cast<unsigned char>(unionArray[i].getU64Const())); break;
+ case EOpConvUint64ToUint16:
+ newConstArray[i].setU16Const(static_cast<unsigned short>(unionArray[i].getU64Const())); break;
+ case EOpConvUint64ToUint:
+ newConstArray[i].setUConst(static_cast<unsigned int>(unionArray[i].getU64Const())); break;
+ case EOpConvInt64ToFloat16:
+ newConstArray[i].setDConst(static_cast<double>(unionArray[i].getI64Const())); break;
+ case EOpConvInt64ToFloat:
+ newConstArray[i].setDConst(static_cast<double>(unionArray[i].getI64Const())); break;
+ case EOpConvInt64ToDouble:
+ newConstArray[i].setDConst(static_cast<double>(unionArray[i].getI64Const())); break;
+ case EOpConvUint64ToFloat16:
+ newConstArray[i].setDConst(static_cast<double>(unionArray[i].getU64Const())); break;
+ case EOpConvUint64ToFloat:
+ newConstArray[i].setDConst(static_cast<double>(unionArray[i].getU64Const())); break;
+ case EOpConvUint64ToDouble:
+ newConstArray[i].setDConst(static_cast<double>(unionArray[i].getU64Const())); break;
+ case EOpConvFloat16ToInt8:
+ newConstArray[i].setI8Const(static_cast<signed char>(unionArray[i].getDConst())); break;
+ case EOpConvFloat16ToInt16:
+ newConstArray[i].setI16Const(static_cast<signed short>(unionArray[i].getDConst())); break;
+ case EOpConvFloat16ToInt:
+ newConstArray[i].setIConst(static_cast<int>(unionArray[i].getDConst())); break;
+ case EOpConvFloat16ToInt64:
+ newConstArray[i].setI64Const(static_cast<long long>(unionArray[i].getDConst())); break;
+ case EOpConvFloat16ToUint8:
+ newConstArray[i].setU8Const(static_cast<unsigned char>(unionArray[i].getDConst())); break;
+ case EOpConvFloat16ToUint16:
+ newConstArray[i].setU16Const(static_cast<unsigned short>(unionArray[i].getDConst())); break;
+ case EOpConvFloat16ToUint:
+ newConstArray[i].setUConst(static_cast<unsigned int>(unionArray[i].getDConst())); break;
+ case EOpConvFloat16ToUint64:
+ newConstArray[i].setU64Const(static_cast<unsigned long long>(unionArray[i].getDConst())); break;
+ case EOpConvFloat16ToFloat:
+ newConstArray[i].setDConst(unionArray[i].getDConst()); break;
+ case EOpConvFloat16ToDouble:
+ newConstArray[i].setDConst(unionArray[i].getDConst()); break;
+ case EOpConvFloatToInt8:
+ newConstArray[i].setI8Const(static_cast<signed char>(unionArray[i].getDConst())); break;
+ case EOpConvFloatToInt16:
+ newConstArray[i].setI16Const(static_cast<signed short>(unionArray[i].getDConst())); break;
+ case EOpConvFloatToInt:
+ newConstArray[i].setIConst(static_cast<int>(unionArray[i].getDConst())); break;
+ case EOpConvFloatToInt64:
+ newConstArray[i].setI64Const(static_cast<long long>(unionArray[i].getDConst())); break;
+ case EOpConvFloatToUint8:
+ newConstArray[i].setU8Const(static_cast<unsigned char>(unionArray[i].getDConst())); break;
+ case EOpConvFloatToUint16:
+ newConstArray[i].setU16Const(static_cast<unsigned short>(unionArray[i].getDConst())); break;
+ case EOpConvFloatToUint:
+ newConstArray[i].setUConst(static_cast<unsigned int>(unionArray[i].getDConst())); break;
+ case EOpConvFloatToUint64:
+ newConstArray[i].setU64Const(static_cast<unsigned long long>(unionArray[i].getDConst())); break;
+ case EOpConvFloatToFloat16:
+ newConstArray[i].setDConst(unionArray[i].getDConst()); break;
+ case EOpConvFloatToDouble:
+ newConstArray[i].setDConst(unionArray[i].getDConst()); break;
+ case EOpConvDoubleToInt8:
+ newConstArray[i].setI8Const(static_cast<signed char>(unionArray[i].getDConst())); break;
+ case EOpConvDoubleToInt16:
+ newConstArray[i].setI16Const(static_cast<signed short>(unionArray[i].getDConst())); break;
+ case EOpConvDoubleToInt:
+ newConstArray[i].setIConst(static_cast<int>(unionArray[i].getDConst())); break;
+ case EOpConvDoubleToInt64:
+ newConstArray[i].setI64Const(static_cast<long long>(unionArray[i].getDConst())); break;
+ case EOpConvDoubleToUint8:
+ newConstArray[i].setU8Const(static_cast<unsigned char>(unionArray[i].getDConst())); break;
+ case EOpConvDoubleToUint16:
+ newConstArray[i].setU16Const(static_cast<unsigned short>(unionArray[i].getDConst())); break;
+ case EOpConvDoubleToUint:
+ newConstArray[i].setUConst(static_cast<unsigned int>(unionArray[i].getDConst())); break;
+ case EOpConvDoubleToUint64:
+ newConstArray[i].setU64Const(static_cast<unsigned long long>(unionArray[i].getDConst())); break;
+ case EOpConvDoubleToFloat16:
+ newConstArray[i].setDConst(unionArray[i].getDConst()); break;
+ case EOpConvDoubleToFloat:
+ newConstArray[i].setDConst(unionArray[i].getDConst()); break;
+ case EOpConvPtrToUint64:
+ case EOpConvUint64ToPtr:
+ case EOpConstructReference:
+ newConstArray[i].setU64Const(unionArray[i].getU64Const()); break;
+
+
+
+ // TODO: 3.0 Functionality: unary constant folding: the rest of the ops have to be fleshed out
+
+ case EOpSinh:
+ case EOpCosh:
+ case EOpTanh:
+ case EOpAsinh:
+ case EOpAcosh:
+ case EOpAtanh:
+
+ case EOpFloatBitsToInt:
+ case EOpFloatBitsToUint:
+ case EOpIntBitsToFloat:
+ case EOpUintBitsToFloat:
+ case EOpDoubleBitsToInt64:
+ case EOpDoubleBitsToUint64:
+ case EOpInt64BitsToDouble:
+ case EOpUint64BitsToDouble:
+ case EOpFloat16BitsToInt16:
+ case EOpFloat16BitsToUint16:
+ case EOpInt16BitsToFloat16:
+ case EOpUint16BitsToFloat16:
+ default:
+ return 0;
+ }
+ }
+
+ TIntermConstantUnion *newNode = new TIntermConstantUnion(newConstArray, returnType);
+ newNode->getWritableType().getQualifier().storage = EvqConst;
+ newNode->setLoc(getLoc());
+
+ return newNode;
+}
+
+//
+// Do constant folding for an aggregate node that has all its children
+// as constants and an operator that requires constant folding.
+//
+TIntermTyped* TIntermediate::fold(TIntermAggregate* aggrNode)
+{
+ if (aggrNode == nullptr)
+ return aggrNode;
+
+ if (! areAllChildConst(aggrNode))
+ return aggrNode;
+
+ if (aggrNode->isConstructor())
+ return foldConstructor(aggrNode);
+
+ TIntermSequence& children = aggrNode->getSequence();
+
+ // First, see if this is an operation to constant fold, kick out if not,
+ // see what size the result is if so.
+
+ bool componentwise = false; // will also say componentwise if a scalar argument gets repeated to make per-component results
+ int objectSize;
+ switch (aggrNode->getOp()) {
+ case EOpAtan:
+ case EOpPow:
+ case EOpMin:
+ case EOpMax:
+ case EOpMix:
+ case EOpClamp:
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+ case EOpVectorEqual:
+ case EOpVectorNotEqual:
+ componentwise = true;
+ objectSize = children[0]->getAsConstantUnion()->getType().computeNumComponents();
+ break;
+ case EOpCross:
+ case EOpReflect:
+ case EOpRefract:
+ case EOpFaceForward:
+ objectSize = children[0]->getAsConstantUnion()->getType().computeNumComponents();
+ break;
+ case EOpDistance:
+ case EOpDot:
+ objectSize = 1;
+ break;
+ case EOpOuterProduct:
+ objectSize = children[0]->getAsTyped()->getType().getVectorSize() *
+ children[1]->getAsTyped()->getType().getVectorSize();
+ break;
+ case EOpStep:
+ componentwise = true;
+ objectSize = std::max(children[0]->getAsTyped()->getType().getVectorSize(),
+ children[1]->getAsTyped()->getType().getVectorSize());
+ break;
+ case EOpSmoothStep:
+ componentwise = true;
+ objectSize = std::max(children[0]->getAsTyped()->getType().getVectorSize(),
+ children[2]->getAsTyped()->getType().getVectorSize());
+ break;
+ default:
+ return aggrNode;
+ }
+ TConstUnionArray newConstArray(objectSize);
+
+ TVector<TConstUnionArray> childConstUnions;
+ for (unsigned int arg = 0; arg < children.size(); ++arg)
+ childConstUnions.push_back(children[arg]->getAsConstantUnion()->getConstArray());
+
+ if (componentwise) {
+ for (int comp = 0; comp < objectSize; comp++) {
+
+ // some arguments are scalars instead of matching vectors; simulate a smear
+ int arg0comp = std::min(comp, children[0]->getAsTyped()->getType().getVectorSize() - 1);
+ int arg1comp = 0;
+ if (children.size() > 1)
+ arg1comp = std::min(comp, children[1]->getAsTyped()->getType().getVectorSize() - 1);
+ int arg2comp = 0;
+ if (children.size() > 2)
+ arg2comp = std::min(comp, children[2]->getAsTyped()->getType().getVectorSize() - 1);
+
+ switch (aggrNode->getOp()) {
+ case EOpAtan:
+ newConstArray[comp].setDConst(atan2(childConstUnions[0][arg0comp].getDConst(), childConstUnions[1][arg1comp].getDConst()));
+ break;
+ case EOpPow:
+ newConstArray[comp].setDConst(pow(childConstUnions[0][arg0comp].getDConst(), childConstUnions[1][arg1comp].getDConst()));
+ break;
+ case EOpMin:
+ switch(children[0]->getAsTyped()->getBasicType()) {
+ case EbtFloat16:
+ case EbtFloat:
+ case EbtDouble:
+ newConstArray[comp].setDConst(std::min(childConstUnions[0][arg0comp].getDConst(), childConstUnions[1][arg1comp].getDConst()));
+ break;
+ case EbtInt8:
+ newConstArray[comp].setI8Const(std::min(childConstUnions[0][arg0comp].getI8Const(), childConstUnions[1][arg1comp].getI8Const()));
+ break;
+ case EbtUint8:
+ newConstArray[comp].setU8Const(std::min(childConstUnions[0][arg0comp].getU8Const(), childConstUnions[1][arg1comp].getU8Const()));
+ break;
+ case EbtInt16:
+ newConstArray[comp].setI16Const(std::min(childConstUnions[0][arg0comp].getI16Const(), childConstUnions[1][arg1comp].getI16Const()));
+ break;
+ case EbtUint16:
+ newConstArray[comp].setU16Const(std::min(childConstUnions[0][arg0comp].getU16Const(), childConstUnions[1][arg1comp].getU16Const()));
+ break;
+ case EbtInt:
+ newConstArray[comp].setIConst(std::min(childConstUnions[0][arg0comp].getIConst(), childConstUnions[1][arg1comp].getIConst()));
+ break;
+ case EbtUint:
+ newConstArray[comp].setUConst(std::min(childConstUnions[0][arg0comp].getUConst(), childConstUnions[1][arg1comp].getUConst()));
+ break;
+ case EbtInt64:
+ newConstArray[comp].setI64Const(std::min(childConstUnions[0][arg0comp].getI64Const(), childConstUnions[1][arg1comp].getI64Const()));
+ break;
+ case EbtUint64:
+ newConstArray[comp].setU64Const(std::min(childConstUnions[0][arg0comp].getU64Const(), childConstUnions[1][arg1comp].getU64Const()));
+ break;
+ default: assert(false && "Default missing");
+ }
+ break;
+ case EOpMax:
+ switch(children[0]->getAsTyped()->getBasicType()) {
+ case EbtFloat16:
+ case EbtFloat:
+ case EbtDouble:
+ newConstArray[comp].setDConst(std::max(childConstUnions[0][arg0comp].getDConst(), childConstUnions[1][arg1comp].getDConst()));
+ break;
+ case EbtInt8:
+ newConstArray[comp].setI8Const(std::max(childConstUnions[0][arg0comp].getI8Const(), childConstUnions[1][arg1comp].getI8Const()));
+ break;
+ case EbtUint8:
+ newConstArray[comp].setU8Const(std::max(childConstUnions[0][arg0comp].getU8Const(), childConstUnions[1][arg1comp].getU8Const()));
+ break;
+ case EbtInt16:
+ newConstArray[comp].setI16Const(std::max(childConstUnions[0][arg0comp].getI16Const(), childConstUnions[1][arg1comp].getI16Const()));
+ break;
+ case EbtUint16:
+ newConstArray[comp].setU16Const(std::max(childConstUnions[0][arg0comp].getU16Const(), childConstUnions[1][arg1comp].getU16Const()));
+ break;
+ case EbtInt:
+ newConstArray[comp].setIConst(std::max(childConstUnions[0][arg0comp].getIConst(), childConstUnions[1][arg1comp].getIConst()));
+ break;
+ case EbtUint:
+ newConstArray[comp].setUConst(std::max(childConstUnions[0][arg0comp].getUConst(), childConstUnions[1][arg1comp].getUConst()));
+ break;
+ case EbtInt64:
+ newConstArray[comp].setI64Const(std::max(childConstUnions[0][arg0comp].getI64Const(), childConstUnions[1][arg1comp].getI64Const()));
+ break;
+ case EbtUint64:
+ newConstArray[comp].setU64Const(std::max(childConstUnions[0][arg0comp].getU64Const(), childConstUnions[1][arg1comp].getU64Const()));
+ break;
+ default: assert(false && "Default missing");
+ }
+ break;
+ case EOpClamp:
+ switch(children[0]->getAsTyped()->getBasicType()) {
+ case EbtFloat16:
+ case EbtFloat:
+ case EbtDouble:
+ newConstArray[comp].setDConst(std::min(std::max(childConstUnions[0][arg0comp].getDConst(), childConstUnions[1][arg1comp].getDConst()),
+ childConstUnions[2][arg2comp].getDConst()));
+ break;
+ case EbtInt8:
+ newConstArray[comp].setI8Const(std::min(std::max(childConstUnions[0][arg0comp].getI8Const(), childConstUnions[1][arg1comp].getI8Const()),
+ childConstUnions[2][arg2comp].getI8Const()));
+ break;
+ case EbtUint8:
+ newConstArray[comp].setU8Const(std::min(std::max(childConstUnions[0][arg0comp].getU8Const(), childConstUnions[1][arg1comp].getU8Const()),
+ childConstUnions[2][arg2comp].getU8Const()));
+ break;
+ case EbtInt16:
+ newConstArray[comp].setI16Const(std::min(std::max(childConstUnions[0][arg0comp].getI16Const(), childConstUnions[1][arg1comp].getI16Const()),
+ childConstUnions[2][arg2comp].getI16Const()));
+ break;
+ case EbtUint16:
+ newConstArray[comp].setU16Const(std::min(std::max(childConstUnions[0][arg0comp].getU16Const(), childConstUnions[1][arg1comp].getU16Const()),
+ childConstUnions[2][arg2comp].getU16Const()));
+ break;
+ case EbtInt:
+ newConstArray[comp].setIConst(std::min(std::max(childConstUnions[0][arg0comp].getIConst(), childConstUnions[1][arg1comp].getIConst()),
+ childConstUnions[2][arg2comp].getIConst()));
+ break;
+ case EbtUint:
+ newConstArray[comp].setUConst(std::min(std::max(childConstUnions[0][arg0comp].getUConst(), childConstUnions[1][arg1comp].getUConst()),
+ childConstUnions[2][arg2comp].getUConst()));
+ break;
+ case EbtInt64:
+ newConstArray[comp].setI64Const(std::min(std::max(childConstUnions[0][arg0comp].getI64Const(), childConstUnions[1][arg1comp].getI64Const()),
+ childConstUnions[2][arg2comp].getI64Const()));
+ break;
+ case EbtUint64:
+ newConstArray[comp].setU64Const(std::min(std::max(childConstUnions[0][arg0comp].getU64Const(), childConstUnions[1][arg1comp].getU64Const()),
+ childConstUnions[2][arg2comp].getU64Const()));
+ break;
+ default: assert(false && "Default missing");
+ }
+ break;
+ case EOpLessThan:
+ newConstArray[comp].setBConst(childConstUnions[0][arg0comp] < childConstUnions[1][arg1comp]);
+ break;
+ case EOpGreaterThan:
+ newConstArray[comp].setBConst(childConstUnions[0][arg0comp] > childConstUnions[1][arg1comp]);
+ break;
+ case EOpLessThanEqual:
+ newConstArray[comp].setBConst(! (childConstUnions[0][arg0comp] > childConstUnions[1][arg1comp]));
+ break;
+ case EOpGreaterThanEqual:
+ newConstArray[comp].setBConst(! (childConstUnions[0][arg0comp] < childConstUnions[1][arg1comp]));
+ break;
+ case EOpVectorEqual:
+ newConstArray[comp].setBConst(childConstUnions[0][arg0comp] == childConstUnions[1][arg1comp]);
+ break;
+ case EOpVectorNotEqual:
+ newConstArray[comp].setBConst(childConstUnions[0][arg0comp] != childConstUnions[1][arg1comp]);
+ break;
+ case EOpMix:
+ if (children[2]->getAsTyped()->getBasicType() == EbtBool)
+ newConstArray[comp].setDConst(childConstUnions[2][arg2comp].getBConst() ? childConstUnions[1][arg1comp].getDConst() :
+ childConstUnions[0][arg0comp].getDConst());
+ else
+ newConstArray[comp].setDConst(childConstUnions[0][arg0comp].getDConst() * (1.0 - childConstUnions[2][arg2comp].getDConst()) +
+ childConstUnions[1][arg1comp].getDConst() * childConstUnions[2][arg2comp].getDConst());
+ break;
+ case EOpStep:
+ newConstArray[comp].setDConst(childConstUnions[1][arg1comp].getDConst() < childConstUnions[0][arg0comp].getDConst() ? 0.0 : 1.0);
+ break;
+ case EOpSmoothStep:
+ {
+ double t = (childConstUnions[2][arg2comp].getDConst() - childConstUnions[0][arg0comp].getDConst()) /
+ (childConstUnions[1][arg1comp].getDConst() - childConstUnions[0][arg0comp].getDConst());
+ if (t < 0.0)
+ t = 0.0;
+ if (t > 1.0)
+ t = 1.0;
+ newConstArray[comp].setDConst(t * t * (3.0 - 2.0 * t));
+ break;
+ }
+ default:
+ return aggrNode;
+ }
+ }
+ } else {
+ // Non-componentwise...
+
+ int numComps = children[0]->getAsConstantUnion()->getType().computeNumComponents();
+ double dot;
+
+ switch (aggrNode->getOp()) {
+ case EOpDistance:
+ {
+ double sum = 0.0;
+ for (int comp = 0; comp < numComps; ++comp) {
+ double diff = childConstUnions[1][comp].getDConst() - childConstUnions[0][comp].getDConst();
+ sum += diff * diff;
+ }
+ newConstArray[0].setDConst(sqrt(sum));
+ break;
+ }
+ case EOpDot:
+ newConstArray[0].setDConst(childConstUnions[0].dot(childConstUnions[1]));
+ break;
+ case EOpCross:
+ newConstArray[0] = childConstUnions[0][1] * childConstUnions[1][2] - childConstUnions[0][2] * childConstUnions[1][1];
+ newConstArray[1] = childConstUnions[0][2] * childConstUnions[1][0] - childConstUnions[0][0] * childConstUnions[1][2];
+ newConstArray[2] = childConstUnions[0][0] * childConstUnions[1][1] - childConstUnions[0][1] * childConstUnions[1][0];
+ break;
+ case EOpFaceForward:
+ // If dot(Nref, I) < 0 return N, otherwise return -N: Arguments are (N, I, Nref).
+ dot = childConstUnions[1].dot(childConstUnions[2]);
+ for (int comp = 0; comp < numComps; ++comp) {
+ if (dot < 0.0)
+ newConstArray[comp] = childConstUnions[0][comp];
+ else
+ newConstArray[comp].setDConst(-childConstUnions[0][comp].getDConst());
+ }
+ break;
+ case EOpReflect:
+ // I - 2 * dot(N, I) * N: Arguments are (I, N).
+ dot = childConstUnions[0].dot(childConstUnions[1]);
+ dot *= 2.0;
+ for (int comp = 0; comp < numComps; ++comp)
+ newConstArray[comp].setDConst(childConstUnions[0][comp].getDConst() - dot * childConstUnions[1][comp].getDConst());
+ break;
+ case EOpRefract:
+ {
+ // Arguments are (I, N, eta).
+ // k = 1.0 - eta * eta * (1.0 - dot(N, I) * dot(N, I))
+ // if (k < 0.0)
+ // return dvec(0.0)
+ // else
+ // return eta * I - (eta * dot(N, I) + sqrt(k)) * N
+ dot = childConstUnions[0].dot(childConstUnions[1]);
+ double eta = childConstUnions[2][0].getDConst();
+ double k = 1.0 - eta * eta * (1.0 - dot * dot);
+ if (k < 0.0) {
+ for (int comp = 0; comp < numComps; ++comp)
+ newConstArray[comp].setDConst(0.0);
+ } else {
+ for (int comp = 0; comp < numComps; ++comp)
+ newConstArray[comp].setDConst(eta * childConstUnions[0][comp].getDConst() - (eta * dot + sqrt(k)) * childConstUnions[1][comp].getDConst());
+ }
+ break;
+ }
+ case EOpOuterProduct:
+ {
+ int numRows = numComps;
+ int numCols = children[1]->getAsConstantUnion()->getType().computeNumComponents();
+ for (int row = 0; row < numRows; ++row)
+ for (int col = 0; col < numCols; ++col)
+ newConstArray[col * numRows + row] = childConstUnions[0][row] * childConstUnions[1][col];
+ break;
+ }
+ default:
+ return aggrNode;
+ }
+ }
+
+ TIntermConstantUnion *newNode = new TIntermConstantUnion(newConstArray, aggrNode->getType());
+ newNode->getWritableType().getQualifier().storage = EvqConst;
+ newNode->setLoc(aggrNode->getLoc());
+
+ return newNode;
+}
+
+bool TIntermediate::areAllChildConst(TIntermAggregate* aggrNode)
+{
+ bool allConstant = true;
+
+ // check if all the child nodes are constants so that they can be inserted into
+ // the parent node
+ if (aggrNode) {
+ TIntermSequence& childSequenceVector = aggrNode->getSequence();
+ for (TIntermSequence::iterator p = childSequenceVector.begin();
+ p != childSequenceVector.end(); p++) {
+ if (!(*p)->getAsTyped()->getAsConstantUnion())
+ return false;
+ }
+ }
+
+ return allConstant;
+}
+
+TIntermTyped* TIntermediate::foldConstructor(TIntermAggregate* aggrNode)
+{
+ bool error = false;
+
+ TConstUnionArray unionArray(aggrNode->getType().computeNumComponents());
+ if (aggrNode->getSequence().size() == 1)
+ error = parseConstTree(aggrNode, unionArray, aggrNode->getOp(), aggrNode->getType(), true);
+ else
+ error = parseConstTree(aggrNode, unionArray, aggrNode->getOp(), aggrNode->getType());
+
+ if (error)
+ return aggrNode;
+
+ return addConstantUnion(unionArray, aggrNode->getType(), aggrNode->getLoc());
+}
+
+//
+// Constant folding of a bracket (array-style) dereference or struct-like dot
+// dereference. Can handle anything except a multi-character swizzle, though
+// all swizzles may go to foldSwizzle().
+//
+TIntermTyped* TIntermediate::foldDereference(TIntermTyped* node, int index, const TSourceLoc& loc)
+{
+ TType dereferencedType(node->getType(), index);
+ dereferencedType.getQualifier().storage = EvqConst;
+ TIntermTyped* result = 0;
+ int size = dereferencedType.computeNumComponents();
+
+ // arrays, vectors, matrices, all use simple multiplicative math
+ // while structures need to add up heterogeneous members
+ int start;
+ if (node->getType().isCoopMat())
+ start = 0;
+ else if (node->isArray() || ! node->isStruct())
+ start = size * index;
+ else {
+ // it is a structure
+ assert(node->isStruct());
+ start = 0;
+ for (int i = 0; i < index; ++i)
+ start += (*node->getType().getStruct())[i].type->computeNumComponents();
+ }
+
+ result = addConstantUnion(TConstUnionArray(node->getAsConstantUnion()->getConstArray(), start, size), node->getType(), loc);
+
+ if (result == 0)
+ result = node;
+ else
+ result->setType(dereferencedType);
+
+ return result;
+}
+
+//
+// Make a constant vector node or constant scalar node, representing a given
+// constant vector and constant swizzle into it.
+//
+TIntermTyped* TIntermediate::foldSwizzle(TIntermTyped* node, TSwizzleSelectors<TVectorSelector>& selectors, const TSourceLoc& loc)
+{
+ const TConstUnionArray& unionArray = node->getAsConstantUnion()->getConstArray();
+ TConstUnionArray constArray(selectors.size());
+
+ for (int i = 0; i < selectors.size(); i++)
+ constArray[i] = unionArray[selectors[i]];
+
+ TIntermTyped* result = addConstantUnion(constArray, node->getType(), loc);
+
+ if (result == 0)
+ result = node;
+ else
+ result->setType(TType(node->getBasicType(), EvqConst, selectors.size()));
+
+ return result;
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/InfoSink.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/InfoSink.cpp
new file mode 100644
index 0000000..d00c422
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/InfoSink.cpp
@@ -0,0 +1,113 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "../Include/InfoSink.h"
+
+#include <cstring>
+
+namespace glslang {
+
+void TInfoSinkBase::append(const char* s)
+{
+ if (outputStream & EString) {
+ if (s == nullptr)
+ sink.append("(null)");
+ else {
+ checkMem(strlen(s));
+ sink.append(s);
+ }
+ }
+
+//#ifdef _WIN32
+// if (outputStream & EDebugger)
+// OutputDebugString(s);
+//#endif
+
+ if (outputStream & EStdOut)
+ fprintf(stdout, "%s", s);
+}
+
+void TInfoSinkBase::append(int count, char c)
+{
+ if (outputStream & EString) {
+ checkMem(count);
+ sink.append(count, c);
+ }
+
+//#ifdef _WIN32
+// if (outputStream & EDebugger) {
+// char str[2];
+// str[0] = c;
+// str[1] = '\0';
+// OutputDebugString(str);
+// }
+//#endif
+
+ if (outputStream & EStdOut)
+ fprintf(stdout, "%c", c);
+}
+
+void TInfoSinkBase::append(const TPersistString& t)
+{
+ if (outputStream & EString) {
+ checkMem(t.size());
+ sink.append(t);
+ }
+
+//#ifdef _WIN32
+// if (outputStream & EDebugger)
+// OutputDebugString(t.c_str());
+//#endif
+
+ if (outputStream & EStdOut)
+ fprintf(stdout, "%s", t.c_str());
+}
+
+void TInfoSinkBase::append(const TString& t)
+{
+ if (outputStream & EString) {
+ checkMem(t.size());
+ sink.append(t.c_str());
+ }
+
+//#ifdef _WIN32
+// if (outputStream & EDebugger)
+// OutputDebugString(t.c_str());
+//#endif
+
+ if (outputStream & EStdOut)
+ fprintf(stdout, "%s", t.c_str());
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/Initialize.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/Initialize.cpp
new file mode 100644
index 0000000..0498b48
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/Initialize.cpp
@@ -0,0 +1,9634 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2012-2016 LunarG, Inc.
+// Copyright (C) 2015-2018 Google, Inc.
+// Copyright (C) 2017 ARM Limited.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+//
+// Create strings that declare built-in definitions, add built-ins programmatically
+// that cannot be expressed in the strings, and establish mappings between
+// built-in functions and operators.
+//
+// Where to put a built-in:
+// TBuiltIns::initialize(version,profile) context-independent textual built-ins; add them to the right string
+// TBuiltIns::initialize(resources,...) context-dependent textual built-ins; add them to the right string
+// TBuiltIns::identifyBuiltIns(...,symbolTable) context-independent programmatic additions/mappings to the symbol table,
+// including identifying what extensions are needed if a version does not allow a symbol
+// TBuiltIns::identifyBuiltIns(...,symbolTable, resources) context-dependent programmatic additions/mappings to the symbol table,
+// including identifying what extensions are needed if a version does not allow a symbol
+//
+
+#include "../Include/intermediate.h"
+#include "Initialize.h"
+
+namespace glslang {
+
+// TODO: ARB_Compatability: do full extension support
+const bool ARBCompatibility = true;
+
+const bool ForwardCompatibility = false;
+
+// change this back to false if depending on textual spellings of texturing calls when consuming the AST
+// Using PureOperatorBuiltins=false is deprecated.
+bool PureOperatorBuiltins = true;
+
+inline bool IncludeLegacy(int version, EProfile profile, const SpvVersion& spvVersion)
+{
+ return profile != EEsProfile && (version <= 130 || (spvVersion.spv == 0 && ARBCompatibility) || profile == ECompatibilityProfile);
+}
+
+// Construct TBuiltInParseables base class. This can be used for language-common constructs.
+TBuiltInParseables::TBuiltInParseables()
+{
+}
+
+// Destroy TBuiltInParseables.
+TBuiltInParseables::~TBuiltInParseables()
+{
+}
+
+TBuiltIns::TBuiltIns()
+{
+ // Set up textual representations for making all the permutations
+ // of texturing/imaging functions.
+ prefixes[EbtFloat] = "";
+#ifdef AMD_EXTENSIONS
+ prefixes[EbtFloat16] = "f16";
+#endif
+ prefixes[EbtInt8] = "i8";
+ prefixes[EbtUint8] = "u8";
+ prefixes[EbtInt16] = "i16";
+ prefixes[EbtUint16] = "u16";
+ prefixes[EbtInt] = "i";
+ prefixes[EbtUint] = "u";
+ postfixes[2] = "2";
+ postfixes[3] = "3";
+ postfixes[4] = "4";
+
+ // Map from symbolic class of texturing dimension to numeric dimensions.
+ dimMap[Esd1D] = 1;
+ dimMap[Esd2D] = 2;
+ dimMap[EsdRect] = 2;
+ dimMap[Esd3D] = 3;
+ dimMap[EsdCube] = 3;
+ dimMap[EsdBuffer] = 1;
+ dimMap[EsdSubpass] = 2; // potientially unused for now
+}
+
+TBuiltIns::~TBuiltIns()
+{
+}
+
+
+//
+// Add all context-independent built-in functions and variables that are present
+// for the given version and profile. Share common ones across stages, otherwise
+// make stage-specific entries.
+//
+// Most built-ins variables can be added as simple text strings. Some need to
+// be added programmatically, which is done later in IdentifyBuiltIns() below.
+//
+void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvVersion)
+{
+ //============================================================================
+ //
+ // Prototypes for built-in functions used repeatly by different shaders
+ //
+ //============================================================================
+
+ //
+ // Derivatives Functions.
+ //
+ TString derivatives (
+ "float dFdx(float p);"
+ "vec2 dFdx(vec2 p);"
+ "vec3 dFdx(vec3 p);"
+ "vec4 dFdx(vec4 p);"
+
+ "float dFdy(float p);"
+ "vec2 dFdy(vec2 p);"
+ "vec3 dFdy(vec3 p);"
+ "vec4 dFdy(vec4 p);"
+
+ "float fwidth(float p);"
+ "vec2 fwidth(vec2 p);"
+ "vec3 fwidth(vec3 p);"
+ "vec4 fwidth(vec4 p);"
+ );
+
+ TString derivativeControls (
+ "float dFdxFine(float p);"
+ "vec2 dFdxFine(vec2 p);"
+ "vec3 dFdxFine(vec3 p);"
+ "vec4 dFdxFine(vec4 p);"
+
+ "float dFdyFine(float p);"
+ "vec2 dFdyFine(vec2 p);"
+ "vec3 dFdyFine(vec3 p);"
+ "vec4 dFdyFine(vec4 p);"
+
+ "float fwidthFine(float p);"
+ "vec2 fwidthFine(vec2 p);"
+ "vec3 fwidthFine(vec3 p);"
+ "vec4 fwidthFine(vec4 p);"
+
+ "float dFdxCoarse(float p);"
+ "vec2 dFdxCoarse(vec2 p);"
+ "vec3 dFdxCoarse(vec3 p);"
+ "vec4 dFdxCoarse(vec4 p);"
+
+ "float dFdyCoarse(float p);"
+ "vec2 dFdyCoarse(vec2 p);"
+ "vec3 dFdyCoarse(vec3 p);"
+ "vec4 dFdyCoarse(vec4 p);"
+
+ "float fwidthCoarse(float p);"
+ "vec2 fwidthCoarse(vec2 p);"
+ "vec3 fwidthCoarse(vec3 p);"
+ "vec4 fwidthCoarse(vec4 p);"
+ );
+
+ TString derivativesAndControl16bits (
+ "float16_t dFdx(float16_t);"
+ "f16vec2 dFdx(f16vec2);"
+ "f16vec3 dFdx(f16vec3);"
+ "f16vec4 dFdx(f16vec4);"
+
+ "float16_t dFdy(float16_t);"
+ "f16vec2 dFdy(f16vec2);"
+ "f16vec3 dFdy(f16vec3);"
+ "f16vec4 dFdy(f16vec4);"
+
+ "float16_t dFdxFine(float16_t);"
+ "f16vec2 dFdxFine(f16vec2);"
+ "f16vec3 dFdxFine(f16vec3);"
+ "f16vec4 dFdxFine(f16vec4);"
+
+ "float16_t dFdyFine(float16_t);"
+ "f16vec2 dFdyFine(f16vec2);"
+ "f16vec3 dFdyFine(f16vec3);"
+ "f16vec4 dFdyFine(f16vec4);"
+
+ "float16_t dFdxCoarse(float16_t);"
+ "f16vec2 dFdxCoarse(f16vec2);"
+ "f16vec3 dFdxCoarse(f16vec3);"
+ "f16vec4 dFdxCoarse(f16vec4);"
+
+ "float16_t dFdyCoarse(float16_t);"
+ "f16vec2 dFdyCoarse(f16vec2);"
+ "f16vec3 dFdyCoarse(f16vec3);"
+ "f16vec4 dFdyCoarse(f16vec4);"
+
+ "float16_t fwidth(float16_t);"
+ "f16vec2 fwidth(f16vec2);"
+ "f16vec3 fwidth(f16vec3);"
+ "f16vec4 fwidth(f16vec4);"
+
+ "float16_t fwidthFine(float16_t);"
+ "f16vec2 fwidthFine(f16vec2);"
+ "f16vec3 fwidthFine(f16vec3);"
+ "f16vec4 fwidthFine(f16vec4);"
+
+ "float16_t fwidthCoarse(float16_t);"
+ "f16vec2 fwidthCoarse(f16vec2);"
+ "f16vec3 fwidthCoarse(f16vec3);"
+ "f16vec4 fwidthCoarse(f16vec4);"
+ );
+
+ TString derivativesAndControl64bits (
+ "float64_t dFdx(float64_t);"
+ "f64vec2 dFdx(f64vec2);"
+ "f64vec3 dFdx(f64vec3);"
+ "f64vec4 dFdx(f64vec4);"
+
+ "float64_t dFdy(float64_t);"
+ "f64vec2 dFdy(f64vec2);"
+ "f64vec3 dFdy(f64vec3);"
+ "f64vec4 dFdy(f64vec4);"
+
+ "float64_t dFdxFine(float64_t);"
+ "f64vec2 dFdxFine(f64vec2);"
+ "f64vec3 dFdxFine(f64vec3);"
+ "f64vec4 dFdxFine(f64vec4);"
+
+ "float64_t dFdyFine(float64_t);"
+ "f64vec2 dFdyFine(f64vec2);"
+ "f64vec3 dFdyFine(f64vec3);"
+ "f64vec4 dFdyFine(f64vec4);"
+
+ "float64_t dFdxCoarse(float64_t);"
+ "f64vec2 dFdxCoarse(f64vec2);"
+ "f64vec3 dFdxCoarse(f64vec3);"
+ "f64vec4 dFdxCoarse(f64vec4);"
+
+ "float64_t dFdyCoarse(float64_t);"
+ "f64vec2 dFdyCoarse(f64vec2);"
+ "f64vec3 dFdyCoarse(f64vec3);"
+ "f64vec4 dFdyCoarse(f64vec4);"
+
+ "float64_t fwidth(float64_t);"
+ "f64vec2 fwidth(f64vec2);"
+ "f64vec3 fwidth(f64vec3);"
+ "f64vec4 fwidth(f64vec4);"
+
+ "float64_t fwidthFine(float64_t);"
+ "f64vec2 fwidthFine(f64vec2);"
+ "f64vec3 fwidthFine(f64vec3);"
+ "f64vec4 fwidthFine(f64vec4);"
+
+ "float64_t fwidthCoarse(float64_t);"
+ "f64vec2 fwidthCoarse(f64vec2);"
+ "f64vec3 fwidthCoarse(f64vec3);"
+ "f64vec4 fwidthCoarse(f64vec4);"
+ );
+
+ //============================================================================
+ //
+ // Prototypes for built-in functions seen by both vertex and fragment shaders.
+ //
+ //============================================================================
+
+ //
+ // Angle and Trigonometric Functions.
+ //
+ commonBuiltins.append(
+ "float radians(float degrees);"
+ "vec2 radians(vec2 degrees);"
+ "vec3 radians(vec3 degrees);"
+ "vec4 radians(vec4 degrees);"
+
+ "float degrees(float radians);"
+ "vec2 degrees(vec2 radians);"
+ "vec3 degrees(vec3 radians);"
+ "vec4 degrees(vec4 radians);"
+
+ "float sin(float angle);"
+ "vec2 sin(vec2 angle);"
+ "vec3 sin(vec3 angle);"
+ "vec4 sin(vec4 angle);"
+
+ "float cos(float angle);"
+ "vec2 cos(vec2 angle);"
+ "vec3 cos(vec3 angle);"
+ "vec4 cos(vec4 angle);"
+
+ "float tan(float angle);"
+ "vec2 tan(vec2 angle);"
+ "vec3 tan(vec3 angle);"
+ "vec4 tan(vec4 angle);"
+
+ "float asin(float x);"
+ "vec2 asin(vec2 x);"
+ "vec3 asin(vec3 x);"
+ "vec4 asin(vec4 x);"
+
+ "float acos(float x);"
+ "vec2 acos(vec2 x);"
+ "vec3 acos(vec3 x);"
+ "vec4 acos(vec4 x);"
+
+ "float atan(float y, float x);"
+ "vec2 atan(vec2 y, vec2 x);"
+ "vec3 atan(vec3 y, vec3 x);"
+ "vec4 atan(vec4 y, vec4 x);"
+
+ "float atan(float y_over_x);"
+ "vec2 atan(vec2 y_over_x);"
+ "vec3 atan(vec3 y_over_x);"
+ "vec4 atan(vec4 y_over_x);"
+
+ "\n");
+
+ if (version >= 130) {
+ commonBuiltins.append(
+ "float sinh(float angle);"
+ "vec2 sinh(vec2 angle);"
+ "vec3 sinh(vec3 angle);"
+ "vec4 sinh(vec4 angle);"
+
+ "float cosh(float angle);"
+ "vec2 cosh(vec2 angle);"
+ "vec3 cosh(vec3 angle);"
+ "vec4 cosh(vec4 angle);"
+
+ "float tanh(float angle);"
+ "vec2 tanh(vec2 angle);"
+ "vec3 tanh(vec3 angle);"
+ "vec4 tanh(vec4 angle);"
+
+ "float asinh(float x);"
+ "vec2 asinh(vec2 x);"
+ "vec3 asinh(vec3 x);"
+ "vec4 asinh(vec4 x);"
+
+ "float acosh(float x);"
+ "vec2 acosh(vec2 x);"
+ "vec3 acosh(vec3 x);"
+ "vec4 acosh(vec4 x);"
+
+ "float atanh(float y_over_x);"
+ "vec2 atanh(vec2 y_over_x);"
+ "vec3 atanh(vec3 y_over_x);"
+ "vec4 atanh(vec4 y_over_x);"
+
+ "\n");
+ }
+
+ //
+ // Exponential Functions.
+ //
+ commonBuiltins.append(
+ "float pow(float x, float y);"
+ "vec2 pow(vec2 x, vec2 y);"
+ "vec3 pow(vec3 x, vec3 y);"
+ "vec4 pow(vec4 x, vec4 y);"
+
+ "float exp(float x);"
+ "vec2 exp(vec2 x);"
+ "vec3 exp(vec3 x);"
+ "vec4 exp(vec4 x);"
+
+ "float log(float x);"
+ "vec2 log(vec2 x);"
+ "vec3 log(vec3 x);"
+ "vec4 log(vec4 x);"
+
+ "float exp2(float x);"
+ "vec2 exp2(vec2 x);"
+ "vec3 exp2(vec3 x);"
+ "vec4 exp2(vec4 x);"
+
+ "float log2(float x);"
+ "vec2 log2(vec2 x);"
+ "vec3 log2(vec3 x);"
+ "vec4 log2(vec4 x);"
+
+ "float sqrt(float x);"
+ "vec2 sqrt(vec2 x);"
+ "vec3 sqrt(vec3 x);"
+ "vec4 sqrt(vec4 x);"
+
+ "float inversesqrt(float x);"
+ "vec2 inversesqrt(vec2 x);"
+ "vec3 inversesqrt(vec3 x);"
+ "vec4 inversesqrt(vec4 x);"
+
+ "\n");
+
+ //
+ // Common Functions.
+ //
+ commonBuiltins.append(
+ "float abs(float x);"
+ "vec2 abs(vec2 x);"
+ "vec3 abs(vec3 x);"
+ "vec4 abs(vec4 x);"
+
+ "float sign(float x);"
+ "vec2 sign(vec2 x);"
+ "vec3 sign(vec3 x);"
+ "vec4 sign(vec4 x);"
+
+ "float floor(float x);"
+ "vec2 floor(vec2 x);"
+ "vec3 floor(vec3 x);"
+ "vec4 floor(vec4 x);"
+
+ "float ceil(float x);"
+ "vec2 ceil(vec2 x);"
+ "vec3 ceil(vec3 x);"
+ "vec4 ceil(vec4 x);"
+
+ "float fract(float x);"
+ "vec2 fract(vec2 x);"
+ "vec3 fract(vec3 x);"
+ "vec4 fract(vec4 x);"
+
+ "float mod(float x, float y);"
+ "vec2 mod(vec2 x, float y);"
+ "vec3 mod(vec3 x, float y);"
+ "vec4 mod(vec4 x, float y);"
+ "vec2 mod(vec2 x, vec2 y);"
+ "vec3 mod(vec3 x, vec3 y);"
+ "vec4 mod(vec4 x, vec4 y);"
+
+ "float min(float x, float y);"
+ "vec2 min(vec2 x, float y);"
+ "vec3 min(vec3 x, float y);"
+ "vec4 min(vec4 x, float y);"
+ "vec2 min(vec2 x, vec2 y);"
+ "vec3 min(vec3 x, vec3 y);"
+ "vec4 min(vec4 x, vec4 y);"
+
+ "float max(float x, float y);"
+ "vec2 max(vec2 x, float y);"
+ "vec3 max(vec3 x, float y);"
+ "vec4 max(vec4 x, float y);"
+ "vec2 max(vec2 x, vec2 y);"
+ "vec3 max(vec3 x, vec3 y);"
+ "vec4 max(vec4 x, vec4 y);"
+
+ "float clamp(float x, float minVal, float maxVal);"
+ "vec2 clamp(vec2 x, float minVal, float maxVal);"
+ "vec3 clamp(vec3 x, float minVal, float maxVal);"
+ "vec4 clamp(vec4 x, float minVal, float maxVal);"
+ "vec2 clamp(vec2 x, vec2 minVal, vec2 maxVal);"
+ "vec3 clamp(vec3 x, vec3 minVal, vec3 maxVal);"
+ "vec4 clamp(vec4 x, vec4 minVal, vec4 maxVal);"
+
+ "float mix(float x, float y, float a);"
+ "vec2 mix(vec2 x, vec2 y, float a);"
+ "vec3 mix(vec3 x, vec3 y, float a);"
+ "vec4 mix(vec4 x, vec4 y, float a);"
+ "vec2 mix(vec2 x, vec2 y, vec2 a);"
+ "vec3 mix(vec3 x, vec3 y, vec3 a);"
+ "vec4 mix(vec4 x, vec4 y, vec4 a);"
+
+ "float step(float edge, float x);"
+ "vec2 step(vec2 edge, vec2 x);"
+ "vec3 step(vec3 edge, vec3 x);"
+ "vec4 step(vec4 edge, vec4 x);"
+ "vec2 step(float edge, vec2 x);"
+ "vec3 step(float edge, vec3 x);"
+ "vec4 step(float edge, vec4 x);"
+
+ "float smoothstep(float edge0, float edge1, float x);"
+ "vec2 smoothstep(vec2 edge0, vec2 edge1, vec2 x);"
+ "vec3 smoothstep(vec3 edge0, vec3 edge1, vec3 x);"
+ "vec4 smoothstep(vec4 edge0, vec4 edge1, vec4 x);"
+ "vec2 smoothstep(float edge0, float edge1, vec2 x);"
+ "vec3 smoothstep(float edge0, float edge1, vec3 x);"
+ "vec4 smoothstep(float edge0, float edge1, vec4 x);"
+
+ "\n");
+
+ if (version >= 130) {
+ commonBuiltins.append(
+ " int abs( int x);"
+ "ivec2 abs(ivec2 x);"
+ "ivec3 abs(ivec3 x);"
+ "ivec4 abs(ivec4 x);"
+
+ " int sign( int x);"
+ "ivec2 sign(ivec2 x);"
+ "ivec3 sign(ivec3 x);"
+ "ivec4 sign(ivec4 x);"
+
+ "float trunc(float x);"
+ "vec2 trunc(vec2 x);"
+ "vec3 trunc(vec3 x);"
+ "vec4 trunc(vec4 x);"
+
+ "float round(float x);"
+ "vec2 round(vec2 x);"
+ "vec3 round(vec3 x);"
+ "vec4 round(vec4 x);"
+
+ "float roundEven(float x);"
+ "vec2 roundEven(vec2 x);"
+ "vec3 roundEven(vec3 x);"
+ "vec4 roundEven(vec4 x);"
+
+ "float modf(float, out float);"
+ "vec2 modf(vec2, out vec2 );"
+ "vec3 modf(vec3, out vec3 );"
+ "vec4 modf(vec4, out vec4 );"
+
+ " int min(int x, int y);"
+ "ivec2 min(ivec2 x, int y);"
+ "ivec3 min(ivec3 x, int y);"
+ "ivec4 min(ivec4 x, int y);"
+ "ivec2 min(ivec2 x, ivec2 y);"
+ "ivec3 min(ivec3 x, ivec3 y);"
+ "ivec4 min(ivec4 x, ivec4 y);"
+
+ " uint min(uint x, uint y);"
+ "uvec2 min(uvec2 x, uint y);"
+ "uvec3 min(uvec3 x, uint y);"
+ "uvec4 min(uvec4 x, uint y);"
+ "uvec2 min(uvec2 x, uvec2 y);"
+ "uvec3 min(uvec3 x, uvec3 y);"
+ "uvec4 min(uvec4 x, uvec4 y);"
+
+ " int max(int x, int y);"
+ "ivec2 max(ivec2 x, int y);"
+ "ivec3 max(ivec3 x, int y);"
+ "ivec4 max(ivec4 x, int y);"
+ "ivec2 max(ivec2 x, ivec2 y);"
+ "ivec3 max(ivec3 x, ivec3 y);"
+ "ivec4 max(ivec4 x, ivec4 y);"
+
+ " uint max(uint x, uint y);"
+ "uvec2 max(uvec2 x, uint y);"
+ "uvec3 max(uvec3 x, uint y);"
+ "uvec4 max(uvec4 x, uint y);"
+ "uvec2 max(uvec2 x, uvec2 y);"
+ "uvec3 max(uvec3 x, uvec3 y);"
+ "uvec4 max(uvec4 x, uvec4 y);"
+
+ "int clamp(int x, int minVal, int maxVal);"
+ "ivec2 clamp(ivec2 x, int minVal, int maxVal);"
+ "ivec3 clamp(ivec3 x, int minVal, int maxVal);"
+ "ivec4 clamp(ivec4 x, int minVal, int maxVal);"
+ "ivec2 clamp(ivec2 x, ivec2 minVal, ivec2 maxVal);"
+ "ivec3 clamp(ivec3 x, ivec3 minVal, ivec3 maxVal);"
+ "ivec4 clamp(ivec4 x, ivec4 minVal, ivec4 maxVal);"
+
+ "uint clamp(uint x, uint minVal, uint maxVal);"
+ "uvec2 clamp(uvec2 x, uint minVal, uint maxVal);"
+ "uvec3 clamp(uvec3 x, uint minVal, uint maxVal);"
+ "uvec4 clamp(uvec4 x, uint minVal, uint maxVal);"
+ "uvec2 clamp(uvec2 x, uvec2 minVal, uvec2 maxVal);"
+ "uvec3 clamp(uvec3 x, uvec3 minVal, uvec3 maxVal);"
+ "uvec4 clamp(uvec4 x, uvec4 minVal, uvec4 maxVal);"
+
+ "float mix(float x, float y, bool a);"
+ "vec2 mix(vec2 x, vec2 y, bvec2 a);"
+ "vec3 mix(vec3 x, vec3 y, bvec3 a);"
+ "vec4 mix(vec4 x, vec4 y, bvec4 a);"
+
+ "bool isnan(float x);"
+ "bvec2 isnan(vec2 x);"
+ "bvec3 isnan(vec3 x);"
+ "bvec4 isnan(vec4 x);"
+
+ "bool isinf(float x);"
+ "bvec2 isinf(vec2 x);"
+ "bvec3 isinf(vec3 x);"
+ "bvec4 isinf(vec4 x);"
+
+ "\n");
+ }
+
+ //
+ // double functions added to desktop 4.00, but not fma, frexp, ldexp, or pack/unpack
+ //
+ if (profile != EEsProfile && version >= 400) {
+ commonBuiltins.append(
+
+ "double sqrt(double);"
+ "dvec2 sqrt(dvec2);"
+ "dvec3 sqrt(dvec3);"
+ "dvec4 sqrt(dvec4);"
+
+ "double inversesqrt(double);"
+ "dvec2 inversesqrt(dvec2);"
+ "dvec3 inversesqrt(dvec3);"
+ "dvec4 inversesqrt(dvec4);"
+
+ "double abs(double);"
+ "dvec2 abs(dvec2);"
+ "dvec3 abs(dvec3);"
+ "dvec4 abs(dvec4);"
+
+ "double sign(double);"
+ "dvec2 sign(dvec2);"
+ "dvec3 sign(dvec3);"
+ "dvec4 sign(dvec4);"
+
+ "double floor(double);"
+ "dvec2 floor(dvec2);"
+ "dvec3 floor(dvec3);"
+ "dvec4 floor(dvec4);"
+
+ "double trunc(double);"
+ "dvec2 trunc(dvec2);"
+ "dvec3 trunc(dvec3);"
+ "dvec4 trunc(dvec4);"
+
+ "double round(double);"
+ "dvec2 round(dvec2);"
+ "dvec3 round(dvec3);"
+ "dvec4 round(dvec4);"
+
+ "double roundEven(double);"
+ "dvec2 roundEven(dvec2);"
+ "dvec3 roundEven(dvec3);"
+ "dvec4 roundEven(dvec4);"
+
+ "double ceil(double);"
+ "dvec2 ceil(dvec2);"
+ "dvec3 ceil(dvec3);"
+ "dvec4 ceil(dvec4);"
+
+ "double fract(double);"
+ "dvec2 fract(dvec2);"
+ "dvec3 fract(dvec3);"
+ "dvec4 fract(dvec4);"
+
+ "double mod(double, double);"
+ "dvec2 mod(dvec2 , double);"
+ "dvec3 mod(dvec3 , double);"
+ "dvec4 mod(dvec4 , double);"
+ "dvec2 mod(dvec2 , dvec2);"
+ "dvec3 mod(dvec3 , dvec3);"
+ "dvec4 mod(dvec4 , dvec4);"
+
+ "double modf(double, out double);"
+ "dvec2 modf(dvec2, out dvec2);"
+ "dvec3 modf(dvec3, out dvec3);"
+ "dvec4 modf(dvec4, out dvec4);"
+
+ "double min(double, double);"
+ "dvec2 min(dvec2, double);"
+ "dvec3 min(dvec3, double);"
+ "dvec4 min(dvec4, double);"
+ "dvec2 min(dvec2, dvec2);"
+ "dvec3 min(dvec3, dvec3);"
+ "dvec4 min(dvec4, dvec4);"
+
+ "double max(double, double);"
+ "dvec2 max(dvec2 , double);"
+ "dvec3 max(dvec3 , double);"
+ "dvec4 max(dvec4 , double);"
+ "dvec2 max(dvec2 , dvec2);"
+ "dvec3 max(dvec3 , dvec3);"
+ "dvec4 max(dvec4 , dvec4);"
+
+ "double clamp(double, double, double);"
+ "dvec2 clamp(dvec2 , double, double);"
+ "dvec3 clamp(dvec3 , double, double);"
+ "dvec4 clamp(dvec4 , double, double);"
+ "dvec2 clamp(dvec2 , dvec2 , dvec2);"
+ "dvec3 clamp(dvec3 , dvec3 , dvec3);"
+ "dvec4 clamp(dvec4 , dvec4 , dvec4);"
+
+ "double mix(double, double, double);"
+ "dvec2 mix(dvec2, dvec2, double);"
+ "dvec3 mix(dvec3, dvec3, double);"
+ "dvec4 mix(dvec4, dvec4, double);"
+ "dvec2 mix(dvec2, dvec2, dvec2);"
+ "dvec3 mix(dvec3, dvec3, dvec3);"
+ "dvec4 mix(dvec4, dvec4, dvec4);"
+ "double mix(double, double, bool);"
+ "dvec2 mix(dvec2, dvec2, bvec2);"
+ "dvec3 mix(dvec3, dvec3, bvec3);"
+ "dvec4 mix(dvec4, dvec4, bvec4);"
+
+ "double step(double, double);"
+ "dvec2 step(dvec2 , dvec2);"
+ "dvec3 step(dvec3 , dvec3);"
+ "dvec4 step(dvec4 , dvec4);"
+ "dvec2 step(double, dvec2);"
+ "dvec3 step(double, dvec3);"
+ "dvec4 step(double, dvec4);"
+
+ "double smoothstep(double, double, double);"
+ "dvec2 smoothstep(dvec2 , dvec2 , dvec2);"
+ "dvec3 smoothstep(dvec3 , dvec3 , dvec3);"
+ "dvec4 smoothstep(dvec4 , dvec4 , dvec4);"
+ "dvec2 smoothstep(double, double, dvec2);"
+ "dvec3 smoothstep(double, double, dvec3);"
+ "dvec4 smoothstep(double, double, dvec4);"
+
+ "bool isnan(double);"
+ "bvec2 isnan(dvec2);"
+ "bvec3 isnan(dvec3);"
+ "bvec4 isnan(dvec4);"
+
+ "bool isinf(double);"
+ "bvec2 isinf(dvec2);"
+ "bvec3 isinf(dvec3);"
+ "bvec4 isinf(dvec4);"
+
+ "double length(double);"
+ "double length(dvec2);"
+ "double length(dvec3);"
+ "double length(dvec4);"
+
+ "double distance(double, double);"
+ "double distance(dvec2 , dvec2);"
+ "double distance(dvec3 , dvec3);"
+ "double distance(dvec4 , dvec4);"
+
+ "double dot(double, double);"
+ "double dot(dvec2 , dvec2);"
+ "double dot(dvec3 , dvec3);"
+ "double dot(dvec4 , dvec4);"
+
+ "dvec3 cross(dvec3, dvec3);"
+
+ "double normalize(double);"
+ "dvec2 normalize(dvec2);"
+ "dvec3 normalize(dvec3);"
+ "dvec4 normalize(dvec4);"
+
+ "double faceforward(double, double, double);"
+ "dvec2 faceforward(dvec2, dvec2, dvec2);"
+ "dvec3 faceforward(dvec3, dvec3, dvec3);"
+ "dvec4 faceforward(dvec4, dvec4, dvec4);"
+
+ "double reflect(double, double);"
+ "dvec2 reflect(dvec2 , dvec2 );"
+ "dvec3 reflect(dvec3 , dvec3 );"
+ "dvec4 reflect(dvec4 , dvec4 );"
+
+ "double refract(double, double, double);"
+ "dvec2 refract(dvec2 , dvec2 , double);"
+ "dvec3 refract(dvec3 , dvec3 , double);"
+ "dvec4 refract(dvec4 , dvec4 , double);"
+
+ "dmat2 matrixCompMult(dmat2, dmat2);"
+ "dmat3 matrixCompMult(dmat3, dmat3);"
+ "dmat4 matrixCompMult(dmat4, dmat4);"
+ "dmat2x3 matrixCompMult(dmat2x3, dmat2x3);"
+ "dmat2x4 matrixCompMult(dmat2x4, dmat2x4);"
+ "dmat3x2 matrixCompMult(dmat3x2, dmat3x2);"
+ "dmat3x4 matrixCompMult(dmat3x4, dmat3x4);"
+ "dmat4x2 matrixCompMult(dmat4x2, dmat4x2);"
+ "dmat4x3 matrixCompMult(dmat4x3, dmat4x3);"
+
+ "dmat2 outerProduct(dvec2, dvec2);"
+ "dmat3 outerProduct(dvec3, dvec3);"
+ "dmat4 outerProduct(dvec4, dvec4);"
+ "dmat2x3 outerProduct(dvec3, dvec2);"
+ "dmat3x2 outerProduct(dvec2, dvec3);"
+ "dmat2x4 outerProduct(dvec4, dvec2);"
+ "dmat4x2 outerProduct(dvec2, dvec4);"
+ "dmat3x4 outerProduct(dvec4, dvec3);"
+ "dmat4x3 outerProduct(dvec3, dvec4);"
+
+ "dmat2 transpose(dmat2);"
+ "dmat3 transpose(dmat3);"
+ "dmat4 transpose(dmat4);"
+ "dmat2x3 transpose(dmat3x2);"
+ "dmat3x2 transpose(dmat2x3);"
+ "dmat2x4 transpose(dmat4x2);"
+ "dmat4x2 transpose(dmat2x4);"
+ "dmat3x4 transpose(dmat4x3);"
+ "dmat4x3 transpose(dmat3x4);"
+
+ "double determinant(dmat2);"
+ "double determinant(dmat3);"
+ "double determinant(dmat4);"
+
+ "dmat2 inverse(dmat2);"
+ "dmat3 inverse(dmat3);"
+ "dmat4 inverse(dmat4);"
+
+ "bvec2 lessThan(dvec2, dvec2);"
+ "bvec3 lessThan(dvec3, dvec3);"
+ "bvec4 lessThan(dvec4, dvec4);"
+
+ "bvec2 lessThanEqual(dvec2, dvec2);"
+ "bvec3 lessThanEqual(dvec3, dvec3);"
+ "bvec4 lessThanEqual(dvec4, dvec4);"
+
+ "bvec2 greaterThan(dvec2, dvec2);"
+ "bvec3 greaterThan(dvec3, dvec3);"
+ "bvec4 greaterThan(dvec4, dvec4);"
+
+ "bvec2 greaterThanEqual(dvec2, dvec2);"
+ "bvec3 greaterThanEqual(dvec3, dvec3);"
+ "bvec4 greaterThanEqual(dvec4, dvec4);"
+
+ "bvec2 equal(dvec2, dvec2);"
+ "bvec3 equal(dvec3, dvec3);"
+ "bvec4 equal(dvec4, dvec4);"
+
+ "bvec2 notEqual(dvec2, dvec2);"
+ "bvec3 notEqual(dvec3, dvec3);"
+ "bvec4 notEqual(dvec4, dvec4);"
+
+ "\n");
+ }
+
+ if (profile != EEsProfile && version >= 450) {
+ commonBuiltins.append(
+
+ "int64_t abs(int64_t);"
+ "i64vec2 abs(i64vec2);"
+ "i64vec3 abs(i64vec3);"
+ "i64vec4 abs(i64vec4);"
+
+ "int64_t sign(int64_t);"
+ "i64vec2 sign(i64vec2);"
+ "i64vec3 sign(i64vec3);"
+ "i64vec4 sign(i64vec4);"
+
+ "int64_t min(int64_t, int64_t);"
+ "i64vec2 min(i64vec2, int64_t);"
+ "i64vec3 min(i64vec3, int64_t);"
+ "i64vec4 min(i64vec4, int64_t);"
+ "i64vec2 min(i64vec2, i64vec2);"
+ "i64vec3 min(i64vec3, i64vec3);"
+ "i64vec4 min(i64vec4, i64vec4);"
+ "uint64_t min(uint64_t, uint64_t);"
+ "u64vec2 min(u64vec2, uint64_t);"
+ "u64vec3 min(u64vec3, uint64_t);"
+ "u64vec4 min(u64vec4, uint64_t);"
+ "u64vec2 min(u64vec2, u64vec2);"
+ "u64vec3 min(u64vec3, u64vec3);"
+ "u64vec4 min(u64vec4, u64vec4);"
+
+ "int64_t max(int64_t, int64_t);"
+ "i64vec2 max(i64vec2, int64_t);"
+ "i64vec3 max(i64vec3, int64_t);"
+ "i64vec4 max(i64vec4, int64_t);"
+ "i64vec2 max(i64vec2, i64vec2);"
+ "i64vec3 max(i64vec3, i64vec3);"
+ "i64vec4 max(i64vec4, i64vec4);"
+ "uint64_t max(uint64_t, uint64_t);"
+ "u64vec2 max(u64vec2, uint64_t);"
+ "u64vec3 max(u64vec3, uint64_t);"
+ "u64vec4 max(u64vec4, uint64_t);"
+ "u64vec2 max(u64vec2, u64vec2);"
+ "u64vec3 max(u64vec3, u64vec3);"
+ "u64vec4 max(u64vec4, u64vec4);"
+
+ "int64_t clamp(int64_t, int64_t, int64_t);"
+ "i64vec2 clamp(i64vec2, int64_t, int64_t);"
+ "i64vec3 clamp(i64vec3, int64_t, int64_t);"
+ "i64vec4 clamp(i64vec4, int64_t, int64_t);"
+ "i64vec2 clamp(i64vec2, i64vec2, i64vec2);"
+ "i64vec3 clamp(i64vec3, i64vec3, i64vec3);"
+ "i64vec4 clamp(i64vec4, i64vec4, i64vec4);"
+ "uint64_t clamp(uint64_t, uint64_t, uint64_t);"
+ "u64vec2 clamp(u64vec2, uint64_t, uint64_t);"
+ "u64vec3 clamp(u64vec3, uint64_t, uint64_t);"
+ "u64vec4 clamp(u64vec4, uint64_t, uint64_t);"
+ "u64vec2 clamp(u64vec2, u64vec2, u64vec2);"
+ "u64vec3 clamp(u64vec3, u64vec3, u64vec3);"
+ "u64vec4 clamp(u64vec4, u64vec4, u64vec4);"
+
+ "int64_t mix(int64_t, int64_t, bool);"
+ "i64vec2 mix(i64vec2, i64vec2, bvec2);"
+ "i64vec3 mix(i64vec3, i64vec3, bvec3);"
+ "i64vec4 mix(i64vec4, i64vec4, bvec4);"
+ "uint64_t mix(uint64_t, uint64_t, bool);"
+ "u64vec2 mix(u64vec2, u64vec2, bvec2);"
+ "u64vec3 mix(u64vec3, u64vec3, bvec3);"
+ "u64vec4 mix(u64vec4, u64vec4, bvec4);"
+
+ "int64_t doubleBitsToInt64(double);"
+ "i64vec2 doubleBitsToInt64(dvec2);"
+ "i64vec3 doubleBitsToInt64(dvec3);"
+ "i64vec4 doubleBitsToInt64(dvec4);"
+
+ "uint64_t doubleBitsToUint64(double);"
+ "u64vec2 doubleBitsToUint64(dvec2);"
+ "u64vec3 doubleBitsToUint64(dvec3);"
+ "u64vec4 doubleBitsToUint64(dvec4);"
+
+ "double int64BitsToDouble(int64_t);"
+ "dvec2 int64BitsToDouble(i64vec2);"
+ "dvec3 int64BitsToDouble(i64vec3);"
+ "dvec4 int64BitsToDouble(i64vec4);"
+
+ "double uint64BitsToDouble(uint64_t);"
+ "dvec2 uint64BitsToDouble(u64vec2);"
+ "dvec3 uint64BitsToDouble(u64vec3);"
+ "dvec4 uint64BitsToDouble(u64vec4);"
+
+ "int64_t packInt2x32(ivec2);"
+ "uint64_t packUint2x32(uvec2);"
+ "ivec2 unpackInt2x32(int64_t);"
+ "uvec2 unpackUint2x32(uint64_t);"
+
+ "bvec2 lessThan(i64vec2, i64vec2);"
+ "bvec3 lessThan(i64vec3, i64vec3);"
+ "bvec4 lessThan(i64vec4, i64vec4);"
+ "bvec2 lessThan(u64vec2, u64vec2);"
+ "bvec3 lessThan(u64vec3, u64vec3);"
+ "bvec4 lessThan(u64vec4, u64vec4);"
+
+ "bvec2 lessThanEqual(i64vec2, i64vec2);"
+ "bvec3 lessThanEqual(i64vec3, i64vec3);"
+ "bvec4 lessThanEqual(i64vec4, i64vec4);"
+ "bvec2 lessThanEqual(u64vec2, u64vec2);"
+ "bvec3 lessThanEqual(u64vec3, u64vec3);"
+ "bvec4 lessThanEqual(u64vec4, u64vec4);"
+
+ "bvec2 greaterThan(i64vec2, i64vec2);"
+ "bvec3 greaterThan(i64vec3, i64vec3);"
+ "bvec4 greaterThan(i64vec4, i64vec4);"
+ "bvec2 greaterThan(u64vec2, u64vec2);"
+ "bvec3 greaterThan(u64vec3, u64vec3);"
+ "bvec4 greaterThan(u64vec4, u64vec4);"
+
+ "bvec2 greaterThanEqual(i64vec2, i64vec2);"
+ "bvec3 greaterThanEqual(i64vec3, i64vec3);"
+ "bvec4 greaterThanEqual(i64vec4, i64vec4);"
+ "bvec2 greaterThanEqual(u64vec2, u64vec2);"
+ "bvec3 greaterThanEqual(u64vec3, u64vec3);"
+ "bvec4 greaterThanEqual(u64vec4, u64vec4);"
+
+ "bvec2 equal(i64vec2, i64vec2);"
+ "bvec3 equal(i64vec3, i64vec3);"
+ "bvec4 equal(i64vec4, i64vec4);"
+ "bvec2 equal(u64vec2, u64vec2);"
+ "bvec3 equal(u64vec3, u64vec3);"
+ "bvec4 equal(u64vec4, u64vec4);"
+
+ "bvec2 notEqual(i64vec2, i64vec2);"
+ "bvec3 notEqual(i64vec3, i64vec3);"
+ "bvec4 notEqual(i64vec4, i64vec4);"
+ "bvec2 notEqual(u64vec2, u64vec2);"
+ "bvec3 notEqual(u64vec3, u64vec3);"
+ "bvec4 notEqual(u64vec4, u64vec4);"
+
+ "int findLSB(int64_t);"
+ "ivec2 findLSB(i64vec2);"
+ "ivec3 findLSB(i64vec3);"
+ "ivec4 findLSB(i64vec4);"
+
+ "int findLSB(uint64_t);"
+ "ivec2 findLSB(u64vec2);"
+ "ivec3 findLSB(u64vec3);"
+ "ivec4 findLSB(u64vec4);"
+
+ "int findMSB(int64_t);"
+ "ivec2 findMSB(i64vec2);"
+ "ivec3 findMSB(i64vec3);"
+ "ivec4 findMSB(i64vec4);"
+
+ "int findMSB(uint64_t);"
+ "ivec2 findMSB(u64vec2);"
+ "ivec3 findMSB(u64vec3);"
+ "ivec4 findMSB(u64vec4);"
+
+ "\n"
+ );
+ }
+
+#ifdef AMD_EXTENSIONS
+ // GL_AMD_shader_trinary_minmax
+ if (profile != EEsProfile && version >= 430) {
+ commonBuiltins.append(
+ "float min3(float, float, float);"
+ "vec2 min3(vec2, vec2, vec2);"
+ "vec3 min3(vec3, vec3, vec3);"
+ "vec4 min3(vec4, vec4, vec4);"
+
+ "int min3(int, int, int);"
+ "ivec2 min3(ivec2, ivec2, ivec2);"
+ "ivec3 min3(ivec3, ivec3, ivec3);"
+ "ivec4 min3(ivec4, ivec4, ivec4);"
+
+ "uint min3(uint, uint, uint);"
+ "uvec2 min3(uvec2, uvec2, uvec2);"
+ "uvec3 min3(uvec3, uvec3, uvec3);"
+ "uvec4 min3(uvec4, uvec4, uvec4);"
+
+ "float max3(float, float, float);"
+ "vec2 max3(vec2, vec2, vec2);"
+ "vec3 max3(vec3, vec3, vec3);"
+ "vec4 max3(vec4, vec4, vec4);"
+
+ "int max3(int, int, int);"
+ "ivec2 max3(ivec2, ivec2, ivec2);"
+ "ivec3 max3(ivec3, ivec3, ivec3);"
+ "ivec4 max3(ivec4, ivec4, ivec4);"
+
+ "uint max3(uint, uint, uint);"
+ "uvec2 max3(uvec2, uvec2, uvec2);"
+ "uvec3 max3(uvec3, uvec3, uvec3);"
+ "uvec4 max3(uvec4, uvec4, uvec4);"
+
+ "float mid3(float, float, float);"
+ "vec2 mid3(vec2, vec2, vec2);"
+ "vec3 mid3(vec3, vec3, vec3);"
+ "vec4 mid3(vec4, vec4, vec4);"
+
+ "int mid3(int, int, int);"
+ "ivec2 mid3(ivec2, ivec2, ivec2);"
+ "ivec3 mid3(ivec3, ivec3, ivec3);"
+ "ivec4 mid3(ivec4, ivec4, ivec4);"
+
+ "uint mid3(uint, uint, uint);"
+ "uvec2 mid3(uvec2, uvec2, uvec2);"
+ "uvec3 mid3(uvec3, uvec3, uvec3);"
+ "uvec4 mid3(uvec4, uvec4, uvec4);"
+
+ "float16_t min3(float16_t, float16_t, float16_t);"
+ "f16vec2 min3(f16vec2, f16vec2, f16vec2);"
+ "f16vec3 min3(f16vec3, f16vec3, f16vec3);"
+ "f16vec4 min3(f16vec4, f16vec4, f16vec4);"
+
+ "float16_t max3(float16_t, float16_t, float16_t);"
+ "f16vec2 max3(f16vec2, f16vec2, f16vec2);"
+ "f16vec3 max3(f16vec3, f16vec3, f16vec3);"
+ "f16vec4 max3(f16vec4, f16vec4, f16vec4);"
+
+ "float16_t mid3(float16_t, float16_t, float16_t);"
+ "f16vec2 mid3(f16vec2, f16vec2, f16vec2);"
+ "f16vec3 mid3(f16vec3, f16vec3, f16vec3);"
+ "f16vec4 mid3(f16vec4, f16vec4, f16vec4);"
+
+ "int16_t min3(int16_t, int16_t, int16_t);"
+ "i16vec2 min3(i16vec2, i16vec2, i16vec2);"
+ "i16vec3 min3(i16vec3, i16vec3, i16vec3);"
+ "i16vec4 min3(i16vec4, i16vec4, i16vec4);"
+
+ "int16_t max3(int16_t, int16_t, int16_t);"
+ "i16vec2 max3(i16vec2, i16vec2, i16vec2);"
+ "i16vec3 max3(i16vec3, i16vec3, i16vec3);"
+ "i16vec4 max3(i16vec4, i16vec4, i16vec4);"
+
+ "int16_t mid3(int16_t, int16_t, int16_t);"
+ "i16vec2 mid3(i16vec2, i16vec2, i16vec2);"
+ "i16vec3 mid3(i16vec3, i16vec3, i16vec3);"
+ "i16vec4 mid3(i16vec4, i16vec4, i16vec4);"
+
+ "uint16_t min3(uint16_t, uint16_t, uint16_t);"
+ "u16vec2 min3(u16vec2, u16vec2, u16vec2);"
+ "u16vec3 min3(u16vec3, u16vec3, u16vec3);"
+ "u16vec4 min3(u16vec4, u16vec4, u16vec4);"
+
+ "uint16_t max3(uint16_t, uint16_t, uint16_t);"
+ "u16vec2 max3(u16vec2, u16vec2, u16vec2);"
+ "u16vec3 max3(u16vec3, u16vec3, u16vec3);"
+ "u16vec4 max3(u16vec4, u16vec4, u16vec4);"
+
+ "uint16_t mid3(uint16_t, uint16_t, uint16_t);"
+ "u16vec2 mid3(u16vec2, u16vec2, u16vec2);"
+ "u16vec3 mid3(u16vec3, u16vec3, u16vec3);"
+ "u16vec4 mid3(u16vec4, u16vec4, u16vec4);"
+
+ "\n"
+ );
+ }
+#endif
+
+ if ((profile == EEsProfile && version >= 310) ||
+ (profile != EEsProfile && version >= 430)) {
+ commonBuiltins.append(
+ "uint atomicAdd(coherent volatile inout uint, uint);"
+ " int atomicAdd(coherent volatile inout int, int);"
+ "uint atomicAdd(coherent volatile inout uint, uint, int, int, int);"
+ " int atomicAdd(coherent volatile inout int, int, int, int, int);"
+
+ "uint atomicMin(coherent volatile inout uint, uint);"
+ " int atomicMin(coherent volatile inout int, int);"
+ "uint atomicMin(coherent volatile inout uint, uint, int, int, int);"
+ " int atomicMin(coherent volatile inout int, int, int, int, int);"
+
+ "uint atomicMax(coherent volatile inout uint, uint);"
+ " int atomicMax(coherent volatile inout int, int);"
+ "uint atomicMax(coherent volatile inout uint, uint, int, int, int);"
+ " int atomicMax(coherent volatile inout int, int, int, int, int);"
+
+ "uint atomicAnd(coherent volatile inout uint, uint);"
+ " int atomicAnd(coherent volatile inout int, int);"
+ "uint atomicAnd(coherent volatile inout uint, uint, int, int, int);"
+ " int atomicAnd(coherent volatile inout int, int, int, int, int);"
+
+ "uint atomicOr (coherent volatile inout uint, uint);"
+ " int atomicOr (coherent volatile inout int, int);"
+ "uint atomicOr (coherent volatile inout uint, uint, int, int, int);"
+ " int atomicOr (coherent volatile inout int, int, int, int, int);"
+
+ "uint atomicXor(coherent volatile inout uint, uint);"
+ " int atomicXor(coherent volatile inout int, int);"
+ "uint atomicXor(coherent volatile inout uint, uint, int, int, int);"
+ " int atomicXor(coherent volatile inout int, int, int, int, int);"
+
+ "uint atomicExchange(coherent volatile inout uint, uint);"
+ " int atomicExchange(coherent volatile inout int, int);"
+ "uint atomicExchange(coherent volatile inout uint, uint, int, int, int);"
+ " int atomicExchange(coherent volatile inout int, int, int, int, int);"
+
+ "uint atomicCompSwap(coherent volatile inout uint, uint, uint);"
+ " int atomicCompSwap(coherent volatile inout int, int, int);"
+ "uint atomicCompSwap(coherent volatile inout uint, uint, uint, int, int, int, int, int);"
+ " int atomicCompSwap(coherent volatile inout int, int, int, int, int, int, int, int);"
+
+ "uint atomicLoad(coherent volatile in uint, int, int, int);"
+ " int atomicLoad(coherent volatile in int, int, int, int);"
+
+ "void atomicStore(coherent volatile out uint, uint, int, int, int);"
+ "void atomicStore(coherent volatile out int, int, int, int, int);"
+
+ "\n");
+ }
+
+ if (profile != EEsProfile && version >= 440) {
+ commonBuiltins.append(
+ "uint64_t atomicMin(coherent volatile inout uint64_t, uint64_t);"
+ " int64_t atomicMin(coherent volatile inout int64_t, int64_t);"
+ "uint64_t atomicMin(coherent volatile inout uint64_t, uint64_t, int, int, int);"
+ " int64_t atomicMin(coherent volatile inout int64_t, int64_t, int, int, int);"
+
+ "uint64_t atomicMax(coherent volatile inout uint64_t, uint64_t);"
+ " int64_t atomicMax(coherent volatile inout int64_t, int64_t);"
+ "uint64_t atomicMax(coherent volatile inout uint64_t, uint64_t, int, int, int);"
+ " int64_t atomicMax(coherent volatile inout int64_t, int64_t, int, int, int);"
+
+ "uint64_t atomicAnd(coherent volatile inout uint64_t, uint64_t);"
+ " int64_t atomicAnd(coherent volatile inout int64_t, int64_t);"
+ "uint64_t atomicAnd(coherent volatile inout uint64_t, uint64_t, int, int, int);"
+ " int64_t atomicAnd(coherent volatile inout int64_t, int64_t, int, int, int);"
+
+ "uint64_t atomicOr (coherent volatile inout uint64_t, uint64_t);"
+ " int64_t atomicOr (coherent volatile inout int64_t, int64_t);"
+ "uint64_t atomicOr (coherent volatile inout uint64_t, uint64_t, int, int, int);"
+ " int64_t atomicOr (coherent volatile inout int64_t, int64_t, int, int, int);"
+
+ "uint64_t atomicXor(coherent volatile inout uint64_t, uint64_t);"
+ " int64_t atomicXor(coherent volatile inout int64_t, int64_t);"
+ "uint64_t atomicXor(coherent volatile inout uint64_t, uint64_t, int, int, int);"
+ " int64_t atomicXor(coherent volatile inout int64_t, int64_t, int, int, int);"
+
+ "uint64_t atomicAdd(coherent volatile inout uint64_t, uint64_t);"
+ " int64_t atomicAdd(coherent volatile inout int64_t, int64_t);"
+ "uint64_t atomicAdd(coherent volatile inout uint64_t, uint64_t, int, int, int);"
+ " int64_t atomicAdd(coherent volatile inout int64_t, int64_t, int, int, int);"
+
+ "uint64_t atomicExchange(coherent volatile inout uint64_t, uint64_t);"
+ " int64_t atomicExchange(coherent volatile inout int64_t, int64_t);"
+ "uint64_t atomicExchange(coherent volatile inout uint64_t, uint64_t, int, int, int);"
+ " int64_t atomicExchange(coherent volatile inout int64_t, int64_t, int, int, int);"
+
+ "uint64_t atomicCompSwap(coherent volatile inout uint64_t, uint64_t, uint64_t);"
+ " int64_t atomicCompSwap(coherent volatile inout int64_t, int64_t, int64_t);"
+ "uint64_t atomicCompSwap(coherent volatile inout uint64_t, uint64_t, uint64_t, int, int, int, int, int);"
+ " int64_t atomicCompSwap(coherent volatile inout int64_t, int64_t, int64_t, int, int, int, int, int);"
+
+ "uint64_t atomicLoad(coherent volatile in uint64_t, int, int, int);"
+ " int64_t atomicLoad(coherent volatile in int64_t, int, int, int);"
+
+ "void atomicStore(coherent volatile out uint64_t, uint64_t, int, int, int);"
+ "void atomicStore(coherent volatile out int64_t, int64_t, int, int, int);"
+ "\n");
+ }
+
+ if ((profile == EEsProfile && version >= 310) ||
+ (profile != EEsProfile && version >= 450)) {
+ commonBuiltins.append(
+ "int mix(int x, int y, bool a);"
+ "ivec2 mix(ivec2 x, ivec2 y, bvec2 a);"
+ "ivec3 mix(ivec3 x, ivec3 y, bvec3 a);"
+ "ivec4 mix(ivec4 x, ivec4 y, bvec4 a);"
+
+ "uint mix(uint x, uint y, bool a);"
+ "uvec2 mix(uvec2 x, uvec2 y, bvec2 a);"
+ "uvec3 mix(uvec3 x, uvec3 y, bvec3 a);"
+ "uvec4 mix(uvec4 x, uvec4 y, bvec4 a);"
+
+ "bool mix(bool x, bool y, bool a);"
+ "bvec2 mix(bvec2 x, bvec2 y, bvec2 a);"
+ "bvec3 mix(bvec3 x, bvec3 y, bvec3 a);"
+ "bvec4 mix(bvec4 x, bvec4 y, bvec4 a);"
+
+ "\n");
+ }
+
+ if ((profile == EEsProfile && version >= 300) ||
+ (profile != EEsProfile && version >= 330)) {
+ commonBuiltins.append(
+ "int floatBitsToInt(highp float value);"
+ "ivec2 floatBitsToInt(highp vec2 value);"
+ "ivec3 floatBitsToInt(highp vec3 value);"
+ "ivec4 floatBitsToInt(highp vec4 value);"
+
+ "uint floatBitsToUint(highp float value);"
+ "uvec2 floatBitsToUint(highp vec2 value);"
+ "uvec3 floatBitsToUint(highp vec3 value);"
+ "uvec4 floatBitsToUint(highp vec4 value);"
+
+ "float intBitsToFloat(highp int value);"
+ "vec2 intBitsToFloat(highp ivec2 value);"
+ "vec3 intBitsToFloat(highp ivec3 value);"
+ "vec4 intBitsToFloat(highp ivec4 value);"
+
+ "float uintBitsToFloat(highp uint value);"
+ "vec2 uintBitsToFloat(highp uvec2 value);"
+ "vec3 uintBitsToFloat(highp uvec3 value);"
+ "vec4 uintBitsToFloat(highp uvec4 value);"
+
+ "\n");
+ }
+
+ if ((profile != EEsProfile && version >= 400) ||
+ (profile == EEsProfile && version >= 310)) { // GL_OES_gpu_shader5
+
+ commonBuiltins.append(
+ "float fma(float, float, float );"
+ "vec2 fma(vec2, vec2, vec2 );"
+ "vec3 fma(vec3, vec3, vec3 );"
+ "vec4 fma(vec4, vec4, vec4 );"
+ "\n");
+
+ if (profile != EEsProfile) {
+ commonBuiltins.append(
+ "double fma(double, double, double);"
+ "dvec2 fma(dvec2, dvec2, dvec2 );"
+ "dvec3 fma(dvec3, dvec3, dvec3 );"
+ "dvec4 fma(dvec4, dvec4, dvec4 );"
+ "\n");
+ }
+ }
+
+ if ((profile == EEsProfile && version >= 310) ||
+ (profile != EEsProfile && version >= 400)) {
+ commonBuiltins.append(
+ "float frexp(highp float, out highp int);"
+ "vec2 frexp(highp vec2, out highp ivec2);"
+ "vec3 frexp(highp vec3, out highp ivec3);"
+ "vec4 frexp(highp vec4, out highp ivec4);"
+
+ "float ldexp(highp float, highp int);"
+ "vec2 ldexp(highp vec2, highp ivec2);"
+ "vec3 ldexp(highp vec3, highp ivec3);"
+ "vec4 ldexp(highp vec4, highp ivec4);"
+
+ "\n");
+ }
+
+ if (profile != EEsProfile && version >= 400) {
+ commonBuiltins.append(
+ "double frexp(double, out int);"
+ "dvec2 frexp( dvec2, out ivec2);"
+ "dvec3 frexp( dvec3, out ivec3);"
+ "dvec4 frexp( dvec4, out ivec4);"
+
+ "double ldexp(double, int);"
+ "dvec2 ldexp( dvec2, ivec2);"
+ "dvec3 ldexp( dvec3, ivec3);"
+ "dvec4 ldexp( dvec4, ivec4);"
+
+ "double packDouble2x32(uvec2);"
+ "uvec2 unpackDouble2x32(double);"
+
+ "\n");
+ }
+
+ if ((profile == EEsProfile && version >= 300) ||
+ (profile != EEsProfile && version >= 400)) {
+ commonBuiltins.append(
+ "highp uint packUnorm2x16(vec2);"
+ "vec2 unpackUnorm2x16(highp uint);"
+ "\n");
+ }
+
+ if ((profile == EEsProfile && version >= 300) ||
+ (profile != EEsProfile && version >= 420)) {
+ commonBuiltins.append(
+ "highp uint packSnorm2x16(vec2);"
+ " vec2 unpackSnorm2x16(highp uint);"
+ "highp uint packHalf2x16(vec2);"
+ "\n");
+ }
+
+ if (profile == EEsProfile && version >= 300) {
+ commonBuiltins.append(
+ "mediump vec2 unpackHalf2x16(highp uint);"
+ "\n");
+ } else if (profile != EEsProfile && version >= 420) {
+ commonBuiltins.append(
+ " vec2 unpackHalf2x16(highp uint);"
+ "\n");
+ }
+
+ if ((profile == EEsProfile && version >= 310) ||
+ (profile != EEsProfile && version >= 400)) {
+ commonBuiltins.append(
+ "highp uint packSnorm4x8(vec4);"
+ "highp uint packUnorm4x8(vec4);"
+ "\n");
+ }
+
+ if (profile == EEsProfile && version >= 310) {
+ commonBuiltins.append(
+ "mediump vec4 unpackSnorm4x8(highp uint);"
+ "mediump vec4 unpackUnorm4x8(highp uint);"
+ "\n");
+ } else if (profile != EEsProfile && version >= 400) {
+ commonBuiltins.append(
+ "vec4 unpackSnorm4x8(highp uint);"
+ "vec4 unpackUnorm4x8(highp uint);"
+ "\n");
+ }
+
+ //
+ // Geometric Functions.
+ //
+ commonBuiltins.append(
+ "float length(float x);"
+ "float length(vec2 x);"
+ "float length(vec3 x);"
+ "float length(vec4 x);"
+
+ "float distance(float p0, float p1);"
+ "float distance(vec2 p0, vec2 p1);"
+ "float distance(vec3 p0, vec3 p1);"
+ "float distance(vec4 p0, vec4 p1);"
+
+ "float dot(float x, float y);"
+ "float dot(vec2 x, vec2 y);"
+ "float dot(vec3 x, vec3 y);"
+ "float dot(vec4 x, vec4 y);"
+
+ "vec3 cross(vec3 x, vec3 y);"
+ "float normalize(float x);"
+ "vec2 normalize(vec2 x);"
+ "vec3 normalize(vec3 x);"
+ "vec4 normalize(vec4 x);"
+
+ "float faceforward(float N, float I, float Nref);"
+ "vec2 faceforward(vec2 N, vec2 I, vec2 Nref);"
+ "vec3 faceforward(vec3 N, vec3 I, vec3 Nref);"
+ "vec4 faceforward(vec4 N, vec4 I, vec4 Nref);"
+
+ "float reflect(float I, float N);"
+ "vec2 reflect(vec2 I, vec2 N);"
+ "vec3 reflect(vec3 I, vec3 N);"
+ "vec4 reflect(vec4 I, vec4 N);"
+
+ "float refract(float I, float N, float eta);"
+ "vec2 refract(vec2 I, vec2 N, float eta);"
+ "vec3 refract(vec3 I, vec3 N, float eta);"
+ "vec4 refract(vec4 I, vec4 N, float eta);"
+
+ "\n");
+
+ //
+ // Matrix Functions.
+ //
+ commonBuiltins.append(
+ "mat2 matrixCompMult(mat2 x, mat2 y);"
+ "mat3 matrixCompMult(mat3 x, mat3 y);"
+ "mat4 matrixCompMult(mat4 x, mat4 y);"
+
+ "\n");
+
+ // 120 is correct for both ES and desktop
+ if (version >= 120) {
+ commonBuiltins.append(
+ "mat2 outerProduct(vec2 c, vec2 r);"
+ "mat3 outerProduct(vec3 c, vec3 r);"
+ "mat4 outerProduct(vec4 c, vec4 r);"
+ "mat2x3 outerProduct(vec3 c, vec2 r);"
+ "mat3x2 outerProduct(vec2 c, vec3 r);"
+ "mat2x4 outerProduct(vec4 c, vec2 r);"
+ "mat4x2 outerProduct(vec2 c, vec4 r);"
+ "mat3x4 outerProduct(vec4 c, vec3 r);"
+ "mat4x3 outerProduct(vec3 c, vec4 r);"
+
+ "mat2 transpose(mat2 m);"
+ "mat3 transpose(mat3 m);"
+ "mat4 transpose(mat4 m);"
+ "mat2x3 transpose(mat3x2 m);"
+ "mat3x2 transpose(mat2x3 m);"
+ "mat2x4 transpose(mat4x2 m);"
+ "mat4x2 transpose(mat2x4 m);"
+ "mat3x4 transpose(mat4x3 m);"
+ "mat4x3 transpose(mat3x4 m);"
+
+ "mat2x3 matrixCompMult(mat2x3, mat2x3);"
+ "mat2x4 matrixCompMult(mat2x4, mat2x4);"
+ "mat3x2 matrixCompMult(mat3x2, mat3x2);"
+ "mat3x4 matrixCompMult(mat3x4, mat3x4);"
+ "mat4x2 matrixCompMult(mat4x2, mat4x2);"
+ "mat4x3 matrixCompMult(mat4x3, mat4x3);"
+
+ "\n");
+
+ // 150 is correct for both ES and desktop
+ if (version >= 150) {
+ commonBuiltins.append(
+ "float determinant(mat2 m);"
+ "float determinant(mat3 m);"
+ "float determinant(mat4 m);"
+
+ "mat2 inverse(mat2 m);"
+ "mat3 inverse(mat3 m);"
+ "mat4 inverse(mat4 m);"
+
+ "\n");
+ }
+ }
+
+ //
+ // Vector relational functions.
+ //
+ commonBuiltins.append(
+ "bvec2 lessThan(vec2 x, vec2 y);"
+ "bvec3 lessThan(vec3 x, vec3 y);"
+ "bvec4 lessThan(vec4 x, vec4 y);"
+
+ "bvec2 lessThan(ivec2 x, ivec2 y);"
+ "bvec3 lessThan(ivec3 x, ivec3 y);"
+ "bvec4 lessThan(ivec4 x, ivec4 y);"
+
+ "bvec2 lessThanEqual(vec2 x, vec2 y);"
+ "bvec3 lessThanEqual(vec3 x, vec3 y);"
+ "bvec4 lessThanEqual(vec4 x, vec4 y);"
+
+ "bvec2 lessThanEqual(ivec2 x, ivec2 y);"
+ "bvec3 lessThanEqual(ivec3 x, ivec3 y);"
+ "bvec4 lessThanEqual(ivec4 x, ivec4 y);"
+
+ "bvec2 greaterThan(vec2 x, vec2 y);"
+ "bvec3 greaterThan(vec3 x, vec3 y);"
+ "bvec4 greaterThan(vec4 x, vec4 y);"
+
+ "bvec2 greaterThan(ivec2 x, ivec2 y);"
+ "bvec3 greaterThan(ivec3 x, ivec3 y);"
+ "bvec4 greaterThan(ivec4 x, ivec4 y);"
+
+ "bvec2 greaterThanEqual(vec2 x, vec2 y);"
+ "bvec3 greaterThanEqual(vec3 x, vec3 y);"
+ "bvec4 greaterThanEqual(vec4 x, vec4 y);"
+
+ "bvec2 greaterThanEqual(ivec2 x, ivec2 y);"
+ "bvec3 greaterThanEqual(ivec3 x, ivec3 y);"
+ "bvec4 greaterThanEqual(ivec4 x, ivec4 y);"
+
+ "bvec2 equal(vec2 x, vec2 y);"
+ "bvec3 equal(vec3 x, vec3 y);"
+ "bvec4 equal(vec4 x, vec4 y);"
+
+ "bvec2 equal(ivec2 x, ivec2 y);"
+ "bvec3 equal(ivec3 x, ivec3 y);"
+ "bvec4 equal(ivec4 x, ivec4 y);"
+
+ "bvec2 equal(bvec2 x, bvec2 y);"
+ "bvec3 equal(bvec3 x, bvec3 y);"
+ "bvec4 equal(bvec4 x, bvec4 y);"
+
+ "bvec2 notEqual(vec2 x, vec2 y);"
+ "bvec3 notEqual(vec3 x, vec3 y);"
+ "bvec4 notEqual(vec4 x, vec4 y);"
+
+ "bvec2 notEqual(ivec2 x, ivec2 y);"
+ "bvec3 notEqual(ivec3 x, ivec3 y);"
+ "bvec4 notEqual(ivec4 x, ivec4 y);"
+
+ "bvec2 notEqual(bvec2 x, bvec2 y);"
+ "bvec3 notEqual(bvec3 x, bvec3 y);"
+ "bvec4 notEqual(bvec4 x, bvec4 y);"
+
+ "bool any(bvec2 x);"
+ "bool any(bvec3 x);"
+ "bool any(bvec4 x);"
+
+ "bool all(bvec2 x);"
+ "bool all(bvec3 x);"
+ "bool all(bvec4 x);"
+
+ "bvec2 not(bvec2 x);"
+ "bvec3 not(bvec3 x);"
+ "bvec4 not(bvec4 x);"
+
+ "\n");
+
+ if (version >= 130) {
+ commonBuiltins.append(
+ "bvec2 lessThan(uvec2 x, uvec2 y);"
+ "bvec3 lessThan(uvec3 x, uvec3 y);"
+ "bvec4 lessThan(uvec4 x, uvec4 y);"
+
+ "bvec2 lessThanEqual(uvec2 x, uvec2 y);"
+ "bvec3 lessThanEqual(uvec3 x, uvec3 y);"
+ "bvec4 lessThanEqual(uvec4 x, uvec4 y);"
+
+ "bvec2 greaterThan(uvec2 x, uvec2 y);"
+ "bvec3 greaterThan(uvec3 x, uvec3 y);"
+ "bvec4 greaterThan(uvec4 x, uvec4 y);"
+
+ "bvec2 greaterThanEqual(uvec2 x, uvec2 y);"
+ "bvec3 greaterThanEqual(uvec3 x, uvec3 y);"
+ "bvec4 greaterThanEqual(uvec4 x, uvec4 y);"
+
+ "bvec2 equal(uvec2 x, uvec2 y);"
+ "bvec3 equal(uvec3 x, uvec3 y);"
+ "bvec4 equal(uvec4 x, uvec4 y);"
+
+ "bvec2 notEqual(uvec2 x, uvec2 y);"
+ "bvec3 notEqual(uvec3 x, uvec3 y);"
+ "bvec4 notEqual(uvec4 x, uvec4 y);"
+
+ "\n");
+ }
+
+ //
+ // Original-style texture functions existing in all stages.
+ // (Per-stage functions below.)
+ //
+ if ((profile == EEsProfile && version == 100) ||
+ profile == ECompatibilityProfile ||
+ (profile == ECoreProfile && version < 420) ||
+ profile == ENoProfile) {
+ if (spvVersion.spv == 0) {
+ commonBuiltins.append(
+ "vec4 texture2D(sampler2D, vec2);"
+
+ "vec4 texture2DProj(sampler2D, vec3);"
+ "vec4 texture2DProj(sampler2D, vec4);"
+
+ "vec4 texture3D(sampler3D, vec3);" // OES_texture_3D, but caught by keyword check
+ "vec4 texture3DProj(sampler3D, vec4);" // OES_texture_3D, but caught by keyword check
+
+ "vec4 textureCube(samplerCube, vec3);"
+
+ "\n");
+ }
+ }
+
+ if ( profile == ECompatibilityProfile ||
+ (profile == ECoreProfile && version < 420) ||
+ profile == ENoProfile) {
+ if (spvVersion.spv == 0) {
+ commonBuiltins.append(
+ "vec4 texture1D(sampler1D, float);"
+
+ "vec4 texture1DProj(sampler1D, vec2);"
+ "vec4 texture1DProj(sampler1D, vec4);"
+
+ "vec4 shadow1D(sampler1DShadow, vec3);"
+ "vec4 shadow2D(sampler2DShadow, vec3);"
+ "vec4 shadow1DProj(sampler1DShadow, vec4);"
+ "vec4 shadow2DProj(sampler2DShadow, vec4);"
+
+ "vec4 texture2DRect(sampler2DRect, vec2);" // GL_ARB_texture_rectangle, caught by keyword check
+ "vec4 texture2DRectProj(sampler2DRect, vec3);" // GL_ARB_texture_rectangle, caught by keyword check
+ "vec4 texture2DRectProj(sampler2DRect, vec4);" // GL_ARB_texture_rectangle, caught by keyword check
+ "vec4 shadow2DRect(sampler2DRectShadow, vec3);" // GL_ARB_texture_rectangle, caught by keyword check
+ "vec4 shadow2DRectProj(sampler2DRectShadow, vec4);" // GL_ARB_texture_rectangle, caught by keyword check
+
+ "\n");
+ }
+ }
+
+ if (profile == EEsProfile) {
+ if (spvVersion.spv == 0) {
+ if (version < 300) {
+ commonBuiltins.append(
+ "vec4 texture2D(samplerExternalOES, vec2 coord);" // GL_OES_EGL_image_external
+ "vec4 texture2DProj(samplerExternalOES, vec3);" // GL_OES_EGL_image_external
+ "vec4 texture2DProj(samplerExternalOES, vec4);" // GL_OES_EGL_image_external
+ "\n");
+ } else {
+ commonBuiltins.append(
+ "highp ivec2 textureSize(samplerExternalOES, int lod);" // GL_OES_EGL_image_external_essl3
+ "vec4 texture(samplerExternalOES, vec2);" // GL_OES_EGL_image_external_essl3
+ "vec4 texture(samplerExternalOES, vec2, float bias);" // GL_OES_EGL_image_external_essl3
+ "vec4 textureProj(samplerExternalOES, vec3);" // GL_OES_EGL_image_external_essl3
+ "vec4 textureProj(samplerExternalOES, vec3, float bias);" // GL_OES_EGL_image_external_essl3
+ "vec4 textureProj(samplerExternalOES, vec4);" // GL_OES_EGL_image_external_essl3
+ "vec4 textureProj(samplerExternalOES, vec4, float bias);" // GL_OES_EGL_image_external_essl3
+ "vec4 texelFetch(samplerExternalOES, ivec2, int lod);" // GL_OES_EGL_image_external_essl3
+ "\n");
+ }
+ commonBuiltins.append(
+ "highp ivec2 textureSize(__samplerExternal2DY2YEXT, int lod);" // GL_EXT_YUV_target
+ "vec4 texture(__samplerExternal2DY2YEXT, vec2);" // GL_EXT_YUV_target
+ "vec4 texture(__samplerExternal2DY2YEXT, vec2, float bias);" // GL_EXT_YUV_target
+ "vec4 textureProj(__samplerExternal2DY2YEXT, vec3);" // GL_EXT_YUV_target
+ "vec4 textureProj(__samplerExternal2DY2YEXT, vec3, float bias);" // GL_EXT_YUV_target
+ "vec4 textureProj(__samplerExternal2DY2YEXT, vec4);" // GL_EXT_YUV_target
+ "vec4 textureProj(__samplerExternal2DY2YEXT, vec4, float bias);" // GL_EXT_YUV_target
+ "vec4 texelFetch(__samplerExternal2DY2YEXT sampler, ivec2, int lod);" // GL_EXT_YUV_target
+ "\n");
+ commonBuiltins.append(
+ "vec4 texture2DGradEXT(sampler2D, vec2, vec2, vec2);" // GL_EXT_shader_texture_lod
+ "vec4 texture2DProjGradEXT(sampler2D, vec3, vec2, vec2);" // GL_EXT_shader_texture_lod
+ "vec4 texture2DProjGradEXT(sampler2D, vec4, vec2, vec2);" // GL_EXT_shader_texture_lod
+ "vec4 textureCubeGradEXT(samplerCube, vec3, vec3, vec3);" // GL_EXT_shader_texture_lod
+
+ "float shadow2DEXT(sampler2DShadow, vec3);" // GL_EXT_shadow_samplers
+ "float shadow2DProjEXT(sampler2DShadow, vec4);" // GL_EXT_shadow_samplers
+
+ "\n");
+ }
+ }
+
+ //
+ // Noise functions.
+ //
+ if (spvVersion.spv == 0 && profile != EEsProfile) {
+ commonBuiltins.append(
+ "float noise1(float x);"
+ "float noise1(vec2 x);"
+ "float noise1(vec3 x);"
+ "float noise1(vec4 x);"
+
+ "vec2 noise2(float x);"
+ "vec2 noise2(vec2 x);"
+ "vec2 noise2(vec3 x);"
+ "vec2 noise2(vec4 x);"
+
+ "vec3 noise3(float x);"
+ "vec3 noise3(vec2 x);"
+ "vec3 noise3(vec3 x);"
+ "vec3 noise3(vec4 x);"
+
+ "vec4 noise4(float x);"
+ "vec4 noise4(vec2 x);"
+ "vec4 noise4(vec3 x);"
+ "vec4 noise4(vec4 x);"
+
+ "\n");
+ }
+
+ if (spvVersion.vulkan == 0) {
+ //
+ // Atomic counter functions.
+ //
+ if ((profile != EEsProfile && version >= 300) ||
+ (profile == EEsProfile && version >= 310)) {
+ commonBuiltins.append(
+ "uint atomicCounterIncrement(atomic_uint);"
+ "uint atomicCounterDecrement(atomic_uint);"
+ "uint atomicCounter(atomic_uint);"
+
+ "\n");
+ }
+ if (profile != EEsProfile && version >= 460) {
+ commonBuiltins.append(
+ "uint atomicCounterAdd(atomic_uint, uint);"
+ "uint atomicCounterSubtract(atomic_uint, uint);"
+ "uint atomicCounterMin(atomic_uint, uint);"
+ "uint atomicCounterMax(atomic_uint, uint);"
+ "uint atomicCounterAnd(atomic_uint, uint);"
+ "uint atomicCounterOr(atomic_uint, uint);"
+ "uint atomicCounterXor(atomic_uint, uint);"
+ "uint atomicCounterExchange(atomic_uint, uint);"
+ "uint atomicCounterCompSwap(atomic_uint, uint, uint);"
+
+ "\n");
+ }
+ }
+
+ // Bitfield
+ if ((profile == EEsProfile && version >= 310) ||
+ (profile != EEsProfile && version >= 400)) {
+ commonBuiltins.append(
+ " int bitfieldExtract( int, int, int);"
+ "ivec2 bitfieldExtract(ivec2, int, int);"
+ "ivec3 bitfieldExtract(ivec3, int, int);"
+ "ivec4 bitfieldExtract(ivec4, int, int);"
+
+ " uint bitfieldExtract( uint, int, int);"
+ "uvec2 bitfieldExtract(uvec2, int, int);"
+ "uvec3 bitfieldExtract(uvec3, int, int);"
+ "uvec4 bitfieldExtract(uvec4, int, int);"
+
+ " int bitfieldInsert( int base, int, int, int);"
+ "ivec2 bitfieldInsert(ivec2 base, ivec2, int, int);"
+ "ivec3 bitfieldInsert(ivec3 base, ivec3, int, int);"
+ "ivec4 bitfieldInsert(ivec4 base, ivec4, int, int);"
+
+ " uint bitfieldInsert( uint base, uint, int, int);"
+ "uvec2 bitfieldInsert(uvec2 base, uvec2, int, int);"
+ "uvec3 bitfieldInsert(uvec3 base, uvec3, int, int);"
+ "uvec4 bitfieldInsert(uvec4 base, uvec4, int, int);"
+
+ "\n");
+ }
+
+ if (profile != EEsProfile && version >= 400) {
+ commonBuiltins.append(
+ " int findLSB( int);"
+ "ivec2 findLSB(ivec2);"
+ "ivec3 findLSB(ivec3);"
+ "ivec4 findLSB(ivec4);"
+
+ " int findLSB( uint);"
+ "ivec2 findLSB(uvec2);"
+ "ivec3 findLSB(uvec3);"
+ "ivec4 findLSB(uvec4);"
+
+ "\n");
+ } else if (profile == EEsProfile && version >= 310) {
+ commonBuiltins.append(
+ "lowp int findLSB( int);"
+ "lowp ivec2 findLSB(ivec2);"
+ "lowp ivec3 findLSB(ivec3);"
+ "lowp ivec4 findLSB(ivec4);"
+
+ "lowp int findLSB( uint);"
+ "lowp ivec2 findLSB(uvec2);"
+ "lowp ivec3 findLSB(uvec3);"
+ "lowp ivec4 findLSB(uvec4);"
+
+ "\n");
+ }
+
+ if (profile != EEsProfile && version >= 400) {
+ commonBuiltins.append(
+ " int bitCount( int);"
+ "ivec2 bitCount(ivec2);"
+ "ivec3 bitCount(ivec3);"
+ "ivec4 bitCount(ivec4);"
+
+ " int bitCount( uint);"
+ "ivec2 bitCount(uvec2);"
+ "ivec3 bitCount(uvec3);"
+ "ivec4 bitCount(uvec4);"
+
+ " int findMSB(highp int);"
+ "ivec2 findMSB(highp ivec2);"
+ "ivec3 findMSB(highp ivec3);"
+ "ivec4 findMSB(highp ivec4);"
+
+ " int findMSB(highp uint);"
+ "ivec2 findMSB(highp uvec2);"
+ "ivec3 findMSB(highp uvec3);"
+ "ivec4 findMSB(highp uvec4);"
+
+ "\n");
+ }
+
+ if ((profile == EEsProfile && version >= 310) ||
+ (profile != EEsProfile && version >= 400)) {
+ commonBuiltins.append(
+ " uint uaddCarry(highp uint, highp uint, out lowp uint carry);"
+ "uvec2 uaddCarry(highp uvec2, highp uvec2, out lowp uvec2 carry);"
+ "uvec3 uaddCarry(highp uvec3, highp uvec3, out lowp uvec3 carry);"
+ "uvec4 uaddCarry(highp uvec4, highp uvec4, out lowp uvec4 carry);"
+
+ " uint usubBorrow(highp uint, highp uint, out lowp uint borrow);"
+ "uvec2 usubBorrow(highp uvec2, highp uvec2, out lowp uvec2 borrow);"
+ "uvec3 usubBorrow(highp uvec3, highp uvec3, out lowp uvec3 borrow);"
+ "uvec4 usubBorrow(highp uvec4, highp uvec4, out lowp uvec4 borrow);"
+
+ "void umulExtended(highp uint, highp uint, out highp uint, out highp uint lsb);"
+ "void umulExtended(highp uvec2, highp uvec2, out highp uvec2, out highp uvec2 lsb);"
+ "void umulExtended(highp uvec3, highp uvec3, out highp uvec3, out highp uvec3 lsb);"
+ "void umulExtended(highp uvec4, highp uvec4, out highp uvec4, out highp uvec4 lsb);"
+
+ "void imulExtended(highp int, highp int, out highp int, out highp int lsb);"
+ "void imulExtended(highp ivec2, highp ivec2, out highp ivec2, out highp ivec2 lsb);"
+ "void imulExtended(highp ivec3, highp ivec3, out highp ivec3, out highp ivec3 lsb);"
+ "void imulExtended(highp ivec4, highp ivec4, out highp ivec4, out highp ivec4 lsb);"
+
+ " int bitfieldReverse(highp int);"
+ "ivec2 bitfieldReverse(highp ivec2);"
+ "ivec3 bitfieldReverse(highp ivec3);"
+ "ivec4 bitfieldReverse(highp ivec4);"
+
+ " uint bitfieldReverse(highp uint);"
+ "uvec2 bitfieldReverse(highp uvec2);"
+ "uvec3 bitfieldReverse(highp uvec3);"
+ "uvec4 bitfieldReverse(highp uvec4);"
+
+ "\n");
+ }
+
+ if (profile == EEsProfile && version >= 310) {
+ commonBuiltins.append(
+ "lowp int bitCount( int);"
+ "lowp ivec2 bitCount(ivec2);"
+ "lowp ivec3 bitCount(ivec3);"
+ "lowp ivec4 bitCount(ivec4);"
+
+ "lowp int bitCount( uint);"
+ "lowp ivec2 bitCount(uvec2);"
+ "lowp ivec3 bitCount(uvec3);"
+ "lowp ivec4 bitCount(uvec4);"
+
+ "lowp int findMSB(highp int);"
+ "lowp ivec2 findMSB(highp ivec2);"
+ "lowp ivec3 findMSB(highp ivec3);"
+ "lowp ivec4 findMSB(highp ivec4);"
+
+ "lowp int findMSB(highp uint);"
+ "lowp ivec2 findMSB(highp uvec2);"
+ "lowp ivec3 findMSB(highp uvec3);"
+ "lowp ivec4 findMSB(highp uvec4);"
+
+ "\n");
+ }
+
+ // GL_ARB_shader_ballot
+ if (profile != EEsProfile && version >= 450) {
+ commonBuiltins.append(
+ "uint64_t ballotARB(bool);"
+
+ "float readInvocationARB(float, uint);"
+ "vec2 readInvocationARB(vec2, uint);"
+ "vec3 readInvocationARB(vec3, uint);"
+ "vec4 readInvocationARB(vec4, uint);"
+
+ "int readInvocationARB(int, uint);"
+ "ivec2 readInvocationARB(ivec2, uint);"
+ "ivec3 readInvocationARB(ivec3, uint);"
+ "ivec4 readInvocationARB(ivec4, uint);"
+
+ "uint readInvocationARB(uint, uint);"
+ "uvec2 readInvocationARB(uvec2, uint);"
+ "uvec3 readInvocationARB(uvec3, uint);"
+ "uvec4 readInvocationARB(uvec4, uint);"
+
+ "float readFirstInvocationARB(float);"
+ "vec2 readFirstInvocationARB(vec2);"
+ "vec3 readFirstInvocationARB(vec3);"
+ "vec4 readFirstInvocationARB(vec4);"
+
+ "int readFirstInvocationARB(int);"
+ "ivec2 readFirstInvocationARB(ivec2);"
+ "ivec3 readFirstInvocationARB(ivec3);"
+ "ivec4 readFirstInvocationARB(ivec4);"
+
+ "uint readFirstInvocationARB(uint);"
+ "uvec2 readFirstInvocationARB(uvec2);"
+ "uvec3 readFirstInvocationARB(uvec3);"
+ "uvec4 readFirstInvocationARB(uvec4);"
+
+ "\n");
+ }
+
+ // GL_ARB_shader_group_vote
+ if (profile != EEsProfile && version >= 430) {
+ commonBuiltins.append(
+ "bool anyInvocationARB(bool);"
+ "bool allInvocationsARB(bool);"
+ "bool allInvocationsEqualARB(bool);"
+
+ "\n");
+ }
+
+ // GL_KHR_shader_subgroup
+ if ((profile == EEsProfile && version >= 310) ||
+ (profile != EEsProfile && version >= 140)) {
+ commonBuiltins.append(
+ "void subgroupBarrier();"
+ "void subgroupMemoryBarrier();"
+ "void subgroupMemoryBarrierBuffer();"
+ "void subgroupMemoryBarrierImage();"
+ "bool subgroupElect();"
+
+ "bool subgroupAll(bool);\n"
+ "bool subgroupAny(bool);\n"
+
+ "bool subgroupAllEqual(float);\n"
+ "bool subgroupAllEqual(vec2);\n"
+ "bool subgroupAllEqual(vec3);\n"
+ "bool subgroupAllEqual(vec4);\n"
+ "bool subgroupAllEqual(int);\n"
+ "bool subgroupAllEqual(ivec2);\n"
+ "bool subgroupAllEqual(ivec3);\n"
+ "bool subgroupAllEqual(ivec4);\n"
+ "bool subgroupAllEqual(uint);\n"
+ "bool subgroupAllEqual(uvec2);\n"
+ "bool subgroupAllEqual(uvec3);\n"
+ "bool subgroupAllEqual(uvec4);\n"
+ "bool subgroupAllEqual(bool);\n"
+ "bool subgroupAllEqual(bvec2);\n"
+ "bool subgroupAllEqual(bvec3);\n"
+ "bool subgroupAllEqual(bvec4);\n"
+
+ "float subgroupBroadcast(float, uint);\n"
+ "vec2 subgroupBroadcast(vec2, uint);\n"
+ "vec3 subgroupBroadcast(vec3, uint);\n"
+ "vec4 subgroupBroadcast(vec4, uint);\n"
+ "int subgroupBroadcast(int, uint);\n"
+ "ivec2 subgroupBroadcast(ivec2, uint);\n"
+ "ivec3 subgroupBroadcast(ivec3, uint);\n"
+ "ivec4 subgroupBroadcast(ivec4, uint);\n"
+ "uint subgroupBroadcast(uint, uint);\n"
+ "uvec2 subgroupBroadcast(uvec2, uint);\n"
+ "uvec3 subgroupBroadcast(uvec3, uint);\n"
+ "uvec4 subgroupBroadcast(uvec4, uint);\n"
+ "bool subgroupBroadcast(bool, uint);\n"
+ "bvec2 subgroupBroadcast(bvec2, uint);\n"
+ "bvec3 subgroupBroadcast(bvec3, uint);\n"
+ "bvec4 subgroupBroadcast(bvec4, uint);\n"
+
+ "float subgroupBroadcastFirst(float);\n"
+ "vec2 subgroupBroadcastFirst(vec2);\n"
+ "vec3 subgroupBroadcastFirst(vec3);\n"
+ "vec4 subgroupBroadcastFirst(vec4);\n"
+ "int subgroupBroadcastFirst(int);\n"
+ "ivec2 subgroupBroadcastFirst(ivec2);\n"
+ "ivec3 subgroupBroadcastFirst(ivec3);\n"
+ "ivec4 subgroupBroadcastFirst(ivec4);\n"
+ "uint subgroupBroadcastFirst(uint);\n"
+ "uvec2 subgroupBroadcastFirst(uvec2);\n"
+ "uvec3 subgroupBroadcastFirst(uvec3);\n"
+ "uvec4 subgroupBroadcastFirst(uvec4);\n"
+ "bool subgroupBroadcastFirst(bool);\n"
+ "bvec2 subgroupBroadcastFirst(bvec2);\n"
+ "bvec3 subgroupBroadcastFirst(bvec3);\n"
+ "bvec4 subgroupBroadcastFirst(bvec4);\n"
+
+ "uvec4 subgroupBallot(bool);\n"
+ "bool subgroupInverseBallot(uvec4);\n"
+ "bool subgroupBallotBitExtract(uvec4, uint);\n"
+ "uint subgroupBallotBitCount(uvec4);\n"
+ "uint subgroupBallotInclusiveBitCount(uvec4);\n"
+ "uint subgroupBallotExclusiveBitCount(uvec4);\n"
+ "uint subgroupBallotFindLSB(uvec4);\n"
+ "uint subgroupBallotFindMSB(uvec4);\n"
+
+ "float subgroupShuffle(float, uint);\n"
+ "vec2 subgroupShuffle(vec2, uint);\n"
+ "vec3 subgroupShuffle(vec3, uint);\n"
+ "vec4 subgroupShuffle(vec4, uint);\n"
+ "int subgroupShuffle(int, uint);\n"
+ "ivec2 subgroupShuffle(ivec2, uint);\n"
+ "ivec3 subgroupShuffle(ivec3, uint);\n"
+ "ivec4 subgroupShuffle(ivec4, uint);\n"
+ "uint subgroupShuffle(uint, uint);\n"
+ "uvec2 subgroupShuffle(uvec2, uint);\n"
+ "uvec3 subgroupShuffle(uvec3, uint);\n"
+ "uvec4 subgroupShuffle(uvec4, uint);\n"
+ "bool subgroupShuffle(bool, uint);\n"
+ "bvec2 subgroupShuffle(bvec2, uint);\n"
+ "bvec3 subgroupShuffle(bvec3, uint);\n"
+ "bvec4 subgroupShuffle(bvec4, uint);\n"
+
+ "float subgroupShuffleXor(float, uint);\n"
+ "vec2 subgroupShuffleXor(vec2, uint);\n"
+ "vec3 subgroupShuffleXor(vec3, uint);\n"
+ "vec4 subgroupShuffleXor(vec4, uint);\n"
+ "int subgroupShuffleXor(int, uint);\n"
+ "ivec2 subgroupShuffleXor(ivec2, uint);\n"
+ "ivec3 subgroupShuffleXor(ivec3, uint);\n"
+ "ivec4 subgroupShuffleXor(ivec4, uint);\n"
+ "uint subgroupShuffleXor(uint, uint);\n"
+ "uvec2 subgroupShuffleXor(uvec2, uint);\n"
+ "uvec3 subgroupShuffleXor(uvec3, uint);\n"
+ "uvec4 subgroupShuffleXor(uvec4, uint);\n"
+ "bool subgroupShuffleXor(bool, uint);\n"
+ "bvec2 subgroupShuffleXor(bvec2, uint);\n"
+ "bvec3 subgroupShuffleXor(bvec3, uint);\n"
+ "bvec4 subgroupShuffleXor(bvec4, uint);\n"
+
+ "float subgroupShuffleUp(float, uint delta);\n"
+ "vec2 subgroupShuffleUp(vec2, uint delta);\n"
+ "vec3 subgroupShuffleUp(vec3, uint delta);\n"
+ "vec4 subgroupShuffleUp(vec4, uint delta);\n"
+ "int subgroupShuffleUp(int, uint delta);\n"
+ "ivec2 subgroupShuffleUp(ivec2, uint delta);\n"
+ "ivec3 subgroupShuffleUp(ivec3, uint delta);\n"
+ "ivec4 subgroupShuffleUp(ivec4, uint delta);\n"
+ "uint subgroupShuffleUp(uint, uint delta);\n"
+ "uvec2 subgroupShuffleUp(uvec2, uint delta);\n"
+ "uvec3 subgroupShuffleUp(uvec3, uint delta);\n"
+ "uvec4 subgroupShuffleUp(uvec4, uint delta);\n"
+ "bool subgroupShuffleUp(bool, uint delta);\n"
+ "bvec2 subgroupShuffleUp(bvec2, uint delta);\n"
+ "bvec3 subgroupShuffleUp(bvec3, uint delta);\n"
+ "bvec4 subgroupShuffleUp(bvec4, uint delta);\n"
+
+ "float subgroupShuffleDown(float, uint delta);\n"
+ "vec2 subgroupShuffleDown(vec2, uint delta);\n"
+ "vec3 subgroupShuffleDown(vec3, uint delta);\n"
+ "vec4 subgroupShuffleDown(vec4, uint delta);\n"
+ "int subgroupShuffleDown(int, uint delta);\n"
+ "ivec2 subgroupShuffleDown(ivec2, uint delta);\n"
+ "ivec3 subgroupShuffleDown(ivec3, uint delta);\n"
+ "ivec4 subgroupShuffleDown(ivec4, uint delta);\n"
+ "uint subgroupShuffleDown(uint, uint delta);\n"
+ "uvec2 subgroupShuffleDown(uvec2, uint delta);\n"
+ "uvec3 subgroupShuffleDown(uvec3, uint delta);\n"
+ "uvec4 subgroupShuffleDown(uvec4, uint delta);\n"
+ "bool subgroupShuffleDown(bool, uint delta);\n"
+ "bvec2 subgroupShuffleDown(bvec2, uint delta);\n"
+ "bvec3 subgroupShuffleDown(bvec3, uint delta);\n"
+ "bvec4 subgroupShuffleDown(bvec4, uint delta);\n"
+
+ "float subgroupAdd(float);\n"
+ "vec2 subgroupAdd(vec2);\n"
+ "vec3 subgroupAdd(vec3);\n"
+ "vec4 subgroupAdd(vec4);\n"
+ "int subgroupAdd(int);\n"
+ "ivec2 subgroupAdd(ivec2);\n"
+ "ivec3 subgroupAdd(ivec3);\n"
+ "ivec4 subgroupAdd(ivec4);\n"
+ "uint subgroupAdd(uint);\n"
+ "uvec2 subgroupAdd(uvec2);\n"
+ "uvec3 subgroupAdd(uvec3);\n"
+ "uvec4 subgroupAdd(uvec4);\n"
+
+ "float subgroupMul(float);\n"
+ "vec2 subgroupMul(vec2);\n"
+ "vec3 subgroupMul(vec3);\n"
+ "vec4 subgroupMul(vec4);\n"
+ "int subgroupMul(int);\n"
+ "ivec2 subgroupMul(ivec2);\n"
+ "ivec3 subgroupMul(ivec3);\n"
+ "ivec4 subgroupMul(ivec4);\n"
+ "uint subgroupMul(uint);\n"
+ "uvec2 subgroupMul(uvec2);\n"
+ "uvec3 subgroupMul(uvec3);\n"
+ "uvec4 subgroupMul(uvec4);\n"
+
+ "float subgroupMin(float);\n"
+ "vec2 subgroupMin(vec2);\n"
+ "vec3 subgroupMin(vec3);\n"
+ "vec4 subgroupMin(vec4);\n"
+ "int subgroupMin(int);\n"
+ "ivec2 subgroupMin(ivec2);\n"
+ "ivec3 subgroupMin(ivec3);\n"
+ "ivec4 subgroupMin(ivec4);\n"
+ "uint subgroupMin(uint);\n"
+ "uvec2 subgroupMin(uvec2);\n"
+ "uvec3 subgroupMin(uvec3);\n"
+ "uvec4 subgroupMin(uvec4);\n"
+
+ "float subgroupMax(float);\n"
+ "vec2 subgroupMax(vec2);\n"
+ "vec3 subgroupMax(vec3);\n"
+ "vec4 subgroupMax(vec4);\n"
+ "int subgroupMax(int);\n"
+ "ivec2 subgroupMax(ivec2);\n"
+ "ivec3 subgroupMax(ivec3);\n"
+ "ivec4 subgroupMax(ivec4);\n"
+ "uint subgroupMax(uint);\n"
+ "uvec2 subgroupMax(uvec2);\n"
+ "uvec3 subgroupMax(uvec3);\n"
+ "uvec4 subgroupMax(uvec4);\n"
+
+ "int subgroupAnd(int);\n"
+ "ivec2 subgroupAnd(ivec2);\n"
+ "ivec3 subgroupAnd(ivec3);\n"
+ "ivec4 subgroupAnd(ivec4);\n"
+ "uint subgroupAnd(uint);\n"
+ "uvec2 subgroupAnd(uvec2);\n"
+ "uvec3 subgroupAnd(uvec3);\n"
+ "uvec4 subgroupAnd(uvec4);\n"
+ "bool subgroupAnd(bool);\n"
+ "bvec2 subgroupAnd(bvec2);\n"
+ "bvec3 subgroupAnd(bvec3);\n"
+ "bvec4 subgroupAnd(bvec4);\n"
+
+ "int subgroupOr(int);\n"
+ "ivec2 subgroupOr(ivec2);\n"
+ "ivec3 subgroupOr(ivec3);\n"
+ "ivec4 subgroupOr(ivec4);\n"
+ "uint subgroupOr(uint);\n"
+ "uvec2 subgroupOr(uvec2);\n"
+ "uvec3 subgroupOr(uvec3);\n"
+ "uvec4 subgroupOr(uvec4);\n"
+ "bool subgroupOr(bool);\n"
+ "bvec2 subgroupOr(bvec2);\n"
+ "bvec3 subgroupOr(bvec3);\n"
+ "bvec4 subgroupOr(bvec4);\n"
+
+ "int subgroupXor(int);\n"
+ "ivec2 subgroupXor(ivec2);\n"
+ "ivec3 subgroupXor(ivec3);\n"
+ "ivec4 subgroupXor(ivec4);\n"
+ "uint subgroupXor(uint);\n"
+ "uvec2 subgroupXor(uvec2);\n"
+ "uvec3 subgroupXor(uvec3);\n"
+ "uvec4 subgroupXor(uvec4);\n"
+ "bool subgroupXor(bool);\n"
+ "bvec2 subgroupXor(bvec2);\n"
+ "bvec3 subgroupXor(bvec3);\n"
+ "bvec4 subgroupXor(bvec4);\n"
+
+ "float subgroupInclusiveAdd(float);\n"
+ "vec2 subgroupInclusiveAdd(vec2);\n"
+ "vec3 subgroupInclusiveAdd(vec3);\n"
+ "vec4 subgroupInclusiveAdd(vec4);\n"
+ "int subgroupInclusiveAdd(int);\n"
+ "ivec2 subgroupInclusiveAdd(ivec2);\n"
+ "ivec3 subgroupInclusiveAdd(ivec3);\n"
+ "ivec4 subgroupInclusiveAdd(ivec4);\n"
+ "uint subgroupInclusiveAdd(uint);\n"
+ "uvec2 subgroupInclusiveAdd(uvec2);\n"
+ "uvec3 subgroupInclusiveAdd(uvec3);\n"
+ "uvec4 subgroupInclusiveAdd(uvec4);\n"
+
+ "float subgroupInclusiveMul(float);\n"
+ "vec2 subgroupInclusiveMul(vec2);\n"
+ "vec3 subgroupInclusiveMul(vec3);\n"
+ "vec4 subgroupInclusiveMul(vec4);\n"
+ "int subgroupInclusiveMul(int);\n"
+ "ivec2 subgroupInclusiveMul(ivec2);\n"
+ "ivec3 subgroupInclusiveMul(ivec3);\n"
+ "ivec4 subgroupInclusiveMul(ivec4);\n"
+ "uint subgroupInclusiveMul(uint);\n"
+ "uvec2 subgroupInclusiveMul(uvec2);\n"
+ "uvec3 subgroupInclusiveMul(uvec3);\n"
+ "uvec4 subgroupInclusiveMul(uvec4);\n"
+
+ "float subgroupInclusiveMin(float);\n"
+ "vec2 subgroupInclusiveMin(vec2);\n"
+ "vec3 subgroupInclusiveMin(vec3);\n"
+ "vec4 subgroupInclusiveMin(vec4);\n"
+ "int subgroupInclusiveMin(int);\n"
+ "ivec2 subgroupInclusiveMin(ivec2);\n"
+ "ivec3 subgroupInclusiveMin(ivec3);\n"
+ "ivec4 subgroupInclusiveMin(ivec4);\n"
+ "uint subgroupInclusiveMin(uint);\n"
+ "uvec2 subgroupInclusiveMin(uvec2);\n"
+ "uvec3 subgroupInclusiveMin(uvec3);\n"
+ "uvec4 subgroupInclusiveMin(uvec4);\n"
+
+ "float subgroupInclusiveMax(float);\n"
+ "vec2 subgroupInclusiveMax(vec2);\n"
+ "vec3 subgroupInclusiveMax(vec3);\n"
+ "vec4 subgroupInclusiveMax(vec4);\n"
+ "int subgroupInclusiveMax(int);\n"
+ "ivec2 subgroupInclusiveMax(ivec2);\n"
+ "ivec3 subgroupInclusiveMax(ivec3);\n"
+ "ivec4 subgroupInclusiveMax(ivec4);\n"
+ "uint subgroupInclusiveMax(uint);\n"
+ "uvec2 subgroupInclusiveMax(uvec2);\n"
+ "uvec3 subgroupInclusiveMax(uvec3);\n"
+ "uvec4 subgroupInclusiveMax(uvec4);\n"
+
+ "int subgroupInclusiveAnd(int);\n"
+ "ivec2 subgroupInclusiveAnd(ivec2);\n"
+ "ivec3 subgroupInclusiveAnd(ivec3);\n"
+ "ivec4 subgroupInclusiveAnd(ivec4);\n"
+ "uint subgroupInclusiveAnd(uint);\n"
+ "uvec2 subgroupInclusiveAnd(uvec2);\n"
+ "uvec3 subgroupInclusiveAnd(uvec3);\n"
+ "uvec4 subgroupInclusiveAnd(uvec4);\n"
+ "bool subgroupInclusiveAnd(bool);\n"
+ "bvec2 subgroupInclusiveAnd(bvec2);\n"
+ "bvec3 subgroupInclusiveAnd(bvec3);\n"
+ "bvec4 subgroupInclusiveAnd(bvec4);\n"
+
+ "int subgroupInclusiveOr(int);\n"
+ "ivec2 subgroupInclusiveOr(ivec2);\n"
+ "ivec3 subgroupInclusiveOr(ivec3);\n"
+ "ivec4 subgroupInclusiveOr(ivec4);\n"
+ "uint subgroupInclusiveOr(uint);\n"
+ "uvec2 subgroupInclusiveOr(uvec2);\n"
+ "uvec3 subgroupInclusiveOr(uvec3);\n"
+ "uvec4 subgroupInclusiveOr(uvec4);\n"
+ "bool subgroupInclusiveOr(bool);\n"
+ "bvec2 subgroupInclusiveOr(bvec2);\n"
+ "bvec3 subgroupInclusiveOr(bvec3);\n"
+ "bvec4 subgroupInclusiveOr(bvec4);\n"
+
+ "int subgroupInclusiveXor(int);\n"
+ "ivec2 subgroupInclusiveXor(ivec2);\n"
+ "ivec3 subgroupInclusiveXor(ivec3);\n"
+ "ivec4 subgroupInclusiveXor(ivec4);\n"
+ "uint subgroupInclusiveXor(uint);\n"
+ "uvec2 subgroupInclusiveXor(uvec2);\n"
+ "uvec3 subgroupInclusiveXor(uvec3);\n"
+ "uvec4 subgroupInclusiveXor(uvec4);\n"
+ "bool subgroupInclusiveXor(bool);\n"
+ "bvec2 subgroupInclusiveXor(bvec2);\n"
+ "bvec3 subgroupInclusiveXor(bvec3);\n"
+ "bvec4 subgroupInclusiveXor(bvec4);\n"
+
+ "float subgroupExclusiveAdd(float);\n"
+ "vec2 subgroupExclusiveAdd(vec2);\n"
+ "vec3 subgroupExclusiveAdd(vec3);\n"
+ "vec4 subgroupExclusiveAdd(vec4);\n"
+ "int subgroupExclusiveAdd(int);\n"
+ "ivec2 subgroupExclusiveAdd(ivec2);\n"
+ "ivec3 subgroupExclusiveAdd(ivec3);\n"
+ "ivec4 subgroupExclusiveAdd(ivec4);\n"
+ "uint subgroupExclusiveAdd(uint);\n"
+ "uvec2 subgroupExclusiveAdd(uvec2);\n"
+ "uvec3 subgroupExclusiveAdd(uvec3);\n"
+ "uvec4 subgroupExclusiveAdd(uvec4);\n"
+
+ "float subgroupExclusiveMul(float);\n"
+ "vec2 subgroupExclusiveMul(vec2);\n"
+ "vec3 subgroupExclusiveMul(vec3);\n"
+ "vec4 subgroupExclusiveMul(vec4);\n"
+ "int subgroupExclusiveMul(int);\n"
+ "ivec2 subgroupExclusiveMul(ivec2);\n"
+ "ivec3 subgroupExclusiveMul(ivec3);\n"
+ "ivec4 subgroupExclusiveMul(ivec4);\n"
+ "uint subgroupExclusiveMul(uint);\n"
+ "uvec2 subgroupExclusiveMul(uvec2);\n"
+ "uvec3 subgroupExclusiveMul(uvec3);\n"
+ "uvec4 subgroupExclusiveMul(uvec4);\n"
+
+ "float subgroupExclusiveMin(float);\n"
+ "vec2 subgroupExclusiveMin(vec2);\n"
+ "vec3 subgroupExclusiveMin(vec3);\n"
+ "vec4 subgroupExclusiveMin(vec4);\n"
+ "int subgroupExclusiveMin(int);\n"
+ "ivec2 subgroupExclusiveMin(ivec2);\n"
+ "ivec3 subgroupExclusiveMin(ivec3);\n"
+ "ivec4 subgroupExclusiveMin(ivec4);\n"
+ "uint subgroupExclusiveMin(uint);\n"
+ "uvec2 subgroupExclusiveMin(uvec2);\n"
+ "uvec3 subgroupExclusiveMin(uvec3);\n"
+ "uvec4 subgroupExclusiveMin(uvec4);\n"
+
+ "float subgroupExclusiveMax(float);\n"
+ "vec2 subgroupExclusiveMax(vec2);\n"
+ "vec3 subgroupExclusiveMax(vec3);\n"
+ "vec4 subgroupExclusiveMax(vec4);\n"
+ "int subgroupExclusiveMax(int);\n"
+ "ivec2 subgroupExclusiveMax(ivec2);\n"
+ "ivec3 subgroupExclusiveMax(ivec3);\n"
+ "ivec4 subgroupExclusiveMax(ivec4);\n"
+ "uint subgroupExclusiveMax(uint);\n"
+ "uvec2 subgroupExclusiveMax(uvec2);\n"
+ "uvec3 subgroupExclusiveMax(uvec3);\n"
+ "uvec4 subgroupExclusiveMax(uvec4);\n"
+
+ "int subgroupExclusiveAnd(int);\n"
+ "ivec2 subgroupExclusiveAnd(ivec2);\n"
+ "ivec3 subgroupExclusiveAnd(ivec3);\n"
+ "ivec4 subgroupExclusiveAnd(ivec4);\n"
+ "uint subgroupExclusiveAnd(uint);\n"
+ "uvec2 subgroupExclusiveAnd(uvec2);\n"
+ "uvec3 subgroupExclusiveAnd(uvec3);\n"
+ "uvec4 subgroupExclusiveAnd(uvec4);\n"
+ "bool subgroupExclusiveAnd(bool);\n"
+ "bvec2 subgroupExclusiveAnd(bvec2);\n"
+ "bvec3 subgroupExclusiveAnd(bvec3);\n"
+ "bvec4 subgroupExclusiveAnd(bvec4);\n"
+
+ "int subgroupExclusiveOr(int);\n"
+ "ivec2 subgroupExclusiveOr(ivec2);\n"
+ "ivec3 subgroupExclusiveOr(ivec3);\n"
+ "ivec4 subgroupExclusiveOr(ivec4);\n"
+ "uint subgroupExclusiveOr(uint);\n"
+ "uvec2 subgroupExclusiveOr(uvec2);\n"
+ "uvec3 subgroupExclusiveOr(uvec3);\n"
+ "uvec4 subgroupExclusiveOr(uvec4);\n"
+ "bool subgroupExclusiveOr(bool);\n"
+ "bvec2 subgroupExclusiveOr(bvec2);\n"
+ "bvec3 subgroupExclusiveOr(bvec3);\n"
+ "bvec4 subgroupExclusiveOr(bvec4);\n"
+
+ "int subgroupExclusiveXor(int);\n"
+ "ivec2 subgroupExclusiveXor(ivec2);\n"
+ "ivec3 subgroupExclusiveXor(ivec3);\n"
+ "ivec4 subgroupExclusiveXor(ivec4);\n"
+ "uint subgroupExclusiveXor(uint);\n"
+ "uvec2 subgroupExclusiveXor(uvec2);\n"
+ "uvec3 subgroupExclusiveXor(uvec3);\n"
+ "uvec4 subgroupExclusiveXor(uvec4);\n"
+ "bool subgroupExclusiveXor(bool);\n"
+ "bvec2 subgroupExclusiveXor(bvec2);\n"
+ "bvec3 subgroupExclusiveXor(bvec3);\n"
+ "bvec4 subgroupExclusiveXor(bvec4);\n"
+
+ "float subgroupClusteredAdd(float, uint);\n"
+ "vec2 subgroupClusteredAdd(vec2, uint);\n"
+ "vec3 subgroupClusteredAdd(vec3, uint);\n"
+ "vec4 subgroupClusteredAdd(vec4, uint);\n"
+ "int subgroupClusteredAdd(int, uint);\n"
+ "ivec2 subgroupClusteredAdd(ivec2, uint);\n"
+ "ivec3 subgroupClusteredAdd(ivec3, uint);\n"
+ "ivec4 subgroupClusteredAdd(ivec4, uint);\n"
+ "uint subgroupClusteredAdd(uint, uint);\n"
+ "uvec2 subgroupClusteredAdd(uvec2, uint);\n"
+ "uvec3 subgroupClusteredAdd(uvec3, uint);\n"
+ "uvec4 subgroupClusteredAdd(uvec4, uint);\n"
+
+ "float subgroupClusteredMul(float, uint);\n"
+ "vec2 subgroupClusteredMul(vec2, uint);\n"
+ "vec3 subgroupClusteredMul(vec3, uint);\n"
+ "vec4 subgroupClusteredMul(vec4, uint);\n"
+ "int subgroupClusteredMul(int, uint);\n"
+ "ivec2 subgroupClusteredMul(ivec2, uint);\n"
+ "ivec3 subgroupClusteredMul(ivec3, uint);\n"
+ "ivec4 subgroupClusteredMul(ivec4, uint);\n"
+ "uint subgroupClusteredMul(uint, uint);\n"
+ "uvec2 subgroupClusteredMul(uvec2, uint);\n"
+ "uvec3 subgroupClusteredMul(uvec3, uint);\n"
+ "uvec4 subgroupClusteredMul(uvec4, uint);\n"
+
+ "float subgroupClusteredMin(float, uint);\n"
+ "vec2 subgroupClusteredMin(vec2, uint);\n"
+ "vec3 subgroupClusteredMin(vec3, uint);\n"
+ "vec4 subgroupClusteredMin(vec4, uint);\n"
+ "int subgroupClusteredMin(int, uint);\n"
+ "ivec2 subgroupClusteredMin(ivec2, uint);\n"
+ "ivec3 subgroupClusteredMin(ivec3, uint);\n"
+ "ivec4 subgroupClusteredMin(ivec4, uint);\n"
+ "uint subgroupClusteredMin(uint, uint);\n"
+ "uvec2 subgroupClusteredMin(uvec2, uint);\n"
+ "uvec3 subgroupClusteredMin(uvec3, uint);\n"
+ "uvec4 subgroupClusteredMin(uvec4, uint);\n"
+
+ "float subgroupClusteredMax(float, uint);\n"
+ "vec2 subgroupClusteredMax(vec2, uint);\n"
+ "vec3 subgroupClusteredMax(vec3, uint);\n"
+ "vec4 subgroupClusteredMax(vec4, uint);\n"
+ "int subgroupClusteredMax(int, uint);\n"
+ "ivec2 subgroupClusteredMax(ivec2, uint);\n"
+ "ivec3 subgroupClusteredMax(ivec3, uint);\n"
+ "ivec4 subgroupClusteredMax(ivec4, uint);\n"
+ "uint subgroupClusteredMax(uint, uint);\n"
+ "uvec2 subgroupClusteredMax(uvec2, uint);\n"
+ "uvec3 subgroupClusteredMax(uvec3, uint);\n"
+ "uvec4 subgroupClusteredMax(uvec4, uint);\n"
+
+ "int subgroupClusteredAnd(int, uint);\n"
+ "ivec2 subgroupClusteredAnd(ivec2, uint);\n"
+ "ivec3 subgroupClusteredAnd(ivec3, uint);\n"
+ "ivec4 subgroupClusteredAnd(ivec4, uint);\n"
+ "uint subgroupClusteredAnd(uint, uint);\n"
+ "uvec2 subgroupClusteredAnd(uvec2, uint);\n"
+ "uvec3 subgroupClusteredAnd(uvec3, uint);\n"
+ "uvec4 subgroupClusteredAnd(uvec4, uint);\n"
+ "bool subgroupClusteredAnd(bool, uint);\n"
+ "bvec2 subgroupClusteredAnd(bvec2, uint);\n"
+ "bvec3 subgroupClusteredAnd(bvec3, uint);\n"
+ "bvec4 subgroupClusteredAnd(bvec4, uint);\n"
+
+ "int subgroupClusteredOr(int, uint);\n"
+ "ivec2 subgroupClusteredOr(ivec2, uint);\n"
+ "ivec3 subgroupClusteredOr(ivec3, uint);\n"
+ "ivec4 subgroupClusteredOr(ivec4, uint);\n"
+ "uint subgroupClusteredOr(uint, uint);\n"
+ "uvec2 subgroupClusteredOr(uvec2, uint);\n"
+ "uvec3 subgroupClusteredOr(uvec3, uint);\n"
+ "uvec4 subgroupClusteredOr(uvec4, uint);\n"
+ "bool subgroupClusteredOr(bool, uint);\n"
+ "bvec2 subgroupClusteredOr(bvec2, uint);\n"
+ "bvec3 subgroupClusteredOr(bvec3, uint);\n"
+ "bvec4 subgroupClusteredOr(bvec4, uint);\n"
+
+ "int subgroupClusteredXor(int, uint);\n"
+ "ivec2 subgroupClusteredXor(ivec2, uint);\n"
+ "ivec3 subgroupClusteredXor(ivec3, uint);\n"
+ "ivec4 subgroupClusteredXor(ivec4, uint);\n"
+ "uint subgroupClusteredXor(uint, uint);\n"
+ "uvec2 subgroupClusteredXor(uvec2, uint);\n"
+ "uvec3 subgroupClusteredXor(uvec3, uint);\n"
+ "uvec4 subgroupClusteredXor(uvec4, uint);\n"
+ "bool subgroupClusteredXor(bool, uint);\n"
+ "bvec2 subgroupClusteredXor(bvec2, uint);\n"
+ "bvec3 subgroupClusteredXor(bvec3, uint);\n"
+ "bvec4 subgroupClusteredXor(bvec4, uint);\n"
+
+ "float subgroupQuadBroadcast(float, uint);\n"
+ "vec2 subgroupQuadBroadcast(vec2, uint);\n"
+ "vec3 subgroupQuadBroadcast(vec3, uint);\n"
+ "vec4 subgroupQuadBroadcast(vec4, uint);\n"
+ "int subgroupQuadBroadcast(int, uint);\n"
+ "ivec2 subgroupQuadBroadcast(ivec2, uint);\n"
+ "ivec3 subgroupQuadBroadcast(ivec3, uint);\n"
+ "ivec4 subgroupQuadBroadcast(ivec4, uint);\n"
+ "uint subgroupQuadBroadcast(uint, uint);\n"
+ "uvec2 subgroupQuadBroadcast(uvec2, uint);\n"
+ "uvec3 subgroupQuadBroadcast(uvec3, uint);\n"
+ "uvec4 subgroupQuadBroadcast(uvec4, uint);\n"
+ "bool subgroupQuadBroadcast(bool, uint);\n"
+ "bvec2 subgroupQuadBroadcast(bvec2, uint);\n"
+ "bvec3 subgroupQuadBroadcast(bvec3, uint);\n"
+ "bvec4 subgroupQuadBroadcast(bvec4, uint);\n"
+
+ "float subgroupQuadSwapHorizontal(float);\n"
+ "vec2 subgroupQuadSwapHorizontal(vec2);\n"
+ "vec3 subgroupQuadSwapHorizontal(vec3);\n"
+ "vec4 subgroupQuadSwapHorizontal(vec4);\n"
+ "int subgroupQuadSwapHorizontal(int);\n"
+ "ivec2 subgroupQuadSwapHorizontal(ivec2);\n"
+ "ivec3 subgroupQuadSwapHorizontal(ivec3);\n"
+ "ivec4 subgroupQuadSwapHorizontal(ivec4);\n"
+ "uint subgroupQuadSwapHorizontal(uint);\n"
+ "uvec2 subgroupQuadSwapHorizontal(uvec2);\n"
+ "uvec3 subgroupQuadSwapHorizontal(uvec3);\n"
+ "uvec4 subgroupQuadSwapHorizontal(uvec4);\n"
+ "bool subgroupQuadSwapHorizontal(bool);\n"
+ "bvec2 subgroupQuadSwapHorizontal(bvec2);\n"
+ "bvec3 subgroupQuadSwapHorizontal(bvec3);\n"
+ "bvec4 subgroupQuadSwapHorizontal(bvec4);\n"
+
+ "float subgroupQuadSwapVertical(float);\n"
+ "vec2 subgroupQuadSwapVertical(vec2);\n"
+ "vec3 subgroupQuadSwapVertical(vec3);\n"
+ "vec4 subgroupQuadSwapVertical(vec4);\n"
+ "int subgroupQuadSwapVertical(int);\n"
+ "ivec2 subgroupQuadSwapVertical(ivec2);\n"
+ "ivec3 subgroupQuadSwapVertical(ivec3);\n"
+ "ivec4 subgroupQuadSwapVertical(ivec4);\n"
+ "uint subgroupQuadSwapVertical(uint);\n"
+ "uvec2 subgroupQuadSwapVertical(uvec2);\n"
+ "uvec3 subgroupQuadSwapVertical(uvec3);\n"
+ "uvec4 subgroupQuadSwapVertical(uvec4);\n"
+ "bool subgroupQuadSwapVertical(bool);\n"
+ "bvec2 subgroupQuadSwapVertical(bvec2);\n"
+ "bvec3 subgroupQuadSwapVertical(bvec3);\n"
+ "bvec4 subgroupQuadSwapVertical(bvec4);\n"
+
+ "float subgroupQuadSwapDiagonal(float);\n"
+ "vec2 subgroupQuadSwapDiagonal(vec2);\n"
+ "vec3 subgroupQuadSwapDiagonal(vec3);\n"
+ "vec4 subgroupQuadSwapDiagonal(vec4);\n"
+ "int subgroupQuadSwapDiagonal(int);\n"
+ "ivec2 subgroupQuadSwapDiagonal(ivec2);\n"
+ "ivec3 subgroupQuadSwapDiagonal(ivec3);\n"
+ "ivec4 subgroupQuadSwapDiagonal(ivec4);\n"
+ "uint subgroupQuadSwapDiagonal(uint);\n"
+ "uvec2 subgroupQuadSwapDiagonal(uvec2);\n"
+ "uvec3 subgroupQuadSwapDiagonal(uvec3);\n"
+ "uvec4 subgroupQuadSwapDiagonal(uvec4);\n"
+ "bool subgroupQuadSwapDiagonal(bool);\n"
+ "bvec2 subgroupQuadSwapDiagonal(bvec2);\n"
+ "bvec3 subgroupQuadSwapDiagonal(bvec3);\n"
+ "bvec4 subgroupQuadSwapDiagonal(bvec4);\n"
+
+#ifdef NV_EXTENSIONS
+ "uvec4 subgroupPartitionNV(float);\n"
+ "uvec4 subgroupPartitionNV(vec2);\n"
+ "uvec4 subgroupPartitionNV(vec3);\n"
+ "uvec4 subgroupPartitionNV(vec4);\n"
+ "uvec4 subgroupPartitionNV(int);\n"
+ "uvec4 subgroupPartitionNV(ivec2);\n"
+ "uvec4 subgroupPartitionNV(ivec3);\n"
+ "uvec4 subgroupPartitionNV(ivec4);\n"
+ "uvec4 subgroupPartitionNV(uint);\n"
+ "uvec4 subgroupPartitionNV(uvec2);\n"
+ "uvec4 subgroupPartitionNV(uvec3);\n"
+ "uvec4 subgroupPartitionNV(uvec4);\n"
+ "uvec4 subgroupPartitionNV(bool);\n"
+ "uvec4 subgroupPartitionNV(bvec2);\n"
+ "uvec4 subgroupPartitionNV(bvec3);\n"
+ "uvec4 subgroupPartitionNV(bvec4);\n"
+
+ "float subgroupPartitionedAddNV(float, uvec4 ballot);\n"
+ "vec2 subgroupPartitionedAddNV(vec2, uvec4 ballot);\n"
+ "vec3 subgroupPartitionedAddNV(vec3, uvec4 ballot);\n"
+ "vec4 subgroupPartitionedAddNV(vec4, uvec4 ballot);\n"
+ "int subgroupPartitionedAddNV(int, uvec4 ballot);\n"
+ "ivec2 subgroupPartitionedAddNV(ivec2, uvec4 ballot);\n"
+ "ivec3 subgroupPartitionedAddNV(ivec3, uvec4 ballot);\n"
+ "ivec4 subgroupPartitionedAddNV(ivec4, uvec4 ballot);\n"
+ "uint subgroupPartitionedAddNV(uint, uvec4 ballot);\n"
+ "uvec2 subgroupPartitionedAddNV(uvec2, uvec4 ballot);\n"
+ "uvec3 subgroupPartitionedAddNV(uvec3, uvec4 ballot);\n"
+ "uvec4 subgroupPartitionedAddNV(uvec4, uvec4 ballot);\n"
+
+ "float subgroupPartitionedMulNV(float, uvec4 ballot);\n"
+ "vec2 subgroupPartitionedMulNV(vec2, uvec4 ballot);\n"
+ "vec3 subgroupPartitionedMulNV(vec3, uvec4 ballot);\n"
+ "vec4 subgroupPartitionedMulNV(vec4, uvec4 ballot);\n"
+ "int subgroupPartitionedMulNV(int, uvec4 ballot);\n"
+ "ivec2 subgroupPartitionedMulNV(ivec2, uvec4 ballot);\n"
+ "ivec3 subgroupPartitionedMulNV(ivec3, uvec4 ballot);\n"
+ "ivec4 subgroupPartitionedMulNV(ivec4, uvec4 ballot);\n"
+ "uint subgroupPartitionedMulNV(uint, uvec4 ballot);\n"
+ "uvec2 subgroupPartitionedMulNV(uvec2, uvec4 ballot);\n"
+ "uvec3 subgroupPartitionedMulNV(uvec3, uvec4 ballot);\n"
+ "uvec4 subgroupPartitionedMulNV(uvec4, uvec4 ballot);\n"
+
+ "float subgroupPartitionedMinNV(float, uvec4 ballot);\n"
+ "vec2 subgroupPartitionedMinNV(vec2, uvec4 ballot);\n"
+ "vec3 subgroupPartitionedMinNV(vec3, uvec4 ballot);\n"
+ "vec4 subgroupPartitionedMinNV(vec4, uvec4 ballot);\n"
+ "int subgroupPartitionedMinNV(int, uvec4 ballot);\n"
+ "ivec2 subgroupPartitionedMinNV(ivec2, uvec4 ballot);\n"
+ "ivec3 subgroupPartitionedMinNV(ivec3, uvec4 ballot);\n"
+ "ivec4 subgroupPartitionedMinNV(ivec4, uvec4 ballot);\n"
+ "uint subgroupPartitionedMinNV(uint, uvec4 ballot);\n"
+ "uvec2 subgroupPartitionedMinNV(uvec2, uvec4 ballot);\n"
+ "uvec3 subgroupPartitionedMinNV(uvec3, uvec4 ballot);\n"
+ "uvec4 subgroupPartitionedMinNV(uvec4, uvec4 ballot);\n"
+
+ "float subgroupPartitionedMaxNV(float, uvec4 ballot);\n"
+ "vec2 subgroupPartitionedMaxNV(vec2, uvec4 ballot);\n"
+ "vec3 subgroupPartitionedMaxNV(vec3, uvec4 ballot);\n"
+ "vec4 subgroupPartitionedMaxNV(vec4, uvec4 ballot);\n"
+ "int subgroupPartitionedMaxNV(int, uvec4 ballot);\n"
+ "ivec2 subgroupPartitionedMaxNV(ivec2, uvec4 ballot);\n"
+ "ivec3 subgroupPartitionedMaxNV(ivec3, uvec4 ballot);\n"
+ "ivec4 subgroupPartitionedMaxNV(ivec4, uvec4 ballot);\n"
+ "uint subgroupPartitionedMaxNV(uint, uvec4 ballot);\n"
+ "uvec2 subgroupPartitionedMaxNV(uvec2, uvec4 ballot);\n"
+ "uvec3 subgroupPartitionedMaxNV(uvec3, uvec4 ballot);\n"
+ "uvec4 subgroupPartitionedMaxNV(uvec4, uvec4 ballot);\n"
+
+ "int subgroupPartitionedAndNV(int, uvec4 ballot);\n"
+ "ivec2 subgroupPartitionedAndNV(ivec2, uvec4 ballot);\n"
+ "ivec3 subgroupPartitionedAndNV(ivec3, uvec4 ballot);\n"
+ "ivec4 subgroupPartitionedAndNV(ivec4, uvec4 ballot);\n"
+ "uint subgroupPartitionedAndNV(uint, uvec4 ballot);\n"
+ "uvec2 subgroupPartitionedAndNV(uvec2, uvec4 ballot);\n"
+ "uvec3 subgroupPartitionedAndNV(uvec3, uvec4 ballot);\n"
+ "uvec4 subgroupPartitionedAndNV(uvec4, uvec4 ballot);\n"
+ "bool subgroupPartitionedAndNV(bool, uvec4 ballot);\n"
+ "bvec2 subgroupPartitionedAndNV(bvec2, uvec4 ballot);\n"
+ "bvec3 subgroupPartitionedAndNV(bvec3, uvec4 ballot);\n"
+ "bvec4 subgroupPartitionedAndNV(bvec4, uvec4 ballot);\n"
+
+ "int subgroupPartitionedOrNV(int, uvec4 ballot);\n"
+ "ivec2 subgroupPartitionedOrNV(ivec2, uvec4 ballot);\n"
+ "ivec3 subgroupPartitionedOrNV(ivec3, uvec4 ballot);\n"
+ "ivec4 subgroupPartitionedOrNV(ivec4, uvec4 ballot);\n"
+ "uint subgroupPartitionedOrNV(uint, uvec4 ballot);\n"
+ "uvec2 subgroupPartitionedOrNV(uvec2, uvec4 ballot);\n"
+ "uvec3 subgroupPartitionedOrNV(uvec3, uvec4 ballot);\n"
+ "uvec4 subgroupPartitionedOrNV(uvec4, uvec4 ballot);\n"
+ "bool subgroupPartitionedOrNV(bool, uvec4 ballot);\n"
+ "bvec2 subgroupPartitionedOrNV(bvec2, uvec4 ballot);\n"
+ "bvec3 subgroupPartitionedOrNV(bvec3, uvec4 ballot);\n"
+ "bvec4 subgroupPartitionedOrNV(bvec4, uvec4 ballot);\n"
+
+ "int subgroupPartitionedXorNV(int, uvec4 ballot);\n"
+ "ivec2 subgroupPartitionedXorNV(ivec2, uvec4 ballot);\n"
+ "ivec3 subgroupPartitionedXorNV(ivec3, uvec4 ballot);\n"
+ "ivec4 subgroupPartitionedXorNV(ivec4, uvec4 ballot);\n"
+ "uint subgroupPartitionedXorNV(uint, uvec4 ballot);\n"
+ "uvec2 subgroupPartitionedXorNV(uvec2, uvec4 ballot);\n"
+ "uvec3 subgroupPartitionedXorNV(uvec3, uvec4 ballot);\n"
+ "uvec4 subgroupPartitionedXorNV(uvec4, uvec4 ballot);\n"
+ "bool subgroupPartitionedXorNV(bool, uvec4 ballot);\n"
+ "bvec2 subgroupPartitionedXorNV(bvec2, uvec4 ballot);\n"
+ "bvec3 subgroupPartitionedXorNV(bvec3, uvec4 ballot);\n"
+ "bvec4 subgroupPartitionedXorNV(bvec4, uvec4 ballot);\n"
+
+ "float subgroupPartitionedInclusiveAddNV(float, uvec4 ballot);\n"
+ "vec2 subgroupPartitionedInclusiveAddNV(vec2, uvec4 ballot);\n"
+ "vec3 subgroupPartitionedInclusiveAddNV(vec3, uvec4 ballot);\n"
+ "vec4 subgroupPartitionedInclusiveAddNV(vec4, uvec4 ballot);\n"
+ "int subgroupPartitionedInclusiveAddNV(int, uvec4 ballot);\n"
+ "ivec2 subgroupPartitionedInclusiveAddNV(ivec2, uvec4 ballot);\n"
+ "ivec3 subgroupPartitionedInclusiveAddNV(ivec3, uvec4 ballot);\n"
+ "ivec4 subgroupPartitionedInclusiveAddNV(ivec4, uvec4 ballot);\n"
+ "uint subgroupPartitionedInclusiveAddNV(uint, uvec4 ballot);\n"
+ "uvec2 subgroupPartitionedInclusiveAddNV(uvec2, uvec4 ballot);\n"
+ "uvec3 subgroupPartitionedInclusiveAddNV(uvec3, uvec4 ballot);\n"
+ "uvec4 subgroupPartitionedInclusiveAddNV(uvec4, uvec4 ballot);\n"
+
+ "float subgroupPartitionedInclusiveMulNV(float, uvec4 ballot);\n"
+ "vec2 subgroupPartitionedInclusiveMulNV(vec2, uvec4 ballot);\n"
+ "vec3 subgroupPartitionedInclusiveMulNV(vec3, uvec4 ballot);\n"
+ "vec4 subgroupPartitionedInclusiveMulNV(vec4, uvec4 ballot);\n"
+ "int subgroupPartitionedInclusiveMulNV(int, uvec4 ballot);\n"
+ "ivec2 subgroupPartitionedInclusiveMulNV(ivec2, uvec4 ballot);\n"
+ "ivec3 subgroupPartitionedInclusiveMulNV(ivec3, uvec4 ballot);\n"
+ "ivec4 subgroupPartitionedInclusiveMulNV(ivec4, uvec4 ballot);\n"
+ "uint subgroupPartitionedInclusiveMulNV(uint, uvec4 ballot);\n"
+ "uvec2 subgroupPartitionedInclusiveMulNV(uvec2, uvec4 ballot);\n"
+ "uvec3 subgroupPartitionedInclusiveMulNV(uvec3, uvec4 ballot);\n"
+ "uvec4 subgroupPartitionedInclusiveMulNV(uvec4, uvec4 ballot);\n"
+
+ "float subgroupPartitionedInclusiveMinNV(float, uvec4 ballot);\n"
+ "vec2 subgroupPartitionedInclusiveMinNV(vec2, uvec4 ballot);\n"
+ "vec3 subgroupPartitionedInclusiveMinNV(vec3, uvec4 ballot);\n"
+ "vec4 subgroupPartitionedInclusiveMinNV(vec4, uvec4 ballot);\n"
+ "int subgroupPartitionedInclusiveMinNV(int, uvec4 ballot);\n"
+ "ivec2 subgroupPartitionedInclusiveMinNV(ivec2, uvec4 ballot);\n"
+ "ivec3 subgroupPartitionedInclusiveMinNV(ivec3, uvec4 ballot);\n"
+ "ivec4 subgroupPartitionedInclusiveMinNV(ivec4, uvec4 ballot);\n"
+ "uint subgroupPartitionedInclusiveMinNV(uint, uvec4 ballot);\n"
+ "uvec2 subgroupPartitionedInclusiveMinNV(uvec2, uvec4 ballot);\n"
+ "uvec3 subgroupPartitionedInclusiveMinNV(uvec3, uvec4 ballot);\n"
+ "uvec4 subgroupPartitionedInclusiveMinNV(uvec4, uvec4 ballot);\n"
+
+ "float subgroupPartitionedInclusiveMaxNV(float, uvec4 ballot);\n"
+ "vec2 subgroupPartitionedInclusiveMaxNV(vec2, uvec4 ballot);\n"
+ "vec3 subgroupPartitionedInclusiveMaxNV(vec3, uvec4 ballot);\n"
+ "vec4 subgroupPartitionedInclusiveMaxNV(vec4, uvec4 ballot);\n"
+ "int subgroupPartitionedInclusiveMaxNV(int, uvec4 ballot);\n"
+ "ivec2 subgroupPartitionedInclusiveMaxNV(ivec2, uvec4 ballot);\n"
+ "ivec3 subgroupPartitionedInclusiveMaxNV(ivec3, uvec4 ballot);\n"
+ "ivec4 subgroupPartitionedInclusiveMaxNV(ivec4, uvec4 ballot);\n"
+ "uint subgroupPartitionedInclusiveMaxNV(uint, uvec4 ballot);\n"
+ "uvec2 subgroupPartitionedInclusiveMaxNV(uvec2, uvec4 ballot);\n"
+ "uvec3 subgroupPartitionedInclusiveMaxNV(uvec3, uvec4 ballot);\n"
+ "uvec4 subgroupPartitionedInclusiveMaxNV(uvec4, uvec4 ballot);\n"
+
+ "int subgroupPartitionedInclusiveAndNV(int, uvec4 ballot);\n"
+ "ivec2 subgroupPartitionedInclusiveAndNV(ivec2, uvec4 ballot);\n"
+ "ivec3 subgroupPartitionedInclusiveAndNV(ivec3, uvec4 ballot);\n"
+ "ivec4 subgroupPartitionedInclusiveAndNV(ivec4, uvec4 ballot);\n"
+ "uint subgroupPartitionedInclusiveAndNV(uint, uvec4 ballot);\n"
+ "uvec2 subgroupPartitionedInclusiveAndNV(uvec2, uvec4 ballot);\n"
+ "uvec3 subgroupPartitionedInclusiveAndNV(uvec3, uvec4 ballot);\n"
+ "uvec4 subgroupPartitionedInclusiveAndNV(uvec4, uvec4 ballot);\n"
+ "bool subgroupPartitionedInclusiveAndNV(bool, uvec4 ballot);\n"
+ "bvec2 subgroupPartitionedInclusiveAndNV(bvec2, uvec4 ballot);\n"
+ "bvec3 subgroupPartitionedInclusiveAndNV(bvec3, uvec4 ballot);\n"
+ "bvec4 subgroupPartitionedInclusiveAndNV(bvec4, uvec4 ballot);\n"
+
+ "int subgroupPartitionedInclusiveOrNV(int, uvec4 ballot);\n"
+ "ivec2 subgroupPartitionedInclusiveOrNV(ivec2, uvec4 ballot);\n"
+ "ivec3 subgroupPartitionedInclusiveOrNV(ivec3, uvec4 ballot);\n"
+ "ivec4 subgroupPartitionedInclusiveOrNV(ivec4, uvec4 ballot);\n"
+ "uint subgroupPartitionedInclusiveOrNV(uint, uvec4 ballot);\n"
+ "uvec2 subgroupPartitionedInclusiveOrNV(uvec2, uvec4 ballot);\n"
+ "uvec3 subgroupPartitionedInclusiveOrNV(uvec3, uvec4 ballot);\n"
+ "uvec4 subgroupPartitionedInclusiveOrNV(uvec4, uvec4 ballot);\n"
+ "bool subgroupPartitionedInclusiveOrNV(bool, uvec4 ballot);\n"
+ "bvec2 subgroupPartitionedInclusiveOrNV(bvec2, uvec4 ballot);\n"
+ "bvec3 subgroupPartitionedInclusiveOrNV(bvec3, uvec4 ballot);\n"
+ "bvec4 subgroupPartitionedInclusiveOrNV(bvec4, uvec4 ballot);\n"
+
+ "int subgroupPartitionedInclusiveXorNV(int, uvec4 ballot);\n"
+ "ivec2 subgroupPartitionedInclusiveXorNV(ivec2, uvec4 ballot);\n"
+ "ivec3 subgroupPartitionedInclusiveXorNV(ivec3, uvec4 ballot);\n"
+ "ivec4 subgroupPartitionedInclusiveXorNV(ivec4, uvec4 ballot);\n"
+ "uint subgroupPartitionedInclusiveXorNV(uint, uvec4 ballot);\n"
+ "uvec2 subgroupPartitionedInclusiveXorNV(uvec2, uvec4 ballot);\n"
+ "uvec3 subgroupPartitionedInclusiveXorNV(uvec3, uvec4 ballot);\n"
+ "uvec4 subgroupPartitionedInclusiveXorNV(uvec4, uvec4 ballot);\n"
+ "bool subgroupPartitionedInclusiveXorNV(bool, uvec4 ballot);\n"
+ "bvec2 subgroupPartitionedInclusiveXorNV(bvec2, uvec4 ballot);\n"
+ "bvec3 subgroupPartitionedInclusiveXorNV(bvec3, uvec4 ballot);\n"
+ "bvec4 subgroupPartitionedInclusiveXorNV(bvec4, uvec4 ballot);\n"
+
+ "float subgroupPartitionedExclusiveAddNV(float, uvec4 ballot);\n"
+ "vec2 subgroupPartitionedExclusiveAddNV(vec2, uvec4 ballot);\n"
+ "vec3 subgroupPartitionedExclusiveAddNV(vec3, uvec4 ballot);\n"
+ "vec4 subgroupPartitionedExclusiveAddNV(vec4, uvec4 ballot);\n"
+ "int subgroupPartitionedExclusiveAddNV(int, uvec4 ballot);\n"
+ "ivec2 subgroupPartitionedExclusiveAddNV(ivec2, uvec4 ballot);\n"
+ "ivec3 subgroupPartitionedExclusiveAddNV(ivec3, uvec4 ballot);\n"
+ "ivec4 subgroupPartitionedExclusiveAddNV(ivec4, uvec4 ballot);\n"
+ "uint subgroupPartitionedExclusiveAddNV(uint, uvec4 ballot);\n"
+ "uvec2 subgroupPartitionedExclusiveAddNV(uvec2, uvec4 ballot);\n"
+ "uvec3 subgroupPartitionedExclusiveAddNV(uvec3, uvec4 ballot);\n"
+ "uvec4 subgroupPartitionedExclusiveAddNV(uvec4, uvec4 ballot);\n"
+
+ "float subgroupPartitionedExclusiveMulNV(float, uvec4 ballot);\n"
+ "vec2 subgroupPartitionedExclusiveMulNV(vec2, uvec4 ballot);\n"
+ "vec3 subgroupPartitionedExclusiveMulNV(vec3, uvec4 ballot);\n"
+ "vec4 subgroupPartitionedExclusiveMulNV(vec4, uvec4 ballot);\n"
+ "int subgroupPartitionedExclusiveMulNV(int, uvec4 ballot);\n"
+ "ivec2 subgroupPartitionedExclusiveMulNV(ivec2, uvec4 ballot);\n"
+ "ivec3 subgroupPartitionedExclusiveMulNV(ivec3, uvec4 ballot);\n"
+ "ivec4 subgroupPartitionedExclusiveMulNV(ivec4, uvec4 ballot);\n"
+ "uint subgroupPartitionedExclusiveMulNV(uint, uvec4 ballot);\n"
+ "uvec2 subgroupPartitionedExclusiveMulNV(uvec2, uvec4 ballot);\n"
+ "uvec3 subgroupPartitionedExclusiveMulNV(uvec3, uvec4 ballot);\n"
+ "uvec4 subgroupPartitionedExclusiveMulNV(uvec4, uvec4 ballot);\n"
+
+ "float subgroupPartitionedExclusiveMinNV(float, uvec4 ballot);\n"
+ "vec2 subgroupPartitionedExclusiveMinNV(vec2, uvec4 ballot);\n"
+ "vec3 subgroupPartitionedExclusiveMinNV(vec3, uvec4 ballot);\n"
+ "vec4 subgroupPartitionedExclusiveMinNV(vec4, uvec4 ballot);\n"
+ "int subgroupPartitionedExclusiveMinNV(int, uvec4 ballot);\n"
+ "ivec2 subgroupPartitionedExclusiveMinNV(ivec2, uvec4 ballot);\n"
+ "ivec3 subgroupPartitionedExclusiveMinNV(ivec3, uvec4 ballot);\n"
+ "ivec4 subgroupPartitionedExclusiveMinNV(ivec4, uvec4 ballot);\n"
+ "uint subgroupPartitionedExclusiveMinNV(uint, uvec4 ballot);\n"
+ "uvec2 subgroupPartitionedExclusiveMinNV(uvec2, uvec4 ballot);\n"
+ "uvec3 subgroupPartitionedExclusiveMinNV(uvec3, uvec4 ballot);\n"
+ "uvec4 subgroupPartitionedExclusiveMinNV(uvec4, uvec4 ballot);\n"
+
+ "float subgroupPartitionedExclusiveMaxNV(float, uvec4 ballot);\n"
+ "vec2 subgroupPartitionedExclusiveMaxNV(vec2, uvec4 ballot);\n"
+ "vec3 subgroupPartitionedExclusiveMaxNV(vec3, uvec4 ballot);\n"
+ "vec4 subgroupPartitionedExclusiveMaxNV(vec4, uvec4 ballot);\n"
+ "int subgroupPartitionedExclusiveMaxNV(int, uvec4 ballot);\n"
+ "ivec2 subgroupPartitionedExclusiveMaxNV(ivec2, uvec4 ballot);\n"
+ "ivec3 subgroupPartitionedExclusiveMaxNV(ivec3, uvec4 ballot);\n"
+ "ivec4 subgroupPartitionedExclusiveMaxNV(ivec4, uvec4 ballot);\n"
+ "uint subgroupPartitionedExclusiveMaxNV(uint, uvec4 ballot);\n"
+ "uvec2 subgroupPartitionedExclusiveMaxNV(uvec2, uvec4 ballot);\n"
+ "uvec3 subgroupPartitionedExclusiveMaxNV(uvec3, uvec4 ballot);\n"
+ "uvec4 subgroupPartitionedExclusiveMaxNV(uvec4, uvec4 ballot);\n"
+
+ "int subgroupPartitionedExclusiveAndNV(int, uvec4 ballot);\n"
+ "ivec2 subgroupPartitionedExclusiveAndNV(ivec2, uvec4 ballot);\n"
+ "ivec3 subgroupPartitionedExclusiveAndNV(ivec3, uvec4 ballot);\n"
+ "ivec4 subgroupPartitionedExclusiveAndNV(ivec4, uvec4 ballot);\n"
+ "uint subgroupPartitionedExclusiveAndNV(uint, uvec4 ballot);\n"
+ "uvec2 subgroupPartitionedExclusiveAndNV(uvec2, uvec4 ballot);\n"
+ "uvec3 subgroupPartitionedExclusiveAndNV(uvec3, uvec4 ballot);\n"
+ "uvec4 subgroupPartitionedExclusiveAndNV(uvec4, uvec4 ballot);\n"
+ "bool subgroupPartitionedExclusiveAndNV(bool, uvec4 ballot);\n"
+ "bvec2 subgroupPartitionedExclusiveAndNV(bvec2, uvec4 ballot);\n"
+ "bvec3 subgroupPartitionedExclusiveAndNV(bvec3, uvec4 ballot);\n"
+ "bvec4 subgroupPartitionedExclusiveAndNV(bvec4, uvec4 ballot);\n"
+
+ "int subgroupPartitionedExclusiveOrNV(int, uvec4 ballot);\n"
+ "ivec2 subgroupPartitionedExclusiveOrNV(ivec2, uvec4 ballot);\n"
+ "ivec3 subgroupPartitionedExclusiveOrNV(ivec3, uvec4 ballot);\n"
+ "ivec4 subgroupPartitionedExclusiveOrNV(ivec4, uvec4 ballot);\n"
+ "uint subgroupPartitionedExclusiveOrNV(uint, uvec4 ballot);\n"
+ "uvec2 subgroupPartitionedExclusiveOrNV(uvec2, uvec4 ballot);\n"
+ "uvec3 subgroupPartitionedExclusiveOrNV(uvec3, uvec4 ballot);\n"
+ "uvec4 subgroupPartitionedExclusiveOrNV(uvec4, uvec4 ballot);\n"
+ "bool subgroupPartitionedExclusiveOrNV(bool, uvec4 ballot);\n"
+ "bvec2 subgroupPartitionedExclusiveOrNV(bvec2, uvec4 ballot);\n"
+ "bvec3 subgroupPartitionedExclusiveOrNV(bvec3, uvec4 ballot);\n"
+ "bvec4 subgroupPartitionedExclusiveOrNV(bvec4, uvec4 ballot);\n"
+
+ "int subgroupPartitionedExclusiveXorNV(int, uvec4 ballot);\n"
+ "ivec2 subgroupPartitionedExclusiveXorNV(ivec2, uvec4 ballot);\n"
+ "ivec3 subgroupPartitionedExclusiveXorNV(ivec3, uvec4 ballot);\n"
+ "ivec4 subgroupPartitionedExclusiveXorNV(ivec4, uvec4 ballot);\n"
+ "uint subgroupPartitionedExclusiveXorNV(uint, uvec4 ballot);\n"
+ "uvec2 subgroupPartitionedExclusiveXorNV(uvec2, uvec4 ballot);\n"
+ "uvec3 subgroupPartitionedExclusiveXorNV(uvec3, uvec4 ballot);\n"
+ "uvec4 subgroupPartitionedExclusiveXorNV(uvec4, uvec4 ballot);\n"
+ "bool subgroupPartitionedExclusiveXorNV(bool, uvec4 ballot);\n"
+ "bvec2 subgroupPartitionedExclusiveXorNV(bvec2, uvec4 ballot);\n"
+ "bvec3 subgroupPartitionedExclusiveXorNV(bvec3, uvec4 ballot);\n"
+ "bvec4 subgroupPartitionedExclusiveXorNV(bvec4, uvec4 ballot);\n"
+#endif
+
+ "\n");
+
+ if (profile != EEsProfile && version >= 400) {
+ commonBuiltins.append(
+ "bool subgroupAllEqual(double);\n"
+ "bool subgroupAllEqual(dvec2);\n"
+ "bool subgroupAllEqual(dvec3);\n"
+ "bool subgroupAllEqual(dvec4);\n"
+
+ "double subgroupBroadcast(double, uint);\n"
+ "dvec2 subgroupBroadcast(dvec2, uint);\n"
+ "dvec3 subgroupBroadcast(dvec3, uint);\n"
+ "dvec4 subgroupBroadcast(dvec4, uint);\n"
+
+ "double subgroupBroadcastFirst(double);\n"
+ "dvec2 subgroupBroadcastFirst(dvec2);\n"
+ "dvec3 subgroupBroadcastFirst(dvec3);\n"
+ "dvec4 subgroupBroadcastFirst(dvec4);\n"
+
+ "double subgroupShuffle(double, uint);\n"
+ "dvec2 subgroupShuffle(dvec2, uint);\n"
+ "dvec3 subgroupShuffle(dvec3, uint);\n"
+ "dvec4 subgroupShuffle(dvec4, uint);\n"
+
+ "double subgroupShuffleXor(double, uint);\n"
+ "dvec2 subgroupShuffleXor(dvec2, uint);\n"
+ "dvec3 subgroupShuffleXor(dvec3, uint);\n"
+ "dvec4 subgroupShuffleXor(dvec4, uint);\n"
+
+ "double subgroupShuffleUp(double, uint delta);\n"
+ "dvec2 subgroupShuffleUp(dvec2, uint delta);\n"
+ "dvec3 subgroupShuffleUp(dvec3, uint delta);\n"
+ "dvec4 subgroupShuffleUp(dvec4, uint delta);\n"
+
+ "double subgroupShuffleDown(double, uint delta);\n"
+ "dvec2 subgroupShuffleDown(dvec2, uint delta);\n"
+ "dvec3 subgroupShuffleDown(dvec3, uint delta);\n"
+ "dvec4 subgroupShuffleDown(dvec4, uint delta);\n"
+
+ "double subgroupAdd(double);\n"
+ "dvec2 subgroupAdd(dvec2);\n"
+ "dvec3 subgroupAdd(dvec3);\n"
+ "dvec4 subgroupAdd(dvec4);\n"
+
+ "double subgroupMul(double);\n"
+ "dvec2 subgroupMul(dvec2);\n"
+ "dvec3 subgroupMul(dvec3);\n"
+ "dvec4 subgroupMul(dvec4);\n"
+
+ "double subgroupMin(double);\n"
+ "dvec2 subgroupMin(dvec2);\n"
+ "dvec3 subgroupMin(dvec3);\n"
+ "dvec4 subgroupMin(dvec4);\n"
+
+ "double subgroupMax(double);\n"
+ "dvec2 subgroupMax(dvec2);\n"
+ "dvec3 subgroupMax(dvec3);\n"
+ "dvec4 subgroupMax(dvec4);\n"
+
+ "double subgroupInclusiveAdd(double);\n"
+ "dvec2 subgroupInclusiveAdd(dvec2);\n"
+ "dvec3 subgroupInclusiveAdd(dvec3);\n"
+ "dvec4 subgroupInclusiveAdd(dvec4);\n"
+
+ "double subgroupInclusiveMul(double);\n"
+ "dvec2 subgroupInclusiveMul(dvec2);\n"
+ "dvec3 subgroupInclusiveMul(dvec3);\n"
+ "dvec4 subgroupInclusiveMul(dvec4);\n"
+
+ "double subgroupInclusiveMin(double);\n"
+ "dvec2 subgroupInclusiveMin(dvec2);\n"
+ "dvec3 subgroupInclusiveMin(dvec3);\n"
+ "dvec4 subgroupInclusiveMin(dvec4);\n"
+
+ "double subgroupInclusiveMax(double);\n"
+ "dvec2 subgroupInclusiveMax(dvec2);\n"
+ "dvec3 subgroupInclusiveMax(dvec3);\n"
+ "dvec4 subgroupInclusiveMax(dvec4);\n"
+
+ "double subgroupExclusiveAdd(double);\n"
+ "dvec2 subgroupExclusiveAdd(dvec2);\n"
+ "dvec3 subgroupExclusiveAdd(dvec3);\n"
+ "dvec4 subgroupExclusiveAdd(dvec4);\n"
+
+ "double subgroupExclusiveMul(double);\n"
+ "dvec2 subgroupExclusiveMul(dvec2);\n"
+ "dvec3 subgroupExclusiveMul(dvec3);\n"
+ "dvec4 subgroupExclusiveMul(dvec4);\n"
+
+ "double subgroupExclusiveMin(double);\n"
+ "dvec2 subgroupExclusiveMin(dvec2);\n"
+ "dvec3 subgroupExclusiveMin(dvec3);\n"
+ "dvec4 subgroupExclusiveMin(dvec4);\n"
+
+ "double subgroupExclusiveMax(double);\n"
+ "dvec2 subgroupExclusiveMax(dvec2);\n"
+ "dvec3 subgroupExclusiveMax(dvec3);\n"
+ "dvec4 subgroupExclusiveMax(dvec4);\n"
+
+ "double subgroupClusteredAdd(double, uint);\n"
+ "dvec2 subgroupClusteredAdd(dvec2, uint);\n"
+ "dvec3 subgroupClusteredAdd(dvec3, uint);\n"
+ "dvec4 subgroupClusteredAdd(dvec4, uint);\n"
+
+ "double subgroupClusteredMul(double, uint);\n"
+ "dvec2 subgroupClusteredMul(dvec2, uint);\n"
+ "dvec3 subgroupClusteredMul(dvec3, uint);\n"
+ "dvec4 subgroupClusteredMul(dvec4, uint);\n"
+
+ "double subgroupClusteredMin(double, uint);\n"
+ "dvec2 subgroupClusteredMin(dvec2, uint);\n"
+ "dvec3 subgroupClusteredMin(dvec3, uint);\n"
+ "dvec4 subgroupClusteredMin(dvec4, uint);\n"
+
+ "double subgroupClusteredMax(double, uint);\n"
+ "dvec2 subgroupClusteredMax(dvec2, uint);\n"
+ "dvec3 subgroupClusteredMax(dvec3, uint);\n"
+ "dvec4 subgroupClusteredMax(dvec4, uint);\n"
+
+ "double subgroupQuadBroadcast(double, uint);\n"
+ "dvec2 subgroupQuadBroadcast(dvec2, uint);\n"
+ "dvec3 subgroupQuadBroadcast(dvec3, uint);\n"
+ "dvec4 subgroupQuadBroadcast(dvec4, uint);\n"
+
+ "double subgroupQuadSwapHorizontal(double);\n"
+ "dvec2 subgroupQuadSwapHorizontal(dvec2);\n"
+ "dvec3 subgroupQuadSwapHorizontal(dvec3);\n"
+ "dvec4 subgroupQuadSwapHorizontal(dvec4);\n"
+
+ "double subgroupQuadSwapVertical(double);\n"
+ "dvec2 subgroupQuadSwapVertical(dvec2);\n"
+ "dvec3 subgroupQuadSwapVertical(dvec3);\n"
+ "dvec4 subgroupQuadSwapVertical(dvec4);\n"
+
+ "double subgroupQuadSwapDiagonal(double);\n"
+ "dvec2 subgroupQuadSwapDiagonal(dvec2);\n"
+ "dvec3 subgroupQuadSwapDiagonal(dvec3);\n"
+ "dvec4 subgroupQuadSwapDiagonal(dvec4);\n"
+
+
+#ifdef NV_EXTENSIONS
+ "uvec4 subgroupPartitionNV(double);\n"
+ "uvec4 subgroupPartitionNV(dvec2);\n"
+ "uvec4 subgroupPartitionNV(dvec3);\n"
+ "uvec4 subgroupPartitionNV(dvec4);\n"
+
+ "double subgroupPartitionedAddNV(double, uvec4 ballot);\n"
+ "dvec2 subgroupPartitionedAddNV(dvec2, uvec4 ballot);\n"
+ "dvec3 subgroupPartitionedAddNV(dvec3, uvec4 ballot);\n"
+ "dvec4 subgroupPartitionedAddNV(dvec4, uvec4 ballot);\n"
+
+ "double subgroupPartitionedMulNV(double, uvec4 ballot);\n"
+ "dvec2 subgroupPartitionedMulNV(dvec2, uvec4 ballot);\n"
+ "dvec3 subgroupPartitionedMulNV(dvec3, uvec4 ballot);\n"
+ "dvec4 subgroupPartitionedMulNV(dvec4, uvec4 ballot);\n"
+
+ "double subgroupPartitionedMinNV(double, uvec4 ballot);\n"
+ "dvec2 subgroupPartitionedMinNV(dvec2, uvec4 ballot);\n"
+ "dvec3 subgroupPartitionedMinNV(dvec3, uvec4 ballot);\n"
+ "dvec4 subgroupPartitionedMinNV(dvec4, uvec4 ballot);\n"
+
+ "double subgroupPartitionedMaxNV(double, uvec4 ballot);\n"
+ "dvec2 subgroupPartitionedMaxNV(dvec2, uvec4 ballot);\n"
+ "dvec3 subgroupPartitionedMaxNV(dvec3, uvec4 ballot);\n"
+ "dvec4 subgroupPartitionedMaxNV(dvec4, uvec4 ballot);\n"
+
+ "double subgroupPartitionedInclusiveAddNV(double, uvec4 ballot);\n"
+ "dvec2 subgroupPartitionedInclusiveAddNV(dvec2, uvec4 ballot);\n"
+ "dvec3 subgroupPartitionedInclusiveAddNV(dvec3, uvec4 ballot);\n"
+ "dvec4 subgroupPartitionedInclusiveAddNV(dvec4, uvec4 ballot);\n"
+
+ "double subgroupPartitionedInclusiveMulNV(double, uvec4 ballot);\n"
+ "dvec2 subgroupPartitionedInclusiveMulNV(dvec2, uvec4 ballot);\n"
+ "dvec3 subgroupPartitionedInclusiveMulNV(dvec3, uvec4 ballot);\n"
+ "dvec4 subgroupPartitionedInclusiveMulNV(dvec4, uvec4 ballot);\n"
+
+ "double subgroupPartitionedInclusiveMinNV(double, uvec4 ballot);\n"
+ "dvec2 subgroupPartitionedInclusiveMinNV(dvec2, uvec4 ballot);\n"
+ "dvec3 subgroupPartitionedInclusiveMinNV(dvec3, uvec4 ballot);\n"
+ "dvec4 subgroupPartitionedInclusiveMinNV(dvec4, uvec4 ballot);\n"
+
+ "double subgroupPartitionedInclusiveMaxNV(double, uvec4 ballot);\n"
+ "dvec2 subgroupPartitionedInclusiveMaxNV(dvec2, uvec4 ballot);\n"
+ "dvec3 subgroupPartitionedInclusiveMaxNV(dvec3, uvec4 ballot);\n"
+ "dvec4 subgroupPartitionedInclusiveMaxNV(dvec4, uvec4 ballot);\n"
+
+ "double subgroupPartitionedExclusiveAddNV(double, uvec4 ballot);\n"
+ "dvec2 subgroupPartitionedExclusiveAddNV(dvec2, uvec4 ballot);\n"
+ "dvec3 subgroupPartitionedExclusiveAddNV(dvec3, uvec4 ballot);\n"
+ "dvec4 subgroupPartitionedExclusiveAddNV(dvec4, uvec4 ballot);\n"
+
+ "double subgroupPartitionedExclusiveMulNV(double, uvec4 ballot);\n"
+ "dvec2 subgroupPartitionedExclusiveMulNV(dvec2, uvec4 ballot);\n"
+ "dvec3 subgroupPartitionedExclusiveMulNV(dvec3, uvec4 ballot);\n"
+ "dvec4 subgroupPartitionedExclusiveMulNV(dvec4, uvec4 ballot);\n"
+
+ "double subgroupPartitionedExclusiveMinNV(double, uvec4 ballot);\n"
+ "dvec2 subgroupPartitionedExclusiveMinNV(dvec2, uvec4 ballot);\n"
+ "dvec3 subgroupPartitionedExclusiveMinNV(dvec3, uvec4 ballot);\n"
+ "dvec4 subgroupPartitionedExclusiveMinNV(dvec4, uvec4 ballot);\n"
+
+ "double subgroupPartitionedExclusiveMaxNV(double, uvec4 ballot);\n"
+ "dvec2 subgroupPartitionedExclusiveMaxNV(dvec2, uvec4 ballot);\n"
+ "dvec3 subgroupPartitionedExclusiveMaxNV(dvec3, uvec4 ballot);\n"
+ "dvec4 subgroupPartitionedExclusiveMaxNV(dvec4, uvec4 ballot);\n"
+#endif
+
+ "\n");
+ }
+
+ stageBuiltins[EShLangCompute].append(
+ "void subgroupMemoryBarrierShared();"
+
+ "\n"
+ );
+#ifdef NV_EXTENSIONS
+ stageBuiltins[EShLangMeshNV].append(
+ "void subgroupMemoryBarrierShared();"
+ "\n"
+ );
+ stageBuiltins[EShLangTaskNV].append(
+ "void subgroupMemoryBarrierShared();"
+ "\n"
+ );
+#endif
+ }
+
+ if (profile != EEsProfile && version >= 460) {
+ commonBuiltins.append(
+ "bool anyInvocation(bool);"
+ "bool allInvocations(bool);"
+ "bool allInvocationsEqual(bool);"
+
+ "\n");
+ }
+
+#ifdef AMD_EXTENSIONS
+ // GL_AMD_shader_ballot
+ if (profile != EEsProfile && version >= 450) {
+ commonBuiltins.append(
+ "float minInvocationsAMD(float);"
+ "vec2 minInvocationsAMD(vec2);"
+ "vec3 minInvocationsAMD(vec3);"
+ "vec4 minInvocationsAMD(vec4);"
+
+ "int minInvocationsAMD(int);"
+ "ivec2 minInvocationsAMD(ivec2);"
+ "ivec3 minInvocationsAMD(ivec3);"
+ "ivec4 minInvocationsAMD(ivec4);"
+
+ "uint minInvocationsAMD(uint);"
+ "uvec2 minInvocationsAMD(uvec2);"
+ "uvec3 minInvocationsAMD(uvec3);"
+ "uvec4 minInvocationsAMD(uvec4);"
+
+ "double minInvocationsAMD(double);"
+ "dvec2 minInvocationsAMD(dvec2);"
+ "dvec3 minInvocationsAMD(dvec3);"
+ "dvec4 minInvocationsAMD(dvec4);"
+
+ "int64_t minInvocationsAMD(int64_t);"
+ "i64vec2 minInvocationsAMD(i64vec2);"
+ "i64vec3 minInvocationsAMD(i64vec3);"
+ "i64vec4 minInvocationsAMD(i64vec4);"
+
+ "uint64_t minInvocationsAMD(uint64_t);"
+ "u64vec2 minInvocationsAMD(u64vec2);"
+ "u64vec3 minInvocationsAMD(u64vec3);"
+ "u64vec4 minInvocationsAMD(u64vec4);"
+
+ "float16_t minInvocationsAMD(float16_t);"
+ "f16vec2 minInvocationsAMD(f16vec2);"
+ "f16vec3 minInvocationsAMD(f16vec3);"
+ "f16vec4 minInvocationsAMD(f16vec4);"
+
+ "int16_t minInvocationsAMD(int16_t);"
+ "i16vec2 minInvocationsAMD(i16vec2);"
+ "i16vec3 minInvocationsAMD(i16vec3);"
+ "i16vec4 minInvocationsAMD(i16vec4);"
+
+ "uint16_t minInvocationsAMD(uint16_t);"
+ "u16vec2 minInvocationsAMD(u16vec2);"
+ "u16vec3 minInvocationsAMD(u16vec3);"
+ "u16vec4 minInvocationsAMD(u16vec4);"
+
+ "float minInvocationsInclusiveScanAMD(float);"
+ "vec2 minInvocationsInclusiveScanAMD(vec2);"
+ "vec3 minInvocationsInclusiveScanAMD(vec3);"
+ "vec4 minInvocationsInclusiveScanAMD(vec4);"
+
+ "int minInvocationsInclusiveScanAMD(int);"
+ "ivec2 minInvocationsInclusiveScanAMD(ivec2);"
+ "ivec3 minInvocationsInclusiveScanAMD(ivec3);"
+ "ivec4 minInvocationsInclusiveScanAMD(ivec4);"
+
+ "uint minInvocationsInclusiveScanAMD(uint);"
+ "uvec2 minInvocationsInclusiveScanAMD(uvec2);"
+ "uvec3 minInvocationsInclusiveScanAMD(uvec3);"
+ "uvec4 minInvocationsInclusiveScanAMD(uvec4);"
+
+ "double minInvocationsInclusiveScanAMD(double);"
+ "dvec2 minInvocationsInclusiveScanAMD(dvec2);"
+ "dvec3 minInvocationsInclusiveScanAMD(dvec3);"
+ "dvec4 minInvocationsInclusiveScanAMD(dvec4);"
+
+ "int64_t minInvocationsInclusiveScanAMD(int64_t);"
+ "i64vec2 minInvocationsInclusiveScanAMD(i64vec2);"
+ "i64vec3 minInvocationsInclusiveScanAMD(i64vec3);"
+ "i64vec4 minInvocationsInclusiveScanAMD(i64vec4);"
+
+ "uint64_t minInvocationsInclusiveScanAMD(uint64_t);"
+ "u64vec2 minInvocationsInclusiveScanAMD(u64vec2);"
+ "u64vec3 minInvocationsInclusiveScanAMD(u64vec3);"
+ "u64vec4 minInvocationsInclusiveScanAMD(u64vec4);"
+
+ "float16_t minInvocationsInclusiveScanAMD(float16_t);"
+ "f16vec2 minInvocationsInclusiveScanAMD(f16vec2);"
+ "f16vec3 minInvocationsInclusiveScanAMD(f16vec3);"
+ "f16vec4 minInvocationsInclusiveScanAMD(f16vec4);"
+
+ "int16_t minInvocationsInclusiveScanAMD(int16_t);"
+ "i16vec2 minInvocationsInclusiveScanAMD(i16vec2);"
+ "i16vec3 minInvocationsInclusiveScanAMD(i16vec3);"
+ "i16vec4 minInvocationsInclusiveScanAMD(i16vec4);"
+
+ "uint16_t minInvocationsInclusiveScanAMD(uint16_t);"
+ "u16vec2 minInvocationsInclusiveScanAMD(u16vec2);"
+ "u16vec3 minInvocationsInclusiveScanAMD(u16vec3);"
+ "u16vec4 minInvocationsInclusiveScanAMD(u16vec4);"
+
+ "float minInvocationsExclusiveScanAMD(float);"
+ "vec2 minInvocationsExclusiveScanAMD(vec2);"
+ "vec3 minInvocationsExclusiveScanAMD(vec3);"
+ "vec4 minInvocationsExclusiveScanAMD(vec4);"
+
+ "int minInvocationsExclusiveScanAMD(int);"
+ "ivec2 minInvocationsExclusiveScanAMD(ivec2);"
+ "ivec3 minInvocationsExclusiveScanAMD(ivec3);"
+ "ivec4 minInvocationsExclusiveScanAMD(ivec4);"
+
+ "uint minInvocationsExclusiveScanAMD(uint);"
+ "uvec2 minInvocationsExclusiveScanAMD(uvec2);"
+ "uvec3 minInvocationsExclusiveScanAMD(uvec3);"
+ "uvec4 minInvocationsExclusiveScanAMD(uvec4);"
+
+ "double minInvocationsExclusiveScanAMD(double);"
+ "dvec2 minInvocationsExclusiveScanAMD(dvec2);"
+ "dvec3 minInvocationsExclusiveScanAMD(dvec3);"
+ "dvec4 minInvocationsExclusiveScanAMD(dvec4);"
+
+ "int64_t minInvocationsExclusiveScanAMD(int64_t);"
+ "i64vec2 minInvocationsExclusiveScanAMD(i64vec2);"
+ "i64vec3 minInvocationsExclusiveScanAMD(i64vec3);"
+ "i64vec4 minInvocationsExclusiveScanAMD(i64vec4);"
+
+ "uint64_t minInvocationsExclusiveScanAMD(uint64_t);"
+ "u64vec2 minInvocationsExclusiveScanAMD(u64vec2);"
+ "u64vec3 minInvocationsExclusiveScanAMD(u64vec3);"
+ "u64vec4 minInvocationsExclusiveScanAMD(u64vec4);"
+
+ "float16_t minInvocationsExclusiveScanAMD(float16_t);"
+ "f16vec2 minInvocationsExclusiveScanAMD(f16vec2);"
+ "f16vec3 minInvocationsExclusiveScanAMD(f16vec3);"
+ "f16vec4 minInvocationsExclusiveScanAMD(f16vec4);"
+
+ "int16_t minInvocationsExclusiveScanAMD(int16_t);"
+ "i16vec2 minInvocationsExclusiveScanAMD(i16vec2);"
+ "i16vec3 minInvocationsExclusiveScanAMD(i16vec3);"
+ "i16vec4 minInvocationsExclusiveScanAMD(i16vec4);"
+
+ "uint16_t minInvocationsExclusiveScanAMD(uint16_t);"
+ "u16vec2 minInvocationsExclusiveScanAMD(u16vec2);"
+ "u16vec3 minInvocationsExclusiveScanAMD(u16vec3);"
+ "u16vec4 minInvocationsExclusiveScanAMD(u16vec4);"
+
+ "float maxInvocationsAMD(float);"
+ "vec2 maxInvocationsAMD(vec2);"
+ "vec3 maxInvocationsAMD(vec3);"
+ "vec4 maxInvocationsAMD(vec4);"
+
+ "int maxInvocationsAMD(int);"
+ "ivec2 maxInvocationsAMD(ivec2);"
+ "ivec3 maxInvocationsAMD(ivec3);"
+ "ivec4 maxInvocationsAMD(ivec4);"
+
+ "uint maxInvocationsAMD(uint);"
+ "uvec2 maxInvocationsAMD(uvec2);"
+ "uvec3 maxInvocationsAMD(uvec3);"
+ "uvec4 maxInvocationsAMD(uvec4);"
+
+ "double maxInvocationsAMD(double);"
+ "dvec2 maxInvocationsAMD(dvec2);"
+ "dvec3 maxInvocationsAMD(dvec3);"
+ "dvec4 maxInvocationsAMD(dvec4);"
+
+ "int64_t maxInvocationsAMD(int64_t);"
+ "i64vec2 maxInvocationsAMD(i64vec2);"
+ "i64vec3 maxInvocationsAMD(i64vec3);"
+ "i64vec4 maxInvocationsAMD(i64vec4);"
+
+ "uint64_t maxInvocationsAMD(uint64_t);"
+ "u64vec2 maxInvocationsAMD(u64vec2);"
+ "u64vec3 maxInvocationsAMD(u64vec3);"
+ "u64vec4 maxInvocationsAMD(u64vec4);"
+
+ "float16_t maxInvocationsAMD(float16_t);"
+ "f16vec2 maxInvocationsAMD(f16vec2);"
+ "f16vec3 maxInvocationsAMD(f16vec3);"
+ "f16vec4 maxInvocationsAMD(f16vec4);"
+
+ "int16_t maxInvocationsAMD(int16_t);"
+ "i16vec2 maxInvocationsAMD(i16vec2);"
+ "i16vec3 maxInvocationsAMD(i16vec3);"
+ "i16vec4 maxInvocationsAMD(i16vec4);"
+
+ "uint16_t maxInvocationsAMD(uint16_t);"
+ "u16vec2 maxInvocationsAMD(u16vec2);"
+ "u16vec3 maxInvocationsAMD(u16vec3);"
+ "u16vec4 maxInvocationsAMD(u16vec4);"
+
+ "float maxInvocationsInclusiveScanAMD(float);"
+ "vec2 maxInvocationsInclusiveScanAMD(vec2);"
+ "vec3 maxInvocationsInclusiveScanAMD(vec3);"
+ "vec4 maxInvocationsInclusiveScanAMD(vec4);"
+
+ "int maxInvocationsInclusiveScanAMD(int);"
+ "ivec2 maxInvocationsInclusiveScanAMD(ivec2);"
+ "ivec3 maxInvocationsInclusiveScanAMD(ivec3);"
+ "ivec4 maxInvocationsInclusiveScanAMD(ivec4);"
+
+ "uint maxInvocationsInclusiveScanAMD(uint);"
+ "uvec2 maxInvocationsInclusiveScanAMD(uvec2);"
+ "uvec3 maxInvocationsInclusiveScanAMD(uvec3);"
+ "uvec4 maxInvocationsInclusiveScanAMD(uvec4);"
+
+ "double maxInvocationsInclusiveScanAMD(double);"
+ "dvec2 maxInvocationsInclusiveScanAMD(dvec2);"
+ "dvec3 maxInvocationsInclusiveScanAMD(dvec3);"
+ "dvec4 maxInvocationsInclusiveScanAMD(dvec4);"
+
+ "int64_t maxInvocationsInclusiveScanAMD(int64_t);"
+ "i64vec2 maxInvocationsInclusiveScanAMD(i64vec2);"
+ "i64vec3 maxInvocationsInclusiveScanAMD(i64vec3);"
+ "i64vec4 maxInvocationsInclusiveScanAMD(i64vec4);"
+
+ "uint64_t maxInvocationsInclusiveScanAMD(uint64_t);"
+ "u64vec2 maxInvocationsInclusiveScanAMD(u64vec2);"
+ "u64vec3 maxInvocationsInclusiveScanAMD(u64vec3);"
+ "u64vec4 maxInvocationsInclusiveScanAMD(u64vec4);"
+
+ "float16_t maxInvocationsInclusiveScanAMD(float16_t);"
+ "f16vec2 maxInvocationsInclusiveScanAMD(f16vec2);"
+ "f16vec3 maxInvocationsInclusiveScanAMD(f16vec3);"
+ "f16vec4 maxInvocationsInclusiveScanAMD(f16vec4);"
+
+ "int16_t maxInvocationsInclusiveScanAMD(int16_t);"
+ "i16vec2 maxInvocationsInclusiveScanAMD(i16vec2);"
+ "i16vec3 maxInvocationsInclusiveScanAMD(i16vec3);"
+ "i16vec4 maxInvocationsInclusiveScanAMD(i16vec4);"
+
+ "uint16_t maxInvocationsInclusiveScanAMD(uint16_t);"
+ "u16vec2 maxInvocationsInclusiveScanAMD(u16vec2);"
+ "u16vec3 maxInvocationsInclusiveScanAMD(u16vec3);"
+ "u16vec4 maxInvocationsInclusiveScanAMD(u16vec4);"
+
+ "float maxInvocationsExclusiveScanAMD(float);"
+ "vec2 maxInvocationsExclusiveScanAMD(vec2);"
+ "vec3 maxInvocationsExclusiveScanAMD(vec3);"
+ "vec4 maxInvocationsExclusiveScanAMD(vec4);"
+
+ "int maxInvocationsExclusiveScanAMD(int);"
+ "ivec2 maxInvocationsExclusiveScanAMD(ivec2);"
+ "ivec3 maxInvocationsExclusiveScanAMD(ivec3);"
+ "ivec4 maxInvocationsExclusiveScanAMD(ivec4);"
+
+ "uint maxInvocationsExclusiveScanAMD(uint);"
+ "uvec2 maxInvocationsExclusiveScanAMD(uvec2);"
+ "uvec3 maxInvocationsExclusiveScanAMD(uvec3);"
+ "uvec4 maxInvocationsExclusiveScanAMD(uvec4);"
+
+ "double maxInvocationsExclusiveScanAMD(double);"
+ "dvec2 maxInvocationsExclusiveScanAMD(dvec2);"
+ "dvec3 maxInvocationsExclusiveScanAMD(dvec3);"
+ "dvec4 maxInvocationsExclusiveScanAMD(dvec4);"
+
+ "int64_t maxInvocationsExclusiveScanAMD(int64_t);"
+ "i64vec2 maxInvocationsExclusiveScanAMD(i64vec2);"
+ "i64vec3 maxInvocationsExclusiveScanAMD(i64vec3);"
+ "i64vec4 maxInvocationsExclusiveScanAMD(i64vec4);"
+
+ "uint64_t maxInvocationsExclusiveScanAMD(uint64_t);"
+ "u64vec2 maxInvocationsExclusiveScanAMD(u64vec2);"
+ "u64vec3 maxInvocationsExclusiveScanAMD(u64vec3);"
+ "u64vec4 maxInvocationsExclusiveScanAMD(u64vec4);"
+
+ "float16_t maxInvocationsExclusiveScanAMD(float16_t);"
+ "f16vec2 maxInvocationsExclusiveScanAMD(f16vec2);"
+ "f16vec3 maxInvocationsExclusiveScanAMD(f16vec3);"
+ "f16vec4 maxInvocationsExclusiveScanAMD(f16vec4);"
+
+ "int16_t maxInvocationsExclusiveScanAMD(int16_t);"
+ "i16vec2 maxInvocationsExclusiveScanAMD(i16vec2);"
+ "i16vec3 maxInvocationsExclusiveScanAMD(i16vec3);"
+ "i16vec4 maxInvocationsExclusiveScanAMD(i16vec4);"
+
+ "uint16_t maxInvocationsExclusiveScanAMD(uint16_t);"
+ "u16vec2 maxInvocationsExclusiveScanAMD(u16vec2);"
+ "u16vec3 maxInvocationsExclusiveScanAMD(u16vec3);"
+ "u16vec4 maxInvocationsExclusiveScanAMD(u16vec4);"
+
+ "float addInvocationsAMD(float);"
+ "vec2 addInvocationsAMD(vec2);"
+ "vec3 addInvocationsAMD(vec3);"
+ "vec4 addInvocationsAMD(vec4);"
+
+ "int addInvocationsAMD(int);"
+ "ivec2 addInvocationsAMD(ivec2);"
+ "ivec3 addInvocationsAMD(ivec3);"
+ "ivec4 addInvocationsAMD(ivec4);"
+
+ "uint addInvocationsAMD(uint);"
+ "uvec2 addInvocationsAMD(uvec2);"
+ "uvec3 addInvocationsAMD(uvec3);"
+ "uvec4 addInvocationsAMD(uvec4);"
+
+ "double addInvocationsAMD(double);"
+ "dvec2 addInvocationsAMD(dvec2);"
+ "dvec3 addInvocationsAMD(dvec3);"
+ "dvec4 addInvocationsAMD(dvec4);"
+
+ "int64_t addInvocationsAMD(int64_t);"
+ "i64vec2 addInvocationsAMD(i64vec2);"
+ "i64vec3 addInvocationsAMD(i64vec3);"
+ "i64vec4 addInvocationsAMD(i64vec4);"
+
+ "uint64_t addInvocationsAMD(uint64_t);"
+ "u64vec2 addInvocationsAMD(u64vec2);"
+ "u64vec3 addInvocationsAMD(u64vec3);"
+ "u64vec4 addInvocationsAMD(u64vec4);"
+
+ "float16_t addInvocationsAMD(float16_t);"
+ "f16vec2 addInvocationsAMD(f16vec2);"
+ "f16vec3 addInvocationsAMD(f16vec3);"
+ "f16vec4 addInvocationsAMD(f16vec4);"
+
+ "int16_t addInvocationsAMD(int16_t);"
+ "i16vec2 addInvocationsAMD(i16vec2);"
+ "i16vec3 addInvocationsAMD(i16vec3);"
+ "i16vec4 addInvocationsAMD(i16vec4);"
+
+ "uint16_t addInvocationsAMD(uint16_t);"
+ "u16vec2 addInvocationsAMD(u16vec2);"
+ "u16vec3 addInvocationsAMD(u16vec3);"
+ "u16vec4 addInvocationsAMD(u16vec4);"
+
+ "float addInvocationsInclusiveScanAMD(float);"
+ "vec2 addInvocationsInclusiveScanAMD(vec2);"
+ "vec3 addInvocationsInclusiveScanAMD(vec3);"
+ "vec4 addInvocationsInclusiveScanAMD(vec4);"
+
+ "int addInvocationsInclusiveScanAMD(int);"
+ "ivec2 addInvocationsInclusiveScanAMD(ivec2);"
+ "ivec3 addInvocationsInclusiveScanAMD(ivec3);"
+ "ivec4 addInvocationsInclusiveScanAMD(ivec4);"
+
+ "uint addInvocationsInclusiveScanAMD(uint);"
+ "uvec2 addInvocationsInclusiveScanAMD(uvec2);"
+ "uvec3 addInvocationsInclusiveScanAMD(uvec3);"
+ "uvec4 addInvocationsInclusiveScanAMD(uvec4);"
+
+ "double addInvocationsInclusiveScanAMD(double);"
+ "dvec2 addInvocationsInclusiveScanAMD(dvec2);"
+ "dvec3 addInvocationsInclusiveScanAMD(dvec3);"
+ "dvec4 addInvocationsInclusiveScanAMD(dvec4);"
+
+ "int64_t addInvocationsInclusiveScanAMD(int64_t);"
+ "i64vec2 addInvocationsInclusiveScanAMD(i64vec2);"
+ "i64vec3 addInvocationsInclusiveScanAMD(i64vec3);"
+ "i64vec4 addInvocationsInclusiveScanAMD(i64vec4);"
+
+ "uint64_t addInvocationsInclusiveScanAMD(uint64_t);"
+ "u64vec2 addInvocationsInclusiveScanAMD(u64vec2);"
+ "u64vec3 addInvocationsInclusiveScanAMD(u64vec3);"
+ "u64vec4 addInvocationsInclusiveScanAMD(u64vec4);"
+
+ "float16_t addInvocationsInclusiveScanAMD(float16_t);"
+ "f16vec2 addInvocationsInclusiveScanAMD(f16vec2);"
+ "f16vec3 addInvocationsInclusiveScanAMD(f16vec3);"
+ "f16vec4 addInvocationsInclusiveScanAMD(f16vec4);"
+
+ "int16_t addInvocationsInclusiveScanAMD(int16_t);"
+ "i16vec2 addInvocationsInclusiveScanAMD(i16vec2);"
+ "i16vec3 addInvocationsInclusiveScanAMD(i16vec3);"
+ "i16vec4 addInvocationsInclusiveScanAMD(i16vec4);"
+
+ "uint16_t addInvocationsInclusiveScanAMD(uint16_t);"
+ "u16vec2 addInvocationsInclusiveScanAMD(u16vec2);"
+ "u16vec3 addInvocationsInclusiveScanAMD(u16vec3);"
+ "u16vec4 addInvocationsInclusiveScanAMD(u16vec4);"
+
+ "float addInvocationsExclusiveScanAMD(float);"
+ "vec2 addInvocationsExclusiveScanAMD(vec2);"
+ "vec3 addInvocationsExclusiveScanAMD(vec3);"
+ "vec4 addInvocationsExclusiveScanAMD(vec4);"
+
+ "int addInvocationsExclusiveScanAMD(int);"
+ "ivec2 addInvocationsExclusiveScanAMD(ivec2);"
+ "ivec3 addInvocationsExclusiveScanAMD(ivec3);"
+ "ivec4 addInvocationsExclusiveScanAMD(ivec4);"
+
+ "uint addInvocationsExclusiveScanAMD(uint);"
+ "uvec2 addInvocationsExclusiveScanAMD(uvec2);"
+ "uvec3 addInvocationsExclusiveScanAMD(uvec3);"
+ "uvec4 addInvocationsExclusiveScanAMD(uvec4);"
+
+ "double addInvocationsExclusiveScanAMD(double);"
+ "dvec2 addInvocationsExclusiveScanAMD(dvec2);"
+ "dvec3 addInvocationsExclusiveScanAMD(dvec3);"
+ "dvec4 addInvocationsExclusiveScanAMD(dvec4);"
+
+ "int64_t addInvocationsExclusiveScanAMD(int64_t);"
+ "i64vec2 addInvocationsExclusiveScanAMD(i64vec2);"
+ "i64vec3 addInvocationsExclusiveScanAMD(i64vec3);"
+ "i64vec4 addInvocationsExclusiveScanAMD(i64vec4);"
+
+ "uint64_t addInvocationsExclusiveScanAMD(uint64_t);"
+ "u64vec2 addInvocationsExclusiveScanAMD(u64vec2);"
+ "u64vec3 addInvocationsExclusiveScanAMD(u64vec3);"
+ "u64vec4 addInvocationsExclusiveScanAMD(u64vec4);"
+
+ "float16_t addInvocationsExclusiveScanAMD(float16_t);"
+ "f16vec2 addInvocationsExclusiveScanAMD(f16vec2);"
+ "f16vec3 addInvocationsExclusiveScanAMD(f16vec3);"
+ "f16vec4 addInvocationsExclusiveScanAMD(f16vec4);"
+
+ "int16_t addInvocationsExclusiveScanAMD(int16_t);"
+ "i16vec2 addInvocationsExclusiveScanAMD(i16vec2);"
+ "i16vec3 addInvocationsExclusiveScanAMD(i16vec3);"
+ "i16vec4 addInvocationsExclusiveScanAMD(i16vec4);"
+
+ "uint16_t addInvocationsExclusiveScanAMD(uint16_t);"
+ "u16vec2 addInvocationsExclusiveScanAMD(u16vec2);"
+ "u16vec3 addInvocationsExclusiveScanAMD(u16vec3);"
+ "u16vec4 addInvocationsExclusiveScanAMD(u16vec4);"
+
+ "float minInvocationsNonUniformAMD(float);"
+ "vec2 minInvocationsNonUniformAMD(vec2);"
+ "vec3 minInvocationsNonUniformAMD(vec3);"
+ "vec4 minInvocationsNonUniformAMD(vec4);"
+
+ "int minInvocationsNonUniformAMD(int);"
+ "ivec2 minInvocationsNonUniformAMD(ivec2);"
+ "ivec3 minInvocationsNonUniformAMD(ivec3);"
+ "ivec4 minInvocationsNonUniformAMD(ivec4);"
+
+ "uint minInvocationsNonUniformAMD(uint);"
+ "uvec2 minInvocationsNonUniformAMD(uvec2);"
+ "uvec3 minInvocationsNonUniformAMD(uvec3);"
+ "uvec4 minInvocationsNonUniformAMD(uvec4);"
+
+ "double minInvocationsNonUniformAMD(double);"
+ "dvec2 minInvocationsNonUniformAMD(dvec2);"
+ "dvec3 minInvocationsNonUniformAMD(dvec3);"
+ "dvec4 minInvocationsNonUniformAMD(dvec4);"
+
+ "int64_t minInvocationsNonUniformAMD(int64_t);"
+ "i64vec2 minInvocationsNonUniformAMD(i64vec2);"
+ "i64vec3 minInvocationsNonUniformAMD(i64vec3);"
+ "i64vec4 minInvocationsNonUniformAMD(i64vec4);"
+
+ "uint64_t minInvocationsNonUniformAMD(uint64_t);"
+ "u64vec2 minInvocationsNonUniformAMD(u64vec2);"
+ "u64vec3 minInvocationsNonUniformAMD(u64vec3);"
+ "u64vec4 minInvocationsNonUniformAMD(u64vec4);"
+
+ "float16_t minInvocationsNonUniformAMD(float16_t);"
+ "f16vec2 minInvocationsNonUniformAMD(f16vec2);"
+ "f16vec3 minInvocationsNonUniformAMD(f16vec3);"
+ "f16vec4 minInvocationsNonUniformAMD(f16vec4);"
+
+ "int16_t minInvocationsNonUniformAMD(int16_t);"
+ "i16vec2 minInvocationsNonUniformAMD(i16vec2);"
+ "i16vec3 minInvocationsNonUniformAMD(i16vec3);"
+ "i16vec4 minInvocationsNonUniformAMD(i16vec4);"
+
+ "uint16_t minInvocationsNonUniformAMD(uint16_t);"
+ "u16vec2 minInvocationsNonUniformAMD(u16vec2);"
+ "u16vec3 minInvocationsNonUniformAMD(u16vec3);"
+ "u16vec4 minInvocationsNonUniformAMD(u16vec4);"
+
+ "float minInvocationsInclusiveScanNonUniformAMD(float);"
+ "vec2 minInvocationsInclusiveScanNonUniformAMD(vec2);"
+ "vec3 minInvocationsInclusiveScanNonUniformAMD(vec3);"
+ "vec4 minInvocationsInclusiveScanNonUniformAMD(vec4);"
+
+ "int minInvocationsInclusiveScanNonUniformAMD(int);"
+ "ivec2 minInvocationsInclusiveScanNonUniformAMD(ivec2);"
+ "ivec3 minInvocationsInclusiveScanNonUniformAMD(ivec3);"
+ "ivec4 minInvocationsInclusiveScanNonUniformAMD(ivec4);"
+
+ "uint minInvocationsInclusiveScanNonUniformAMD(uint);"
+ "uvec2 minInvocationsInclusiveScanNonUniformAMD(uvec2);"
+ "uvec3 minInvocationsInclusiveScanNonUniformAMD(uvec3);"
+ "uvec4 minInvocationsInclusiveScanNonUniformAMD(uvec4);"
+
+ "double minInvocationsInclusiveScanNonUniformAMD(double);"
+ "dvec2 minInvocationsInclusiveScanNonUniformAMD(dvec2);"
+ "dvec3 minInvocationsInclusiveScanNonUniformAMD(dvec3);"
+ "dvec4 minInvocationsInclusiveScanNonUniformAMD(dvec4);"
+
+ "int64_t minInvocationsInclusiveScanNonUniformAMD(int64_t);"
+ "i64vec2 minInvocationsInclusiveScanNonUniformAMD(i64vec2);"
+ "i64vec3 minInvocationsInclusiveScanNonUniformAMD(i64vec3);"
+ "i64vec4 minInvocationsInclusiveScanNonUniformAMD(i64vec4);"
+
+ "uint64_t minInvocationsInclusiveScanNonUniformAMD(uint64_t);"
+ "u64vec2 minInvocationsInclusiveScanNonUniformAMD(u64vec2);"
+ "u64vec3 minInvocationsInclusiveScanNonUniformAMD(u64vec3);"
+ "u64vec4 minInvocationsInclusiveScanNonUniformAMD(u64vec4);"
+
+ "float16_t minInvocationsInclusiveScanNonUniformAMD(float16_t);"
+ "f16vec2 minInvocationsInclusiveScanNonUniformAMD(f16vec2);"
+ "f16vec3 minInvocationsInclusiveScanNonUniformAMD(f16vec3);"
+ "f16vec4 minInvocationsInclusiveScanNonUniformAMD(f16vec4);"
+
+ "int16_t minInvocationsInclusiveScanNonUniformAMD(int16_t);"
+ "i16vec2 minInvocationsInclusiveScanNonUniformAMD(i16vec2);"
+ "i16vec3 minInvocationsInclusiveScanNonUniformAMD(i16vec3);"
+ "i16vec4 minInvocationsInclusiveScanNonUniformAMD(i16vec4);"
+
+ "uint16_t minInvocationsInclusiveScanNonUniformAMD(uint16_t);"
+ "u16vec2 minInvocationsInclusiveScanNonUniformAMD(u16vec2);"
+ "u16vec3 minInvocationsInclusiveScanNonUniformAMD(u16vec3);"
+ "u16vec4 minInvocationsInclusiveScanNonUniformAMD(u16vec4);"
+
+ "float minInvocationsExclusiveScanNonUniformAMD(float);"
+ "vec2 minInvocationsExclusiveScanNonUniformAMD(vec2);"
+ "vec3 minInvocationsExclusiveScanNonUniformAMD(vec3);"
+ "vec4 minInvocationsExclusiveScanNonUniformAMD(vec4);"
+
+ "int minInvocationsExclusiveScanNonUniformAMD(int);"
+ "ivec2 minInvocationsExclusiveScanNonUniformAMD(ivec2);"
+ "ivec3 minInvocationsExclusiveScanNonUniformAMD(ivec3);"
+ "ivec4 minInvocationsExclusiveScanNonUniformAMD(ivec4);"
+
+ "uint minInvocationsExclusiveScanNonUniformAMD(uint);"
+ "uvec2 minInvocationsExclusiveScanNonUniformAMD(uvec2);"
+ "uvec3 minInvocationsExclusiveScanNonUniformAMD(uvec3);"
+ "uvec4 minInvocationsExclusiveScanNonUniformAMD(uvec4);"
+
+ "double minInvocationsExclusiveScanNonUniformAMD(double);"
+ "dvec2 minInvocationsExclusiveScanNonUniformAMD(dvec2);"
+ "dvec3 minInvocationsExclusiveScanNonUniformAMD(dvec3);"
+ "dvec4 minInvocationsExclusiveScanNonUniformAMD(dvec4);"
+
+ "int64_t minInvocationsExclusiveScanNonUniformAMD(int64_t);"
+ "i64vec2 minInvocationsExclusiveScanNonUniformAMD(i64vec2);"
+ "i64vec3 minInvocationsExclusiveScanNonUniformAMD(i64vec3);"
+ "i64vec4 minInvocationsExclusiveScanNonUniformAMD(i64vec4);"
+
+ "uint64_t minInvocationsExclusiveScanNonUniformAMD(uint64_t);"
+ "u64vec2 minInvocationsExclusiveScanNonUniformAMD(u64vec2);"
+ "u64vec3 minInvocationsExclusiveScanNonUniformAMD(u64vec3);"
+ "u64vec4 minInvocationsExclusiveScanNonUniformAMD(u64vec4);"
+
+ "float16_t minInvocationsExclusiveScanNonUniformAMD(float16_t);"
+ "f16vec2 minInvocationsExclusiveScanNonUniformAMD(f16vec2);"
+ "f16vec3 minInvocationsExclusiveScanNonUniformAMD(f16vec3);"
+ "f16vec4 minInvocationsExclusiveScanNonUniformAMD(f16vec4);"
+
+ "int16_t minInvocationsExclusiveScanNonUniformAMD(int16_t);"
+ "i16vec2 minInvocationsExclusiveScanNonUniformAMD(i16vec2);"
+ "i16vec3 minInvocationsExclusiveScanNonUniformAMD(i16vec3);"
+ "i16vec4 minInvocationsExclusiveScanNonUniformAMD(i16vec4);"
+
+ "uint16_t minInvocationsExclusiveScanNonUniformAMD(uint16_t);"
+ "u16vec2 minInvocationsExclusiveScanNonUniformAMD(u16vec2);"
+ "u16vec3 minInvocationsExclusiveScanNonUniformAMD(u16vec3);"
+ "u16vec4 minInvocationsExclusiveScanNonUniformAMD(u16vec4);"
+
+ "float maxInvocationsNonUniformAMD(float);"
+ "vec2 maxInvocationsNonUniformAMD(vec2);"
+ "vec3 maxInvocationsNonUniformAMD(vec3);"
+ "vec4 maxInvocationsNonUniformAMD(vec4);"
+
+ "int maxInvocationsNonUniformAMD(int);"
+ "ivec2 maxInvocationsNonUniformAMD(ivec2);"
+ "ivec3 maxInvocationsNonUniformAMD(ivec3);"
+ "ivec4 maxInvocationsNonUniformAMD(ivec4);"
+
+ "uint maxInvocationsNonUniformAMD(uint);"
+ "uvec2 maxInvocationsNonUniformAMD(uvec2);"
+ "uvec3 maxInvocationsNonUniformAMD(uvec3);"
+ "uvec4 maxInvocationsNonUniformAMD(uvec4);"
+
+ "double maxInvocationsNonUniformAMD(double);"
+ "dvec2 maxInvocationsNonUniformAMD(dvec2);"
+ "dvec3 maxInvocationsNonUniformAMD(dvec3);"
+ "dvec4 maxInvocationsNonUniformAMD(dvec4);"
+
+ "int64_t maxInvocationsNonUniformAMD(int64_t);"
+ "i64vec2 maxInvocationsNonUniformAMD(i64vec2);"
+ "i64vec3 maxInvocationsNonUniformAMD(i64vec3);"
+ "i64vec4 maxInvocationsNonUniformAMD(i64vec4);"
+
+ "uint64_t maxInvocationsNonUniformAMD(uint64_t);"
+ "u64vec2 maxInvocationsNonUniformAMD(u64vec2);"
+ "u64vec3 maxInvocationsNonUniformAMD(u64vec3);"
+ "u64vec4 maxInvocationsNonUniformAMD(u64vec4);"
+
+ "float16_t maxInvocationsNonUniformAMD(float16_t);"
+ "f16vec2 maxInvocationsNonUniformAMD(f16vec2);"
+ "f16vec3 maxInvocationsNonUniformAMD(f16vec3);"
+ "f16vec4 maxInvocationsNonUniformAMD(f16vec4);"
+
+ "int16_t maxInvocationsNonUniformAMD(int16_t);"
+ "i16vec2 maxInvocationsNonUniformAMD(i16vec2);"
+ "i16vec3 maxInvocationsNonUniformAMD(i16vec3);"
+ "i16vec4 maxInvocationsNonUniformAMD(i16vec4);"
+
+ "uint16_t maxInvocationsNonUniformAMD(uint16_t);"
+ "u16vec2 maxInvocationsNonUniformAMD(u16vec2);"
+ "u16vec3 maxInvocationsNonUniformAMD(u16vec3);"
+ "u16vec4 maxInvocationsNonUniformAMD(u16vec4);"
+
+ "float maxInvocationsInclusiveScanNonUniformAMD(float);"
+ "vec2 maxInvocationsInclusiveScanNonUniformAMD(vec2);"
+ "vec3 maxInvocationsInclusiveScanNonUniformAMD(vec3);"
+ "vec4 maxInvocationsInclusiveScanNonUniformAMD(vec4);"
+
+ "int maxInvocationsInclusiveScanNonUniformAMD(int);"
+ "ivec2 maxInvocationsInclusiveScanNonUniformAMD(ivec2);"
+ "ivec3 maxInvocationsInclusiveScanNonUniformAMD(ivec3);"
+ "ivec4 maxInvocationsInclusiveScanNonUniformAMD(ivec4);"
+
+ "uint maxInvocationsInclusiveScanNonUniformAMD(uint);"
+ "uvec2 maxInvocationsInclusiveScanNonUniformAMD(uvec2);"
+ "uvec3 maxInvocationsInclusiveScanNonUniformAMD(uvec3);"
+ "uvec4 maxInvocationsInclusiveScanNonUniformAMD(uvec4);"
+
+ "double maxInvocationsInclusiveScanNonUniformAMD(double);"
+ "dvec2 maxInvocationsInclusiveScanNonUniformAMD(dvec2);"
+ "dvec3 maxInvocationsInclusiveScanNonUniformAMD(dvec3);"
+ "dvec4 maxInvocationsInclusiveScanNonUniformAMD(dvec4);"
+
+ "int64_t maxInvocationsInclusiveScanNonUniformAMD(int64_t);"
+ "i64vec2 maxInvocationsInclusiveScanNonUniformAMD(i64vec2);"
+ "i64vec3 maxInvocationsInclusiveScanNonUniformAMD(i64vec3);"
+ "i64vec4 maxInvocationsInclusiveScanNonUniformAMD(i64vec4);"
+
+ "uint64_t maxInvocationsInclusiveScanNonUniformAMD(uint64_t);"
+ "u64vec2 maxInvocationsInclusiveScanNonUniformAMD(u64vec2);"
+ "u64vec3 maxInvocationsInclusiveScanNonUniformAMD(u64vec3);"
+ "u64vec4 maxInvocationsInclusiveScanNonUniformAMD(u64vec4);"
+
+ "float16_t maxInvocationsInclusiveScanNonUniformAMD(float16_t);"
+ "f16vec2 maxInvocationsInclusiveScanNonUniformAMD(f16vec2);"
+ "f16vec3 maxInvocationsInclusiveScanNonUniformAMD(f16vec3);"
+ "f16vec4 maxInvocationsInclusiveScanNonUniformAMD(f16vec4);"
+
+ "int16_t maxInvocationsInclusiveScanNonUniformAMD(int16_t);"
+ "i16vec2 maxInvocationsInclusiveScanNonUniformAMD(i16vec2);"
+ "i16vec3 maxInvocationsInclusiveScanNonUniformAMD(i16vec3);"
+ "i16vec4 maxInvocationsInclusiveScanNonUniformAMD(i16vec4);"
+
+ "uint16_t maxInvocationsInclusiveScanNonUniformAMD(uint16_t);"
+ "u16vec2 maxInvocationsInclusiveScanNonUniformAMD(u16vec2);"
+ "u16vec3 maxInvocationsInclusiveScanNonUniformAMD(u16vec3);"
+ "u16vec4 maxInvocationsInclusiveScanNonUniformAMD(u16vec4);"
+
+ "float maxInvocationsExclusiveScanNonUniformAMD(float);"
+ "vec2 maxInvocationsExclusiveScanNonUniformAMD(vec2);"
+ "vec3 maxInvocationsExclusiveScanNonUniformAMD(vec3);"
+ "vec4 maxInvocationsExclusiveScanNonUniformAMD(vec4);"
+
+ "int maxInvocationsExclusiveScanNonUniformAMD(int);"
+ "ivec2 maxInvocationsExclusiveScanNonUniformAMD(ivec2);"
+ "ivec3 maxInvocationsExclusiveScanNonUniformAMD(ivec3);"
+ "ivec4 maxInvocationsExclusiveScanNonUniformAMD(ivec4);"
+
+ "uint maxInvocationsExclusiveScanNonUniformAMD(uint);"
+ "uvec2 maxInvocationsExclusiveScanNonUniformAMD(uvec2);"
+ "uvec3 maxInvocationsExclusiveScanNonUniformAMD(uvec3);"
+ "uvec4 maxInvocationsExclusiveScanNonUniformAMD(uvec4);"
+
+ "double maxInvocationsExclusiveScanNonUniformAMD(double);"
+ "dvec2 maxInvocationsExclusiveScanNonUniformAMD(dvec2);"
+ "dvec3 maxInvocationsExclusiveScanNonUniformAMD(dvec3);"
+ "dvec4 maxInvocationsExclusiveScanNonUniformAMD(dvec4);"
+
+ "int64_t maxInvocationsExclusiveScanNonUniformAMD(int64_t);"
+ "i64vec2 maxInvocationsExclusiveScanNonUniformAMD(i64vec2);"
+ "i64vec3 maxInvocationsExclusiveScanNonUniformAMD(i64vec3);"
+ "i64vec4 maxInvocationsExclusiveScanNonUniformAMD(i64vec4);"
+
+ "uint64_t maxInvocationsExclusiveScanNonUniformAMD(uint64_t);"
+ "u64vec2 maxInvocationsExclusiveScanNonUniformAMD(u64vec2);"
+ "u64vec3 maxInvocationsExclusiveScanNonUniformAMD(u64vec3);"
+ "u64vec4 maxInvocationsExclusiveScanNonUniformAMD(u64vec4);"
+
+ "float16_t maxInvocationsExclusiveScanNonUniformAMD(float16_t);"
+ "f16vec2 maxInvocationsExclusiveScanNonUniformAMD(f16vec2);"
+ "f16vec3 maxInvocationsExclusiveScanNonUniformAMD(f16vec3);"
+ "f16vec4 maxInvocationsExclusiveScanNonUniformAMD(f16vec4);"
+
+ "int16_t maxInvocationsExclusiveScanNonUniformAMD(int16_t);"
+ "i16vec2 maxInvocationsExclusiveScanNonUniformAMD(i16vec2);"
+ "i16vec3 maxInvocationsExclusiveScanNonUniformAMD(i16vec3);"
+ "i16vec4 maxInvocationsExclusiveScanNonUniformAMD(i16vec4);"
+
+ "uint16_t maxInvocationsExclusiveScanNonUniformAMD(uint16_t);"
+ "u16vec2 maxInvocationsExclusiveScanNonUniformAMD(u16vec2);"
+ "u16vec3 maxInvocationsExclusiveScanNonUniformAMD(u16vec3);"
+ "u16vec4 maxInvocationsExclusiveScanNonUniformAMD(u16vec4);"
+
+ "float addInvocationsNonUniformAMD(float);"
+ "vec2 addInvocationsNonUniformAMD(vec2);"
+ "vec3 addInvocationsNonUniformAMD(vec3);"
+ "vec4 addInvocationsNonUniformAMD(vec4);"
+
+ "int addInvocationsNonUniformAMD(int);"
+ "ivec2 addInvocationsNonUniformAMD(ivec2);"
+ "ivec3 addInvocationsNonUniformAMD(ivec3);"
+ "ivec4 addInvocationsNonUniformAMD(ivec4);"
+
+ "uint addInvocationsNonUniformAMD(uint);"
+ "uvec2 addInvocationsNonUniformAMD(uvec2);"
+ "uvec3 addInvocationsNonUniformAMD(uvec3);"
+ "uvec4 addInvocationsNonUniformAMD(uvec4);"
+
+ "double addInvocationsNonUniformAMD(double);"
+ "dvec2 addInvocationsNonUniformAMD(dvec2);"
+ "dvec3 addInvocationsNonUniformAMD(dvec3);"
+ "dvec4 addInvocationsNonUniformAMD(dvec4);"
+
+ "int64_t addInvocationsNonUniformAMD(int64_t);"
+ "i64vec2 addInvocationsNonUniformAMD(i64vec2);"
+ "i64vec3 addInvocationsNonUniformAMD(i64vec3);"
+ "i64vec4 addInvocationsNonUniformAMD(i64vec4);"
+
+ "uint64_t addInvocationsNonUniformAMD(uint64_t);"
+ "u64vec2 addInvocationsNonUniformAMD(u64vec2);"
+ "u64vec3 addInvocationsNonUniformAMD(u64vec3);"
+ "u64vec4 addInvocationsNonUniformAMD(u64vec4);"
+
+ "float16_t addInvocationsNonUniformAMD(float16_t);"
+ "f16vec2 addInvocationsNonUniformAMD(f16vec2);"
+ "f16vec3 addInvocationsNonUniformAMD(f16vec3);"
+ "f16vec4 addInvocationsNonUniformAMD(f16vec4);"
+
+ "int16_t addInvocationsNonUniformAMD(int16_t);"
+ "i16vec2 addInvocationsNonUniformAMD(i16vec2);"
+ "i16vec3 addInvocationsNonUniformAMD(i16vec3);"
+ "i16vec4 addInvocationsNonUniformAMD(i16vec4);"
+
+ "uint16_t addInvocationsNonUniformAMD(uint16_t);"
+ "u16vec2 addInvocationsNonUniformAMD(u16vec2);"
+ "u16vec3 addInvocationsNonUniformAMD(u16vec3);"
+ "u16vec4 addInvocationsNonUniformAMD(u16vec4);"
+
+ "float addInvocationsInclusiveScanNonUniformAMD(float);"
+ "vec2 addInvocationsInclusiveScanNonUniformAMD(vec2);"
+ "vec3 addInvocationsInclusiveScanNonUniformAMD(vec3);"
+ "vec4 addInvocationsInclusiveScanNonUniformAMD(vec4);"
+
+ "int addInvocationsInclusiveScanNonUniformAMD(int);"
+ "ivec2 addInvocationsInclusiveScanNonUniformAMD(ivec2);"
+ "ivec3 addInvocationsInclusiveScanNonUniformAMD(ivec3);"
+ "ivec4 addInvocationsInclusiveScanNonUniformAMD(ivec4);"
+
+ "uint addInvocationsInclusiveScanNonUniformAMD(uint);"
+ "uvec2 addInvocationsInclusiveScanNonUniformAMD(uvec2);"
+ "uvec3 addInvocationsInclusiveScanNonUniformAMD(uvec3);"
+ "uvec4 addInvocationsInclusiveScanNonUniformAMD(uvec4);"
+
+ "double addInvocationsInclusiveScanNonUniformAMD(double);"
+ "dvec2 addInvocationsInclusiveScanNonUniformAMD(dvec2);"
+ "dvec3 addInvocationsInclusiveScanNonUniformAMD(dvec3);"
+ "dvec4 addInvocationsInclusiveScanNonUniformAMD(dvec4);"
+
+ "int64_t addInvocationsInclusiveScanNonUniformAMD(int64_t);"
+ "i64vec2 addInvocationsInclusiveScanNonUniformAMD(i64vec2);"
+ "i64vec3 addInvocationsInclusiveScanNonUniformAMD(i64vec3);"
+ "i64vec4 addInvocationsInclusiveScanNonUniformAMD(i64vec4);"
+
+ "uint64_t addInvocationsInclusiveScanNonUniformAMD(uint64_t);"
+ "u64vec2 addInvocationsInclusiveScanNonUniformAMD(u64vec2);"
+ "u64vec3 addInvocationsInclusiveScanNonUniformAMD(u64vec3);"
+ "u64vec4 addInvocationsInclusiveScanNonUniformAMD(u64vec4);"
+
+ "float16_t addInvocationsInclusiveScanNonUniformAMD(float16_t);"
+ "f16vec2 addInvocationsInclusiveScanNonUniformAMD(f16vec2);"
+ "f16vec3 addInvocationsInclusiveScanNonUniformAMD(f16vec3);"
+ "f16vec4 addInvocationsInclusiveScanNonUniformAMD(f16vec4);"
+
+ "int16_t addInvocationsInclusiveScanNonUniformAMD(int16_t);"
+ "i16vec2 addInvocationsInclusiveScanNonUniformAMD(i16vec2);"
+ "i16vec3 addInvocationsInclusiveScanNonUniformAMD(i16vec3);"
+ "i16vec4 addInvocationsInclusiveScanNonUniformAMD(i16vec4);"
+
+ "uint16_t addInvocationsInclusiveScanNonUniformAMD(uint16_t);"
+ "u16vec2 addInvocationsInclusiveScanNonUniformAMD(u16vec2);"
+ "u16vec3 addInvocationsInclusiveScanNonUniformAMD(u16vec3);"
+ "u16vec4 addInvocationsInclusiveScanNonUniformAMD(u16vec4);"
+
+ "float addInvocationsExclusiveScanNonUniformAMD(float);"
+ "vec2 addInvocationsExclusiveScanNonUniformAMD(vec2);"
+ "vec3 addInvocationsExclusiveScanNonUniformAMD(vec3);"
+ "vec4 addInvocationsExclusiveScanNonUniformAMD(vec4);"
+
+ "int addInvocationsExclusiveScanNonUniformAMD(int);"
+ "ivec2 addInvocationsExclusiveScanNonUniformAMD(ivec2);"
+ "ivec3 addInvocationsExclusiveScanNonUniformAMD(ivec3);"
+ "ivec4 addInvocationsExclusiveScanNonUniformAMD(ivec4);"
+
+ "uint addInvocationsExclusiveScanNonUniformAMD(uint);"
+ "uvec2 addInvocationsExclusiveScanNonUniformAMD(uvec2);"
+ "uvec3 addInvocationsExclusiveScanNonUniformAMD(uvec3);"
+ "uvec4 addInvocationsExclusiveScanNonUniformAMD(uvec4);"
+
+ "double addInvocationsExclusiveScanNonUniformAMD(double);"
+ "dvec2 addInvocationsExclusiveScanNonUniformAMD(dvec2);"
+ "dvec3 addInvocationsExclusiveScanNonUniformAMD(dvec3);"
+ "dvec4 addInvocationsExclusiveScanNonUniformAMD(dvec4);"
+
+ "int64_t addInvocationsExclusiveScanNonUniformAMD(int64_t);"
+ "i64vec2 addInvocationsExclusiveScanNonUniformAMD(i64vec2);"
+ "i64vec3 addInvocationsExclusiveScanNonUniformAMD(i64vec3);"
+ "i64vec4 addInvocationsExclusiveScanNonUniformAMD(i64vec4);"
+
+ "uint64_t addInvocationsExclusiveScanNonUniformAMD(uint64_t);"
+ "u64vec2 addInvocationsExclusiveScanNonUniformAMD(u64vec2);"
+ "u64vec3 addInvocationsExclusiveScanNonUniformAMD(u64vec3);"
+ "u64vec4 addInvocationsExclusiveScanNonUniformAMD(u64vec4);"
+
+ "float16_t addInvocationsExclusiveScanNonUniformAMD(float16_t);"
+ "f16vec2 addInvocationsExclusiveScanNonUniformAMD(f16vec2);"
+ "f16vec3 addInvocationsExclusiveScanNonUniformAMD(f16vec3);"
+ "f16vec4 addInvocationsExclusiveScanNonUniformAMD(f16vec4);"
+
+ "int16_t addInvocationsExclusiveScanNonUniformAMD(int16_t);"
+ "i16vec2 addInvocationsExclusiveScanNonUniformAMD(i16vec2);"
+ "i16vec3 addInvocationsExclusiveScanNonUniformAMD(i16vec3);"
+ "i16vec4 addInvocationsExclusiveScanNonUniformAMD(i16vec4);"
+
+ "uint16_t addInvocationsExclusiveScanNonUniformAMD(uint16_t);"
+ "u16vec2 addInvocationsExclusiveScanNonUniformAMD(u16vec2);"
+ "u16vec3 addInvocationsExclusiveScanNonUniformAMD(u16vec3);"
+ "u16vec4 addInvocationsExclusiveScanNonUniformAMD(u16vec4);"
+
+ "float swizzleInvocationsAMD(float, uvec4);"
+ "vec2 swizzleInvocationsAMD(vec2, uvec4);"
+ "vec3 swizzleInvocationsAMD(vec3, uvec4);"
+ "vec4 swizzleInvocationsAMD(vec4, uvec4);"
+
+ "int swizzleInvocationsAMD(int, uvec4);"
+ "ivec2 swizzleInvocationsAMD(ivec2, uvec4);"
+ "ivec3 swizzleInvocationsAMD(ivec3, uvec4);"
+ "ivec4 swizzleInvocationsAMD(ivec4, uvec4);"
+
+ "uint swizzleInvocationsAMD(uint, uvec4);"
+ "uvec2 swizzleInvocationsAMD(uvec2, uvec4);"
+ "uvec3 swizzleInvocationsAMD(uvec3, uvec4);"
+ "uvec4 swizzleInvocationsAMD(uvec4, uvec4);"
+
+ "float swizzleInvocationsMaskedAMD(float, uvec3);"
+ "vec2 swizzleInvocationsMaskedAMD(vec2, uvec3);"
+ "vec3 swizzleInvocationsMaskedAMD(vec3, uvec3);"
+ "vec4 swizzleInvocationsMaskedAMD(vec4, uvec3);"
+
+ "int swizzleInvocationsMaskedAMD(int, uvec3);"
+ "ivec2 swizzleInvocationsMaskedAMD(ivec2, uvec3);"
+ "ivec3 swizzleInvocationsMaskedAMD(ivec3, uvec3);"
+ "ivec4 swizzleInvocationsMaskedAMD(ivec4, uvec3);"
+
+ "uint swizzleInvocationsMaskedAMD(uint, uvec3);"
+ "uvec2 swizzleInvocationsMaskedAMD(uvec2, uvec3);"
+ "uvec3 swizzleInvocationsMaskedAMD(uvec3, uvec3);"
+ "uvec4 swizzleInvocationsMaskedAMD(uvec4, uvec3);"
+
+ "float writeInvocationAMD(float, float, uint);"
+ "vec2 writeInvocationAMD(vec2, vec2, uint);"
+ "vec3 writeInvocationAMD(vec3, vec3, uint);"
+ "vec4 writeInvocationAMD(vec4, vec4, uint);"
+
+ "int writeInvocationAMD(int, int, uint);"
+ "ivec2 writeInvocationAMD(ivec2, ivec2, uint);"
+ "ivec3 writeInvocationAMD(ivec3, ivec3, uint);"
+ "ivec4 writeInvocationAMD(ivec4, ivec4, uint);"
+
+ "uint writeInvocationAMD(uint, uint, uint);"
+ "uvec2 writeInvocationAMD(uvec2, uvec2, uint);"
+ "uvec3 writeInvocationAMD(uvec3, uvec3, uint);"
+ "uvec4 writeInvocationAMD(uvec4, uvec4, uint);"
+
+ "uint mbcntAMD(uint64_t);"
+
+ "\n");
+ }
+
+ // GL_AMD_gcn_shader
+ if (profile != EEsProfile && version >= 450) {
+ commonBuiltins.append(
+ "float cubeFaceIndexAMD(vec3);"
+ "vec2 cubeFaceCoordAMD(vec3);"
+ "uint64_t timeAMD();"
+
+ "\n");
+ }
+
+ // GL_AMD_shader_fragment_mask
+ if (profile != EEsProfile && version >= 450) {
+ commonBuiltins.append(
+ "uint fragmentMaskFetchAMD(sampler2DMS, ivec2);"
+ "uint fragmentMaskFetchAMD(isampler2DMS, ivec2);"
+ "uint fragmentMaskFetchAMD(usampler2DMS, ivec2);"
+
+ "uint fragmentMaskFetchAMD(sampler2DMSArray, ivec3);"
+ "uint fragmentMaskFetchAMD(isampler2DMSArray, ivec3);"
+ "uint fragmentMaskFetchAMD(usampler2DMSArray, ivec3);"
+
+ "vec4 fragmentFetchAMD(sampler2DMS, ivec2, uint);"
+ "ivec4 fragmentFetchAMD(isampler2DMS, ivec2, uint);"
+ "uvec4 fragmentFetchAMD(usampler2DMS, ivec2, uint);"
+
+ "vec4 fragmentFetchAMD(sampler2DMSArray, ivec3, uint);"
+ "ivec4 fragmentFetchAMD(isampler2DMSArray, ivec3, uint);"
+ "uvec4 fragmentFetchAMD(usampler2DMSArray, ivec3, uint);"
+
+ "\n");
+ }
+
+#endif // AMD_EXTENSIONS
+
+
+#ifdef NV_EXTENSIONS
+ if ((profile != EEsProfile && version >= 450) ||
+ (profile == EEsProfile && version >= 320)) {
+ commonBuiltins.append(
+ "struct gl_TextureFootprint2DNV {"
+ "uvec2 anchor;"
+ "uvec2 offset;"
+ "uvec2 mask;"
+ "uint lod;"
+ "uint granularity;"
+ "};"
+
+ "struct gl_TextureFootprint3DNV {"
+ "uvec3 anchor;"
+ "uvec3 offset;"
+ "uvec2 mask;"
+ "uint lod;"
+ "uint granularity;"
+ "};"
+ "bool textureFootprintNV(sampler2D, vec2, int, bool, out gl_TextureFootprint2DNV);"
+ "bool textureFootprintNV(sampler3D, vec3, int, bool, out gl_TextureFootprint3DNV);"
+ "bool textureFootprintNV(sampler2D, vec2, int, bool, out gl_TextureFootprint2DNV, float);"
+ "bool textureFootprintNV(sampler3D, vec3, int, bool, out gl_TextureFootprint3DNV, float);"
+ "bool textureFootprintClampNV(sampler2D, vec2, float, int, bool, out gl_TextureFootprint2DNV);"
+ "bool textureFootprintClampNV(sampler3D, vec3, float, int, bool, out gl_TextureFootprint3DNV);"
+ "bool textureFootprintClampNV(sampler2D, vec2, float, int, bool, out gl_TextureFootprint2DNV, float);"
+ "bool textureFootprintClampNV(sampler3D, vec3, float, int, bool, out gl_TextureFootprint3DNV, float);"
+ "bool textureFootprintLodNV(sampler2D, vec2, float, int, bool, out gl_TextureFootprint2DNV);"
+ "bool textureFootprintLodNV(sampler3D, vec3, float, int, bool, out gl_TextureFootprint3DNV);"
+ "bool textureFootprintGradNV(sampler2D, vec2, vec2, vec2, int, bool, out gl_TextureFootprint2DNV);"
+ "bool textureFootprintGradClampNV(sampler2D, vec2, vec2, vec2, float, int, bool, out gl_TextureFootprint2DNV);"
+ "\n");
+ }
+
+#endif // NV_EXTENSIONS
+ // GL_AMD_gpu_shader_half_float/Explicit types
+ if (profile != EEsProfile && version >= 450) {
+ commonBuiltins.append(
+ "float16_t radians(float16_t);"
+ "f16vec2 radians(f16vec2);"
+ "f16vec3 radians(f16vec3);"
+ "f16vec4 radians(f16vec4);"
+
+ "float16_t degrees(float16_t);"
+ "f16vec2 degrees(f16vec2);"
+ "f16vec3 degrees(f16vec3);"
+ "f16vec4 degrees(f16vec4);"
+
+ "float16_t sin(float16_t);"
+ "f16vec2 sin(f16vec2);"
+ "f16vec3 sin(f16vec3);"
+ "f16vec4 sin(f16vec4);"
+
+ "float16_t cos(float16_t);"
+ "f16vec2 cos(f16vec2);"
+ "f16vec3 cos(f16vec3);"
+ "f16vec4 cos(f16vec4);"
+
+ "float16_t tan(float16_t);"
+ "f16vec2 tan(f16vec2);"
+ "f16vec3 tan(f16vec3);"
+ "f16vec4 tan(f16vec4);"
+
+ "float16_t asin(float16_t);"
+ "f16vec2 asin(f16vec2);"
+ "f16vec3 asin(f16vec3);"
+ "f16vec4 asin(f16vec4);"
+
+ "float16_t acos(float16_t);"
+ "f16vec2 acos(f16vec2);"
+ "f16vec3 acos(f16vec3);"
+ "f16vec4 acos(f16vec4);"
+
+ "float16_t atan(float16_t, float16_t);"
+ "f16vec2 atan(f16vec2, f16vec2);"
+ "f16vec3 atan(f16vec3, f16vec3);"
+ "f16vec4 atan(f16vec4, f16vec4);"
+
+ "float16_t atan(float16_t);"
+ "f16vec2 atan(f16vec2);"
+ "f16vec3 atan(f16vec3);"
+ "f16vec4 atan(f16vec4);"
+
+ "float16_t sinh(float16_t);"
+ "f16vec2 sinh(f16vec2);"
+ "f16vec3 sinh(f16vec3);"
+ "f16vec4 sinh(f16vec4);"
+
+ "float16_t cosh(float16_t);"
+ "f16vec2 cosh(f16vec2);"
+ "f16vec3 cosh(f16vec3);"
+ "f16vec4 cosh(f16vec4);"
+
+ "float16_t tanh(float16_t);"
+ "f16vec2 tanh(f16vec2);"
+ "f16vec3 tanh(f16vec3);"
+ "f16vec4 tanh(f16vec4);"
+
+ "float16_t asinh(float16_t);"
+ "f16vec2 asinh(f16vec2);"
+ "f16vec3 asinh(f16vec3);"
+ "f16vec4 asinh(f16vec4);"
+
+ "float16_t acosh(float16_t);"
+ "f16vec2 acosh(f16vec2);"
+ "f16vec3 acosh(f16vec3);"
+ "f16vec4 acosh(f16vec4);"
+
+ "float16_t atanh(float16_t);"
+ "f16vec2 atanh(f16vec2);"
+ "f16vec3 atanh(f16vec3);"
+ "f16vec4 atanh(f16vec4);"
+
+ "float16_t pow(float16_t, float16_t);"
+ "f16vec2 pow(f16vec2, f16vec2);"
+ "f16vec3 pow(f16vec3, f16vec3);"
+ "f16vec4 pow(f16vec4, f16vec4);"
+
+ "float16_t exp(float16_t);"
+ "f16vec2 exp(f16vec2);"
+ "f16vec3 exp(f16vec3);"
+ "f16vec4 exp(f16vec4);"
+
+ "float16_t log(float16_t);"
+ "f16vec2 log(f16vec2);"
+ "f16vec3 log(f16vec3);"
+ "f16vec4 log(f16vec4);"
+
+ "float16_t exp2(float16_t);"
+ "f16vec2 exp2(f16vec2);"
+ "f16vec3 exp2(f16vec3);"
+ "f16vec4 exp2(f16vec4);"
+
+ "float16_t log2(float16_t);"
+ "f16vec2 log2(f16vec2);"
+ "f16vec3 log2(f16vec3);"
+ "f16vec4 log2(f16vec4);"
+
+ "float16_t sqrt(float16_t);"
+ "f16vec2 sqrt(f16vec2);"
+ "f16vec3 sqrt(f16vec3);"
+ "f16vec4 sqrt(f16vec4);"
+
+ "float16_t inversesqrt(float16_t);"
+ "f16vec2 inversesqrt(f16vec2);"
+ "f16vec3 inversesqrt(f16vec3);"
+ "f16vec4 inversesqrt(f16vec4);"
+
+ "float16_t abs(float16_t);"
+ "f16vec2 abs(f16vec2);"
+ "f16vec3 abs(f16vec3);"
+ "f16vec4 abs(f16vec4);"
+
+ "float16_t sign(float16_t);"
+ "f16vec2 sign(f16vec2);"
+ "f16vec3 sign(f16vec3);"
+ "f16vec4 sign(f16vec4);"
+
+ "float16_t floor(float16_t);"
+ "f16vec2 floor(f16vec2);"
+ "f16vec3 floor(f16vec3);"
+ "f16vec4 floor(f16vec4);"
+
+ "float16_t trunc(float16_t);"
+ "f16vec2 trunc(f16vec2);"
+ "f16vec3 trunc(f16vec3);"
+ "f16vec4 trunc(f16vec4);"
+
+ "float16_t round(float16_t);"
+ "f16vec2 round(f16vec2);"
+ "f16vec3 round(f16vec3);"
+ "f16vec4 round(f16vec4);"
+
+ "float16_t roundEven(float16_t);"
+ "f16vec2 roundEven(f16vec2);"
+ "f16vec3 roundEven(f16vec3);"
+ "f16vec4 roundEven(f16vec4);"
+
+ "float16_t ceil(float16_t);"
+ "f16vec2 ceil(f16vec2);"
+ "f16vec3 ceil(f16vec3);"
+ "f16vec4 ceil(f16vec4);"
+
+ "float16_t fract(float16_t);"
+ "f16vec2 fract(f16vec2);"
+ "f16vec3 fract(f16vec3);"
+ "f16vec4 fract(f16vec4);"
+
+ "float16_t mod(float16_t, float16_t);"
+ "f16vec2 mod(f16vec2, float16_t);"
+ "f16vec3 mod(f16vec3, float16_t);"
+ "f16vec4 mod(f16vec4, float16_t);"
+ "f16vec2 mod(f16vec2, f16vec2);"
+ "f16vec3 mod(f16vec3, f16vec3);"
+ "f16vec4 mod(f16vec4, f16vec4);"
+
+ "float16_t modf(float16_t, out float16_t);"
+ "f16vec2 modf(f16vec2, out f16vec2);"
+ "f16vec3 modf(f16vec3, out f16vec3);"
+ "f16vec4 modf(f16vec4, out f16vec4);"
+
+ "float16_t min(float16_t, float16_t);"
+ "f16vec2 min(f16vec2, float16_t);"
+ "f16vec3 min(f16vec3, float16_t);"
+ "f16vec4 min(f16vec4, float16_t);"
+ "f16vec2 min(f16vec2, f16vec2);"
+ "f16vec3 min(f16vec3, f16vec3);"
+ "f16vec4 min(f16vec4, f16vec4);"
+
+ "float16_t max(float16_t, float16_t);"
+ "f16vec2 max(f16vec2, float16_t);"
+ "f16vec3 max(f16vec3, float16_t);"
+ "f16vec4 max(f16vec4, float16_t);"
+ "f16vec2 max(f16vec2, f16vec2);"
+ "f16vec3 max(f16vec3, f16vec3);"
+ "f16vec4 max(f16vec4, f16vec4);"
+
+ "float16_t clamp(float16_t, float16_t, float16_t);"
+ "f16vec2 clamp(f16vec2, float16_t, float16_t);"
+ "f16vec3 clamp(f16vec3, float16_t, float16_t);"
+ "f16vec4 clamp(f16vec4, float16_t, float16_t);"
+ "f16vec2 clamp(f16vec2, f16vec2, f16vec2);"
+ "f16vec3 clamp(f16vec3, f16vec3, f16vec3);"
+ "f16vec4 clamp(f16vec4, f16vec4, f16vec4);"
+
+ "float16_t mix(float16_t, float16_t, float16_t);"
+ "f16vec2 mix(f16vec2, f16vec2, float16_t);"
+ "f16vec3 mix(f16vec3, f16vec3, float16_t);"
+ "f16vec4 mix(f16vec4, f16vec4, float16_t);"
+ "f16vec2 mix(f16vec2, f16vec2, f16vec2);"
+ "f16vec3 mix(f16vec3, f16vec3, f16vec3);"
+ "f16vec4 mix(f16vec4, f16vec4, f16vec4);"
+ "float16_t mix(float16_t, float16_t, bool);"
+ "f16vec2 mix(f16vec2, f16vec2, bvec2);"
+ "f16vec3 mix(f16vec3, f16vec3, bvec3);"
+ "f16vec4 mix(f16vec4, f16vec4, bvec4);"
+
+ "float16_t step(float16_t, float16_t);"
+ "f16vec2 step(f16vec2, f16vec2);"
+ "f16vec3 step(f16vec3, f16vec3);"
+ "f16vec4 step(f16vec4, f16vec4);"
+ "f16vec2 step(float16_t, f16vec2);"
+ "f16vec3 step(float16_t, f16vec3);"
+ "f16vec4 step(float16_t, f16vec4);"
+
+ "float16_t smoothstep(float16_t, float16_t, float16_t);"
+ "f16vec2 smoothstep(f16vec2, f16vec2, f16vec2);"
+ "f16vec3 smoothstep(f16vec3, f16vec3, f16vec3);"
+ "f16vec4 smoothstep(f16vec4, f16vec4, f16vec4);"
+ "f16vec2 smoothstep(float16_t, float16_t, f16vec2);"
+ "f16vec3 smoothstep(float16_t, float16_t, f16vec3);"
+ "f16vec4 smoothstep(float16_t, float16_t, f16vec4);"
+
+ "bool isnan(float16_t);"
+ "bvec2 isnan(f16vec2);"
+ "bvec3 isnan(f16vec3);"
+ "bvec4 isnan(f16vec4);"
+
+ "bool isinf(float16_t);"
+ "bvec2 isinf(f16vec2);"
+ "bvec3 isinf(f16vec3);"
+ "bvec4 isinf(f16vec4);"
+
+ "float16_t fma(float16_t, float16_t, float16_t);"
+ "f16vec2 fma(f16vec2, f16vec2, f16vec2);"
+ "f16vec3 fma(f16vec3, f16vec3, f16vec3);"
+ "f16vec4 fma(f16vec4, f16vec4, f16vec4);"
+
+ "float16_t frexp(float16_t, out int);"
+ "f16vec2 frexp(f16vec2, out ivec2);"
+ "f16vec3 frexp(f16vec3, out ivec3);"
+ "f16vec4 frexp(f16vec4, out ivec4);"
+
+ "float16_t ldexp(float16_t, in int);"
+ "f16vec2 ldexp(f16vec2, in ivec2);"
+ "f16vec3 ldexp(f16vec3, in ivec3);"
+ "f16vec4 ldexp(f16vec4, in ivec4);"
+
+ "uint packFloat2x16(f16vec2);"
+ "f16vec2 unpackFloat2x16(uint);"
+
+ "float16_t length(float16_t);"
+ "float16_t length(f16vec2);"
+ "float16_t length(f16vec3);"
+ "float16_t length(f16vec4);"
+
+ "float16_t distance(float16_t, float16_t);"
+ "float16_t distance(f16vec2, f16vec2);"
+ "float16_t distance(f16vec3, f16vec3);"
+ "float16_t distance(f16vec4, f16vec4);"
+
+ "float16_t dot(float16_t, float16_t);"
+ "float16_t dot(f16vec2, f16vec2);"
+ "float16_t dot(f16vec3, f16vec3);"
+ "float16_t dot(f16vec4, f16vec4);"
+
+ "f16vec3 cross(f16vec3, f16vec3);"
+
+ "float16_t normalize(float16_t);"
+ "f16vec2 normalize(f16vec2);"
+ "f16vec3 normalize(f16vec3);"
+ "f16vec4 normalize(f16vec4);"
+
+ "float16_t faceforward(float16_t, float16_t, float16_t);"
+ "f16vec2 faceforward(f16vec2, f16vec2, f16vec2);"
+ "f16vec3 faceforward(f16vec3, f16vec3, f16vec3);"
+ "f16vec4 faceforward(f16vec4, f16vec4, f16vec4);"
+
+ "float16_t reflect(float16_t, float16_t);"
+ "f16vec2 reflect(f16vec2, f16vec2);"
+ "f16vec3 reflect(f16vec3, f16vec3);"
+ "f16vec4 reflect(f16vec4, f16vec4);"
+
+ "float16_t refract(float16_t, float16_t, float16_t);"
+ "f16vec2 refract(f16vec2, f16vec2, float16_t);"
+ "f16vec3 refract(f16vec3, f16vec3, float16_t);"
+ "f16vec4 refract(f16vec4, f16vec4, float16_t);"
+
+ "f16mat2 matrixCompMult(f16mat2, f16mat2);"
+ "f16mat3 matrixCompMult(f16mat3, f16mat3);"
+ "f16mat4 matrixCompMult(f16mat4, f16mat4);"
+ "f16mat2x3 matrixCompMult(f16mat2x3, f16mat2x3);"
+ "f16mat2x4 matrixCompMult(f16mat2x4, f16mat2x4);"
+ "f16mat3x2 matrixCompMult(f16mat3x2, f16mat3x2);"
+ "f16mat3x4 matrixCompMult(f16mat3x4, f16mat3x4);"
+ "f16mat4x2 matrixCompMult(f16mat4x2, f16mat4x2);"
+ "f16mat4x3 matrixCompMult(f16mat4x3, f16mat4x3);"
+
+ "f16mat2 outerProduct(f16vec2, f16vec2);"
+ "f16mat3 outerProduct(f16vec3, f16vec3);"
+ "f16mat4 outerProduct(f16vec4, f16vec4);"
+ "f16mat2x3 outerProduct(f16vec3, f16vec2);"
+ "f16mat3x2 outerProduct(f16vec2, f16vec3);"
+ "f16mat2x4 outerProduct(f16vec4, f16vec2);"
+ "f16mat4x2 outerProduct(f16vec2, f16vec4);"
+ "f16mat3x4 outerProduct(f16vec4, f16vec3);"
+ "f16mat4x3 outerProduct(f16vec3, f16vec4);"
+
+ "f16mat2 transpose(f16mat2);"
+ "f16mat3 transpose(f16mat3);"
+ "f16mat4 transpose(f16mat4);"
+ "f16mat2x3 transpose(f16mat3x2);"
+ "f16mat3x2 transpose(f16mat2x3);"
+ "f16mat2x4 transpose(f16mat4x2);"
+ "f16mat4x2 transpose(f16mat2x4);"
+ "f16mat3x4 transpose(f16mat4x3);"
+ "f16mat4x3 transpose(f16mat3x4);"
+
+ "float16_t determinant(f16mat2);"
+ "float16_t determinant(f16mat3);"
+ "float16_t determinant(f16mat4);"
+
+ "f16mat2 inverse(f16mat2);"
+ "f16mat3 inverse(f16mat3);"
+ "f16mat4 inverse(f16mat4);"
+
+ "bvec2 lessThan(f16vec2, f16vec2);"
+ "bvec3 lessThan(f16vec3, f16vec3);"
+ "bvec4 lessThan(f16vec4, f16vec4);"
+
+ "bvec2 lessThanEqual(f16vec2, f16vec2);"
+ "bvec3 lessThanEqual(f16vec3, f16vec3);"
+ "bvec4 lessThanEqual(f16vec4, f16vec4);"
+
+ "bvec2 greaterThan(f16vec2, f16vec2);"
+ "bvec3 greaterThan(f16vec3, f16vec3);"
+ "bvec4 greaterThan(f16vec4, f16vec4);"
+
+ "bvec2 greaterThanEqual(f16vec2, f16vec2);"
+ "bvec3 greaterThanEqual(f16vec3, f16vec3);"
+ "bvec4 greaterThanEqual(f16vec4, f16vec4);"
+
+ "bvec2 equal(f16vec2, f16vec2);"
+ "bvec3 equal(f16vec3, f16vec3);"
+ "bvec4 equal(f16vec4, f16vec4);"
+
+ "bvec2 notEqual(f16vec2, f16vec2);"
+ "bvec3 notEqual(f16vec3, f16vec3);"
+ "bvec4 notEqual(f16vec4, f16vec4);"
+
+ "\n");
+ }
+
+ // Explicit types
+ if (profile != EEsProfile && version >= 450) {
+ commonBuiltins.append(
+ "int8_t abs(int8_t);"
+ "i8vec2 abs(i8vec2);"
+ "i8vec3 abs(i8vec3);"
+ "i8vec4 abs(i8vec4);"
+
+ "int8_t sign(int8_t);"
+ "i8vec2 sign(i8vec2);"
+ "i8vec3 sign(i8vec3);"
+ "i8vec4 sign(i8vec4);"
+
+ "int8_t min(int8_t x, int8_t y);"
+ "i8vec2 min(i8vec2 x, int8_t y);"
+ "i8vec3 min(i8vec3 x, int8_t y);"
+ "i8vec4 min(i8vec4 x, int8_t y);"
+ "i8vec2 min(i8vec2 x, i8vec2 y);"
+ "i8vec3 min(i8vec3 x, i8vec3 y);"
+ "i8vec4 min(i8vec4 x, i8vec4 y);"
+
+ "uint8_t min(uint8_t x, uint8_t y);"
+ "u8vec2 min(u8vec2 x, uint8_t y);"
+ "u8vec3 min(u8vec3 x, uint8_t y);"
+ "u8vec4 min(u8vec4 x, uint8_t y);"
+ "u8vec2 min(u8vec2 x, u8vec2 y);"
+ "u8vec3 min(u8vec3 x, u8vec3 y);"
+ "u8vec4 min(u8vec4 x, u8vec4 y);"
+
+ "int8_t max(int8_t x, int8_t y);"
+ "i8vec2 max(i8vec2 x, int8_t y);"
+ "i8vec3 max(i8vec3 x, int8_t y);"
+ "i8vec4 max(i8vec4 x, int8_t y);"
+ "i8vec2 max(i8vec2 x, i8vec2 y);"
+ "i8vec3 max(i8vec3 x, i8vec3 y);"
+ "i8vec4 max(i8vec4 x, i8vec4 y);"
+
+ "uint8_t max(uint8_t x, uint8_t y);"
+ "u8vec2 max(u8vec2 x, uint8_t y);"
+ "u8vec3 max(u8vec3 x, uint8_t y);"
+ "u8vec4 max(u8vec4 x, uint8_t y);"
+ "u8vec2 max(u8vec2 x, u8vec2 y);"
+ "u8vec3 max(u8vec3 x, u8vec3 y);"
+ "u8vec4 max(u8vec4 x, u8vec4 y);"
+
+ "int8_t clamp(int8_t x, int8_t minVal, int8_t maxVal);"
+ "i8vec2 clamp(i8vec2 x, int8_t minVal, int8_t maxVal);"
+ "i8vec3 clamp(i8vec3 x, int8_t minVal, int8_t maxVal);"
+ "i8vec4 clamp(i8vec4 x, int8_t minVal, int8_t maxVal);"
+ "i8vec2 clamp(i8vec2 x, i8vec2 minVal, i8vec2 maxVal);"
+ "i8vec3 clamp(i8vec3 x, i8vec3 minVal, i8vec3 maxVal);"
+ "i8vec4 clamp(i8vec4 x, i8vec4 minVal, i8vec4 maxVal);"
+
+ "uint8_t clamp(uint8_t x, uint8_t minVal, uint8_t maxVal);"
+ "u8vec2 clamp(u8vec2 x, uint8_t minVal, uint8_t maxVal);"
+ "u8vec3 clamp(u8vec3 x, uint8_t minVal, uint8_t maxVal);"
+ "u8vec4 clamp(u8vec4 x, uint8_t minVal, uint8_t maxVal);"
+ "u8vec2 clamp(u8vec2 x, u8vec2 minVal, u8vec2 maxVal);"
+ "u8vec3 clamp(u8vec3 x, u8vec3 minVal, u8vec3 maxVal);"
+ "u8vec4 clamp(u8vec4 x, u8vec4 minVal, u8vec4 maxVal);"
+
+ "int8_t mix(int8_t, int8_t, bool);"
+ "i8vec2 mix(i8vec2, i8vec2, bvec2);"
+ "i8vec3 mix(i8vec3, i8vec3, bvec3);"
+ "i8vec4 mix(i8vec4, i8vec4, bvec4);"
+ "uint8_t mix(uint8_t, uint8_t, bool);"
+ "u8vec2 mix(u8vec2, u8vec2, bvec2);"
+ "u8vec3 mix(u8vec3, u8vec3, bvec3);"
+ "u8vec4 mix(u8vec4, u8vec4, bvec4);"
+
+ "bvec2 lessThan(i8vec2, i8vec2);"
+ "bvec3 lessThan(i8vec3, i8vec3);"
+ "bvec4 lessThan(i8vec4, i8vec4);"
+ "bvec2 lessThan(u8vec2, u8vec2);"
+ "bvec3 lessThan(u8vec3, u8vec3);"
+ "bvec4 lessThan(u8vec4, u8vec4);"
+
+ "bvec2 lessThanEqual(i8vec2, i8vec2);"
+ "bvec3 lessThanEqual(i8vec3, i8vec3);"
+ "bvec4 lessThanEqual(i8vec4, i8vec4);"
+ "bvec2 lessThanEqual(u8vec2, u8vec2);"
+ "bvec3 lessThanEqual(u8vec3, u8vec3);"
+ "bvec4 lessThanEqual(u8vec4, u8vec4);"
+
+ "bvec2 greaterThan(i8vec2, i8vec2);"
+ "bvec3 greaterThan(i8vec3, i8vec3);"
+ "bvec4 greaterThan(i8vec4, i8vec4);"
+ "bvec2 greaterThan(u8vec2, u8vec2);"
+ "bvec3 greaterThan(u8vec3, u8vec3);"
+ "bvec4 greaterThan(u8vec4, u8vec4);"
+
+ "bvec2 greaterThanEqual(i8vec2, i8vec2);"
+ "bvec3 greaterThanEqual(i8vec3, i8vec3);"
+ "bvec4 greaterThanEqual(i8vec4, i8vec4);"
+ "bvec2 greaterThanEqual(u8vec2, u8vec2);"
+ "bvec3 greaterThanEqual(u8vec3, u8vec3);"
+ "bvec4 greaterThanEqual(u8vec4, u8vec4);"
+
+ "bvec2 equal(i8vec2, i8vec2);"
+ "bvec3 equal(i8vec3, i8vec3);"
+ "bvec4 equal(i8vec4, i8vec4);"
+ "bvec2 equal(u8vec2, u8vec2);"
+ "bvec3 equal(u8vec3, u8vec3);"
+ "bvec4 equal(u8vec4, u8vec4);"
+
+ "bvec2 notEqual(i8vec2, i8vec2);"
+ "bvec3 notEqual(i8vec3, i8vec3);"
+ "bvec4 notEqual(i8vec4, i8vec4);"
+ "bvec2 notEqual(u8vec2, u8vec2);"
+ "bvec3 notEqual(u8vec3, u8vec3);"
+ "bvec4 notEqual(u8vec4, u8vec4);"
+
+ " int8_t bitfieldExtract( int8_t, int8_t, int8_t);"
+ "i8vec2 bitfieldExtract(i8vec2, int8_t, int8_t);"
+ "i8vec3 bitfieldExtract(i8vec3, int8_t, int8_t);"
+ "i8vec4 bitfieldExtract(i8vec4, int8_t, int8_t);"
+
+ " uint8_t bitfieldExtract( uint8_t, int8_t, int8_t);"
+ "u8vec2 bitfieldExtract(u8vec2, int8_t, int8_t);"
+ "u8vec3 bitfieldExtract(u8vec3, int8_t, int8_t);"
+ "u8vec4 bitfieldExtract(u8vec4, int8_t, int8_t);"
+
+ " int8_t bitfieldInsert( int8_t base, int8_t, int8_t, int8_t);"
+ "i8vec2 bitfieldInsert(i8vec2 base, i8vec2, int8_t, int8_t);"
+ "i8vec3 bitfieldInsert(i8vec3 base, i8vec3, int8_t, int8_t);"
+ "i8vec4 bitfieldInsert(i8vec4 base, i8vec4, int8_t, int8_t);"
+
+ " uint8_t bitfieldInsert( uint8_t base, uint8_t, int8_t, int8_t);"
+ "u8vec2 bitfieldInsert(u8vec2 base, u8vec2, int8_t, int8_t);"
+ "u8vec3 bitfieldInsert(u8vec3 base, u8vec3, int8_t, int8_t);"
+ "u8vec4 bitfieldInsert(u8vec4 base, u8vec4, int8_t, int8_t);"
+
+ " int8_t bitCount( int8_t);"
+ "i8vec2 bitCount(i8vec2);"
+ "i8vec3 bitCount(i8vec3);"
+ "i8vec4 bitCount(i8vec4);"
+
+ " int8_t bitCount( uint8_t);"
+ "i8vec2 bitCount(u8vec2);"
+ "i8vec3 bitCount(u8vec3);"
+ "i8vec4 bitCount(u8vec4);"
+
+ " int8_t findLSB( int8_t);"
+ "i8vec2 findLSB(i8vec2);"
+ "i8vec3 findLSB(i8vec3);"
+ "i8vec4 findLSB(i8vec4);"
+
+ " int8_t findLSB( uint8_t);"
+ "i8vec2 findLSB(u8vec2);"
+ "i8vec3 findLSB(u8vec3);"
+ "i8vec4 findLSB(u8vec4);"
+
+ " int8_t findMSB( int8_t);"
+ "i8vec2 findMSB(i8vec2);"
+ "i8vec3 findMSB(i8vec3);"
+ "i8vec4 findMSB(i8vec4);"
+
+ " int8_t findMSB( uint8_t);"
+ "i8vec2 findMSB(u8vec2);"
+ "i8vec3 findMSB(u8vec3);"
+ "i8vec4 findMSB(u8vec4);"
+
+ "int16_t abs(int16_t);"
+ "i16vec2 abs(i16vec2);"
+ "i16vec3 abs(i16vec3);"
+ "i16vec4 abs(i16vec4);"
+
+ "int16_t sign(int16_t);"
+ "i16vec2 sign(i16vec2);"
+ "i16vec3 sign(i16vec3);"
+ "i16vec4 sign(i16vec4);"
+
+ "int16_t min(int16_t x, int16_t y);"
+ "i16vec2 min(i16vec2 x, int16_t y);"
+ "i16vec3 min(i16vec3 x, int16_t y);"
+ "i16vec4 min(i16vec4 x, int16_t y);"
+ "i16vec2 min(i16vec2 x, i16vec2 y);"
+ "i16vec3 min(i16vec3 x, i16vec3 y);"
+ "i16vec4 min(i16vec4 x, i16vec4 y);"
+
+ "uint16_t min(uint16_t x, uint16_t y);"
+ "u16vec2 min(u16vec2 x, uint16_t y);"
+ "u16vec3 min(u16vec3 x, uint16_t y);"
+ "u16vec4 min(u16vec4 x, uint16_t y);"
+ "u16vec2 min(u16vec2 x, u16vec2 y);"
+ "u16vec3 min(u16vec3 x, u16vec3 y);"
+ "u16vec4 min(u16vec4 x, u16vec4 y);"
+
+ "int16_t max(int16_t x, int16_t y);"
+ "i16vec2 max(i16vec2 x, int16_t y);"
+ "i16vec3 max(i16vec3 x, int16_t y);"
+ "i16vec4 max(i16vec4 x, int16_t y);"
+ "i16vec2 max(i16vec2 x, i16vec2 y);"
+ "i16vec3 max(i16vec3 x, i16vec3 y);"
+ "i16vec4 max(i16vec4 x, i16vec4 y);"
+
+ "uint16_t max(uint16_t x, uint16_t y);"
+ "u16vec2 max(u16vec2 x, uint16_t y);"
+ "u16vec3 max(u16vec3 x, uint16_t y);"
+ "u16vec4 max(u16vec4 x, uint16_t y);"
+ "u16vec2 max(u16vec2 x, u16vec2 y);"
+ "u16vec3 max(u16vec3 x, u16vec3 y);"
+ "u16vec4 max(u16vec4 x, u16vec4 y);"
+
+ "int16_t clamp(int16_t x, int16_t minVal, int16_t maxVal);"
+ "i16vec2 clamp(i16vec2 x, int16_t minVal, int16_t maxVal);"
+ "i16vec3 clamp(i16vec3 x, int16_t minVal, int16_t maxVal);"
+ "i16vec4 clamp(i16vec4 x, int16_t minVal, int16_t maxVal);"
+ "i16vec2 clamp(i16vec2 x, i16vec2 minVal, i16vec2 maxVal);"
+ "i16vec3 clamp(i16vec3 x, i16vec3 minVal, i16vec3 maxVal);"
+ "i16vec4 clamp(i16vec4 x, i16vec4 minVal, i16vec4 maxVal);"
+
+ "uint16_t clamp(uint16_t x, uint16_t minVal, uint16_t maxVal);"
+ "u16vec2 clamp(u16vec2 x, uint16_t minVal, uint16_t maxVal);"
+ "u16vec3 clamp(u16vec3 x, uint16_t minVal, uint16_t maxVal);"
+ "u16vec4 clamp(u16vec4 x, uint16_t minVal, uint16_t maxVal);"
+ "u16vec2 clamp(u16vec2 x, u16vec2 minVal, u16vec2 maxVal);"
+ "u16vec3 clamp(u16vec3 x, u16vec3 minVal, u16vec3 maxVal);"
+ "u16vec4 clamp(u16vec4 x, u16vec4 minVal, u16vec4 maxVal);"
+
+ "int16_t mix(int16_t, int16_t, bool);"
+ "i16vec2 mix(i16vec2, i16vec2, bvec2);"
+ "i16vec3 mix(i16vec3, i16vec3, bvec3);"
+ "i16vec4 mix(i16vec4, i16vec4, bvec4);"
+ "uint16_t mix(uint16_t, uint16_t, bool);"
+ "u16vec2 mix(u16vec2, u16vec2, bvec2);"
+ "u16vec3 mix(u16vec3, u16vec3, bvec3);"
+ "u16vec4 mix(u16vec4, u16vec4, bvec4);"
+
+ "float16_t frexp(float16_t, out int16_t);"
+ "f16vec2 frexp(f16vec2, out i16vec2);"
+ "f16vec3 frexp(f16vec3, out i16vec3);"
+ "f16vec4 frexp(f16vec4, out i16vec4);"
+
+ "float16_t ldexp(float16_t, int16_t);"
+ "f16vec2 ldexp(f16vec2, i16vec2);"
+ "f16vec3 ldexp(f16vec3, i16vec3);"
+ "f16vec4 ldexp(f16vec4, i16vec4);"
+
+ "int16_t halfBitsToInt16(float16_t);"
+ "i16vec2 halfBitsToInt16(f16vec2);"
+ "i16vec3 halhBitsToInt16(f16vec3);"
+ "i16vec4 halfBitsToInt16(f16vec4);"
+
+ "uint16_t halfBitsToUint16(float16_t);"
+ "u16vec2 halfBitsToUint16(f16vec2);"
+ "u16vec3 halfBitsToUint16(f16vec3);"
+ "u16vec4 halfBitsToUint16(f16vec4);"
+
+ "int16_t float16BitsToInt16(float16_t);"
+ "i16vec2 float16BitsToInt16(f16vec2);"
+ "i16vec3 float16BitsToInt16(f16vec3);"
+ "i16vec4 float16BitsToInt16(f16vec4);"
+
+ "uint16_t float16BitsToUint16(float16_t);"
+ "u16vec2 float16BitsToUint16(f16vec2);"
+ "u16vec3 float16BitsToUint16(f16vec3);"
+ "u16vec4 float16BitsToUint16(f16vec4);"
+
+ "float16_t int16BitsToFloat16(int16_t);"
+ "f16vec2 int16BitsToFloat16(i16vec2);"
+ "f16vec3 int16BitsToFloat16(i16vec3);"
+ "f16vec4 int16BitsToFloat16(i16vec4);"
+
+ "float16_t uint16BitsToFloat16(uint16_t);"
+ "f16vec2 uint16BitsToFloat16(u16vec2);"
+ "f16vec3 uint16BitsToFloat16(u16vec3);"
+ "f16vec4 uint16BitsToFloat16(u16vec4);"
+
+ "float16_t int16BitsToHalf(int16_t);"
+ "f16vec2 int16BitsToHalf(i16vec2);"
+ "f16vec3 int16BitsToHalf(i16vec3);"
+ "f16vec4 int16BitsToHalf(i16vec4);"
+
+ "float16_t uint16BitsToHalf(uint16_t);"
+ "f16vec2 uint16BitsToHalf(u16vec2);"
+ "f16vec3 uint16BitsToHalf(u16vec3);"
+ "f16vec4 uint16BitsToHalf(u16vec4);"
+
+ "int packInt2x16(i16vec2);"
+ "uint packUint2x16(u16vec2);"
+ "int64_t packInt4x16(i16vec4);"
+ "uint64_t packUint4x16(u16vec4);"
+ "i16vec2 unpackInt2x16(int);"
+ "u16vec2 unpackUint2x16(uint);"
+ "i16vec4 unpackInt4x16(int64_t);"
+ "u16vec4 unpackUint4x16(uint64_t);"
+
+ "bvec2 lessThan(i16vec2, i16vec2);"
+ "bvec3 lessThan(i16vec3, i16vec3);"
+ "bvec4 lessThan(i16vec4, i16vec4);"
+ "bvec2 lessThan(u16vec2, u16vec2);"
+ "bvec3 lessThan(u16vec3, u16vec3);"
+ "bvec4 lessThan(u16vec4, u16vec4);"
+
+ "bvec2 lessThanEqual(i16vec2, i16vec2);"
+ "bvec3 lessThanEqual(i16vec3, i16vec3);"
+ "bvec4 lessThanEqual(i16vec4, i16vec4);"
+ "bvec2 lessThanEqual(u16vec2, u16vec2);"
+ "bvec3 lessThanEqual(u16vec3, u16vec3);"
+ "bvec4 lessThanEqual(u16vec4, u16vec4);"
+
+ "bvec2 greaterThan(i16vec2, i16vec2);"
+ "bvec3 greaterThan(i16vec3, i16vec3);"
+ "bvec4 greaterThan(i16vec4, i16vec4);"
+ "bvec2 greaterThan(u16vec2, u16vec2);"
+ "bvec3 greaterThan(u16vec3, u16vec3);"
+ "bvec4 greaterThan(u16vec4, u16vec4);"
+
+ "bvec2 greaterThanEqual(i16vec2, i16vec2);"
+ "bvec3 greaterThanEqual(i16vec3, i16vec3);"
+ "bvec4 greaterThanEqual(i16vec4, i16vec4);"
+ "bvec2 greaterThanEqual(u16vec2, u16vec2);"
+ "bvec3 greaterThanEqual(u16vec3, u16vec3);"
+ "bvec4 greaterThanEqual(u16vec4, u16vec4);"
+
+ "bvec2 equal(i16vec2, i16vec2);"
+ "bvec3 equal(i16vec3, i16vec3);"
+ "bvec4 equal(i16vec4, i16vec4);"
+ "bvec2 equal(u16vec2, u16vec2);"
+ "bvec3 equal(u16vec3, u16vec3);"
+ "bvec4 equal(u16vec4, u16vec4);"
+
+ "bvec2 notEqual(i16vec2, i16vec2);"
+ "bvec3 notEqual(i16vec3, i16vec3);"
+ "bvec4 notEqual(i16vec4, i16vec4);"
+ "bvec2 notEqual(u16vec2, u16vec2);"
+ "bvec3 notEqual(u16vec3, u16vec3);"
+ "bvec4 notEqual(u16vec4, u16vec4);"
+
+ " int16_t bitfieldExtract( int16_t, int16_t, int16_t);"
+ "i16vec2 bitfieldExtract(i16vec2, int16_t, int16_t);"
+ "i16vec3 bitfieldExtract(i16vec3, int16_t, int16_t);"
+ "i16vec4 bitfieldExtract(i16vec4, int16_t, int16_t);"
+
+ " uint16_t bitfieldExtract( uint16_t, int16_t, int16_t);"
+ "u16vec2 bitfieldExtract(u16vec2, int16_t, int16_t);"
+ "u16vec3 bitfieldExtract(u16vec3, int16_t, int16_t);"
+ "u16vec4 bitfieldExtract(u16vec4, int16_t, int16_t);"
+
+ " int16_t bitfieldInsert( int16_t base, int16_t, int16_t, int16_t);"
+ "i16vec2 bitfieldInsert(i16vec2 base, i16vec2, int16_t, int16_t);"
+ "i16vec3 bitfieldInsert(i16vec3 base, i16vec3, int16_t, int16_t);"
+ "i16vec4 bitfieldInsert(i16vec4 base, i16vec4, int16_t, int16_t);"
+
+ " uint16_t bitfieldInsert( uint16_t base, uint16_t, int16_t, int16_t);"
+ "u16vec2 bitfieldInsert(u16vec2 base, u16vec2, int16_t, int16_t);"
+ "u16vec3 bitfieldInsert(u16vec3 base, u16vec3, int16_t, int16_t);"
+ "u16vec4 bitfieldInsert(u16vec4 base, u16vec4, int16_t, int16_t);"
+
+ " int16_t bitCount( int16_t);"
+ "i16vec2 bitCount(i16vec2);"
+ "i16vec3 bitCount(i16vec3);"
+ "i16vec4 bitCount(i16vec4);"
+
+ " int16_t bitCount( uint16_t);"
+ "i16vec2 bitCount(u16vec2);"
+ "i16vec3 bitCount(u16vec3);"
+ "i16vec4 bitCount(u16vec4);"
+
+ " int16_t findLSB( int16_t);"
+ "i16vec2 findLSB(i16vec2);"
+ "i16vec3 findLSB(i16vec3);"
+ "i16vec4 findLSB(i16vec4);"
+
+ " int16_t findLSB( uint16_t);"
+ "i16vec2 findLSB(u16vec2);"
+ "i16vec3 findLSB(u16vec3);"
+ "i16vec4 findLSB(u16vec4);"
+
+ " int16_t findMSB( int16_t);"
+ "i16vec2 findMSB(i16vec2);"
+ "i16vec3 findMSB(i16vec3);"
+ "i16vec4 findMSB(i16vec4);"
+
+ " int16_t findMSB( uint16_t);"
+ "i16vec2 findMSB(u16vec2);"
+ "i16vec3 findMSB(u16vec3);"
+ "i16vec4 findMSB(u16vec4);"
+
+ "int16_t pack16(i8vec2);"
+ "uint16_t pack16(u8vec2);"
+ "int32_t pack32(i8vec4);"
+ "uint32_t pack32(u8vec4);"
+ "int32_t pack32(i16vec2);"
+ "uint32_t pack32(u16vec2);"
+ "int64_t pack64(i16vec4);"
+ "uint64_t pack64(u16vec4);"
+ "int64_t pack64(i32vec2);"
+ "uint64_t pack64(u32vec2);"
+
+ "i8vec2 unpack8(int16_t);"
+ "u8vec2 unpack8(uint16_t);"
+ "i8vec4 unpack8(int32_t);"
+ "u8vec4 unpack8(uint32_t);"
+ "i16vec2 unpack16(int32_t);"
+ "u16vec2 unpack16(uint32_t);"
+ "i16vec4 unpack16(int64_t);"
+ "u16vec4 unpack16(uint64_t);"
+ "i32vec2 unpack32(int64_t);"
+ "u32vec2 unpack32(uint64_t);"
+
+ "float64_t radians(float64_t);"
+ "f64vec2 radians(f64vec2);"
+ "f64vec3 radians(f64vec3);"
+ "f64vec4 radians(f64vec4);"
+
+ "float64_t degrees(float64_t);"
+ "f64vec2 degrees(f64vec2);"
+ "f64vec3 degrees(f64vec3);"
+ "f64vec4 degrees(f64vec4);"
+
+ "float64_t sin(float64_t);"
+ "f64vec2 sin(f64vec2);"
+ "f64vec3 sin(f64vec3);"
+ "f64vec4 sin(f64vec4);"
+
+ "float64_t cos(float64_t);"
+ "f64vec2 cos(f64vec2);"
+ "f64vec3 cos(f64vec3);"
+ "f64vec4 cos(f64vec4);"
+
+ "float64_t tan(float64_t);"
+ "f64vec2 tan(f64vec2);"
+ "f64vec3 tan(f64vec3);"
+ "f64vec4 tan(f64vec4);"
+
+ "float64_t asin(float64_t);"
+ "f64vec2 asin(f64vec2);"
+ "f64vec3 asin(f64vec3);"
+ "f64vec4 asin(f64vec4);"
+
+ "float64_t acos(float64_t);"
+ "f64vec2 acos(f64vec2);"
+ "f64vec3 acos(f64vec3);"
+ "f64vec4 acos(f64vec4);"
+
+ "float64_t atan(float64_t, float64_t);"
+ "f64vec2 atan(f64vec2, f64vec2);"
+ "f64vec3 atan(f64vec3, f64vec3);"
+ "f64vec4 atan(f64vec4, f64vec4);"
+
+ "float64_t atan(float64_t);"
+ "f64vec2 atan(f64vec2);"
+ "f64vec3 atan(f64vec3);"
+ "f64vec4 atan(f64vec4);"
+
+ "float64_t sinh(float64_t);"
+ "f64vec2 sinh(f64vec2);"
+ "f64vec3 sinh(f64vec3);"
+ "f64vec4 sinh(f64vec4);"
+
+ "float64_t cosh(float64_t);"
+ "f64vec2 cosh(f64vec2);"
+ "f64vec3 cosh(f64vec3);"
+ "f64vec4 cosh(f64vec4);"
+
+ "float64_t tanh(float64_t);"
+ "f64vec2 tanh(f64vec2);"
+ "f64vec3 tanh(f64vec3);"
+ "f64vec4 tanh(f64vec4);"
+
+ "float64_t asinh(float64_t);"
+ "f64vec2 asinh(f64vec2);"
+ "f64vec3 asinh(f64vec3);"
+ "f64vec4 asinh(f64vec4);"
+
+ "float64_t acosh(float64_t);"
+ "f64vec2 acosh(f64vec2);"
+ "f64vec3 acosh(f64vec3);"
+ "f64vec4 acosh(f64vec4);"
+
+ "float64_t atanh(float64_t);"
+ "f64vec2 atanh(f64vec2);"
+ "f64vec3 atanh(f64vec3);"
+ "f64vec4 atanh(f64vec4);"
+
+ "float64_t pow(float64_t, float64_t);"
+ "f64vec2 pow(f64vec2, f64vec2);"
+ "f64vec3 pow(f64vec3, f64vec3);"
+ "f64vec4 pow(f64vec4, f64vec4);"
+
+ "float64_t exp(float64_t);"
+ "f64vec2 exp(f64vec2);"
+ "f64vec3 exp(f64vec3);"
+ "f64vec4 exp(f64vec4);"
+
+ "float64_t log(float64_t);"
+ "f64vec2 log(f64vec2);"
+ "f64vec3 log(f64vec3);"
+ "f64vec4 log(f64vec4);"
+
+ "float64_t exp2(float64_t);"
+ "f64vec2 exp2(f64vec2);"
+ "f64vec3 exp2(f64vec3);"
+ "f64vec4 exp2(f64vec4);"
+
+ "float64_t log2(float64_t);"
+ "f64vec2 log2(f64vec2);"
+ "f64vec3 log2(f64vec3);"
+ "f64vec4 log2(f64vec4);"
+ "\n");
+ }
+ if (profile != EEsProfile && version >= 450) {
+ stageBuiltins[EShLangFragment].append(derivativesAndControl64bits);
+ stageBuiltins[EShLangFragment].append(
+ "float64_t interpolateAtCentroid(float64_t);"
+ "f64vec2 interpolateAtCentroid(f64vec2);"
+ "f64vec3 interpolateAtCentroid(f64vec3);"
+ "f64vec4 interpolateAtCentroid(f64vec4);"
+
+ "float64_t interpolateAtSample(float64_t, int);"
+ "f64vec2 interpolateAtSample(f64vec2, int);"
+ "f64vec3 interpolateAtSample(f64vec3, int);"
+ "f64vec4 interpolateAtSample(f64vec4, int);"
+
+ "float64_t interpolateAtOffset(float64_t, f64vec2);"
+ "f64vec2 interpolateAtOffset(f64vec2, f64vec2);"
+ "f64vec3 interpolateAtOffset(f64vec3, f64vec2);"
+ "f64vec4 interpolateAtOffset(f64vec4, f64vec2);"
+
+ "\n");
+
+ }
+
+ //============================================================================
+ //
+ // Prototypes for built-in functions seen by vertex shaders only.
+ // (Except legacy lod functions, where it depends which release they are
+ // vertex only.)
+ //
+ //============================================================================
+
+ //
+ // Geometric Functions.
+ //
+ if (IncludeLegacy(version, profile, spvVersion))
+ stageBuiltins[EShLangVertex].append("vec4 ftransform();");
+
+ //
+ // Original-style texture Functions with lod.
+ //
+ TString* s;
+ if (version == 100)
+ s = &stageBuiltins[EShLangVertex];
+ else
+ s = &commonBuiltins;
+ if ((profile == EEsProfile && version == 100) ||
+ profile == ECompatibilityProfile ||
+ (profile == ECoreProfile && version < 420) ||
+ profile == ENoProfile) {
+ if (spvVersion.spv == 0) {
+ s->append(
+ "vec4 texture2DLod(sampler2D, vec2, float);" // GL_ARB_shader_texture_lod
+ "vec4 texture2DProjLod(sampler2D, vec3, float);" // GL_ARB_shader_texture_lod
+ "vec4 texture2DProjLod(sampler2D, vec4, float);" // GL_ARB_shader_texture_lod
+ "vec4 texture3DLod(sampler3D, vec3, float);" // GL_ARB_shader_texture_lod // OES_texture_3D, but caught by keyword check
+ "vec4 texture3DProjLod(sampler3D, vec4, float);" // GL_ARB_shader_texture_lod // OES_texture_3D, but caught by keyword check
+ "vec4 textureCubeLod(samplerCube, vec3, float);" // GL_ARB_shader_texture_lod
+
+ "\n");
+ }
+ }
+ if ( profile == ECompatibilityProfile ||
+ (profile == ECoreProfile && version < 420) ||
+ profile == ENoProfile) {
+ if (spvVersion.spv == 0) {
+ s->append(
+ "vec4 texture1DLod(sampler1D, float, float);" // GL_ARB_shader_texture_lod
+ "vec4 texture1DProjLod(sampler1D, vec2, float);" // GL_ARB_shader_texture_lod
+ "vec4 texture1DProjLod(sampler1D, vec4, float);" // GL_ARB_shader_texture_lod
+ "vec4 shadow1DLod(sampler1DShadow, vec3, float);" // GL_ARB_shader_texture_lod
+ "vec4 shadow2DLod(sampler2DShadow, vec3, float);" // GL_ARB_shader_texture_lod
+ "vec4 shadow1DProjLod(sampler1DShadow, vec4, float);" // GL_ARB_shader_texture_lod
+ "vec4 shadow2DProjLod(sampler2DShadow, vec4, float);" // GL_ARB_shader_texture_lod
+
+ "vec4 texture1DGradARB(sampler1D, float, float, float);" // GL_ARB_shader_texture_lod
+ "vec4 texture1DProjGradARB(sampler1D, vec2, float, float);" // GL_ARB_shader_texture_lod
+ "vec4 texture1DProjGradARB(sampler1D, vec4, float, float);" // GL_ARB_shader_texture_lod
+ "vec4 texture2DGradARB(sampler2D, vec2, vec2, vec2);" // GL_ARB_shader_texture_lod
+ "vec4 texture2DProjGradARB(sampler2D, vec3, vec2, vec2);" // GL_ARB_shader_texture_lod
+ "vec4 texture2DProjGradARB(sampler2D, vec4, vec2, vec2);" // GL_ARB_shader_texture_lod
+ "vec4 texture3DGradARB(sampler3D, vec3, vec3, vec3);" // GL_ARB_shader_texture_lod
+ "vec4 texture3DProjGradARB(sampler3D, vec4, vec3, vec3);" // GL_ARB_shader_texture_lod
+ "vec4 textureCubeGradARB(samplerCube, vec3, vec3, vec3);" // GL_ARB_shader_texture_lod
+ "vec4 shadow1DGradARB(sampler1DShadow, vec3, float, float);" // GL_ARB_shader_texture_lod
+ "vec4 shadow1DProjGradARB( sampler1DShadow, vec4, float, float);" // GL_ARB_shader_texture_lod
+ "vec4 shadow2DGradARB(sampler2DShadow, vec3, vec2, vec2);" // GL_ARB_shader_texture_lod
+ "vec4 shadow2DProjGradARB( sampler2DShadow, vec4, vec2, vec2);" // GL_ARB_shader_texture_lod
+ "vec4 texture2DRectGradARB(sampler2DRect, vec2, vec2, vec2);" // GL_ARB_shader_texture_lod
+ "vec4 texture2DRectProjGradARB( sampler2DRect, vec3, vec2, vec2);" // GL_ARB_shader_texture_lod
+ "vec4 texture2DRectProjGradARB( sampler2DRect, vec4, vec2, vec2);" // GL_ARB_shader_texture_lod
+ "vec4 shadow2DRectGradARB( sampler2DRectShadow, vec3, vec2, vec2);" // GL_ARB_shader_texture_lod
+ "vec4 shadow2DRectProjGradARB(sampler2DRectShadow, vec4, vec2, vec2);" // GL_ARB_shader_texture_lod
+
+ "\n");
+ }
+ }
+
+ if ((profile != EEsProfile && version >= 150) ||
+ (profile == EEsProfile && version >= 310)) {
+ //============================================================================
+ //
+ // Prototypes for built-in functions seen by geometry shaders only.
+ //
+ //============================================================================
+
+ if (profile != EEsProfile && version >= 400) {
+ stageBuiltins[EShLangGeometry].append(
+ "void EmitStreamVertex(int);"
+ "void EndStreamPrimitive(int);"
+ );
+ }
+ stageBuiltins[EShLangGeometry].append(
+ "void EmitVertex();"
+ "void EndPrimitive();"
+ "\n");
+ }
+
+ //============================================================================
+ //
+ // Prototypes for all control functions.
+ //
+ //============================================================================
+ bool esBarrier = (profile == EEsProfile && version >= 310);
+ if ((profile != EEsProfile && version >= 150) || esBarrier)
+ stageBuiltins[EShLangTessControl].append(
+ "void barrier();"
+ );
+ if ((profile != EEsProfile && version >= 420) || esBarrier)
+ stageBuiltins[EShLangCompute].append(
+ "void barrier();"
+ );
+#ifdef NV_EXTENSIONS
+ if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) {
+ stageBuiltins[EShLangMeshNV].append(
+ "void barrier();"
+ );
+ stageBuiltins[EShLangTaskNV].append(
+ "void barrier();"
+ );
+ }
+#endif
+ if ((profile != EEsProfile && version >= 130) || esBarrier)
+ commonBuiltins.append(
+ "void memoryBarrier();"
+ );
+ if ((profile != EEsProfile && version >= 420) || esBarrier) {
+ commonBuiltins.append(
+ "void memoryBarrierAtomicCounter();"
+ "void memoryBarrierBuffer();"
+ "void memoryBarrierImage();"
+ );
+ stageBuiltins[EShLangCompute].append(
+ "void memoryBarrierShared();"
+ "void groupMemoryBarrier();"
+ );
+ }
+#ifdef NV_EXTENSIONS
+ if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) {
+ stageBuiltins[EShLangMeshNV].append(
+ "void memoryBarrierShared();"
+ "void groupMemoryBarrier();"
+ );
+ stageBuiltins[EShLangTaskNV].append(
+ "void memoryBarrierShared();"
+ "void groupMemoryBarrier();"
+ );
+ }
+#endif
+
+ commonBuiltins.append("void controlBarrier(int, int, int, int);\n"
+ "void memoryBarrier(int, int, int);\n");
+
+ if (profile != EEsProfile && version >= 450) {
+ // coopMatStoreNV perhaps ought to have "out" on the buf parameter, but
+ // adding it introduces undesirable tempArgs on the stack. What we want
+ // is more like "buf" thought of as a pointer value being an in parameter.
+ stageBuiltins[EShLangCompute].append(
+ "void coopMatLoadNV(out fcoopmatNV m, volatile coherent float16_t[] buf, uint element, uint stride, bool colMajor);\n"
+ "void coopMatLoadNV(out fcoopmatNV m, volatile coherent float[] buf, uint element, uint stride, bool colMajor);\n"
+ "void coopMatLoadNV(out fcoopmatNV m, volatile coherent uint8_t[] buf, uint element, uint stride, bool colMajor);\n"
+ "void coopMatLoadNV(out fcoopmatNV m, volatile coherent uint16_t[] buf, uint element, uint stride, bool colMajor);\n"
+ "void coopMatLoadNV(out fcoopmatNV m, volatile coherent uint[] buf, uint element, uint stride, bool colMajor);\n"
+ "void coopMatLoadNV(out fcoopmatNV m, volatile coherent uint64_t[] buf, uint element, uint stride, bool colMajor);\n"
+ "void coopMatLoadNV(out fcoopmatNV m, volatile coherent uvec2[] buf, uint element, uint stride, bool colMajor);\n"
+ "void coopMatLoadNV(out fcoopmatNV m, volatile coherent uvec4[] buf, uint element, uint stride, bool colMajor);\n"
+
+ "void coopMatStoreNV(fcoopmatNV m, volatile coherent float16_t[] buf, uint element, uint stride, bool colMajor);\n"
+ "void coopMatStoreNV(fcoopmatNV m, volatile coherent float[] buf, uint element, uint stride, bool colMajor);\n"
+ "void coopMatStoreNV(fcoopmatNV m, volatile coherent float64_t[] buf, uint element, uint stride, bool colMajor);\n"
+ "void coopMatStoreNV(fcoopmatNV m, volatile coherent uint8_t[] buf, uint element, uint stride, bool colMajor);\n"
+ "void coopMatStoreNV(fcoopmatNV m, volatile coherent uint16_t[] buf, uint element, uint stride, bool colMajor);\n"
+ "void coopMatStoreNV(fcoopmatNV m, volatile coherent uint[] buf, uint element, uint stride, bool colMajor);\n"
+ "void coopMatStoreNV(fcoopmatNV m, volatile coherent uint64_t[] buf, uint element, uint stride, bool colMajor);\n"
+ "void coopMatStoreNV(fcoopmatNV m, volatile coherent uvec2[] buf, uint element, uint stride, bool colMajor);\n"
+ "void coopMatStoreNV(fcoopmatNV m, volatile coherent uvec4[] buf, uint element, uint stride, bool colMajor);\n"
+
+ "fcoopmatNV coopMatMulAddNV(fcoopmatNV A, fcoopmatNV B, fcoopmatNV C);\n"
+ );
+ }
+
+ //============================================================================
+ //
+ // Prototypes for built-in functions seen by fragment shaders only.
+ //
+ //============================================================================
+
+ //
+ // Original-style texture Functions with bias.
+ //
+ if (spvVersion.spv == 0 && (profile != EEsProfile || version == 100)) {
+ stageBuiltins[EShLangFragment].append(
+ "vec4 texture2D(sampler2D, vec2, float);"
+ "vec4 texture2DProj(sampler2D, vec3, float);"
+ "vec4 texture2DProj(sampler2D, vec4, float);"
+ "vec4 texture3D(sampler3D, vec3, float);" // OES_texture_3D
+ "vec4 texture3DProj(sampler3D, vec4, float);" // OES_texture_3D
+ "vec4 textureCube(samplerCube, vec3, float);"
+
+ "\n");
+ }
+ if (spvVersion.spv == 0 && (profile != EEsProfile && version > 100)) {
+ stageBuiltins[EShLangFragment].append(
+ "vec4 texture1D(sampler1D, float, float);"
+ "vec4 texture1DProj(sampler1D, vec2, float);"
+ "vec4 texture1DProj(sampler1D, vec4, float);"
+ "vec4 shadow1D(sampler1DShadow, vec3, float);"
+ "vec4 shadow2D(sampler2DShadow, vec3, float);"
+ "vec4 shadow1DProj(sampler1DShadow, vec4, float);"
+ "vec4 shadow2DProj(sampler2DShadow, vec4, float);"
+
+ "\n");
+ }
+ if (spvVersion.spv == 0 && profile == EEsProfile) {
+ stageBuiltins[EShLangFragment].append(
+ "vec4 texture2DLodEXT(sampler2D, vec2, float);" // GL_EXT_shader_texture_lod
+ "vec4 texture2DProjLodEXT(sampler2D, vec3, float);" // GL_EXT_shader_texture_lod
+ "vec4 texture2DProjLodEXT(sampler2D, vec4, float);" // GL_EXT_shader_texture_lod
+ "vec4 textureCubeLodEXT(samplerCube, vec3, float);" // GL_EXT_shader_texture_lod
+
+ "\n");
+ }
+
+ stageBuiltins[EShLangFragment].append(derivatives);
+ stageBuiltins[EShLangFragment].append("\n");
+
+ // GL_ARB_derivative_control
+ if (profile != EEsProfile && version >= 400) {
+ stageBuiltins[EShLangFragment].append(derivativeControls);
+ stageBuiltins[EShLangFragment].append("\n");
+ }
+
+ // GL_OES_shader_multisample_interpolation
+ if ((profile == EEsProfile && version >= 310) ||
+ (profile != EEsProfile && version >= 400)) {
+ stageBuiltins[EShLangFragment].append(
+ "float interpolateAtCentroid(float);"
+ "vec2 interpolateAtCentroid(vec2);"
+ "vec3 interpolateAtCentroid(vec3);"
+ "vec4 interpolateAtCentroid(vec4);"
+
+ "float interpolateAtSample(float, int);"
+ "vec2 interpolateAtSample(vec2, int);"
+ "vec3 interpolateAtSample(vec3, int);"
+ "vec4 interpolateAtSample(vec4, int);"
+
+ "float interpolateAtOffset(float, vec2);"
+ "vec2 interpolateAtOffset(vec2, vec2);"
+ "vec3 interpolateAtOffset(vec3, vec2);"
+ "vec4 interpolateAtOffset(vec4, vec2);"
+
+ "\n");
+ }
+
+#ifdef AMD_EXTENSIONS
+ // GL_AMD_shader_explicit_vertex_parameter
+ if (profile != EEsProfile && version >= 450) {
+ stageBuiltins[EShLangFragment].append(
+ "float interpolateAtVertexAMD(float, uint);"
+ "vec2 interpolateAtVertexAMD(vec2, uint);"
+ "vec3 interpolateAtVertexAMD(vec3, uint);"
+ "vec4 interpolateAtVertexAMD(vec4, uint);"
+
+ "int interpolateAtVertexAMD(int, uint);"
+ "ivec2 interpolateAtVertexAMD(ivec2, uint);"
+ "ivec3 interpolateAtVertexAMD(ivec3, uint);"
+ "ivec4 interpolateAtVertexAMD(ivec4, uint);"
+
+ "uint interpolateAtVertexAMD(uint, uint);"
+ "uvec2 interpolateAtVertexAMD(uvec2, uint);"
+ "uvec3 interpolateAtVertexAMD(uvec3, uint);"
+ "uvec4 interpolateAtVertexAMD(uvec4, uint);"
+
+ "float16_t interpolateAtVertexAMD(float16_t, uint);"
+ "f16vec2 interpolateAtVertexAMD(f16vec2, uint);"
+ "f16vec3 interpolateAtVertexAMD(f16vec3, uint);"
+ "f16vec4 interpolateAtVertexAMD(f16vec4, uint);"
+
+ "\n");
+ }
+
+ // GL_AMD_gpu_shader_half_float
+ if (profile != EEsProfile && version >= 450) {
+ stageBuiltins[EShLangFragment].append(derivativesAndControl16bits);
+ stageBuiltins[EShLangFragment].append("\n");
+
+ stageBuiltins[EShLangFragment].append(
+ "float16_t interpolateAtCentroid(float16_t);"
+ "f16vec2 interpolateAtCentroid(f16vec2);"
+ "f16vec3 interpolateAtCentroid(f16vec3);"
+ "f16vec4 interpolateAtCentroid(f16vec4);"
+
+ "float16_t interpolateAtSample(float16_t, int);"
+ "f16vec2 interpolateAtSample(f16vec2, int);"
+ "f16vec3 interpolateAtSample(f16vec3, int);"
+ "f16vec4 interpolateAtSample(f16vec4, int);"
+
+ "float16_t interpolateAtOffset(float16_t, f16vec2);"
+ "f16vec2 interpolateAtOffset(f16vec2, f16vec2);"
+ "f16vec3 interpolateAtOffset(f16vec3, f16vec2);"
+ "f16vec4 interpolateAtOffset(f16vec4, f16vec2);"
+
+ "\n");
+ }
+
+ // GL_AMD_shader_fragment_mask
+ if (profile != EEsProfile && version >= 450 && spvVersion.vulkan > 0) {
+ stageBuiltins[EShLangFragment].append(
+ "uint fragmentMaskFetchAMD(subpassInputMS);"
+ "uint fragmentMaskFetchAMD(isubpassInputMS);"
+ "uint fragmentMaskFetchAMD(usubpassInputMS);"
+
+ "vec4 fragmentFetchAMD(subpassInputMS, uint);"
+ "ivec4 fragmentFetchAMD(isubpassInputMS, uint);"
+ "uvec4 fragmentFetchAMD(usubpassInputMS, uint);"
+
+ "\n");
+ }
+#endif
+
+#ifdef NV_EXTENSIONS
+
+ // Builtins for GL_NV_ray_tracing
+ if (profile != EEsProfile && version >= 460) {
+ stageBuiltins[EShLangRayGenNV].append(
+ "void traceNV(accelerationStructureNV,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);"
+ "void executeCallableNV(uint, int);"
+ "\n");
+ stageBuiltins[EShLangIntersectNV].append(
+ "bool reportIntersectionNV(float, uint);"
+ "\n");
+ stageBuiltins[EShLangAnyHitNV].append(
+ "void ignoreIntersectionNV();"
+ "void terminateRayNV();"
+ "\n");
+ stageBuiltins[EShLangClosestHitNV].append(
+ "void traceNV(accelerationStructureNV,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);"
+ "void executeCallableNV(uint, int);"
+ "\n");
+ stageBuiltins[EShLangMissNV].append(
+ "void traceNV(accelerationStructureNV,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);"
+ "void executeCallableNV(uint, int);"
+ "\n");
+ stageBuiltins[EShLangCallableNV].append(
+ "void executeCallableNV(uint, int);"
+ "\n");
+ }
+
+ //E_SPV_NV_compute_shader_derivatives
+
+ stageBuiltins[EShLangCompute].append(derivatives);
+ stageBuiltins[EShLangCompute].append(derivativeControls);
+ stageBuiltins[EShLangCompute].append("\n");
+
+
+ if (profile != EEsProfile && version >= 450) {
+
+ stageBuiltins[EShLangCompute].append(derivativesAndControl16bits);
+ stageBuiltins[EShLangCompute].append(derivativesAndControl64bits);
+ stageBuiltins[EShLangCompute].append("\n");
+ }
+
+ // Builtins for GL_NV_mesh_shader
+ if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) {
+ stageBuiltins[EShLangMeshNV].append(
+ "void writePackedPrimitiveIndices4x8NV(uint, uint);"
+ "\n");
+ }
+#endif
+
+ //============================================================================
+ //
+ // Standard Uniforms
+ //
+ //============================================================================
+
+ //
+ // Depth range in window coordinates, p. 33
+ //
+ if (spvVersion.spv == 0) {
+ commonBuiltins.append(
+ "struct gl_DepthRangeParameters {"
+ );
+ if (profile == EEsProfile) {
+ commonBuiltins.append(
+ "highp float near;" // n
+ "highp float far;" // f
+ "highp float diff;" // f - n
+ );
+ } else {
+ commonBuiltins.append(
+ "float near;" // n
+ "float far;" // f
+ "float diff;" // f - n
+ );
+ }
+
+ commonBuiltins.append(
+ "};"
+ "uniform gl_DepthRangeParameters gl_DepthRange;"
+ "\n");
+ }
+
+ if (spvVersion.spv == 0 && IncludeLegacy(version, profile, spvVersion)) {
+ //
+ // Matrix state. p. 31, 32, 37, 39, 40.
+ //
+ commonBuiltins.append(
+ "uniform mat4 gl_ModelViewMatrix;"
+ "uniform mat4 gl_ProjectionMatrix;"
+ "uniform mat4 gl_ModelViewProjectionMatrix;"
+
+ //
+ // Derived matrix state that provides inverse and transposed versions
+ // of the matrices above.
+ //
+ "uniform mat3 gl_NormalMatrix;"
+
+ "uniform mat4 gl_ModelViewMatrixInverse;"
+ "uniform mat4 gl_ProjectionMatrixInverse;"
+ "uniform mat4 gl_ModelViewProjectionMatrixInverse;"
+
+ "uniform mat4 gl_ModelViewMatrixTranspose;"
+ "uniform mat4 gl_ProjectionMatrixTranspose;"
+ "uniform mat4 gl_ModelViewProjectionMatrixTranspose;"
+
+ "uniform mat4 gl_ModelViewMatrixInverseTranspose;"
+ "uniform mat4 gl_ProjectionMatrixInverseTranspose;"
+ "uniform mat4 gl_ModelViewProjectionMatrixInverseTranspose;"
+
+ //
+ // Normal scaling p. 39.
+ //
+ "uniform float gl_NormalScale;"
+
+ //
+ // Point Size, p. 66, 67.
+ //
+ "struct gl_PointParameters {"
+ "float size;"
+ "float sizeMin;"
+ "float sizeMax;"
+ "float fadeThresholdSize;"
+ "float distanceConstantAttenuation;"
+ "float distanceLinearAttenuation;"
+ "float distanceQuadraticAttenuation;"
+ "};"
+
+ "uniform gl_PointParameters gl_Point;"
+
+ //
+ // Material State p. 50, 55.
+ //
+ "struct gl_MaterialParameters {"
+ "vec4 emission;" // Ecm
+ "vec4 ambient;" // Acm
+ "vec4 diffuse;" // Dcm
+ "vec4 specular;" // Scm
+ "float shininess;" // Srm
+ "};"
+ "uniform gl_MaterialParameters gl_FrontMaterial;"
+ "uniform gl_MaterialParameters gl_BackMaterial;"
+
+ //
+ // Light State p 50, 53, 55.
+ //
+ "struct gl_LightSourceParameters {"
+ "vec4 ambient;" // Acli
+ "vec4 diffuse;" // Dcli
+ "vec4 specular;" // Scli
+ "vec4 position;" // Ppli
+ "vec4 halfVector;" // Derived: Hi
+ "vec3 spotDirection;" // Sdli
+ "float spotExponent;" // Srli
+ "float spotCutoff;" // Crli
+ // (range: [0.0,90.0], 180.0)
+ "float spotCosCutoff;" // Derived: cos(Crli)
+ // (range: [1.0,0.0],-1.0)
+ "float constantAttenuation;" // K0
+ "float linearAttenuation;" // K1
+ "float quadraticAttenuation;"// K2
+ "};"
+
+ "struct gl_LightModelParameters {"
+ "vec4 ambient;" // Acs
+ "};"
+
+ "uniform gl_LightModelParameters gl_LightModel;"
+
+ //
+ // Derived state from products of light and material.
+ //
+ "struct gl_LightModelProducts {"
+ "vec4 sceneColor;" // Derived. Ecm + Acm * Acs
+ "};"
+
+ "uniform gl_LightModelProducts gl_FrontLightModelProduct;"
+ "uniform gl_LightModelProducts gl_BackLightModelProduct;"
+
+ "struct gl_LightProducts {"
+ "vec4 ambient;" // Acm * Acli
+ "vec4 diffuse;" // Dcm * Dcli
+ "vec4 specular;" // Scm * Scli
+ "};"
+
+ //
+ // Fog p. 161
+ //
+ "struct gl_FogParameters {"
+ "vec4 color;"
+ "float density;"
+ "float start;"
+ "float end;"
+ "float scale;" // 1 / (gl_FogEnd - gl_FogStart)
+ "};"
+
+ "uniform gl_FogParameters gl_Fog;"
+
+ "\n");
+ }
+
+ //============================================================================
+ //
+ // Define the interface to the compute shader.
+ //
+ //============================================================================
+
+ if ((profile != EEsProfile && version >= 420) ||
+ (profile == EEsProfile && version >= 310)) {
+ stageBuiltins[EShLangCompute].append(
+ "in highp uvec3 gl_NumWorkGroups;"
+ "const highp uvec3 gl_WorkGroupSize = uvec3(1,1,1);"
+
+ "in highp uvec3 gl_WorkGroupID;"
+ "in highp uvec3 gl_LocalInvocationID;"
+
+ "in highp uvec3 gl_GlobalInvocationID;"
+ "in highp uint gl_LocalInvocationIndex;"
+
+ "\n");
+ }
+
+ if ((profile != EEsProfile && version >= 140) ||
+ (profile == EEsProfile && version >= 310)) {
+ stageBuiltins[EShLangCompute].append(
+ "in highp int gl_DeviceIndex;" // GL_EXT_device_group
+ "\n");
+ }
+
+#ifdef NV_EXTENSIONS
+ //============================================================================
+ //
+ // Define the interface to the mesh/task shader.
+ //
+ //============================================================================
+
+ if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) {
+ // per-vertex attributes
+ stageBuiltins[EShLangMeshNV].append(
+ "out gl_MeshPerVertexNV {"
+ "vec4 gl_Position;"
+ "float gl_PointSize;"
+ "float gl_ClipDistance[];"
+ "float gl_CullDistance[];"
+ "perviewNV vec4 gl_PositionPerViewNV[];"
+ "perviewNV float gl_ClipDistancePerViewNV[][];"
+ "perviewNV float gl_CullDistancePerViewNV[][];"
+ "} gl_MeshVerticesNV[];"
+ );
+
+ // per-primitive attributes
+ stageBuiltins[EShLangMeshNV].append(
+ "perprimitiveNV out gl_MeshPerPrimitiveNV {"
+ "int gl_PrimitiveID;"
+ "int gl_Layer;"
+ "int gl_ViewportIndex;"
+ "int gl_ViewportMask[];"
+ "perviewNV int gl_LayerPerViewNV[];"
+ "perviewNV int gl_ViewportMaskPerViewNV[][];"
+ "} gl_MeshPrimitivesNV[];"
+ );
+
+ stageBuiltins[EShLangMeshNV].append(
+ "out uint gl_PrimitiveCountNV;"
+ "out uint gl_PrimitiveIndicesNV[];"
+
+ "in uint gl_MeshViewCountNV;"
+ "in uint gl_MeshViewIndicesNV[4];"
+
+ "const highp uvec3 gl_WorkGroupSize = uvec3(1,1,1);"
+
+ "in highp uvec3 gl_WorkGroupID;"
+ "in highp uvec3 gl_LocalInvocationID;"
+
+ "in highp uvec3 gl_GlobalInvocationID;"
+ "in highp uint gl_LocalInvocationIndex;"
+
+ "\n");
+
+ stageBuiltins[EShLangTaskNV].append(
+ "out uint gl_TaskCountNV;"
+
+ "const highp uvec3 gl_WorkGroupSize = uvec3(1,1,1);"
+
+ "in highp uvec3 gl_WorkGroupID;"
+ "in highp uvec3 gl_LocalInvocationID;"
+
+ "in highp uvec3 gl_GlobalInvocationID;"
+ "in highp uint gl_LocalInvocationIndex;"
+
+ "in uint gl_MeshViewCountNV;"
+ "in uint gl_MeshViewIndicesNV[4];"
+
+ "\n");
+ }
+
+ if (profile != EEsProfile && version >= 450) {
+ stageBuiltins[EShLangMeshNV].append(
+ "in highp int gl_DeviceIndex;" // GL_EXT_device_group
+ "in int gl_DrawIDARB;" // GL_ARB_shader_draw_parameters
+ "\n");
+
+ stageBuiltins[EShLangTaskNV].append(
+ "in highp int gl_DeviceIndex;" // GL_EXT_device_group
+ "in int gl_DrawIDARB;" // GL_ARB_shader_draw_parameters
+ "\n");
+
+ if (version >= 460) {
+ stageBuiltins[EShLangMeshNV].append(
+ "in int gl_DrawID;"
+ "\n");
+
+ stageBuiltins[EShLangTaskNV].append(
+ "in int gl_DrawID;"
+ "\n");
+ }
+ }
+#endif
+
+ //============================================================================
+ //
+ // Define the interface to the vertex shader.
+ //
+ //============================================================================
+
+ if (profile != EEsProfile) {
+ if (version < 130) {
+ stageBuiltins[EShLangVertex].append(
+ "attribute vec4 gl_Color;"
+ "attribute vec4 gl_SecondaryColor;"
+ "attribute vec3 gl_Normal;"
+ "attribute vec4 gl_Vertex;"
+ "attribute vec4 gl_MultiTexCoord0;"
+ "attribute vec4 gl_MultiTexCoord1;"
+ "attribute vec4 gl_MultiTexCoord2;"
+ "attribute vec4 gl_MultiTexCoord3;"
+ "attribute vec4 gl_MultiTexCoord4;"
+ "attribute vec4 gl_MultiTexCoord5;"
+ "attribute vec4 gl_MultiTexCoord6;"
+ "attribute vec4 gl_MultiTexCoord7;"
+ "attribute float gl_FogCoord;"
+ "\n");
+ } else if (IncludeLegacy(version, profile, spvVersion)) {
+ stageBuiltins[EShLangVertex].append(
+ "in vec4 gl_Color;"
+ "in vec4 gl_SecondaryColor;"
+ "in vec3 gl_Normal;"
+ "in vec4 gl_Vertex;"
+ "in vec4 gl_MultiTexCoord0;"
+ "in vec4 gl_MultiTexCoord1;"
+ "in vec4 gl_MultiTexCoord2;"
+ "in vec4 gl_MultiTexCoord3;"
+ "in vec4 gl_MultiTexCoord4;"
+ "in vec4 gl_MultiTexCoord5;"
+ "in vec4 gl_MultiTexCoord6;"
+ "in vec4 gl_MultiTexCoord7;"
+ "in float gl_FogCoord;"
+ "\n");
+ }
+
+ if (version < 150) {
+ if (version < 130) {
+ stageBuiltins[EShLangVertex].append(
+ " vec4 gl_ClipVertex;" // needs qualifier fixed later
+ "varying vec4 gl_FrontColor;"
+ "varying vec4 gl_BackColor;"
+ "varying vec4 gl_FrontSecondaryColor;"
+ "varying vec4 gl_BackSecondaryColor;"
+ "varying vec4 gl_TexCoord[];"
+ "varying float gl_FogFragCoord;"
+ "\n");
+ } else if (IncludeLegacy(version, profile, spvVersion)) {
+ stageBuiltins[EShLangVertex].append(
+ " vec4 gl_ClipVertex;" // needs qualifier fixed later
+ "out vec4 gl_FrontColor;"
+ "out vec4 gl_BackColor;"
+ "out vec4 gl_FrontSecondaryColor;"
+ "out vec4 gl_BackSecondaryColor;"
+ "out vec4 gl_TexCoord[];"
+ "out float gl_FogFragCoord;"
+ "\n");
+ }
+ stageBuiltins[EShLangVertex].append(
+ "vec4 gl_Position;" // needs qualifier fixed later
+ "float gl_PointSize;" // needs qualifier fixed later
+ );
+
+ if (version == 130 || version == 140)
+ stageBuiltins[EShLangVertex].append(
+ "out float gl_ClipDistance[];"
+ );
+ } else {
+ // version >= 150
+ stageBuiltins[EShLangVertex].append(
+ "out gl_PerVertex {"
+ "vec4 gl_Position;" // needs qualifier fixed later
+ "float gl_PointSize;" // needs qualifier fixed later
+ "float gl_ClipDistance[];"
+ );
+ if (IncludeLegacy(version, profile, spvVersion))
+ stageBuiltins[EShLangVertex].append(
+ "vec4 gl_ClipVertex;" // needs qualifier fixed later
+ "vec4 gl_FrontColor;"
+ "vec4 gl_BackColor;"
+ "vec4 gl_FrontSecondaryColor;"
+ "vec4 gl_BackSecondaryColor;"
+ "vec4 gl_TexCoord[];"
+ "float gl_FogFragCoord;"
+ );
+ if (version >= 450)
+ stageBuiltins[EShLangVertex].append(
+ "float gl_CullDistance[];"
+ );
+ stageBuiltins[EShLangVertex].append(
+ "};"
+ "\n");
+ }
+ if (version >= 130 && spvVersion.vulkan == 0)
+ stageBuiltins[EShLangVertex].append(
+ "int gl_VertexID;" // needs qualifier fixed later
+ );
+ if (version >= 140 && spvVersion.vulkan == 0)
+ stageBuiltins[EShLangVertex].append(
+ "int gl_InstanceID;" // needs qualifier fixed later
+ );
+ if (spvVersion.vulkan > 0 && version >= 140)
+ stageBuiltins[EShLangVertex].append(
+ "in int gl_VertexIndex;"
+ "in int gl_InstanceIndex;"
+ );
+ if (version >= 440) {
+ stageBuiltins[EShLangVertex].append(
+ "in int gl_BaseVertexARB;"
+ "in int gl_BaseInstanceARB;"
+ "in int gl_DrawIDARB;"
+ );
+ }
+ if (version >= 410) {
+ stageBuiltins[EShLangVertex].append(
+ "out int gl_ViewportIndex;"
+ "out int gl_Layer;"
+ );
+ }
+ if (version >= 460) {
+ stageBuiltins[EShLangVertex].append(
+ "in int gl_BaseVertex;"
+ "in int gl_BaseInstance;"
+ "in int gl_DrawID;"
+ );
+ }
+
+#ifdef NV_EXTENSIONS
+ if (version >= 450)
+ stageBuiltins[EShLangVertex].append(
+ "out int gl_ViewportMask[];" // GL_NV_viewport_array2
+ "out int gl_SecondaryViewportMaskNV[];" // GL_NV_stereo_view_rendering
+ "out vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering
+ "out vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes
+ "out int gl_ViewportMaskPerViewNV[];" // GL_NVX_multiview_per_view_attributes
+ );
+#endif
+
+ } else {
+ // ES profile
+ if (version == 100) {
+ stageBuiltins[EShLangVertex].append(
+ "highp vec4 gl_Position;" // needs qualifier fixed later
+ "mediump float gl_PointSize;" // needs qualifier fixed later
+ );
+ } else {
+ if (spvVersion.vulkan == 0)
+ stageBuiltins[EShLangVertex].append(
+ "in highp int gl_VertexID;" // needs qualifier fixed later
+ "in highp int gl_InstanceID;" // needs qualifier fixed later
+ );
+ if (spvVersion.vulkan > 0)
+ stageBuiltins[EShLangVertex].append(
+ "in highp int gl_VertexIndex;"
+ "in highp int gl_InstanceIndex;"
+ );
+ if (version < 310)
+ stageBuiltins[EShLangVertex].append(
+ "highp vec4 gl_Position;" // needs qualifier fixed later
+ "highp float gl_PointSize;" // needs qualifier fixed later
+ );
+ else
+ stageBuiltins[EShLangVertex].append(
+ "out gl_PerVertex {"
+ "highp vec4 gl_Position;" // needs qualifier fixed later
+ "highp float gl_PointSize;" // needs qualifier fixed later
+ "};"
+ );
+ }
+ }
+
+ if ((profile != EEsProfile && version >= 140) ||
+ (profile == EEsProfile && version >= 310)) {
+ stageBuiltins[EShLangVertex].append(
+ "in highp int gl_DeviceIndex;" // GL_EXT_device_group
+ "in highp int gl_ViewIndex;" // GL_EXT_multiview
+ "\n");
+ }
+
+ if (version >= 300 /* both ES and non-ES */) {
+ stageBuiltins[EShLangVertex].append(
+ "in highp uint gl_ViewID_OVR;" // GL_OVR_multiview, GL_OVR_multiview2
+ "\n");
+ }
+
+
+ //============================================================================
+ //
+ // Define the interface to the geometry shader.
+ //
+ //============================================================================
+
+ if (profile == ECoreProfile || profile == ECompatibilityProfile) {
+ stageBuiltins[EShLangGeometry].append(
+ "in gl_PerVertex {"
+ "vec4 gl_Position;"
+ "float gl_PointSize;"
+ "float gl_ClipDistance[];"
+ );
+ if (profile == ECompatibilityProfile)
+ stageBuiltins[EShLangGeometry].append(
+ "vec4 gl_ClipVertex;"
+ "vec4 gl_FrontColor;"
+ "vec4 gl_BackColor;"
+ "vec4 gl_FrontSecondaryColor;"
+ "vec4 gl_BackSecondaryColor;"
+ "vec4 gl_TexCoord[];"
+ "float gl_FogFragCoord;"
+ );
+ if (version >= 450)
+ stageBuiltins[EShLangGeometry].append(
+ "float gl_CullDistance[];"
+#ifdef NV_EXTENSIONS
+ "vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering
+ "vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes
+#endif
+ );
+ stageBuiltins[EShLangGeometry].append(
+ "} gl_in[];"
+
+ "in int gl_PrimitiveIDIn;"
+ "out gl_PerVertex {"
+ "vec4 gl_Position;"
+ "float gl_PointSize;"
+ "float gl_ClipDistance[];"
+ "\n");
+ if (profile == ECompatibilityProfile && version >= 400)
+ stageBuiltins[EShLangGeometry].append(
+ "vec4 gl_ClipVertex;"
+ "vec4 gl_FrontColor;"
+ "vec4 gl_BackColor;"
+ "vec4 gl_FrontSecondaryColor;"
+ "vec4 gl_BackSecondaryColor;"
+ "vec4 gl_TexCoord[];"
+ "float gl_FogFragCoord;"
+ );
+ if (version >= 450)
+ stageBuiltins[EShLangGeometry].append(
+ "float gl_CullDistance[];"
+ );
+ stageBuiltins[EShLangGeometry].append(
+ "};"
+
+ "out int gl_PrimitiveID;"
+ "out int gl_Layer;");
+
+ if (version >= 150)
+ stageBuiltins[EShLangGeometry].append(
+ "out int gl_ViewportIndex;"
+ );
+
+ if (profile == ECompatibilityProfile && version < 400)
+ stageBuiltins[EShLangGeometry].append(
+ "out vec4 gl_ClipVertex;"
+ );
+
+ if (version >= 400)
+ stageBuiltins[EShLangGeometry].append(
+ "in int gl_InvocationID;"
+ );
+
+#ifdef NV_EXTENSIONS
+ if (version >= 450)
+ stageBuiltins[EShLangGeometry].append(
+ "out int gl_ViewportMask[];" // GL_NV_viewport_array2
+ "out int gl_SecondaryViewportMaskNV[];" // GL_NV_stereo_view_rendering
+ "out vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering
+ "out vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes
+ "out int gl_ViewportMaskPerViewNV[];" // GL_NVX_multiview_per_view_attributes
+ );
+#endif
+
+ stageBuiltins[EShLangGeometry].append("\n");
+ } else if (profile == EEsProfile && version >= 310) {
+ stageBuiltins[EShLangGeometry].append(
+ "in gl_PerVertex {"
+ "highp vec4 gl_Position;"
+ "highp float gl_PointSize;"
+ "} gl_in[];"
+ "\n"
+ "in highp int gl_PrimitiveIDIn;"
+ "in highp int gl_InvocationID;"
+ "\n"
+ "out gl_PerVertex {"
+ "highp vec4 gl_Position;"
+ "highp float gl_PointSize;"
+ "};"
+ "\n"
+ "out highp int gl_PrimitiveID;"
+ "out highp int gl_Layer;"
+ "\n"
+ );
+ }
+
+ if ((profile != EEsProfile && version >= 140) ||
+ (profile == EEsProfile && version >= 310)) {
+ stageBuiltins[EShLangGeometry].append(
+ "in highp int gl_DeviceIndex;" // GL_EXT_device_group
+ "in highp int gl_ViewIndex;" // GL_EXT_multiview
+ "\n");
+ }
+
+ //============================================================================
+ //
+ // Define the interface to the tessellation control shader.
+ //
+ //============================================================================
+
+ if (profile != EEsProfile && version >= 150) {
+ // Note: "in gl_PerVertex {...} gl_in[gl_MaxPatchVertices];" is declared in initialize() below,
+ // as it depends on the resource sizing of gl_MaxPatchVertices.
+
+ stageBuiltins[EShLangTessControl].append(
+ "in int gl_PatchVerticesIn;"
+ "in int gl_PrimitiveID;"
+ "in int gl_InvocationID;"
+
+ "out gl_PerVertex {"
+ "vec4 gl_Position;"
+ "float gl_PointSize;"
+ "float gl_ClipDistance[];"
+ );
+ if (profile == ECompatibilityProfile)
+ stageBuiltins[EShLangTessControl].append(
+ "vec4 gl_ClipVertex;"
+ "vec4 gl_FrontColor;"
+ "vec4 gl_BackColor;"
+ "vec4 gl_FrontSecondaryColor;"
+ "vec4 gl_BackSecondaryColor;"
+ "vec4 gl_TexCoord[];"
+ "float gl_FogFragCoord;"
+ );
+ if (version >= 450)
+ stageBuiltins[EShLangTessControl].append(
+ "float gl_CullDistance[];"
+#ifdef NV_EXTENSIONS
+ "int gl_ViewportMask[];" // GL_NV_viewport_array2
+ "vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering
+ "int gl_SecondaryViewportMaskNV[];" // GL_NV_stereo_view_rendering
+ "vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes
+ "int gl_ViewportMaskPerViewNV[];" // GL_NVX_multiview_per_view_attributes
+#endif
+ );
+ stageBuiltins[EShLangTessControl].append(
+ "} gl_out[];"
+
+ "patch out float gl_TessLevelOuter[4];"
+ "patch out float gl_TessLevelInner[2];"
+ "\n");
+
+ if (version >= 410)
+ stageBuiltins[EShLangTessControl].append(
+ "out int gl_ViewportIndex;"
+ "out int gl_Layer;"
+ "\n");
+
+ } else {
+ // Note: "in gl_PerVertex {...} gl_in[gl_MaxPatchVertices];" is declared in initialize() below,
+ // as it depends on the resource sizing of gl_MaxPatchVertices.
+
+ stageBuiltins[EShLangTessControl].append(
+ "in highp int gl_PatchVerticesIn;"
+ "in highp int gl_PrimitiveID;"
+ "in highp int gl_InvocationID;"
+
+ "out gl_PerVertex {"
+ "highp vec4 gl_Position;"
+ "highp float gl_PointSize;"
+ );
+ stageBuiltins[EShLangTessControl].append(
+ "} gl_out[];"
+
+ "patch out highp float gl_TessLevelOuter[4];"
+ "patch out highp float gl_TessLevelInner[2];"
+ "patch out highp vec4 gl_BoundingBoxOES[2];"
+ "patch out highp vec4 gl_BoundingBoxEXT[2];"
+ "\n");
+ if (profile == EEsProfile && version >= 320) {
+ stageBuiltins[EShLangTessControl].append(
+ "patch out highp vec4 gl_BoundingBox[2];"
+ "\n"
+ );
+ }
+ }
+
+ if ((profile != EEsProfile && version >= 140) ||
+ (profile == EEsProfile && version >= 310)) {
+ stageBuiltins[EShLangTessControl].append(
+ "in highp int gl_DeviceIndex;" // GL_EXT_device_group
+ "in highp int gl_ViewIndex;" // GL_EXT_multiview
+ "\n");
+ }
+
+ //============================================================================
+ //
+ // Define the interface to the tessellation evaluation shader.
+ //
+ //============================================================================
+
+ if (profile != EEsProfile && version >= 150) {
+ // Note: "in gl_PerVertex {...} gl_in[gl_MaxPatchVertices];" is declared in initialize() below,
+ // as it depends on the resource sizing of gl_MaxPatchVertices.
+
+ stageBuiltins[EShLangTessEvaluation].append(
+ "in int gl_PatchVerticesIn;"
+ "in int gl_PrimitiveID;"
+ "in vec3 gl_TessCoord;"
+
+ "patch in float gl_TessLevelOuter[4];"
+ "patch in float gl_TessLevelInner[2];"
+
+ "out gl_PerVertex {"
+ "vec4 gl_Position;"
+ "float gl_PointSize;"
+ "float gl_ClipDistance[];"
+ );
+ if (version >= 400 && profile == ECompatibilityProfile)
+ stageBuiltins[EShLangTessEvaluation].append(
+ "vec4 gl_ClipVertex;"
+ "vec4 gl_FrontColor;"
+ "vec4 gl_BackColor;"
+ "vec4 gl_FrontSecondaryColor;"
+ "vec4 gl_BackSecondaryColor;"
+ "vec4 gl_TexCoord[];"
+ "float gl_FogFragCoord;"
+ );
+ if (version >= 450)
+ stageBuiltins[EShLangTessEvaluation].append(
+ "float gl_CullDistance[];"
+ );
+ stageBuiltins[EShLangTessEvaluation].append(
+ "};"
+ "\n");
+
+ if (version >= 410)
+ stageBuiltins[EShLangTessEvaluation].append(
+ "out int gl_ViewportIndex;"
+ "out int gl_Layer;"
+ "\n");
+
+#ifdef NV_EXTENSIONS
+ if (version >= 450)
+ stageBuiltins[EShLangTessEvaluation].append(
+ "out int gl_ViewportMask[];" // GL_NV_viewport_array2
+ "out vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering
+ "out int gl_SecondaryViewportMaskNV[];" // GL_NV_stereo_view_rendering
+ "out vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes
+ "out int gl_ViewportMaskPerViewNV[];" // GL_NVX_multiview_per_view_attributes
+ );
+#endif
+
+ } else if (profile == EEsProfile && version >= 310) {
+ // Note: "in gl_PerVertex {...} gl_in[gl_MaxPatchVertices];" is declared in initialize() below,
+ // as it depends on the resource sizing of gl_MaxPatchVertices.
+
+ stageBuiltins[EShLangTessEvaluation].append(
+ "in highp int gl_PatchVerticesIn;"
+ "in highp int gl_PrimitiveID;"
+ "in highp vec3 gl_TessCoord;"
+
+ "patch in highp float gl_TessLevelOuter[4];"
+ "patch in highp float gl_TessLevelInner[2];"
+
+ "out gl_PerVertex {"
+ "highp vec4 gl_Position;"
+ "highp float gl_PointSize;"
+ );
+ stageBuiltins[EShLangTessEvaluation].append(
+ "};"
+ "\n");
+ }
+
+ if ((profile != EEsProfile && version >= 140) ||
+ (profile == EEsProfile && version >= 310)) {
+ stageBuiltins[EShLangTessEvaluation].append(
+ "in highp int gl_DeviceIndex;" // GL_EXT_device_group
+ "in highp int gl_ViewIndex;" // GL_EXT_multiview
+ "\n");
+ }
+
+ //============================================================================
+ //
+ // Define the interface to the fragment shader.
+ //
+ //============================================================================
+
+ if (profile != EEsProfile) {
+
+ stageBuiltins[EShLangFragment].append(
+ "vec4 gl_FragCoord;" // needs qualifier fixed later
+ "bool gl_FrontFacing;" // needs qualifier fixed later
+ "float gl_FragDepth;" // needs qualifier fixed later
+ );
+ if (version >= 120)
+ stageBuiltins[EShLangFragment].append(
+ "vec2 gl_PointCoord;" // needs qualifier fixed later
+ );
+ if (version >= 140)
+ stageBuiltins[EShLangFragment].append(
+ "out int gl_FragStencilRefARB;"
+ );
+ if (IncludeLegacy(version, profile, spvVersion) || (! ForwardCompatibility && version < 420))
+ stageBuiltins[EShLangFragment].append(
+ "vec4 gl_FragColor;" // needs qualifier fixed later
+ );
+
+ if (version < 130) {
+ stageBuiltins[EShLangFragment].append(
+ "varying vec4 gl_Color;"
+ "varying vec4 gl_SecondaryColor;"
+ "varying vec4 gl_TexCoord[];"
+ "varying float gl_FogFragCoord;"
+ );
+ } else {
+ stageBuiltins[EShLangFragment].append(
+ "in float gl_ClipDistance[];"
+ );
+
+ if (IncludeLegacy(version, profile, spvVersion)) {
+ if (version < 150)
+ stageBuiltins[EShLangFragment].append(
+ "in float gl_FogFragCoord;"
+ "in vec4 gl_TexCoord[];"
+ "in vec4 gl_Color;"
+ "in vec4 gl_SecondaryColor;"
+ );
+ else
+ stageBuiltins[EShLangFragment].append(
+ "in gl_PerFragment {"
+ "in float gl_FogFragCoord;"
+ "in vec4 gl_TexCoord[];"
+ "in vec4 gl_Color;"
+ "in vec4 gl_SecondaryColor;"
+ "};"
+ );
+ }
+ }
+
+ if (version >= 150)
+ stageBuiltins[EShLangFragment].append(
+ "flat in int gl_PrimitiveID;"
+ );
+
+ if (version >= 400) {
+ stageBuiltins[EShLangFragment].append(
+ "flat in int gl_SampleID;"
+ " in vec2 gl_SamplePosition;"
+ "flat in int gl_SampleMaskIn[];"
+ " out int gl_SampleMask[];"
+ );
+ if (spvVersion.spv == 0)
+ stageBuiltins[EShLangFragment].append(
+ "uniform int gl_NumSamples;"
+ );
+ }
+
+ if (version >= 430)
+ stageBuiltins[EShLangFragment].append(
+ "flat in int gl_Layer;"
+ "flat in int gl_ViewportIndex;"
+ );
+
+ if (version >= 450)
+ stageBuiltins[EShLangFragment].append(
+ "in float gl_CullDistance[];"
+ "bool gl_HelperInvocation;" // needs qualifier fixed later
+ );
+
+ if (version >= 450)
+ stageBuiltins[EShLangFragment].append( // GL_EXT_fragment_invocation_density
+ "flat in ivec2 gl_FragSizeEXT;"
+ "flat in int gl_FragInvocationCountEXT;"
+ );
+
+#ifdef AMD_EXTENSIONS
+ if (version >= 450)
+ stageBuiltins[EShLangFragment].append(
+ "in vec2 gl_BaryCoordNoPerspAMD;"
+ "in vec2 gl_BaryCoordNoPerspCentroidAMD;"
+ "in vec2 gl_BaryCoordNoPerspSampleAMD;"
+ "in vec2 gl_BaryCoordSmoothAMD;"
+ "in vec2 gl_BaryCoordSmoothCentroidAMD;"
+ "in vec2 gl_BaryCoordSmoothSampleAMD;"
+ "in vec3 gl_BaryCoordPullModelAMD;"
+ );
+#endif
+
+#ifdef NV_EXTENSIONS
+ if (version >= 430)
+ stageBuiltins[EShLangFragment].append(
+ "in bool gl_FragFullyCoveredNV;"
+ );
+ if (version >= 450)
+ stageBuiltins[EShLangFragment].append(
+ "flat in ivec2 gl_FragmentSizeNV;" // GL_NV_shading_rate_image
+ "flat in int gl_InvocationsPerPixelNV;"
+ "in vec3 gl_BaryCoordNV;" // GL_NV_fragment_shader_barycentric
+ "in vec3 gl_BaryCoordNoPerspNV;"
+ );
+
+#endif
+ } else {
+ // ES profile
+
+ if (version == 100) {
+ stageBuiltins[EShLangFragment].append(
+ "mediump vec4 gl_FragCoord;" // needs qualifier fixed later
+ " bool gl_FrontFacing;" // needs qualifier fixed later
+ "mediump vec4 gl_FragColor;" // needs qualifier fixed later
+ "mediump vec2 gl_PointCoord;" // needs qualifier fixed later
+ );
+ }
+ if (version >= 300) {
+ stageBuiltins[EShLangFragment].append(
+ "highp vec4 gl_FragCoord;" // needs qualifier fixed later
+ " bool gl_FrontFacing;" // needs qualifier fixed later
+ "mediump vec2 gl_PointCoord;" // needs qualifier fixed later
+ "highp float gl_FragDepth;" // needs qualifier fixed later
+ );
+ }
+ if (version >= 310) {
+ stageBuiltins[EShLangFragment].append(
+ "bool gl_HelperInvocation;" // needs qualifier fixed later
+ "flat in highp int gl_PrimitiveID;" // needs qualifier fixed later
+ "flat in highp int gl_Layer;" // needs qualifier fixed later
+ );
+
+ stageBuiltins[EShLangFragment].append( // GL_OES_sample_variables
+ "flat in lowp int gl_SampleID;"
+ " in mediump vec2 gl_SamplePosition;"
+ "flat in highp int gl_SampleMaskIn[];"
+ " out highp int gl_SampleMask[];"
+ );
+ if (spvVersion.spv == 0)
+ stageBuiltins[EShLangFragment].append( // GL_OES_sample_variables
+ "uniform lowp int gl_NumSamples;"
+ );
+ }
+ stageBuiltins[EShLangFragment].append(
+ "highp float gl_FragDepthEXT;" // GL_EXT_frag_depth
+ );
+
+ if (version >= 310)
+ stageBuiltins[EShLangFragment].append( // GL_EXT_fragment_invocation_density
+ "flat in ivec2 gl_FragSizeEXT;"
+ "flat in int gl_FragInvocationCountEXT;"
+ );
+#ifdef NV_EXTENSIONS
+ if (version >= 320)
+ stageBuiltins[EShLangFragment].append( // GL_NV_shading_rate_image
+ "flat in ivec2 gl_FragmentSizeNV;"
+ "flat in int gl_InvocationsPerPixelNV;"
+ );
+ if (version >= 320)
+ stageBuiltins[EShLangFragment].append(
+ "in vec3 gl_BaryCoordNV;"
+ "in vec3 gl_BaryCoordNoPerspNV;"
+ );
+#endif
+
+ }
+ stageBuiltins[EShLangFragment].append("\n");
+
+ if (version >= 130)
+ add2ndGenerationSamplingImaging(version, profile, spvVersion);
+
+ // GL_ARB_shader_ballot
+ if (profile != EEsProfile && version >= 450) {
+ const char* ballotDecls =
+ "uniform uint gl_SubGroupSizeARB;"
+ "in uint gl_SubGroupInvocationARB;"
+ "in uint64_t gl_SubGroupEqMaskARB;"
+ "in uint64_t gl_SubGroupGeMaskARB;"
+ "in uint64_t gl_SubGroupGtMaskARB;"
+ "in uint64_t gl_SubGroupLeMaskARB;"
+ "in uint64_t gl_SubGroupLtMaskARB;"
+ "\n";
+ const char* fragmentBallotDecls =
+ "uniform uint gl_SubGroupSizeARB;"
+ "flat in uint gl_SubGroupInvocationARB;"
+ "flat in uint64_t gl_SubGroupEqMaskARB;"
+ "flat in uint64_t gl_SubGroupGeMaskARB;"
+ "flat in uint64_t gl_SubGroupGtMaskARB;"
+ "flat in uint64_t gl_SubGroupLeMaskARB;"
+ "flat in uint64_t gl_SubGroupLtMaskARB;"
+ "\n";
+ stageBuiltins[EShLangVertex] .append(ballotDecls);
+ stageBuiltins[EShLangTessControl] .append(ballotDecls);
+ stageBuiltins[EShLangTessEvaluation].append(ballotDecls);
+ stageBuiltins[EShLangGeometry] .append(ballotDecls);
+ stageBuiltins[EShLangCompute] .append(ballotDecls);
+ stageBuiltins[EShLangFragment] .append(fragmentBallotDecls);
+#ifdef NV_EXTENSIONS
+ stageBuiltins[EShLangMeshNV] .append(ballotDecls);
+ stageBuiltins[EShLangTaskNV] .append(ballotDecls);
+#endif
+ }
+
+ if ((profile != EEsProfile && version >= 140) ||
+ (profile == EEsProfile && version >= 310)) {
+ stageBuiltins[EShLangFragment].append(
+ "flat in highp int gl_DeviceIndex;" // GL_EXT_device_group
+ "flat in highp int gl_ViewIndex;" // GL_EXT_multiview
+ "\n");
+ }
+
+ // GL_KHR_shader_subgroup
+ if ((profile == EEsProfile && version >= 310) ||
+ (profile != EEsProfile && version >= 140)) {
+ const char* ballotDecls =
+ "in mediump uint gl_SubgroupSize;"
+ "in mediump uint gl_SubgroupInvocationID;"
+ "in highp uvec4 gl_SubgroupEqMask;"
+ "in highp uvec4 gl_SubgroupGeMask;"
+ "in highp uvec4 gl_SubgroupGtMask;"
+ "in highp uvec4 gl_SubgroupLeMask;"
+ "in highp uvec4 gl_SubgroupLtMask;"
+ "\n";
+ const char* fragmentBallotDecls =
+ "flat in mediump uint gl_SubgroupSize;"
+ "flat in mediump uint gl_SubgroupInvocationID;"
+ "flat in highp uvec4 gl_SubgroupEqMask;"
+ "flat in highp uvec4 gl_SubgroupGeMask;"
+ "flat in highp uvec4 gl_SubgroupGtMask;"
+ "flat in highp uvec4 gl_SubgroupLeMask;"
+ "flat in highp uvec4 gl_SubgroupLtMask;"
+ "\n";
+ stageBuiltins[EShLangVertex] .append(ballotDecls);
+ stageBuiltins[EShLangTessControl] .append(ballotDecls);
+ stageBuiltins[EShLangTessEvaluation].append(ballotDecls);
+ stageBuiltins[EShLangGeometry] .append(ballotDecls);
+ stageBuiltins[EShLangCompute] .append(ballotDecls);
+ stageBuiltins[EShLangFragment] .append(fragmentBallotDecls);
+#ifdef NV_EXTENSIONS
+ stageBuiltins[EShLangMeshNV] .append(ballotDecls);
+ stageBuiltins[EShLangTaskNV] .append(ballotDecls);
+#endif
+
+ stageBuiltins[EShLangCompute].append(
+ "highp in uint gl_NumSubgroups;"
+ "highp in uint gl_SubgroupID;"
+ "\n");
+#ifdef NV_EXTENSIONS
+ stageBuiltins[EShLangMeshNV].append(
+ "highp in uint gl_NumSubgroups;"
+ "highp in uint gl_SubgroupID;"
+ "\n");
+ stageBuiltins[EShLangTaskNV].append(
+ "highp in uint gl_NumSubgroups;"
+ "highp in uint gl_SubgroupID;"
+ "\n");
+#endif
+ }
+
+#ifdef NV_EXTENSIONS
+ // GL_NV_ray_tracing
+ if (profile != EEsProfile && version >= 460) {
+
+ const char *constRayFlags =
+ "const uint gl_RayFlagsNoneNV = 0U;"
+ "const uint gl_RayFlagsOpaqueNV = 1U;"
+ "const uint gl_RayFlagsNoOpaqueNV = 2U;"
+ "const uint gl_RayFlagsTerminateOnFirstHitNV = 4U;"
+ "const uint gl_RayFlagsSkipClosestHitShaderNV = 8U;"
+ "const uint gl_RayFlagsCullBackFacingTrianglesNV = 16U;"
+ "const uint gl_RayFlagsCullFrontFacingTrianglesNV = 32U;"
+ "const uint gl_RayFlagsCullOpaqueNV = 64U;"
+ "const uint gl_RayFlagsCullNoOpaqueNV = 128U;"
+ "\n";
+ const char *rayGenDecls =
+ "in uvec3 gl_LaunchIDNV;"
+ "in uvec3 gl_LaunchSizeNV;"
+ "\n";
+ const char *intersectDecls =
+ "in uvec3 gl_LaunchIDNV;"
+ "in uvec3 gl_LaunchSizeNV;"
+ "in int gl_PrimitiveID;"
+ "in int gl_InstanceID;"
+ "in int gl_InstanceCustomIndexNV;"
+ "in vec3 gl_WorldRayOriginNV;"
+ "in vec3 gl_WorldRayDirectionNV;"
+ "in vec3 gl_ObjectRayOriginNV;"
+ "in vec3 gl_ObjectRayDirectionNV;"
+ "in float gl_RayTminNV;"
+ "in float gl_RayTmaxNV;"
+ "in mat4x3 gl_ObjectToWorldNV;"
+ "in mat4x3 gl_WorldToObjectNV;"
+ "in uint gl_IncomingRayFlagsNV;"
+ "\n";
+ const char *hitDecls =
+ "in uvec3 gl_LaunchIDNV;"
+ "in uvec3 gl_LaunchSizeNV;"
+ "in int gl_PrimitiveID;"
+ "in int gl_InstanceID;"
+ "in int gl_InstanceCustomIndexNV;"
+ "in vec3 gl_WorldRayOriginNV;"
+ "in vec3 gl_WorldRayDirectionNV;"
+ "in vec3 gl_ObjectRayOriginNV;"
+ "in vec3 gl_ObjectRayDirectionNV;"
+ "in float gl_RayTminNV;"
+ "in float gl_RayTmaxNV;"
+ "in float gl_HitTNV;"
+ "in uint gl_HitKindNV;"
+ "in mat4x3 gl_ObjectToWorldNV;"
+ "in mat4x3 gl_WorldToObjectNV;"
+ "in uint gl_IncomingRayFlagsNV;"
+ "\n";
+ const char *missDecls =
+ "in uvec3 gl_LaunchIDNV;"
+ "in uvec3 gl_LaunchSizeNV;"
+ "in vec3 gl_WorldRayOriginNV;"
+ "in vec3 gl_WorldRayDirectionNV;"
+ "in vec3 gl_ObjectRayOriginNV;"
+ "in vec3 gl_ObjectRayDirectionNV;"
+ "in float gl_RayTminNV;"
+ "in float gl_RayTmaxNV;"
+ "in uint gl_IncomingRayFlagsNV;"
+ "\n";
+
+ const char *callableDecls =
+ "in uvec3 gl_LaunchIDNV;"
+ "in uvec3 gl_LaunchSizeNV;"
+ "\n";
+
+ stageBuiltins[EShLangRayGenNV].append(rayGenDecls);
+ stageBuiltins[EShLangRayGenNV].append(constRayFlags);
+
+ stageBuiltins[EShLangIntersectNV].append(intersectDecls);
+ stageBuiltins[EShLangIntersectNV].append(constRayFlags);
+
+ stageBuiltins[EShLangAnyHitNV].append(hitDecls);
+ stageBuiltins[EShLangAnyHitNV].append(constRayFlags);
+
+ stageBuiltins[EShLangClosestHitNV].append(hitDecls);
+ stageBuiltins[EShLangClosestHitNV].append(constRayFlags);
+
+ stageBuiltins[EShLangMissNV].append(missDecls);
+ stageBuiltins[EShLangMissNV].append(constRayFlags);
+
+ stageBuiltins[EShLangCallableNV].append(callableDecls);
+ stageBuiltins[EShLangCallableNV].append(constRayFlags);
+
+ }
+ if ((profile != EEsProfile && version >= 140)) {
+ const char *deviceIndex =
+ "in highp int gl_DeviceIndex;" // GL_EXT_device_group
+ "\n";
+
+ stageBuiltins[EShLangRayGenNV].append(deviceIndex);
+ stageBuiltins[EShLangIntersectNV].append(deviceIndex);
+ stageBuiltins[EShLangAnyHitNV].append(deviceIndex);
+ stageBuiltins[EShLangClosestHitNV].append(deviceIndex);
+ stageBuiltins[EShLangMissNV].append(deviceIndex);
+ }
+#endif
+
+ if (version >= 300 /* both ES and non-ES */) {
+ stageBuiltins[EShLangFragment].append(
+ "flat in highp uint gl_ViewID_OVR;" // GL_OVR_multiview, GL_OVR_multiview2
+ "\n");
+ }
+
+ if ((profile != EEsProfile && version >= 420) ||
+ (profile == EEsProfile && version >= 310)) {
+ commonBuiltins.append("const int gl_ScopeDevice = 1;\n");
+ commonBuiltins.append("const int gl_ScopeWorkgroup = 2;\n");
+ commonBuiltins.append("const int gl_ScopeSubgroup = 3;\n");
+ commonBuiltins.append("const int gl_ScopeInvocation = 4;\n");
+ commonBuiltins.append("const int gl_ScopeQueueFamily = 5;\n");
+
+ commonBuiltins.append("const int gl_SemanticsRelaxed = 0x0;\n");
+ commonBuiltins.append("const int gl_SemanticsAcquire = 0x2;\n");
+ commonBuiltins.append("const int gl_SemanticsRelease = 0x4;\n");
+ commonBuiltins.append("const int gl_SemanticsAcquireRelease = 0x8;\n");
+ commonBuiltins.append("const int gl_SemanticsMakeAvailable = 0x2000;\n");
+ commonBuiltins.append("const int gl_SemanticsMakeVisible = 0x4000;\n");
+
+ commonBuiltins.append("const int gl_StorageSemanticsNone = 0x0;\n");
+ commonBuiltins.append("const int gl_StorageSemanticsBuffer = 0x40;\n");
+ commonBuiltins.append("const int gl_StorageSemanticsShared = 0x100;\n");
+ commonBuiltins.append("const int gl_StorageSemanticsImage = 0x800;\n");
+ commonBuiltins.append("const int gl_StorageSemanticsOutput = 0x1000;\n");
+ }
+
+ // printf("%s\n", commonBuiltins.c_str());
+ // printf("%s\n", stageBuiltins[EShLangFragment].c_str());
+}
+
+//
+// Helper function for initialize(), to add the second set of names for texturing,
+// when adding context-independent built-in functions.
+//
+void TBuiltIns::add2ndGenerationSamplingImaging(int version, EProfile profile, const SpvVersion& spvVersion)
+{
+ //
+ // In this function proper, enumerate the types, then calls the next set of functions
+ // to enumerate all the uses for that type.
+ //
+#ifdef AMD_EXTENSIONS
+ TBasicType bTypes[4] = { EbtFloat, EbtFloat16, EbtInt, EbtUint };
+#else
+ TBasicType bTypes[3] = { EbtFloat, EbtInt, EbtUint };
+#endif
+ bool skipBuffer = (profile == EEsProfile && version < 310) || (profile != EEsProfile && version < 140);
+ bool skipCubeArrayed = (profile == EEsProfile && version < 310) || (profile != EEsProfile && version < 130);
+
+ // enumerate all the types
+ for (int image = 0; image <= 1; ++image) { // loop over "bool" image vs sampler
+
+ for (int shadow = 0; shadow <= 1; ++shadow) { // loop over "bool" shadow or not
+ for (int ms = 0; ms <=1; ++ms) {
+ if ((ms || image) && shadow)
+ continue;
+ if (ms && profile != EEsProfile && version < 150)
+ continue;
+ if (ms && image && profile == EEsProfile)
+ continue;
+ if (ms && profile == EEsProfile && version < 310)
+ continue;
+
+ for (int arrayed = 0; arrayed <= 1; ++arrayed) { // loop over "bool" arrayed or not
+ for (int dim = Esd1D; dim < EsdNumDims; ++dim) { // 1D, 2D, ..., buffer
+ if (dim == EsdSubpass && spvVersion.vulkan == 0)
+ continue;
+ if (dim == EsdSubpass && (image || shadow || arrayed))
+ continue;
+ if ((dim == Esd1D || dim == EsdRect) && profile == EEsProfile)
+ continue;
+ if (dim != Esd2D && dim != EsdSubpass && ms)
+ continue;
+ if ((dim == Esd3D || dim == EsdRect) && arrayed)
+ continue;
+ if (dim == Esd3D && shadow)
+ continue;
+ if (dim == EsdCube && arrayed && skipCubeArrayed)
+ continue;
+ if (dim == EsdBuffer && skipBuffer)
+ continue;
+ if (dim == EsdBuffer && (shadow || arrayed || ms))
+ continue;
+ if (ms && arrayed && profile == EEsProfile && version < 310)
+ continue;
+#ifdef AMD_EXTENSIONS
+ for (int bType = 0; bType < 4; ++bType) { // float, float16, int, uint results
+
+ if (shadow && bType > 1)
+ continue;
+
+ if (bTypes[bType] == EbtFloat16 && (profile == EEsProfile ||version < 450))
+ continue;
+#else
+ for (int bType = 0; bType < 3; ++bType) { // float, int, uint results
+
+ if (shadow && bType > 0)
+ continue;
+#endif
+ if (dim == EsdRect && version < 140 && bType > 0)
+ continue;
+
+ //
+ // Now, make all the function prototypes for the type we just built...
+ //
+
+ TSampler sampler;
+ if (dim == EsdSubpass) {
+ sampler.setSubpass(bTypes[bType], ms ? true : false);
+ } else if (image) {
+ sampler.setImage(bTypes[bType], (TSamplerDim)dim, arrayed ? true : false,
+ shadow ? true : false,
+ ms ? true : false);
+ } else {
+ sampler.set(bTypes[bType], (TSamplerDim)dim, arrayed ? true : false,
+ shadow ? true : false,
+ ms ? true : false);
+ }
+
+ TString typeName = sampler.getString();
+
+ if (dim == EsdSubpass) {
+ addSubpassSampling(sampler, typeName, version, profile);
+ continue;
+ }
+
+ addQueryFunctions(sampler, typeName, version, profile);
+
+ if (image)
+ addImageFunctions(sampler, typeName, version, profile);
+ else {
+ addSamplingFunctions(sampler, typeName, version, profile);
+ addGatherFunctions(sampler, typeName, version, profile);
+
+ if (spvVersion.vulkan > 0 && sampler.isCombined() && !sampler.shadow) {
+ // Base Vulkan allows texelFetch() for
+ // textureBuffer (i.e. without sampler).
+ //
+ // GL_EXT_samplerless_texture_functions
+ // allows texelFetch() and query functions
+ // (other than textureQueryLod()) for all
+ // texture types.
+ sampler.setTexture(sampler.type, sampler.dim, sampler.arrayed, sampler.shadow,
+ sampler.ms);
+ TString textureTypeName = sampler.getString();
+ addSamplingFunctions(sampler, textureTypeName, version, profile);
+ addQueryFunctions(sampler, textureTypeName, version, profile);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ //
+ // sparseTexelsResidentARB()
+ //
+
+ if (profile != EEsProfile && version >= 450) {
+ commonBuiltins.append("bool sparseTexelsResidentARB(int code);\n");
+ }
+}
+
+//
+// Helper function for add2ndGenerationSamplingImaging(),
+// when adding context-independent built-in functions.
+//
+// Add all the query functions for the given type.
+//
+void TBuiltIns::addQueryFunctions(TSampler sampler, const TString& typeName, int version, EProfile profile)
+{
+ if (sampler.image && ((profile == EEsProfile && version < 310) || (profile != EEsProfile && version < 430)))
+ return;
+
+ //
+ // textureSize() and imageSize()
+ //
+
+ int sizeDims = dimMap[sampler.dim] + (sampler.arrayed ? 1 : 0) - (sampler.dim == EsdCube ? 1 : 0);
+ if (profile == EEsProfile)
+ commonBuiltins.append("highp ");
+ if (sizeDims == 1)
+ commonBuiltins.append("int");
+ else {
+ commonBuiltins.append("ivec");
+ commonBuiltins.append(postfixes[sizeDims]);
+ }
+ if (sampler.image)
+ commonBuiltins.append(" imageSize(readonly writeonly volatile coherent ");
+ else
+ commonBuiltins.append(" textureSize(");
+ commonBuiltins.append(typeName);
+ if (! sampler.image && sampler.dim != EsdRect && sampler.dim != EsdBuffer && ! sampler.ms)
+ commonBuiltins.append(",int);\n");
+ else
+ commonBuiltins.append(");\n");
+
+ //
+ // textureSamples() and imageSamples()
+ //
+
+ // GL_ARB_shader_texture_image_samples
+ // TODO: spec issue? there are no memory qualifiers; how to query a writeonly/readonly image, etc?
+ if (profile != EEsProfile && version >= 430 && sampler.ms) {
+ commonBuiltins.append("int ");
+ if (sampler.image)
+ commonBuiltins.append("imageSamples(readonly writeonly volatile coherent ");
+ else
+ commonBuiltins.append("textureSamples(");
+ commonBuiltins.append(typeName);
+ commonBuiltins.append(");\n");
+ }
+
+ //
+ // textureQueryLod(), fragment stage only
+ //
+
+ if (profile != EEsProfile && version >= 400 && sampler.combined && sampler.dim != EsdRect && ! sampler.ms && sampler.dim != EsdBuffer) {
+#ifdef AMD_EXTENSIONS
+ for (int f16TexAddr = 0; f16TexAddr < 2; ++f16TexAddr) {
+ if (f16TexAddr && sampler.type != EbtFloat16)
+ continue;
+#endif
+ stageBuiltins[EShLangFragment].append("vec2 textureQueryLod(");
+ stageBuiltins[EShLangFragment].append(typeName);
+ if (dimMap[sampler.dim] == 1)
+#ifdef AMD_EXTENSIONS
+ if (f16TexAddr)
+ stageBuiltins[EShLangFragment].append(", float16_t");
+ else
+ stageBuiltins[EShLangFragment].append(", float");
+#else
+ stageBuiltins[EShLangFragment].append(", float");
+#endif
+ else {
+#ifdef AMD_EXTENSIONS
+ if (f16TexAddr)
+ stageBuiltins[EShLangFragment].append(", f16vec");
+ else
+ stageBuiltins[EShLangFragment].append(", vec");
+#else
+ stageBuiltins[EShLangFragment].append(", vec");
+#endif
+ stageBuiltins[EShLangFragment].append(postfixes[dimMap[sampler.dim]]);
+ }
+ stageBuiltins[EShLangFragment].append(");\n");
+#ifdef AMD_EXTENSIONS
+ }
+#endif
+
+#ifdef NV_EXTENSIONS
+ stageBuiltins[EShLangCompute].append("vec2 textureQueryLod(");
+ stageBuiltins[EShLangCompute].append(typeName);
+ if (dimMap[sampler.dim] == 1)
+ stageBuiltins[EShLangCompute].append(", float");
+ else {
+ stageBuiltins[EShLangCompute].append(", vec");
+ stageBuiltins[EShLangCompute].append(postfixes[dimMap[sampler.dim]]);
+ }
+ stageBuiltins[EShLangCompute].append(");\n");
+#endif
+ }
+
+ //
+ // textureQueryLevels()
+ //
+
+ if (profile != EEsProfile && version >= 430 && ! sampler.image && sampler.dim != EsdRect && ! sampler.ms && sampler.dim != EsdBuffer) {
+ commonBuiltins.append("int textureQueryLevels(");
+ commonBuiltins.append(typeName);
+ commonBuiltins.append(");\n");
+ }
+}
+
+//
+// Helper function for add2ndGenerationSamplingImaging(),
+// when adding context-independent built-in functions.
+//
+// Add all the image access functions for the given type.
+//
+void TBuiltIns::addImageFunctions(TSampler sampler, const TString& typeName, int version, EProfile profile)
+{
+ int dims = dimMap[sampler.dim];
+ // most things with an array add a dimension, except for cubemaps
+ if (sampler.arrayed && sampler.dim != EsdCube)
+ ++dims;
+
+ TString imageParams = typeName;
+ if (dims == 1)
+ imageParams.append(", int");
+ else {
+ imageParams.append(", ivec");
+ imageParams.append(postfixes[dims]);
+ }
+ if (sampler.ms)
+ imageParams.append(", int");
+
+ if (profile == EEsProfile)
+ commonBuiltins.append("highp ");
+ commonBuiltins.append(prefixes[sampler.type]);
+ commonBuiltins.append("vec4 imageLoad(readonly volatile coherent ");
+ commonBuiltins.append(imageParams);
+ commonBuiltins.append(");\n");
+
+ commonBuiltins.append("void imageStore(writeonly volatile coherent ");
+ commonBuiltins.append(imageParams);
+ commonBuiltins.append(", ");
+ commonBuiltins.append(prefixes[sampler.type]);
+ commonBuiltins.append("vec4);\n");
+
+ if (sampler.dim != Esd1D && sampler.dim != EsdBuffer && profile != EEsProfile && version >= 450) {
+ commonBuiltins.append("int sparseImageLoadARB(readonly volatile coherent ");
+ commonBuiltins.append(imageParams);
+ commonBuiltins.append(", out ");
+ commonBuiltins.append(prefixes[sampler.type]);
+ commonBuiltins.append("vec4");
+ commonBuiltins.append(");\n");
+ }
+
+ if ( profile != EEsProfile ||
+ (profile == EEsProfile && version >= 310)) {
+ if (sampler.type == EbtInt || sampler.type == EbtUint) {
+ const char* dataType = sampler.type == EbtInt ? "highp int" : "highp uint";
+
+ const int numBuiltins = 7;
+
+ static const char* atomicFunc[numBuiltins] = {
+ " imageAtomicAdd(volatile coherent ",
+ " imageAtomicMin(volatile coherent ",
+ " imageAtomicMax(volatile coherent ",
+ " imageAtomicAnd(volatile coherent ",
+ " imageAtomicOr(volatile coherent ",
+ " imageAtomicXor(volatile coherent ",
+ " imageAtomicExchange(volatile coherent "
+ };
+
+ // Loop twice to add prototypes with/without scope/semantics
+ for (int j = 0; j < 2; ++j) {
+ for (size_t i = 0; i < numBuiltins; ++i) {
+ commonBuiltins.append(dataType);
+ commonBuiltins.append(atomicFunc[i]);
+ commonBuiltins.append(imageParams);
+ commonBuiltins.append(", ");
+ commonBuiltins.append(dataType);
+ if (j == 1) {
+ commonBuiltins.append(", int, int, int");
+ }
+ commonBuiltins.append(");\n");
+ }
+
+ commonBuiltins.append(dataType);
+ commonBuiltins.append(" imageAtomicCompSwap(volatile coherent ");
+ commonBuiltins.append(imageParams);
+ commonBuiltins.append(", ");
+ commonBuiltins.append(dataType);
+ commonBuiltins.append(", ");
+ commonBuiltins.append(dataType);
+ if (j == 1) {
+ commonBuiltins.append(", int, int, int, int, int");
+ }
+ commonBuiltins.append(");\n");
+ }
+
+ commonBuiltins.append(dataType);
+ commonBuiltins.append(" imageAtomicLoad(volatile coherent ");
+ commonBuiltins.append(imageParams);
+ commonBuiltins.append(", int, int, int);\n");
+
+ commonBuiltins.append("void imageAtomicStore(volatile coherent ");
+ commonBuiltins.append(imageParams);
+ commonBuiltins.append(", ");
+ commonBuiltins.append(dataType);
+ commonBuiltins.append(", int, int, int);\n");
+
+ } else {
+ // not int or uint
+ // GL_ARB_ES3_1_compatibility
+ // TODO: spec issue: are there restrictions on the kind of layout() that can be used? what about dropping memory qualifiers?
+ if ((profile != EEsProfile && version >= 450) ||
+ (profile == EEsProfile && version >= 310)) {
+ commonBuiltins.append("float imageAtomicExchange(volatile coherent ");
+ commonBuiltins.append(imageParams);
+ commonBuiltins.append(", float);\n");
+ }
+ }
+ }
+
+#ifdef AMD_EXTENSIONS
+ if (sampler.dim == EsdRect || sampler.dim == EsdBuffer || sampler.shadow || sampler.ms)
+ return;
+
+ if (profile == EEsProfile || version < 450)
+ return;
+
+ TString imageLodParams = typeName;
+ if (dims == 1)
+ imageLodParams.append(", int");
+ else {
+ imageLodParams.append(", ivec");
+ imageLodParams.append(postfixes[dims]);
+ }
+ imageLodParams.append(", int");
+
+ commonBuiltins.append(prefixes[sampler.type]);
+ commonBuiltins.append("vec4 imageLoadLodAMD(readonly volatile coherent ");
+ commonBuiltins.append(imageLodParams);
+ commonBuiltins.append(");\n");
+
+ commonBuiltins.append("void imageStoreLodAMD(writeonly volatile coherent ");
+ commonBuiltins.append(imageLodParams);
+ commonBuiltins.append(", ");
+ commonBuiltins.append(prefixes[sampler.type]);
+ commonBuiltins.append("vec4);\n");
+
+ if (sampler.dim != Esd1D) {
+ commonBuiltins.append("int sparseImageLoadLodAMD(readonly volatile coherent ");
+ commonBuiltins.append(imageLodParams);
+ commonBuiltins.append(", out ");
+ commonBuiltins.append(prefixes[sampler.type]);
+ commonBuiltins.append("vec4");
+ commonBuiltins.append(");\n");
+ }
+#endif
+}
+
+//
+// Helper function for initialize(),
+// when adding context-independent built-in functions.
+//
+// Add all the subpass access functions for the given type.
+//
+void TBuiltIns::addSubpassSampling(TSampler sampler, const TString& typeName, int /*version*/, EProfile /*profile*/)
+{
+ stageBuiltins[EShLangFragment].append(prefixes[sampler.type]);
+ stageBuiltins[EShLangFragment].append("vec4 subpassLoad");
+ stageBuiltins[EShLangFragment].append("(");
+ stageBuiltins[EShLangFragment].append(typeName.c_str());
+ if (sampler.ms)
+ stageBuiltins[EShLangFragment].append(", int");
+ stageBuiltins[EShLangFragment].append(");\n");
+}
+
+//
+// Helper function for add2ndGenerationSamplingImaging(),
+// when adding context-independent built-in functions.
+//
+// Add all the texture lookup functions for the given type.
+//
+void TBuiltIns::addSamplingFunctions(TSampler sampler, const TString& typeName, int version, EProfile profile)
+{
+ //
+ // texturing
+ //
+ for (int proj = 0; proj <= 1; ++proj) { // loop over "bool" projective or not
+
+ if (proj && (sampler.dim == EsdCube || sampler.dim == EsdBuffer || sampler.arrayed || sampler.ms || !sampler.combined))
+ continue;
+
+ for (int lod = 0; lod <= 1; ++lod) {
+
+ if (lod && (sampler.dim == EsdBuffer || sampler.dim == EsdRect || sampler.ms || !sampler.combined))
+ continue;
+ if (lod && sampler.dim == Esd2D && sampler.arrayed && sampler.shadow)
+ continue;
+ if (lod && sampler.dim == EsdCube && sampler.shadow)
+ continue;
+
+ for (int bias = 0; bias <= 1; ++bias) {
+
+ if (bias && (lod || sampler.ms || !sampler.combined))
+ continue;
+ if (bias && (sampler.dim == Esd2D || sampler.dim == EsdCube) && sampler.shadow && sampler.arrayed)
+ continue;
+ if (bias && (sampler.dim == EsdRect || sampler.dim == EsdBuffer))
+ continue;
+
+ for (int offset = 0; offset <= 1; ++offset) { // loop over "bool" offset or not
+
+ if (proj + offset + bias + lod > 3)
+ continue;
+ if (offset && (sampler.dim == EsdCube || sampler.dim == EsdBuffer || sampler.ms))
+ continue;
+
+ for (int fetch = 0; fetch <= 1; ++fetch) { // loop over "bool" fetch or not
+
+ if (proj + offset + fetch + bias + lod > 3)
+ continue;
+ if (fetch && (lod || bias))
+ continue;
+ if (fetch && (sampler.shadow || sampler.dim == EsdCube))
+ continue;
+ if (fetch == 0 && (sampler.ms || sampler.dim == EsdBuffer || !sampler.combined))
+ continue;
+
+ for (int grad = 0; grad <= 1; ++grad) { // loop over "bool" grad or not
+
+ if (grad && (lod || bias || sampler.ms || !sampler.combined))
+ continue;
+ if (grad && sampler.dim == EsdBuffer)
+ continue;
+ if (proj + offset + fetch + grad + bias + lod > 3)
+ continue;
+
+ for (int extraProj = 0; extraProj <= 1; ++extraProj) {
+ bool compare = false;
+ int totalDims = dimMap[sampler.dim] + (sampler.arrayed ? 1 : 0);
+ // skip dummy unused second component for 1D non-array shadows
+ if (sampler.shadow && totalDims < 2)
+ totalDims = 2;
+ totalDims += (sampler.shadow ? 1 : 0) + proj;
+ if (totalDims > 4 && sampler.shadow) {
+ compare = true;
+ totalDims = 4;
+ }
+ assert(totalDims <= 4);
+
+ if (extraProj && ! proj)
+ continue;
+ if (extraProj && (sampler.dim == Esd3D || sampler.shadow || !sampler.combined))
+ continue;
+#ifdef AMD_EXTENSIONS
+ for (int f16TexAddr = 0; f16TexAddr <= 1; ++f16TexAddr) { // loop over 16-bit floating-point texel addressing
+
+ if (f16TexAddr && sampler.type != EbtFloat16)
+ continue;
+ if (f16TexAddr && sampler.shadow && ! compare) {
+ compare = true; // compare argument is always present
+ totalDims--;
+ }
+#endif
+ for (int lodClamp = 0; lodClamp <= 1 ;++lodClamp) { // loop over "bool" lod clamp
+
+ if (lodClamp && (profile == EEsProfile || version < 450))
+ continue;
+ if (lodClamp && (proj || lod || fetch))
+ continue;
+
+ for (int sparse = 0; sparse <= 1; ++sparse) { // loop over "bool" sparse or not
+
+ if (sparse && (profile == EEsProfile || version < 450))
+ continue;
+ // Sparse sampling is not for 1D/1D array texture, buffer texture, and projective texture
+ if (sparse && (sampler.dim == Esd1D || sampler.dim == EsdBuffer || proj))
+ continue;
+
+ TString s;
+
+ // return type
+ if (sparse)
+ s.append("int ");
+ else {
+ if (sampler.shadow)
+#ifdef AMD_EXTENSIONS
+ if (sampler.type == EbtFloat16)
+ s.append("float16_t ");
+ else
+ s.append("float ");
+#else
+ s.append("float ");
+#endif
+ else {
+ s.append(prefixes[sampler.type]);
+ s.append("vec4 ");
+ }
+ }
+
+ // name
+ if (sparse) {
+ if (fetch)
+ s.append("sparseTexel");
+ else
+ s.append("sparseTexture");
+ }
+ else {
+ if (fetch)
+ s.append("texel");
+ else
+ s.append("texture");
+ }
+ if (proj)
+ s.append("Proj");
+ if (lod)
+ s.append("Lod");
+ if (grad)
+ s.append("Grad");
+ if (fetch)
+ s.append("Fetch");
+ if (offset)
+ s.append("Offset");
+ if (lodClamp)
+ s.append("Clamp");
+ if (lodClamp || sparse)
+ s.append("ARB");
+ s.append("(");
+
+ // sampler type
+ s.append(typeName);
+#ifdef AMD_EXTENSIONS
+ // P coordinate
+ if (extraProj) {
+ if (f16TexAddr)
+ s.append(",f16vec4");
+ else
+ s.append(",vec4");
+ } else {
+ s.append(",");
+ TBasicType t = fetch ? EbtInt : (f16TexAddr ? EbtFloat16 : EbtFloat);
+ if (totalDims == 1)
+ s.append(TType::getBasicString(t));
+ else {
+ s.append(prefixes[t]);
+ s.append("vec");
+ s.append(postfixes[totalDims]);
+ }
+ }
+#else
+ // P coordinate
+ if (extraProj)
+ s.append(",vec4");
+ else {
+ s.append(",");
+ TBasicType t = fetch ? EbtInt : EbtFloat;
+ if (totalDims == 1)
+ s.append(TType::getBasicString(t));
+ else {
+ s.append(prefixes[t]);
+ s.append("vec");
+ s.append(postfixes[totalDims]);
+ }
+ }
+#endif
+ // non-optional compare
+ if (compare)
+ s.append(",float");
+
+ // non-optional lod argument (lod that's not driven by lod loop) or sample
+ if ((fetch && sampler.dim != EsdBuffer && sampler.dim != EsdRect && !sampler.ms) ||
+ (sampler.ms && fetch))
+ s.append(",int");
+#ifdef AMD_EXTENSIONS
+ // non-optional lod
+ if (lod) {
+ if (f16TexAddr)
+ s.append(",float16_t");
+ else
+ s.append(",float");
+ }
+
+ // gradient arguments
+ if (grad) {
+ if (dimMap[sampler.dim] == 1) {
+ if (f16TexAddr)
+ s.append(",float16_t,float16_t");
+ else
+ s.append(",float,float");
+ } else {
+ if (f16TexAddr)
+ s.append(",f16vec");
+ else
+ s.append(",vec");
+ s.append(postfixes[dimMap[sampler.dim]]);
+ if (f16TexAddr)
+ s.append(",f16vec");
+ else
+ s.append(",vec");
+ s.append(postfixes[dimMap[sampler.dim]]);
+ }
+ }
+#else
+ // non-optional lod
+ if (lod)
+ s.append(",float");
+
+ // gradient arguments
+ if (grad) {
+ if (dimMap[sampler.dim] == 1)
+ s.append(",float,float");
+ else {
+ s.append(",vec");
+ s.append(postfixes[dimMap[sampler.dim]]);
+ s.append(",vec");
+ s.append(postfixes[dimMap[sampler.dim]]);
+ }
+ }
+#endif
+ // offset
+ if (offset) {
+ if (dimMap[sampler.dim] == 1)
+ s.append(",int");
+ else {
+ s.append(",ivec");
+ s.append(postfixes[dimMap[sampler.dim]]);
+ }
+ }
+
+#ifdef AMD_EXTENSIONS
+ // lod clamp
+ if (lodClamp) {
+ if (f16TexAddr)
+ s.append(",float16_t");
+ else
+ s.append(",float");
+ }
+#else
+ // lod clamp
+ if (lodClamp)
+ s.append(",float");
+#endif
+ // texel out (for sparse texture)
+ if (sparse) {
+ s.append(",out ");
+ if (sampler.shadow)
+#ifdef AMD_EXTENSIONS
+ if (sampler.type == EbtFloat16)
+ s.append("float16_t");
+ else
+ s.append("float");
+#else
+ s.append("float");
+#endif
+ else {
+ s.append(prefixes[sampler.type]);
+ s.append("vec4");
+ }
+ }
+#ifdef AMD_EXTENSIONS
+ // optional bias
+ if (bias) {
+ if (f16TexAddr)
+ s.append(",float16_t");
+ else
+ s.append(",float");
+ }
+#else
+ // optional bias
+ if (bias)
+ s.append(",float");
+#endif
+ s.append(");\n");
+
+ // Add to the per-language set of built-ins
+ if (bias || lodClamp) {
+ stageBuiltins[EShLangFragment].append(s);
+#ifdef NV_EXTENSIONS
+ stageBuiltins[EShLangCompute].append(s);
+#endif
+ } else
+ commonBuiltins.append(s);
+
+ }
+ }
+#ifdef AMD_EXTENSIONS
+ }
+#endif
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//
+// Helper function for add2ndGenerationSamplingImaging(),
+// when adding context-independent built-in functions.
+//
+// Add all the texture gather functions for the given type.
+//
+void TBuiltIns::addGatherFunctions(TSampler sampler, const TString& typeName, int version, EProfile profile)
+{
+ switch (sampler.dim) {
+ case Esd2D:
+ case EsdRect:
+ case EsdCube:
+ break;
+ default:
+ return;
+ }
+
+ if (sampler.ms)
+ return;
+
+ if (version < 140 && sampler.dim == EsdRect && sampler.type != EbtFloat)
+ return;
+
+#ifdef AMD_EXTENSIONS
+ for (int f16TexAddr = 0; f16TexAddr <= 1; ++f16TexAddr) { // loop over 16-bit floating-point texel addressing
+
+ if (f16TexAddr && sampler.type != EbtFloat16)
+ continue;
+#endif
+ for (int offset = 0; offset < 3; ++offset) { // loop over three forms of offset in the call name: none, Offset, and Offsets
+
+ for (int comp = 0; comp < 2; ++comp) { // loop over presence of comp argument
+
+ if (comp > 0 && sampler.shadow)
+ continue;
+
+ if (offset > 0 && sampler.dim == EsdCube)
+ continue;
+
+ for (int sparse = 0; sparse <= 1; ++sparse) { // loop over "bool" sparse or not
+ if (sparse && (profile == EEsProfile || version < 450))
+ continue;
+
+ TString s;
+
+ // return type
+ if (sparse)
+ s.append("int ");
+ else {
+ s.append(prefixes[sampler.type]);
+ s.append("vec4 ");
+ }
+
+ // name
+ if (sparse)
+ s.append("sparseTextureGather");
+ else
+ s.append("textureGather");
+ switch (offset) {
+ case 1:
+ s.append("Offset");
+ break;
+ case 2:
+ s.append("Offsets");
+ break;
+ default:
+ break;
+ }
+ if (sparse)
+ s.append("ARB");
+ s.append("(");
+
+ // sampler type argument
+ s.append(typeName);
+
+ // P coordinate argument
+#ifdef AMD_EXTENSIONS
+ if (f16TexAddr)
+ s.append(",f16vec");
+ else
+ s.append(",vec");
+#else
+ s.append(",vec");
+#endif
+ int totalDims = dimMap[sampler.dim] + (sampler.arrayed ? 1 : 0);
+ s.append(postfixes[totalDims]);
+
+ // refZ argument
+ if (sampler.shadow)
+ s.append(",float");
+
+ // offset argument
+ if (offset > 0) {
+ s.append(",ivec2");
+ if (offset == 2)
+ s.append("[4]");
+ }
+
+ // texel out (for sparse texture)
+ if (sparse) {
+ s.append(",out ");
+ s.append(prefixes[sampler.type]);
+ s.append("vec4 ");
+ }
+
+ // comp argument
+ if (comp)
+ s.append(",int");
+
+ s.append(");\n");
+ commonBuiltins.append(s);
+#ifdef AMD_EXTENSIONS
+ }
+#endif
+ }
+ }
+ }
+
+#ifdef AMD_EXTENSIONS
+ if (sampler.dim == EsdRect || sampler.shadow)
+ return;
+
+ if (profile == EEsProfile || version < 450)
+ return;
+
+ for (int bias = 0; bias < 2; ++bias) { // loop over presence of bias argument
+
+ for (int lod = 0; lod < 2; ++lod) { // loop over presence of lod argument
+
+ if ((lod && bias) || (lod == 0 && bias == 0))
+ continue;
+
+ for (int f16TexAddr = 0; f16TexAddr <= 1; ++f16TexAddr) { // loop over 16-bit floating-point texel addressing
+
+ if (f16TexAddr && sampler.type != EbtFloat16)
+ continue;
+
+ for (int offset = 0; offset < 3; ++offset) { // loop over three forms of offset in the call name: none, Offset, and Offsets
+
+ for (int comp = 0; comp < 2; ++comp) { // loop over presence of comp argument
+
+ if (comp == 0 && bias)
+ continue;
+
+ if (offset > 0 && sampler.dim == EsdCube)
+ continue;
+
+ for (int sparse = 0; sparse <= 1; ++sparse) { // loop over "bool" sparse or not
+ if (sparse && (profile == EEsProfile || version < 450))
+ continue;
+
+ TString s;
+
+ // return type
+ if (sparse)
+ s.append("int ");
+ else {
+ s.append(prefixes[sampler.type]);
+ s.append("vec4 ");
+ }
+
+ // name
+ if (sparse)
+ s.append("sparseTextureGather");
+ else
+ s.append("textureGather");
+
+ if (lod)
+ s.append("Lod");
+
+ switch (offset) {
+ case 1:
+ s.append("Offset");
+ break;
+ case 2:
+ s.append("Offsets");
+ break;
+ default:
+ break;
+ }
+
+ if (lod)
+ s.append("AMD");
+ else if (sparse)
+ s.append("ARB");
+
+ s.append("(");
+
+ // sampler type argument
+ s.append(typeName);
+
+ // P coordinate argument
+ if (f16TexAddr)
+ s.append(",f16vec");
+ else
+ s.append(",vec");
+ int totalDims = dimMap[sampler.dim] + (sampler.arrayed ? 1 : 0);
+ s.append(postfixes[totalDims]);
+
+ // lod argument
+ if (lod) {
+ if (f16TexAddr)
+ s.append(",float16_t");
+ else
+ s.append(",float");
+ }
+
+ // offset argument
+ if (offset > 0) {
+ s.append(",ivec2");
+ if (offset == 2)
+ s.append("[4]");
+ }
+
+ // texel out (for sparse texture)
+ if (sparse) {
+ s.append(",out ");
+ s.append(prefixes[sampler.type]);
+ s.append("vec4 ");
+ }
+
+ // comp argument
+ if (comp)
+ s.append(",int");
+
+ // bias argument
+ if (bias) {
+ if (f16TexAddr)
+ s.append(",float16_t");
+ else
+ s.append(",float");
+ }
+
+ s.append(");\n");
+ if (bias)
+ stageBuiltins[EShLangFragment].append(s);
+ else
+ commonBuiltins.append(s);
+ }
+ }
+ }
+ }
+ }
+ }
+#endif
+}
+
+//
+// Add context-dependent built-in functions and variables that are present
+// for the given version and profile. All the results are put into just the
+// commonBuiltins, because it is called for just a specific stage. So,
+// add stage-specific entries to the commonBuiltins, and only if that stage
+// was requested.
+//
+void TBuiltIns::initialize(const TBuiltInResource &resources, int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language)
+{
+ //
+ // Initialize the context-dependent (resource-dependent) built-in strings for parsing.
+ //
+
+ //============================================================================
+ //
+ // Standard Uniforms
+ //
+ //============================================================================
+
+ TString& s = commonBuiltins;
+ const int maxSize = 80;
+ char builtInConstant[maxSize];
+
+ //
+ // Build string of implementation dependent constants.
+ //
+
+ if (profile == EEsProfile) {
+ snprintf(builtInConstant, maxSize, "const mediump int gl_MaxVertexAttribs = %d;", resources.maxVertexAttribs);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const mediump int gl_MaxVertexUniformVectors = %d;", resources.maxVertexUniformVectors);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const mediump int gl_MaxVertexTextureImageUnits = %d;", resources.maxVertexTextureImageUnits);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const mediump int gl_MaxCombinedTextureImageUnits = %d;", resources.maxCombinedTextureImageUnits);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const mediump int gl_MaxTextureImageUnits = %d;", resources.maxTextureImageUnits);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const mediump int gl_MaxFragmentUniformVectors = %d;", resources.maxFragmentUniformVectors);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const mediump int gl_MaxDrawBuffers = %d;", resources.maxDrawBuffers);
+ s.append(builtInConstant);
+
+ if (version == 100) {
+ snprintf(builtInConstant, maxSize, "const mediump int gl_MaxVaryingVectors = %d;", resources.maxVaryingVectors);
+ s.append(builtInConstant);
+ } else {
+ snprintf(builtInConstant, maxSize, "const mediump int gl_MaxVertexOutputVectors = %d;", resources.maxVertexOutputVectors);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const mediump int gl_MaxFragmentInputVectors = %d;", resources.maxFragmentInputVectors);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const mediump int gl_MinProgramTexelOffset = %d;", resources.minProgramTexelOffset);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const mediump int gl_MaxProgramTexelOffset = %d;", resources.maxProgramTexelOffset);
+ s.append(builtInConstant);
+ }
+
+ if (version >= 310) {
+ // geometry
+
+ snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryInputComponents = %d;", resources.maxGeometryInputComponents);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryOutputComponents = %d;", resources.maxGeometryOutputComponents);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryImageUniforms = %d;", resources.maxGeometryImageUniforms);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryTextureImageUnits = %d;", resources.maxGeometryTextureImageUnits);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryOutputVertices = %d;", resources.maxGeometryOutputVertices);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryTotalOutputComponents = %d;", resources.maxGeometryTotalOutputComponents);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryUniformComponents = %d;", resources.maxGeometryUniformComponents);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryAtomicCounters = %d;", resources.maxGeometryAtomicCounters);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryAtomicCounterBuffers = %d;", resources.maxGeometryAtomicCounterBuffers);
+ s.append(builtInConstant);
+
+ // tessellation
+
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlInputComponents = %d;", resources.maxTessControlInputComponents);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlOutputComponents = %d;", resources.maxTessControlOutputComponents);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlTextureImageUnits = %d;", resources.maxTessControlTextureImageUnits);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlUniformComponents = %d;", resources.maxTessControlUniformComponents);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlTotalOutputComponents = %d;", resources.maxTessControlTotalOutputComponents);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationInputComponents = %d;", resources.maxTessEvaluationInputComponents);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationOutputComponents = %d;", resources.maxTessEvaluationOutputComponents);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationTextureImageUnits = %d;", resources.maxTessEvaluationTextureImageUnits);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationUniformComponents = %d;", resources.maxTessEvaluationUniformComponents);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessPatchComponents = %d;", resources.maxTessPatchComponents);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const int gl_MaxPatchVertices = %d;", resources.maxPatchVertices);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessGenLevel = %d;", resources.maxTessGenLevel);
+ s.append(builtInConstant);
+
+ // this is here instead of with the others in initialize(version, profile) due to the dependence on gl_MaxPatchVertices
+ if (language == EShLangTessControl || language == EShLangTessEvaluation) {
+ s.append(
+ "in gl_PerVertex {"
+ "highp vec4 gl_Position;"
+ "highp float gl_PointSize;"
+#ifdef NV_EXTENSIONS
+ "highp vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering
+ "highp vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes
+#endif
+ "} gl_in[gl_MaxPatchVertices];"
+ "\n");
+ }
+ }
+
+ } else {
+ // non-ES profile
+
+ snprintf(builtInConstant, maxSize, "const int gl_MaxVertexAttribs = %d;", resources.maxVertexAttribs);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const int gl_MaxVertexTextureImageUnits = %d;", resources.maxVertexTextureImageUnits);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedTextureImageUnits = %d;", resources.maxCombinedTextureImageUnits);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTextureImageUnits = %d;", resources.maxTextureImageUnits);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const int gl_MaxDrawBuffers = %d;", resources.maxDrawBuffers);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const int gl_MaxLights = %d;", resources.maxLights);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const int gl_MaxClipPlanes = %d;", resources.maxClipPlanes);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTextureUnits = %d;", resources.maxTextureUnits);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTextureCoords = %d;", resources.maxTextureCoords);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const int gl_MaxVertexUniformComponents = %d;", resources.maxVertexUniformComponents);
+ s.append(builtInConstant);
+
+ if (version < 150 || ARBCompatibility) {
+ snprintf(builtInConstant, maxSize, "const int gl_MaxVaryingFloats = %d;", resources.maxVaryingFloats);
+ s.append(builtInConstant);
+ }
+
+ snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentUniformComponents = %d;", resources.maxFragmentUniformComponents);
+ s.append(builtInConstant);
+
+ if (spvVersion.spv == 0 && IncludeLegacy(version, profile, spvVersion)) {
+ //
+ // OpenGL'uniform' state. Page numbers are in reference to version
+ // 1.4 of the OpenGL specification.
+ //
+
+ //
+ // Matrix state. p. 31, 32, 37, 39, 40.
+ //
+ s.append("uniform mat4 gl_TextureMatrix[gl_MaxTextureCoords];"
+
+ //
+ // Derived matrix state that provides inverse and transposed versions
+ // of the matrices above.
+ //
+ "uniform mat4 gl_TextureMatrixInverse[gl_MaxTextureCoords];"
+
+ "uniform mat4 gl_TextureMatrixTranspose[gl_MaxTextureCoords];"
+
+ "uniform mat4 gl_TextureMatrixInverseTranspose[gl_MaxTextureCoords];"
+
+ //
+ // Clip planes p. 42.
+ //
+ "uniform vec4 gl_ClipPlane[gl_MaxClipPlanes];"
+
+ //
+ // Light State p 50, 53, 55.
+ //
+ "uniform gl_LightSourceParameters gl_LightSource[gl_MaxLights];"
+
+ //
+ // Derived state from products of light.
+ //
+ "uniform gl_LightProducts gl_FrontLightProduct[gl_MaxLights];"
+ "uniform gl_LightProducts gl_BackLightProduct[gl_MaxLights];"
+
+ //
+ // Texture Environment and Generation, p. 152, p. 40-42.
+ //
+ "uniform vec4 gl_TextureEnvColor[gl_MaxTextureImageUnits];"
+ "uniform vec4 gl_EyePlaneS[gl_MaxTextureCoords];"
+ "uniform vec4 gl_EyePlaneT[gl_MaxTextureCoords];"
+ "uniform vec4 gl_EyePlaneR[gl_MaxTextureCoords];"
+ "uniform vec4 gl_EyePlaneQ[gl_MaxTextureCoords];"
+ "uniform vec4 gl_ObjectPlaneS[gl_MaxTextureCoords];"
+ "uniform vec4 gl_ObjectPlaneT[gl_MaxTextureCoords];"
+ "uniform vec4 gl_ObjectPlaneR[gl_MaxTextureCoords];"
+ "uniform vec4 gl_ObjectPlaneQ[gl_MaxTextureCoords];");
+ }
+
+ if (version >= 130) {
+ snprintf(builtInConstant, maxSize, "const int gl_MaxClipDistances = %d;", resources.maxClipDistances);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxVaryingComponents = %d;", resources.maxVaryingComponents);
+ s.append(builtInConstant);
+
+ // GL_ARB_shading_language_420pack
+ snprintf(builtInConstant, maxSize, "const mediump int gl_MinProgramTexelOffset = %d;", resources.minProgramTexelOffset);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const mediump int gl_MaxProgramTexelOffset = %d;", resources.maxProgramTexelOffset);
+ s.append(builtInConstant);
+ }
+
+ // geometry
+ if (version >= 150) {
+ snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryInputComponents = %d;", resources.maxGeometryInputComponents);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryOutputComponents = %d;", resources.maxGeometryOutputComponents);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryTextureImageUnits = %d;", resources.maxGeometryTextureImageUnits);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryOutputVertices = %d;", resources.maxGeometryOutputVertices);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryTotalOutputComponents = %d;", resources.maxGeometryTotalOutputComponents);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryUniformComponents = %d;", resources.maxGeometryUniformComponents);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryVaryingComponents = %d;", resources.maxGeometryVaryingComponents);
+ s.append(builtInConstant);
+
+ }
+
+ if (version >= 150) {
+ snprintf(builtInConstant, maxSize, "const int gl_MaxVertexOutputComponents = %d;", resources.maxVertexOutputComponents);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentInputComponents = %d;", resources.maxFragmentInputComponents);
+ s.append(builtInConstant);
+ }
+
+ // tessellation
+ if (version >= 150) {
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlInputComponents = %d;", resources.maxTessControlInputComponents);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlOutputComponents = %d;", resources.maxTessControlOutputComponents);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlTextureImageUnits = %d;", resources.maxTessControlTextureImageUnits);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlUniformComponents = %d;", resources.maxTessControlUniformComponents);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlTotalOutputComponents = %d;", resources.maxTessControlTotalOutputComponents);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationInputComponents = %d;", resources.maxTessEvaluationInputComponents);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationOutputComponents = %d;", resources.maxTessEvaluationOutputComponents);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationTextureImageUnits = %d;", resources.maxTessEvaluationTextureImageUnits);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationUniformComponents = %d;", resources.maxTessEvaluationUniformComponents);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessPatchComponents = %d;", resources.maxTessPatchComponents);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessGenLevel = %d;", resources.maxTessGenLevel);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxPatchVertices = %d;", resources.maxPatchVertices);
+ s.append(builtInConstant);
+
+ // this is here instead of with the others in initialize(version, profile) due to the dependence on gl_MaxPatchVertices
+ if (language == EShLangTessControl || language == EShLangTessEvaluation) {
+ s.append(
+ "in gl_PerVertex {"
+ "vec4 gl_Position;"
+ "float gl_PointSize;"
+ "float gl_ClipDistance[];"
+ );
+ if (profile == ECompatibilityProfile)
+ s.append(
+ "vec4 gl_ClipVertex;"
+ "vec4 gl_FrontColor;"
+ "vec4 gl_BackColor;"
+ "vec4 gl_FrontSecondaryColor;"
+ "vec4 gl_BackSecondaryColor;"
+ "vec4 gl_TexCoord[];"
+ "float gl_FogFragCoord;"
+ );
+ if (profile != EEsProfile && version >= 450)
+ s.append(
+ "float gl_CullDistance[];"
+#ifdef NV_EXTENSIONS
+ "vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering
+ "vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes
+#endif
+ );
+ s.append(
+ "} gl_in[gl_MaxPatchVertices];"
+ "\n");
+ }
+ }
+
+ if (version >= 150) {
+ snprintf(builtInConstant, maxSize, "const int gl_MaxViewports = %d;", resources.maxViewports);
+ s.append(builtInConstant);
+ }
+
+ // images
+ if (version >= 130) {
+ snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedImageUnitsAndFragmentOutputs = %d;", resources.maxCombinedImageUnitsAndFragmentOutputs);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxImageSamples = %d;", resources.maxImageSamples);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlImageUniforms = %d;", resources.maxTessControlImageUniforms);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationImageUniforms = %d;", resources.maxTessEvaluationImageUniforms);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryImageUniforms = %d;", resources.maxGeometryImageUniforms);
+ s.append(builtInConstant);
+ }
+
+ // enhanced layouts
+ if (version >= 430) {
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTransformFeedbackBuffers = %d;", resources.maxTransformFeedbackBuffers);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTransformFeedbackInterleavedComponents = %d;", resources.maxTransformFeedbackInterleavedComponents);
+ s.append(builtInConstant);
+ }
+ }
+
+ // images (some in compute below)
+ if ((profile == EEsProfile && version >= 310) ||
+ (profile != EEsProfile && version >= 130)) {
+ snprintf(builtInConstant, maxSize, "const int gl_MaxImageUnits = %d;", resources.maxImageUnits);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedShaderOutputResources = %d;", resources.maxCombinedShaderOutputResources);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxVertexImageUniforms = %d;", resources.maxVertexImageUniforms);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentImageUniforms = %d;", resources.maxFragmentImageUniforms);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedImageUniforms = %d;", resources.maxCombinedImageUniforms);
+ s.append(builtInConstant);
+ }
+
+ // atomic counters (some in compute below)
+ if ((profile == EEsProfile && version >= 310) ||
+ (profile != EEsProfile && version >= 420)) {
+ snprintf(builtInConstant, maxSize, "const int gl_MaxVertexAtomicCounters = %d;", resources. maxVertexAtomicCounters);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentAtomicCounters = %d;", resources. maxFragmentAtomicCounters);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedAtomicCounters = %d;", resources. maxCombinedAtomicCounters);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxAtomicCounterBindings = %d;", resources. maxAtomicCounterBindings);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxVertexAtomicCounterBuffers = %d;", resources. maxVertexAtomicCounterBuffers);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentAtomicCounterBuffers = %d;", resources. maxFragmentAtomicCounterBuffers);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedAtomicCounterBuffers = %d;", resources. maxCombinedAtomicCounterBuffers);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxAtomicCounterBufferSize = %d;", resources. maxAtomicCounterBufferSize);
+ s.append(builtInConstant);
+ }
+ if (profile != EEsProfile && version >= 420) {
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlAtomicCounters = %d;", resources. maxTessControlAtomicCounters);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationAtomicCounters = %d;", resources. maxTessEvaluationAtomicCounters);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryAtomicCounters = %d;", resources. maxGeometryAtomicCounters);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlAtomicCounterBuffers = %d;", resources. maxTessControlAtomicCounterBuffers);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationAtomicCounterBuffers = %d;", resources. maxTessEvaluationAtomicCounterBuffers);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryAtomicCounterBuffers = %d;", resources. maxGeometryAtomicCounterBuffers);
+ s.append(builtInConstant);
+
+ s.append("\n");
+ }
+
+ // compute
+ if ((profile == EEsProfile && version >= 310) || (profile != EEsProfile && version >= 420)) {
+ snprintf(builtInConstant, maxSize, "const ivec3 gl_MaxComputeWorkGroupCount = ivec3(%d,%d,%d);", resources.maxComputeWorkGroupCountX,
+ resources.maxComputeWorkGroupCountY,
+ resources.maxComputeWorkGroupCountZ);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const ivec3 gl_MaxComputeWorkGroupSize = ivec3(%d,%d,%d);", resources.maxComputeWorkGroupSizeX,
+ resources.maxComputeWorkGroupSizeY,
+ resources.maxComputeWorkGroupSizeZ);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const int gl_MaxComputeUniformComponents = %d;", resources.maxComputeUniformComponents);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxComputeTextureImageUnits = %d;", resources.maxComputeTextureImageUnits);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxComputeImageUniforms = %d;", resources.maxComputeImageUniforms);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxComputeAtomicCounters = %d;", resources.maxComputeAtomicCounters);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxComputeAtomicCounterBuffers = %d;", resources.maxComputeAtomicCounterBuffers);
+ s.append(builtInConstant);
+
+ s.append("\n");
+ }
+
+ // GL_ARB_cull_distance
+ if (profile != EEsProfile && version >= 450) {
+ snprintf(builtInConstant, maxSize, "const int gl_MaxCullDistances = %d;", resources.maxCullDistances);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedClipAndCullDistances = %d;", resources.maxCombinedClipAndCullDistances);
+ s.append(builtInConstant);
+ }
+
+ // GL_ARB_ES3_1_compatibility
+ if ((profile != EEsProfile && version >= 450) ||
+ (profile == EEsProfile && version >= 310)) {
+ snprintf(builtInConstant, maxSize, "const int gl_MaxSamples = %d;", resources.maxSamples);
+ s.append(builtInConstant);
+ }
+
+#ifdef AMD_EXTENSIONS
+ // GL_AMD_gcn_shader
+ if (profile != EEsProfile && version >= 450) {
+ snprintf(builtInConstant, maxSize, "const int gl_SIMDGroupSizeAMD = 64;");
+ s.append(builtInConstant);
+ }
+#endif
+
+#ifdef NV_EXTENSIONS
+ // SPV_NV_mesh_shader
+ if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) {
+ snprintf(builtInConstant, maxSize, "const int gl_MaxMeshOutputVerticesNV = %d;", resources.maxMeshOutputVerticesNV);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const int gl_MaxMeshOutputPrimitivesNV = %d;", resources.maxMeshOutputPrimitivesNV);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const ivec3 gl_MaxMeshWorkGroupSizeNV = ivec3(%d,%d,%d);", resources.maxMeshWorkGroupSizeX_NV,
+ resources.maxMeshWorkGroupSizeY_NV,
+ resources.maxMeshWorkGroupSizeZ_NV);
+ s.append(builtInConstant);
+ snprintf(builtInConstant, maxSize, "const ivec3 gl_MaxTaskWorkGroupSizeNV = ivec3(%d,%d,%d);", resources.maxTaskWorkGroupSizeX_NV,
+ resources.maxTaskWorkGroupSizeY_NV,
+ resources.maxTaskWorkGroupSizeZ_NV);
+ s.append(builtInConstant);
+
+ snprintf(builtInConstant, maxSize, "const int gl_MaxMeshViewCountNV = %d;", resources.maxMeshViewCountNV);
+ s.append(builtInConstant);
+
+ s.append("\n");
+ }
+#endif
+
+ s.append("\n");
+}
+
+//
+// To support special built-ins that have a special qualifier that cannot be declared textually
+// in a shader, like gl_Position.
+//
+// This lets the type of the built-in be declared textually, and then have just its qualifier be
+// updated afterward.
+//
+// Safe to call even if name is not present.
+//
+// Only use this for built-in variables that have a special qualifier in TStorageQualifier.
+// New built-in variables should use a generic (textually declarable) qualifier in
+// TStoraregQualifier and only call BuiltInVariable().
+//
+static void SpecialQualifier(const char* name, TStorageQualifier qualifier, TBuiltInVariable builtIn, TSymbolTable& symbolTable)
+{
+ TSymbol* symbol = symbolTable.find(name);
+ if (symbol == nullptr)
+ return;
+
+ TQualifier& symQualifier = symbol->getWritableType().getQualifier();
+ symQualifier.storage = qualifier;
+ symQualifier.builtIn = builtIn;
+}
+
+//
+// To tag built-in variables with their TBuiltInVariable enum. Use this when the
+// normal declaration text already gets the qualifier right, and all that's needed
+// is setting the builtIn field. This should be the normal way for all new
+// built-in variables.
+//
+// If SpecialQualifier() was called, this does not need to be called.
+//
+// Safe to call even if name is not present.
+//
+static void BuiltInVariable(const char* name, TBuiltInVariable builtIn, TSymbolTable& symbolTable)
+{
+ TSymbol* symbol = symbolTable.find(name);
+ if (symbol == nullptr)
+ return;
+
+ TQualifier& symQualifier = symbol->getWritableType().getQualifier();
+ symQualifier.builtIn = builtIn;
+}
+
+//
+// For built-in variables inside a named block.
+// SpecialQualifier() won't ever go inside a block; their member's qualifier come
+// from the qualification of the block.
+//
+// See comments above for other detail.
+//
+static void BuiltInVariable(const char* blockName, const char* name, TBuiltInVariable builtIn, TSymbolTable& symbolTable)
+{
+ TSymbol* symbol = symbolTable.find(blockName);
+ if (symbol == nullptr)
+ return;
+
+ TTypeList& structure = *symbol->getWritableType().getWritableStruct();
+ for (int i = 0; i < (int)structure.size(); ++i) {
+ if (structure[i].type->getFieldName().compare(name) == 0) {
+ structure[i].type->getQualifier().builtIn = builtIn;
+ return;
+ }
+ }
+}
+
+//
+// Finish adding/processing context-independent built-in symbols.
+// 1) Programmatically add symbols that could not be added by simple text strings above.
+// 2) Map built-in functions to operators, for those that will turn into an operation node
+// instead of remaining a function call.
+// 3) Tag extension-related symbols added to their base version with their extensions, so
+// that if an early version has the extension turned off, there is an error reported on use.
+//
+void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable)
+{
+ //
+ // Tag built-in variables and functions with additional qualifier and extension information
+ // that cannot be declared with the text strings.
+ //
+
+ // N.B.: a symbol should only be tagged once, and this function is called multiple times, once
+ // per stage that's used for this profile. So
+ // - generally, stick common ones in the fragment stage to ensure they are tagged exactly once
+ // - for ES, which has different precisions for different stages, the coarsest-grained tagging
+ // for a built-in used in many stages needs to be once for the fragment stage and once for
+ // the vertex stage
+
+ switch(language) {
+ case EShLangVertex:
+ if (profile != EEsProfile) {
+ if (version >= 440) {
+ symbolTable.setVariableExtensions("gl_BaseVertexARB", 1, &E_GL_ARB_shader_draw_parameters);
+ symbolTable.setVariableExtensions("gl_BaseInstanceARB", 1, &E_GL_ARB_shader_draw_parameters);
+ symbolTable.setVariableExtensions("gl_DrawIDARB", 1, &E_GL_ARB_shader_draw_parameters);
+ BuiltInVariable("gl_BaseVertexARB", EbvBaseVertex, symbolTable);
+ BuiltInVariable("gl_BaseInstanceARB", EbvBaseInstance, symbolTable);
+ BuiltInVariable("gl_DrawIDARB", EbvDrawId, symbolTable);
+ }
+ if (version >= 460) {
+ BuiltInVariable("gl_BaseVertex", EbvBaseVertex, symbolTable);
+ BuiltInVariable("gl_BaseInstance", EbvBaseInstance, symbolTable);
+ BuiltInVariable("gl_DrawID", EbvDrawId, symbolTable);
+ }
+ symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot);
+
+ symbolTable.setFunctionExtensions("ballotARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setFunctionExtensions("readInvocationARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setFunctionExtensions("readFirstInvocationARB", 1, &E_GL_ARB_shader_ballot);
+
+ BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable);
+ BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable);
+ BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable);
+ BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable);
+ BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable);
+ BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable);
+
+ if (spvVersion.vulkan > 0)
+ // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan
+ SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable);
+ else
+ BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable);
+
+ if (version >= 430) {
+ symbolTable.setFunctionExtensions("anyInvocationARB", 1, &E_GL_ARB_shader_group_vote);
+ symbolTable.setFunctionExtensions("allInvocationsARB", 1, &E_GL_ARB_shader_group_vote);
+ symbolTable.setFunctionExtensions("allInvocationsEqualARB", 1, &E_GL_ARB_shader_group_vote);
+ }
+ }
+
+#ifdef AMD_EXTENSIONS
+ if (profile != EEsProfile) {
+ symbolTable.setFunctionExtensions("minInvocationsAMD", 1, &E_GL_AMD_shader_ballot);
+ symbolTable.setFunctionExtensions("maxInvocationsAMD", 1, &E_GL_AMD_shader_ballot);
+ symbolTable.setFunctionExtensions("addInvocationsAMD", 1, &E_GL_AMD_shader_ballot);
+ symbolTable.setFunctionExtensions("minInvocationsNonUniformAMD", 1, &E_GL_AMD_shader_ballot);
+ symbolTable.setFunctionExtensions("maxInvocationsNonUniformAMD", 1, &E_GL_AMD_shader_ballot);
+ symbolTable.setFunctionExtensions("addInvocationsNonUniformAMD", 1, &E_GL_AMD_shader_ballot);
+ symbolTable.setFunctionExtensions("swizzleInvocationsAMD", 1, &E_GL_AMD_shader_ballot);
+ symbolTable.setFunctionExtensions("swizzleInvocationsWithPatternAMD", 1, &E_GL_AMD_shader_ballot);
+ symbolTable.setFunctionExtensions("writeInvocationAMD", 1, &E_GL_AMD_shader_ballot);
+ symbolTable.setFunctionExtensions("mbcntAMD", 1, &E_GL_AMD_shader_ballot);
+
+ symbolTable.setFunctionExtensions("minInvocationsInclusiveScanAMD", 1, &E_GL_AMD_shader_ballot);
+ symbolTable.setFunctionExtensions("maxInvocationsInclusiveScanAMD", 1, &E_GL_AMD_shader_ballot);
+ symbolTable.setFunctionExtensions("addInvocationsInclusiveScanAMD", 1, &E_GL_AMD_shader_ballot);
+ symbolTable.setFunctionExtensions("minInvocationsInclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot);
+ symbolTable.setFunctionExtensions("maxInvocationsInclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot);
+ symbolTable.setFunctionExtensions("addInvocationsInclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot);
+ symbolTable.setFunctionExtensions("minInvocationsExclusiveScanAMD", 1, &E_GL_AMD_shader_ballot);
+ symbolTable.setFunctionExtensions("maxInvocationsExclusiveScanAMD", 1, &E_GL_AMD_shader_ballot);
+ symbolTable.setFunctionExtensions("addInvocationsExclusiveScanAMD", 1, &E_GL_AMD_shader_ballot);
+ symbolTable.setFunctionExtensions("minInvocationsExclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot);
+ symbolTable.setFunctionExtensions("maxInvocationsExclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot);
+ symbolTable.setFunctionExtensions("addInvocationsExclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot);
+ }
+
+ if (profile != EEsProfile) {
+ symbolTable.setFunctionExtensions("min3", 1, &E_GL_AMD_shader_trinary_minmax);
+ symbolTable.setFunctionExtensions("max3", 1, &E_GL_AMD_shader_trinary_minmax);
+ symbolTable.setFunctionExtensions("mid3", 1, &E_GL_AMD_shader_trinary_minmax);
+ }
+
+ if (profile != EEsProfile) {
+ symbolTable.setFunctionExtensions("cubeFaceIndexAMD", 1, &E_GL_AMD_gcn_shader);
+ symbolTable.setFunctionExtensions("cubeFaceCoordAMD", 1, &E_GL_AMD_gcn_shader);
+ symbolTable.setFunctionExtensions("timeAMD", 1, &E_GL_AMD_gcn_shader);
+ }
+
+ if (profile != EEsProfile) {
+ symbolTable.setFunctionExtensions("fragmentMaskFetchAMD", 1, &E_GL_AMD_shader_fragment_mask);
+ symbolTable.setFunctionExtensions("fragmentFetchAMD", 1, &E_GL_AMD_shader_fragment_mask);
+ }
+#endif
+
+#ifdef NV_EXTENSIONS
+ symbolTable.setFunctionExtensions("textureFootprintNV", 1, &E_GL_NV_shader_texture_footprint);
+ symbolTable.setFunctionExtensions("textureFootprintClampNV", 1, &E_GL_NV_shader_texture_footprint);
+ symbolTable.setFunctionExtensions("textureFootprintLodNV", 1, &E_GL_NV_shader_texture_footprint);
+ symbolTable.setFunctionExtensions("textureFootprintGradNV", 1, &E_GL_NV_shader_texture_footprint);
+ symbolTable.setFunctionExtensions("textureFootprintGradClampNV", 1, &E_GL_NV_shader_texture_footprint);
+#endif
+ // Compatibility variables, vertex only
+ if (spvVersion.spv == 0) {
+ BuiltInVariable("gl_Color", EbvColor, symbolTable);
+ BuiltInVariable("gl_SecondaryColor", EbvSecondaryColor, symbolTable);
+ BuiltInVariable("gl_Normal", EbvNormal, symbolTable);
+ BuiltInVariable("gl_Vertex", EbvVertex, symbolTable);
+ BuiltInVariable("gl_MultiTexCoord0", EbvMultiTexCoord0, symbolTable);
+ BuiltInVariable("gl_MultiTexCoord1", EbvMultiTexCoord1, symbolTable);
+ BuiltInVariable("gl_MultiTexCoord2", EbvMultiTexCoord2, symbolTable);
+ BuiltInVariable("gl_MultiTexCoord3", EbvMultiTexCoord3, symbolTable);
+ BuiltInVariable("gl_MultiTexCoord4", EbvMultiTexCoord4, symbolTable);
+ BuiltInVariable("gl_MultiTexCoord5", EbvMultiTexCoord5, symbolTable);
+ BuiltInVariable("gl_MultiTexCoord6", EbvMultiTexCoord6, symbolTable);
+ BuiltInVariable("gl_MultiTexCoord7", EbvMultiTexCoord7, symbolTable);
+ BuiltInVariable("gl_FogCoord", EbvFogFragCoord, symbolTable);
+ }
+
+ if (profile == EEsProfile) {
+ if (spvVersion.spv == 0) {
+ symbolTable.setFunctionExtensions("texture2DGradEXT", 1, &E_GL_EXT_shader_texture_lod);
+ symbolTable.setFunctionExtensions("texture2DProjGradEXT", 1, &E_GL_EXT_shader_texture_lod);
+ symbolTable.setFunctionExtensions("textureCubeGradEXT", 1, &E_GL_EXT_shader_texture_lod);
+ if (version == 310)
+ symbolTable.setFunctionExtensions("textureGatherOffsets", Num_AEP_gpu_shader5, AEP_gpu_shader5);
+ }
+ if (version == 310)
+ symbolTable.setFunctionExtensions("fma", Num_AEP_gpu_shader5, AEP_gpu_shader5);
+ }
+
+ if (profile == EEsProfile && version < 320) {
+ symbolTable.setFunctionExtensions("imageAtomicAdd", 1, &E_GL_OES_shader_image_atomic);
+ symbolTable.setFunctionExtensions("imageAtomicMin", 1, &E_GL_OES_shader_image_atomic);
+ symbolTable.setFunctionExtensions("imageAtomicMax", 1, &E_GL_OES_shader_image_atomic);
+ symbolTable.setFunctionExtensions("imageAtomicAnd", 1, &E_GL_OES_shader_image_atomic);
+ symbolTable.setFunctionExtensions("imageAtomicOr", 1, &E_GL_OES_shader_image_atomic);
+ symbolTable.setFunctionExtensions("imageAtomicXor", 1, &E_GL_OES_shader_image_atomic);
+ symbolTable.setFunctionExtensions("imageAtomicExchange", 1, &E_GL_OES_shader_image_atomic);
+ symbolTable.setFunctionExtensions("imageAtomicCompSwap", 1, &E_GL_OES_shader_image_atomic);
+ }
+
+ if (spvVersion.vulkan == 0) {
+ SpecialQualifier("gl_VertexID", EvqVertexId, EbvVertexId, symbolTable);
+ SpecialQualifier("gl_InstanceID", EvqInstanceId, EbvInstanceId, symbolTable);
+ }
+
+ if (spvVersion.vulkan > 0) {
+ BuiltInVariable("gl_VertexIndex", EbvVertexIndex, symbolTable);
+ BuiltInVariable("gl_InstanceIndex", EbvInstanceIndex, symbolTable);
+ }
+
+ if (version >= 300 /* both ES and non-ES */) {
+ symbolTable.setVariableExtensions("gl_ViewID_OVR", Num_OVR_multiview_EXTs, OVR_multiview_EXTs);
+ BuiltInVariable("gl_ViewID_OVR", EbvViewIndex, symbolTable);
+ }
+
+ if (profile == EEsProfile) {
+ symbolTable.setFunctionExtensions("shadow2DEXT", 1, &E_GL_EXT_shadow_samplers);
+ symbolTable.setFunctionExtensions("shadow2DProjEXT", 1, &E_GL_EXT_shadow_samplers);
+ }
+
+ // Fall through
+
+ case EShLangTessControl:
+ if (profile == EEsProfile && version >= 310) {
+ BuiltInVariable("gl_BoundingBoxEXT", EbvBoundingBox, symbolTable);
+ symbolTable.setVariableExtensions("gl_BoundingBoxEXT", 1,
+ &E_GL_EXT_primitive_bounding_box);
+ BuiltInVariable("gl_BoundingBoxOES", EbvBoundingBox, symbolTable);
+ symbolTable.setVariableExtensions("gl_BoundingBoxOES", 1,
+ &E_GL_OES_primitive_bounding_box);
+
+ if (version >= 320) {
+ BuiltInVariable("gl_BoundingBox", EbvBoundingBox, symbolTable);
+ }
+ }
+
+ // Fall through
+
+ case EShLangTessEvaluation:
+ case EShLangGeometry:
+ SpecialQualifier("gl_Position", EvqPosition, EbvPosition, symbolTable);
+ SpecialQualifier("gl_PointSize", EvqPointSize, EbvPointSize, symbolTable);
+ SpecialQualifier("gl_ClipVertex", EvqClipVertex, EbvClipVertex, symbolTable);
+
+ BuiltInVariable("gl_in", "gl_Position", EbvPosition, symbolTable);
+ BuiltInVariable("gl_in", "gl_PointSize", EbvPointSize, symbolTable);
+ BuiltInVariable("gl_in", "gl_ClipDistance", EbvClipDistance, symbolTable);
+ BuiltInVariable("gl_in", "gl_CullDistance", EbvCullDistance, symbolTable);
+
+ BuiltInVariable("gl_out", "gl_Position", EbvPosition, symbolTable);
+ BuiltInVariable("gl_out", "gl_PointSize", EbvPointSize, symbolTable);
+ BuiltInVariable("gl_out", "gl_ClipDistance", EbvClipDistance, symbolTable);
+ BuiltInVariable("gl_out", "gl_CullDistance", EbvCullDistance, symbolTable);
+
+ BuiltInVariable("gl_ClipDistance", EbvClipDistance, symbolTable);
+ BuiltInVariable("gl_CullDistance", EbvCullDistance, symbolTable);
+ BuiltInVariable("gl_PrimitiveIDIn", EbvPrimitiveId, symbolTable);
+ BuiltInVariable("gl_PrimitiveID", EbvPrimitiveId, symbolTable);
+ BuiltInVariable("gl_InvocationID", EbvInvocationId, symbolTable);
+ BuiltInVariable("gl_Layer", EbvLayer, symbolTable);
+ BuiltInVariable("gl_ViewportIndex", EbvViewportIndex, symbolTable);
+
+#ifdef NV_EXTENSIONS
+ if (language != EShLangGeometry) {
+ symbolTable.setVariableExtensions("gl_Layer", Num_viewportEXTs, viewportEXTs);
+ symbolTable.setVariableExtensions("gl_ViewportIndex", Num_viewportEXTs, viewportEXTs);
+ }
+#else
+ if (language != EShLangGeometry && version >= 410) {
+ symbolTable.setVariableExtensions("gl_Layer", 1, &E_GL_ARB_shader_viewport_layer_array);
+ symbolTable.setVariableExtensions("gl_ViewportIndex", 1, &E_GL_ARB_shader_viewport_layer_array);
+ }
+#endif
+
+#ifdef NV_EXTENSIONS
+ symbolTable.setVariableExtensions("gl_ViewportMask", 1, &E_GL_NV_viewport_array2);
+ symbolTable.setVariableExtensions("gl_SecondaryPositionNV", 1, &E_GL_NV_stereo_view_rendering);
+ symbolTable.setVariableExtensions("gl_SecondaryViewportMaskNV", 1, &E_GL_NV_stereo_view_rendering);
+ symbolTable.setVariableExtensions("gl_PositionPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes);
+ symbolTable.setVariableExtensions("gl_ViewportMaskPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes);
+
+ BuiltInVariable("gl_ViewportMask", EbvViewportMaskNV, symbolTable);
+ BuiltInVariable("gl_SecondaryPositionNV", EbvSecondaryPositionNV, symbolTable);
+ BuiltInVariable("gl_SecondaryViewportMaskNV", EbvSecondaryViewportMaskNV, symbolTable);
+ BuiltInVariable("gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable);
+ BuiltInVariable("gl_ViewportMaskPerViewNV", EbvViewportMaskPerViewNV, symbolTable);
+
+ if (language != EShLangVertex) {
+ symbolTable.setVariableExtensions("gl_in", "gl_SecondaryPositionNV", 1, &E_GL_NV_stereo_view_rendering);
+ symbolTable.setVariableExtensions("gl_in", "gl_PositionPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes);
+
+ BuiltInVariable("gl_in", "gl_SecondaryPositionNV", EbvSecondaryPositionNV, symbolTable);
+ BuiltInVariable("gl_in", "gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable);
+ }
+ symbolTable.setVariableExtensions("gl_out", "gl_ViewportMask", 1, &E_GL_NV_viewport_array2);
+ symbolTable.setVariableExtensions("gl_out", "gl_SecondaryPositionNV", 1, &E_GL_NV_stereo_view_rendering);
+ symbolTable.setVariableExtensions("gl_out", "gl_SecondaryViewportMaskNV", 1, &E_GL_NV_stereo_view_rendering);
+ symbolTable.setVariableExtensions("gl_out", "gl_PositionPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes);
+ symbolTable.setVariableExtensions("gl_out", "gl_ViewportMaskPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes);
+
+ BuiltInVariable("gl_out", "gl_ViewportMask", EbvViewportMaskNV, symbolTable);
+ BuiltInVariable("gl_out", "gl_SecondaryPositionNV", EbvSecondaryPositionNV, symbolTable);
+ BuiltInVariable("gl_out", "gl_SecondaryViewportMaskNV", EbvSecondaryViewportMaskNV, symbolTable);
+ BuiltInVariable("gl_out", "gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable);
+ BuiltInVariable("gl_out", "gl_ViewportMaskPerViewNV", EbvViewportMaskPerViewNV, symbolTable);
+#endif
+
+ BuiltInVariable("gl_PatchVerticesIn", EbvPatchVertices, symbolTable);
+ BuiltInVariable("gl_TessLevelOuter", EbvTessLevelOuter, symbolTable);
+ BuiltInVariable("gl_TessLevelInner", EbvTessLevelInner, symbolTable);
+ BuiltInVariable("gl_TessCoord", EbvTessCoord, symbolTable);
+
+ if (version < 410)
+ symbolTable.setVariableExtensions("gl_ViewportIndex", 1, &E_GL_ARB_viewport_array);
+
+ // Compatibility variables
+
+ BuiltInVariable("gl_in", "gl_ClipVertex", EbvClipVertex, symbolTable);
+ BuiltInVariable("gl_in", "gl_FrontColor", EbvFrontColor, symbolTable);
+ BuiltInVariable("gl_in", "gl_BackColor", EbvBackColor, symbolTable);
+ BuiltInVariable("gl_in", "gl_FrontSecondaryColor", EbvFrontSecondaryColor, symbolTable);
+ BuiltInVariable("gl_in", "gl_BackSecondaryColor", EbvBackSecondaryColor, symbolTable);
+ BuiltInVariable("gl_in", "gl_TexCoord", EbvTexCoord, symbolTable);
+ BuiltInVariable("gl_in", "gl_FogFragCoord", EbvFogFragCoord, symbolTable);
+
+ BuiltInVariable("gl_out", "gl_ClipVertex", EbvClipVertex, symbolTable);
+ BuiltInVariable("gl_out", "gl_FrontColor", EbvFrontColor, symbolTable);
+ BuiltInVariable("gl_out", "gl_BackColor", EbvBackColor, symbolTable);
+ BuiltInVariable("gl_out", "gl_FrontSecondaryColor", EbvFrontSecondaryColor, symbolTable);
+ BuiltInVariable("gl_out", "gl_BackSecondaryColor", EbvBackSecondaryColor, symbolTable);
+ BuiltInVariable("gl_out", "gl_TexCoord", EbvTexCoord, symbolTable);
+ BuiltInVariable("gl_out", "gl_FogFragCoord", EbvFogFragCoord, symbolTable);
+
+ BuiltInVariable("gl_ClipVertex", EbvClipVertex, symbolTable);
+ BuiltInVariable("gl_FrontColor", EbvFrontColor, symbolTable);
+ BuiltInVariable("gl_BackColor", EbvBackColor, symbolTable);
+ BuiltInVariable("gl_FrontSecondaryColor", EbvFrontSecondaryColor, symbolTable);
+ BuiltInVariable("gl_BackSecondaryColor", EbvBackSecondaryColor, symbolTable);
+ BuiltInVariable("gl_TexCoord", EbvTexCoord, symbolTable);
+ BuiltInVariable("gl_FogFragCoord", EbvFogFragCoord, symbolTable);
+
+ // gl_PointSize, when it needs to be tied to an extension, is always a member of a block.
+ // (Sometimes with an instance name, sometimes anonymous).
+ if (profile == EEsProfile) {
+ if (language == EShLangGeometry) {
+ symbolTable.setVariableExtensions("gl_PointSize", Num_AEP_geometry_point_size, AEP_geometry_point_size);
+ symbolTable.setVariableExtensions("gl_in", "gl_PointSize", Num_AEP_geometry_point_size, AEP_geometry_point_size);
+ } else if (language == EShLangTessEvaluation || language == EShLangTessControl) {
+ // gl_in tessellation settings of gl_PointSize are in the context-dependent paths
+ symbolTable.setVariableExtensions("gl_PointSize", Num_AEP_tessellation_point_size, AEP_tessellation_point_size);
+ symbolTable.setVariableExtensions("gl_out", "gl_PointSize", Num_AEP_tessellation_point_size, AEP_tessellation_point_size);
+ }
+ }
+
+ if ((profile != EEsProfile && version >= 140) ||
+ (profile == EEsProfile && version >= 310)) {
+ symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group);
+ BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable);
+ symbolTable.setVariableExtensions("gl_ViewIndex", 1, &E_GL_EXT_multiview);
+ BuiltInVariable("gl_ViewIndex", EbvViewIndex, symbolTable);
+ }
+
+ // GL_KHR_shader_subgroup
+ if ((profile == EEsProfile && version >= 310) ||
+ (profile != EEsProfile && version >= 140)) {
+ symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic);
+ symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic);
+ symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+
+ BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable);
+ BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable);
+ BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable);
+ BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable);
+ BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable);
+ BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable);
+ BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable);
+ }
+
+ break;
+
+ case EShLangFragment:
+ SpecialQualifier("gl_FrontFacing", EvqFace, EbvFace, symbolTable);
+ SpecialQualifier("gl_FragCoord", EvqFragCoord, EbvFragCoord, symbolTable);
+ SpecialQualifier("gl_PointCoord", EvqPointCoord, EbvPointCoord, symbolTable);
+ if (spvVersion.spv == 0)
+ SpecialQualifier("gl_FragColor", EvqFragColor, EbvFragColor, symbolTable);
+ else {
+ TSymbol* symbol = symbolTable.find("gl_FragColor");
+ if (symbol) {
+ symbol->getWritableType().getQualifier().storage = EvqVaryingOut;
+ symbol->getWritableType().getQualifier().layoutLocation = 0;
+ }
+ }
+ SpecialQualifier("gl_FragDepth", EvqFragDepth, EbvFragDepth, symbolTable);
+ SpecialQualifier("gl_FragDepthEXT", EvqFragDepth, EbvFragDepth, symbolTable);
+ SpecialQualifier("gl_HelperInvocation", EvqVaryingIn, EbvHelperInvocation, symbolTable);
+
+ BuiltInVariable("gl_ClipDistance", EbvClipDistance, symbolTable);
+ BuiltInVariable("gl_CullDistance", EbvCullDistance, symbolTable);
+ BuiltInVariable("gl_PrimitiveID", EbvPrimitiveId, symbolTable);
+
+ if (profile != EEsProfile && version >= 140) {
+ symbolTable.setVariableExtensions("gl_FragStencilRefARB", 1, &E_GL_ARB_shader_stencil_export);
+ BuiltInVariable("gl_FragStencilRefARB", EbvFragStencilRef, symbolTable);
+ }
+
+ if ((profile != EEsProfile && version >= 400) ||
+ (profile == EEsProfile && version >= 310)) {
+ BuiltInVariable("gl_SampleID", EbvSampleId, symbolTable);
+ BuiltInVariable("gl_SamplePosition", EbvSamplePosition, symbolTable);
+ BuiltInVariable("gl_SampleMaskIn", EbvSampleMask, symbolTable);
+ BuiltInVariable("gl_SampleMask", EbvSampleMask, symbolTable);
+ if (profile == EEsProfile && version < 320) {
+ symbolTable.setVariableExtensions("gl_SampleID", 1, &E_GL_OES_sample_variables);
+ symbolTable.setVariableExtensions("gl_SamplePosition", 1, &E_GL_OES_sample_variables);
+ symbolTable.setVariableExtensions("gl_SampleMaskIn", 1, &E_GL_OES_sample_variables);
+ symbolTable.setVariableExtensions("gl_SampleMask", 1, &E_GL_OES_sample_variables);
+ symbolTable.setVariableExtensions("gl_NumSamples", 1, &E_GL_OES_sample_variables);
+ }
+ }
+
+ BuiltInVariable("gl_Layer", EbvLayer, symbolTable);
+ BuiltInVariable("gl_ViewportIndex", EbvViewportIndex, symbolTable);
+
+ // Compatibility variables
+
+ BuiltInVariable("gl_in", "gl_FogFragCoord", EbvFogFragCoord, symbolTable);
+ BuiltInVariable("gl_in", "gl_TexCoord", EbvTexCoord, symbolTable);
+ BuiltInVariable("gl_in", "gl_Color", EbvColor, symbolTable);
+ BuiltInVariable("gl_in", "gl_SecondaryColor", EbvSecondaryColor, symbolTable);
+
+ BuiltInVariable("gl_FogFragCoord", EbvFogFragCoord, symbolTable);
+ BuiltInVariable("gl_TexCoord", EbvTexCoord, symbolTable);
+ BuiltInVariable("gl_Color", EbvColor, symbolTable);
+ BuiltInVariable("gl_SecondaryColor", EbvSecondaryColor, symbolTable);
+
+ // built-in functions
+
+ if (profile == EEsProfile) {
+ if (spvVersion.spv == 0) {
+ symbolTable.setFunctionExtensions("texture2DLodEXT", 1, &E_GL_EXT_shader_texture_lod);
+ symbolTable.setFunctionExtensions("texture2DProjLodEXT", 1, &E_GL_EXT_shader_texture_lod);
+ symbolTable.setFunctionExtensions("textureCubeLodEXT", 1, &E_GL_EXT_shader_texture_lod);
+ symbolTable.setFunctionExtensions("texture2DGradEXT", 1, &E_GL_EXT_shader_texture_lod);
+ symbolTable.setFunctionExtensions("texture2DProjGradEXT", 1, &E_GL_EXT_shader_texture_lod);
+ symbolTable.setFunctionExtensions("textureCubeGradEXT", 1, &E_GL_EXT_shader_texture_lod);
+ if (version < 320)
+ symbolTable.setFunctionExtensions("textureGatherOffsets", Num_AEP_gpu_shader5, AEP_gpu_shader5);
+ }
+ if (version == 100) {
+ symbolTable.setFunctionExtensions("dFdx", 1, &E_GL_OES_standard_derivatives);
+ symbolTable.setFunctionExtensions("dFdy", 1, &E_GL_OES_standard_derivatives);
+ symbolTable.setFunctionExtensions("fwidth", 1, &E_GL_OES_standard_derivatives);
+ }
+ if (version == 310) {
+ symbolTable.setFunctionExtensions("fma", Num_AEP_gpu_shader5, AEP_gpu_shader5);
+ symbolTable.setFunctionExtensions("interpolateAtCentroid", 1, &E_GL_OES_shader_multisample_interpolation);
+ symbolTable.setFunctionExtensions("interpolateAtSample", 1, &E_GL_OES_shader_multisample_interpolation);
+ symbolTable.setFunctionExtensions("interpolateAtOffset", 1, &E_GL_OES_shader_multisample_interpolation);
+ }
+ } else if (version < 130) {
+ if (spvVersion.spv == 0) {
+ symbolTable.setFunctionExtensions("texture1DLod", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("texture2DLod", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("texture3DLod", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("textureCubeLod", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("texture1DProjLod", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("texture2DProjLod", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("texture3DProjLod", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("shadow1DLod", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("shadow2DLod", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("shadow1DProjLod", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("shadow2DProjLod", 1, &E_GL_ARB_shader_texture_lod);
+ }
+ }
+
+ // E_GL_ARB_shader_texture_lod functions usable only with the extension enabled
+ if (profile != EEsProfile && spvVersion.spv == 0) {
+ symbolTable.setFunctionExtensions("texture1DGradARB", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("texture1DProjGradARB", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("texture2DGradARB", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("texture2DProjGradARB", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("texture3DGradARB", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("texture3DProjGradARB", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("textureCubeGradARB", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("shadow1DGradARB", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("shadow1DProjGradARB", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("shadow2DGradARB", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("shadow2DProjGradARB", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("texture2DRectGradARB", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("texture2DRectProjGradARB", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("shadow2DRectGradARB", 1, &E_GL_ARB_shader_texture_lod);
+ symbolTable.setFunctionExtensions("shadow2DRectProjGradARB", 1, &E_GL_ARB_shader_texture_lod);
+ }
+
+ // E_GL_ARB_shader_image_load_store
+ if (profile != EEsProfile && version < 420)
+ symbolTable.setFunctionExtensions("memoryBarrier", 1, &E_GL_ARB_shader_image_load_store);
+ // All the image access functions are protected by checks on the type of the first argument.
+
+ // E_GL_ARB_shader_atomic_counters
+ if (profile != EEsProfile && version < 420) {
+ symbolTable.setFunctionExtensions("atomicCounterIncrement", 1, &E_GL_ARB_shader_atomic_counters);
+ symbolTable.setFunctionExtensions("atomicCounterDecrement", 1, &E_GL_ARB_shader_atomic_counters);
+ symbolTable.setFunctionExtensions("atomicCounter" , 1, &E_GL_ARB_shader_atomic_counters);
+ }
+
+ // E_GL_ARB_derivative_control
+ if (profile != EEsProfile && version < 450) {
+ symbolTable.setFunctionExtensions("dFdxFine", 1, &E_GL_ARB_derivative_control);
+ symbolTable.setFunctionExtensions("dFdyFine", 1, &E_GL_ARB_derivative_control);
+ symbolTable.setFunctionExtensions("fwidthFine", 1, &E_GL_ARB_derivative_control);
+ symbolTable.setFunctionExtensions("dFdxCoarse", 1, &E_GL_ARB_derivative_control);
+ symbolTable.setFunctionExtensions("dFdyCoarse", 1, &E_GL_ARB_derivative_control);
+ symbolTable.setFunctionExtensions("fwidthCoarse", 1, &E_GL_ARB_derivative_control);
+ }
+
+ // E_GL_ARB_sparse_texture2
+ if (profile != EEsProfile)
+ {
+ symbolTable.setFunctionExtensions("sparseTextureARB", 1, &E_GL_ARB_sparse_texture2);
+ symbolTable.setFunctionExtensions("sparseTextureLodARB", 1, &E_GL_ARB_sparse_texture2);
+ symbolTable.setFunctionExtensions("sparseTextureOffsetARB", 1, &E_GL_ARB_sparse_texture2);
+ symbolTable.setFunctionExtensions("sparseTexelFetchARB", 1, &E_GL_ARB_sparse_texture2);
+ symbolTable.setFunctionExtensions("sparseTexelFetchOffsetARB", 1, &E_GL_ARB_sparse_texture2);
+ symbolTable.setFunctionExtensions("sparseTextureLodOffsetARB", 1, &E_GL_ARB_sparse_texture2);
+ symbolTable.setFunctionExtensions("sparseTextureGradARB", 1, &E_GL_ARB_sparse_texture2);
+ symbolTable.setFunctionExtensions("sparseTextureGradOffsetARB", 1, &E_GL_ARB_sparse_texture2);
+ symbolTable.setFunctionExtensions("sparseTextureGatherARB", 1, &E_GL_ARB_sparse_texture2);
+ symbolTable.setFunctionExtensions("sparseTextureGatherOffsetARB", 1, &E_GL_ARB_sparse_texture2);
+ symbolTable.setFunctionExtensions("sparseTextureGatherOffsetsARB", 1, &E_GL_ARB_sparse_texture2);
+ symbolTable.setFunctionExtensions("sparseImageLoadARB", 1, &E_GL_ARB_sparse_texture2);
+ symbolTable.setFunctionExtensions("sparseTexelsResident", 1, &E_GL_ARB_sparse_texture2);
+ }
+
+ // E_GL_ARB_sparse_texture_clamp
+ if (profile != EEsProfile)
+ {
+ symbolTable.setFunctionExtensions("sparseTextureClampARB", 1, &E_GL_ARB_sparse_texture_clamp);
+ symbolTable.setFunctionExtensions("sparseTextureOffsetClampARB", 1, &E_GL_ARB_sparse_texture_clamp);
+ symbolTable.setFunctionExtensions("sparseTextureGradClampARB", 1, &E_GL_ARB_sparse_texture_clamp);
+ symbolTable.setFunctionExtensions("sparseTextureGradOffsetClampARB", 1, &E_GL_ARB_sparse_texture_clamp);
+ symbolTable.setFunctionExtensions("textureClampARB", 1, &E_GL_ARB_sparse_texture_clamp);
+ symbolTable.setFunctionExtensions("textureOffsetClampARB", 1, &E_GL_ARB_sparse_texture_clamp);
+ symbolTable.setFunctionExtensions("textureGradClampARB", 1, &E_GL_ARB_sparse_texture_clamp);
+ symbolTable.setFunctionExtensions("textureGradOffsetClampARB", 1, &E_GL_ARB_sparse_texture_clamp);
+ }
+
+#ifdef AMD_EXTENSIONS
+ // E_GL_AMD_shader_explicit_vertex_parameter
+ if (profile != EEsProfile) {
+ symbolTable.setVariableExtensions("gl_BaryCoordNoPerspAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter);
+ symbolTable.setVariableExtensions("gl_BaryCoordNoPerspCentroidAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter);
+ symbolTable.setVariableExtensions("gl_BaryCoordNoPerspSampleAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter);
+ symbolTable.setVariableExtensions("gl_BaryCoordSmoothAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter);
+ symbolTable.setVariableExtensions("gl_BaryCoordSmoothCentroidAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter);
+ symbolTable.setVariableExtensions("gl_BaryCoordSmoothSampleAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter);
+ symbolTable.setVariableExtensions("gl_BaryCoordPullModelAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter);
+
+ symbolTable.setFunctionExtensions("interpolateAtVertexAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter);
+
+ BuiltInVariable("gl_BaryCoordNoPerspAMD", EbvBaryCoordNoPersp, symbolTable);
+ BuiltInVariable("gl_BaryCoordNoPerspCentroidAMD", EbvBaryCoordNoPerspCentroid, symbolTable);
+ BuiltInVariable("gl_BaryCoordNoPerspSampleAMD", EbvBaryCoordNoPerspSample, symbolTable);
+ BuiltInVariable("gl_BaryCoordSmoothAMD", EbvBaryCoordSmooth, symbolTable);
+ BuiltInVariable("gl_BaryCoordSmoothCentroidAMD", EbvBaryCoordSmoothCentroid, symbolTable);
+ BuiltInVariable("gl_BaryCoordSmoothSampleAMD", EbvBaryCoordSmoothSample, symbolTable);
+ BuiltInVariable("gl_BaryCoordPullModelAMD", EbvBaryCoordPullModel, symbolTable);
+ }
+
+ // E_GL_AMD_texture_gather_bias_lod
+ if (profile != EEsProfile) {
+ symbolTable.setFunctionExtensions("textureGatherLodAMD", 1, &E_GL_AMD_texture_gather_bias_lod);
+ symbolTable.setFunctionExtensions("textureGatherLodOffsetAMD", 1, &E_GL_AMD_texture_gather_bias_lod);
+ symbolTable.setFunctionExtensions("textureGatherLodOffsetsAMD", 1, &E_GL_AMD_texture_gather_bias_lod);
+ symbolTable.setFunctionExtensions("sparseTextureGatherLodAMD", 1, &E_GL_AMD_texture_gather_bias_lod);
+ symbolTable.setFunctionExtensions("sparseTextureGatherLodOffsetAMD", 1, &E_GL_AMD_texture_gather_bias_lod);
+ symbolTable.setFunctionExtensions("sparseTextureGatherLodOffsetsAMD", 1, &E_GL_AMD_texture_gather_bias_lod);
+ }
+
+ // E_GL_AMD_shader_image_load_store_lod
+ if (profile != EEsProfile) {
+ symbolTable.setFunctionExtensions("imageLoadLodAMD", 1, &E_GL_AMD_shader_image_load_store_lod);
+ symbolTable.setFunctionExtensions("imageStoreLodAMD", 1, &E_GL_AMD_shader_image_load_store_lod);
+ symbolTable.setFunctionExtensions("sparseImageLoadLodAMD", 1, &E_GL_AMD_shader_image_load_store_lod);
+ }
+#endif
+
+#ifdef NV_EXTENSIONS
+ if (profile != EEsProfile && version >= 430) {
+ symbolTable.setVariableExtensions("gl_FragFullyCoveredNV", 1, &E_GL_NV_conservative_raster_underestimation);
+ BuiltInVariable("gl_FragFullyCoveredNV", EbvFragFullyCoveredNV, symbolTable);
+ }
+ if ((profile != EEsProfile && version >= 450) ||
+ (profile == EEsProfile && version >= 320)) {
+ symbolTable.setVariableExtensions("gl_FragmentSizeNV", 1, &E_GL_NV_shading_rate_image);
+ symbolTable.setVariableExtensions("gl_InvocationsPerPixelNV", 1, &E_GL_NV_shading_rate_image);
+ BuiltInVariable("gl_FragmentSizeNV", EbvFragmentSizeNV, symbolTable);
+ BuiltInVariable("gl_InvocationsPerPixelNV", EbvInvocationsPerPixelNV, symbolTable);
+ symbolTable.setVariableExtensions("gl_BaryCoordNV", 1, &E_GL_NV_fragment_shader_barycentric);
+ symbolTable.setVariableExtensions("gl_BaryCoordNoPerspNV", 1, &E_GL_NV_fragment_shader_barycentric);
+ BuiltInVariable("gl_BaryCoordNV", EbvBaryCoordNV, symbolTable);
+ BuiltInVariable("gl_BaryCoordNoPerspNV", EbvBaryCoordNoPerspNV, symbolTable);
+ }
+ if (((profile != EEsProfile && version >= 450) ||
+ (profile == EEsProfile && version >= 320)) &&
+ language == EShLangCompute) {
+ symbolTable.setFunctionExtensions("dFdx", 1, &E_GL_NV_compute_shader_derivatives);
+ symbolTable.setFunctionExtensions("dFdy", 1, &E_GL_NV_compute_shader_derivatives);
+ symbolTable.setFunctionExtensions("fwidth", 1, &E_GL_NV_compute_shader_derivatives);
+ symbolTable.setFunctionExtensions("dFdxFine", 1, &E_GL_NV_compute_shader_derivatives);
+ symbolTable.setFunctionExtensions("dFdyFine", 1, &E_GL_NV_compute_shader_derivatives);
+ symbolTable.setFunctionExtensions("fwidthFine", 1, &E_GL_NV_compute_shader_derivatives);
+ symbolTable.setFunctionExtensions("dFdxCoarse", 1, &E_GL_NV_compute_shader_derivatives);
+ symbolTable.setFunctionExtensions("dFdyCoarse", 1, &E_GL_NV_compute_shader_derivatives);
+ symbolTable.setFunctionExtensions("fwidthCoarse", 1, &E_GL_NV_compute_shader_derivatives);
+ }
+#endif
+
+ if ((profile != EEsProfile && version >= 450) ||
+ (profile == EEsProfile && version >= 310)) {
+ symbolTable.setVariableExtensions("gl_FragSizeEXT", 1, &E_GL_EXT_fragment_invocation_density);
+ symbolTable.setVariableExtensions("gl_FragInvocationCountEXT", 1, &E_GL_EXT_fragment_invocation_density);
+ BuiltInVariable("gl_FragSizeEXT", EbvFragSizeEXT, symbolTable);
+ BuiltInVariable("gl_FragInvocationCountEXT", EbvFragInvocationCountEXT, symbolTable);
+ }
+
+ symbolTable.setVariableExtensions("gl_FragDepthEXT", 1, &E_GL_EXT_frag_depth);
+
+ if (profile == EEsProfile && version < 320) {
+ symbolTable.setVariableExtensions("gl_PrimitiveID", Num_AEP_geometry_shader, AEP_geometry_shader);
+ symbolTable.setVariableExtensions("gl_Layer", Num_AEP_geometry_shader, AEP_geometry_shader);
+ }
+
+ if (profile == EEsProfile && version < 320) {
+ symbolTable.setFunctionExtensions("imageAtomicAdd", 1, &E_GL_OES_shader_image_atomic);
+ symbolTable.setFunctionExtensions("imageAtomicMin", 1, &E_GL_OES_shader_image_atomic);
+ symbolTable.setFunctionExtensions("imageAtomicMax", 1, &E_GL_OES_shader_image_atomic);
+ symbolTable.setFunctionExtensions("imageAtomicAnd", 1, &E_GL_OES_shader_image_atomic);
+ symbolTable.setFunctionExtensions("imageAtomicOr", 1, &E_GL_OES_shader_image_atomic);
+ symbolTable.setFunctionExtensions("imageAtomicXor", 1, &E_GL_OES_shader_image_atomic);
+ symbolTable.setFunctionExtensions("imageAtomicExchange", 1, &E_GL_OES_shader_image_atomic);
+ symbolTable.setFunctionExtensions("imageAtomicCompSwap", 1, &E_GL_OES_shader_image_atomic);
+ }
+
+ symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group);
+ BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable);
+ symbolTable.setVariableExtensions("gl_ViewIndex", 1, &E_GL_EXT_multiview);
+ BuiltInVariable("gl_ViewIndex", EbvViewIndex, symbolTable);
+ if (version >= 300 /* both ES and non-ES */) {
+ symbolTable.setVariableExtensions("gl_ViewID_OVR", Num_OVR_multiview_EXTs, OVR_multiview_EXTs);
+ BuiltInVariable("gl_ViewID_OVR", EbvViewIndex, symbolTable);
+ }
+
+ // GL_ARB_shader_ballot
+ if (profile != EEsProfile) {
+ symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot);
+
+ BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable);
+ BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable);
+ BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable);
+ BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable);
+ BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable);
+ BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable);
+
+ if (spvVersion.vulkan > 0)
+ // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan
+ SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable);
+ else
+ BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable);
+ }
+
+ // GL_KHR_shader_subgroup
+ if ((profile == EEsProfile && version >= 310) ||
+ (profile != EEsProfile && version >= 140)) {
+ symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic);
+ symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic);
+ symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+
+ BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable);
+ BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable);
+ BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable);
+ BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable);
+ BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable);
+ BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable);
+ BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable);
+
+ symbolTable.setFunctionExtensions("subgroupBarrier", 1, &E_GL_KHR_shader_subgroup_basic);
+ symbolTable.setFunctionExtensions("subgroupMemoryBarrier", 1, &E_GL_KHR_shader_subgroup_basic);
+ symbolTable.setFunctionExtensions("subgroupMemoryBarrierBuffer", 1, &E_GL_KHR_shader_subgroup_basic);
+ symbolTable.setFunctionExtensions("subgroupMemoryBarrierImage", 1, &E_GL_KHR_shader_subgroup_basic);
+ symbolTable.setFunctionExtensions("subgroupElect", 1, &E_GL_KHR_shader_subgroup_basic);
+ symbolTable.setFunctionExtensions("subgroupAll", 1, &E_GL_KHR_shader_subgroup_vote);
+ symbolTable.setFunctionExtensions("subgroupAny", 1, &E_GL_KHR_shader_subgroup_vote);
+ symbolTable.setFunctionExtensions("subgroupAllEqual", 1, &E_GL_KHR_shader_subgroup_vote);
+ symbolTable.setFunctionExtensions("subgroupBroadcast", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setFunctionExtensions("subgroupBroadcastFirst", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setFunctionExtensions("subgroupBallot", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setFunctionExtensions("subgroupInverseBallot", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setFunctionExtensions("subgroupBallotBitExtract", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setFunctionExtensions("subgroupBallotBitCount", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setFunctionExtensions("subgroupBallotInclusiveBitCount", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setFunctionExtensions("subgroupBallotExclusiveBitCount", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setFunctionExtensions("subgroupBallotFindLSB", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setFunctionExtensions("subgroupBallotFindMSB", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setFunctionExtensions("subgroupShuffle", 1, &E_GL_KHR_shader_subgroup_shuffle);
+ symbolTable.setFunctionExtensions("subgroupShuffleXor", 1, &E_GL_KHR_shader_subgroup_shuffle);
+ symbolTable.setFunctionExtensions("subgroupShuffleUp", 1, &E_GL_KHR_shader_subgroup_shuffle_relative);
+ symbolTable.setFunctionExtensions("subgroupShuffleDown", 1, &E_GL_KHR_shader_subgroup_shuffle_relative);
+ symbolTable.setFunctionExtensions("subgroupAdd", 1, &E_GL_KHR_shader_subgroup_arithmetic);
+ symbolTable.setFunctionExtensions("subgroupMul", 1, &E_GL_KHR_shader_subgroup_arithmetic);
+ symbolTable.setFunctionExtensions("subgroupMin", 1, &E_GL_KHR_shader_subgroup_arithmetic);
+ symbolTable.setFunctionExtensions("subgroupMax", 1, &E_GL_KHR_shader_subgroup_arithmetic);
+ symbolTable.setFunctionExtensions("subgroupAnd", 1, &E_GL_KHR_shader_subgroup_arithmetic);
+ symbolTable.setFunctionExtensions("subgroupOr", 1, &E_GL_KHR_shader_subgroup_arithmetic);
+ symbolTable.setFunctionExtensions("subgroupXor", 1, &E_GL_KHR_shader_subgroup_arithmetic);
+ symbolTable.setFunctionExtensions("subgroupInclusiveAdd", 1, &E_GL_KHR_shader_subgroup_arithmetic);
+ symbolTable.setFunctionExtensions("subgroupInclusiveMul", 1, &E_GL_KHR_shader_subgroup_arithmetic);
+ symbolTable.setFunctionExtensions("subgroupInclusiveMin", 1, &E_GL_KHR_shader_subgroup_arithmetic);
+ symbolTable.setFunctionExtensions("subgroupInclusiveMax", 1, &E_GL_KHR_shader_subgroup_arithmetic);
+ symbolTable.setFunctionExtensions("subgroupInclusiveAnd", 1, &E_GL_KHR_shader_subgroup_arithmetic);
+ symbolTable.setFunctionExtensions("subgroupInclusiveOr", 1, &E_GL_KHR_shader_subgroup_arithmetic);
+ symbolTable.setFunctionExtensions("subgroupInclusiveXor", 1, &E_GL_KHR_shader_subgroup_arithmetic);
+ symbolTable.setFunctionExtensions("subgroupExclusiveAdd", 1, &E_GL_KHR_shader_subgroup_arithmetic);
+ symbolTable.setFunctionExtensions("subgroupExclusiveMul", 1, &E_GL_KHR_shader_subgroup_arithmetic);
+ symbolTable.setFunctionExtensions("subgroupExclusiveMin", 1, &E_GL_KHR_shader_subgroup_arithmetic);
+ symbolTable.setFunctionExtensions("subgroupExclusiveMax", 1, &E_GL_KHR_shader_subgroup_arithmetic);
+ symbolTable.setFunctionExtensions("subgroupExclusiveAnd", 1, &E_GL_KHR_shader_subgroup_arithmetic);
+ symbolTable.setFunctionExtensions("subgroupExclusiveOr", 1, &E_GL_KHR_shader_subgroup_arithmetic);
+ symbolTable.setFunctionExtensions("subgroupExclusiveXor", 1, &E_GL_KHR_shader_subgroup_arithmetic);
+ symbolTable.setFunctionExtensions("subgroupClusteredAdd", 1, &E_GL_KHR_shader_subgroup_clustered);
+ symbolTable.setFunctionExtensions("subgroupClusteredMul", 1, &E_GL_KHR_shader_subgroup_clustered);
+ symbolTable.setFunctionExtensions("subgroupClusteredMin", 1, &E_GL_KHR_shader_subgroup_clustered);
+ symbolTable.setFunctionExtensions("subgroupClusteredMax", 1, &E_GL_KHR_shader_subgroup_clustered);
+ symbolTable.setFunctionExtensions("subgroupClusteredAnd", 1, &E_GL_KHR_shader_subgroup_clustered);
+ symbolTable.setFunctionExtensions("subgroupClusteredOr", 1, &E_GL_KHR_shader_subgroup_clustered);
+ symbolTable.setFunctionExtensions("subgroupClusteredXor", 1, &E_GL_KHR_shader_subgroup_clustered);
+ symbolTable.setFunctionExtensions("subgroupQuadBroadcast", 1, &E_GL_KHR_shader_subgroup_quad);
+ symbolTable.setFunctionExtensions("subgroupQuadSwapHorizontal", 1, &E_GL_KHR_shader_subgroup_quad);
+ symbolTable.setFunctionExtensions("subgroupQuadSwapVertical", 1, &E_GL_KHR_shader_subgroup_quad);
+ symbolTable.setFunctionExtensions("subgroupQuadSwapDiagonal", 1, &E_GL_KHR_shader_subgroup_quad);
+
+#ifdef NV_EXTENSIONS
+ symbolTable.setFunctionExtensions("subgroupPartitionNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+ symbolTable.setFunctionExtensions("subgroupPartitionedAddNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+ symbolTable.setFunctionExtensions("subgroupPartitionedMulNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+ symbolTable.setFunctionExtensions("subgroupPartitionedMinNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+ symbolTable.setFunctionExtensions("subgroupPartitionedMaxNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+ symbolTable.setFunctionExtensions("subgroupPartitionedAndNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+ symbolTable.setFunctionExtensions("subgroupPartitionedOrNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+ symbolTable.setFunctionExtensions("subgroupPartitionedXorNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+ symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveAddNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+ symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveMulNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+ symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveMinNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+ symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveMaxNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+ symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveAndNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+ symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveOrNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+ symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveXorNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+ symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveAddNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+ symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveMulNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+ symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveMinNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+ symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveMaxNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+ symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveAndNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+ symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveOrNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+ symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveXorNV", 1, &E_GL_NV_shader_subgroup_partitioned);
+#endif
+
+ }
+
+ if (profile == EEsProfile) {
+ symbolTable.setFunctionExtensions("shadow2DEXT", 1, &E_GL_EXT_shadow_samplers);
+ symbolTable.setFunctionExtensions("shadow2DProjEXT", 1, &E_GL_EXT_shadow_samplers);
+ }
+
+ if (spvVersion.vulkan > 0) {
+ symbolTable.setVariableExtensions("gl_ScopeDevice", 1, &E_GL_KHR_memory_scope_semantics);
+ symbolTable.setVariableExtensions("gl_ScopeWorkgroup", 1, &E_GL_KHR_memory_scope_semantics);
+ symbolTable.setVariableExtensions("gl_ScopeSubgroup", 1, &E_GL_KHR_memory_scope_semantics);
+ symbolTable.setVariableExtensions("gl_ScopeInvocation", 1, &E_GL_KHR_memory_scope_semantics);
+
+ symbolTable.setVariableExtensions("gl_SemanticsRelaxed", 1, &E_GL_KHR_memory_scope_semantics);
+ symbolTable.setVariableExtensions("gl_SemanticsAcquire", 1, &E_GL_KHR_memory_scope_semantics);
+ symbolTable.setVariableExtensions("gl_SemanticsRelease", 1, &E_GL_KHR_memory_scope_semantics);
+ symbolTable.setVariableExtensions("gl_SemanticsAcquireRelease", 1, &E_GL_KHR_memory_scope_semantics);
+ symbolTable.setVariableExtensions("gl_SemanticsMakeAvailable", 1, &E_GL_KHR_memory_scope_semantics);
+ symbolTable.setVariableExtensions("gl_SemanticsMakeVisible", 1, &E_GL_KHR_memory_scope_semantics);
+
+ symbolTable.setVariableExtensions("gl_StorageSemanticsNone", 1, &E_GL_KHR_memory_scope_semantics);
+ symbolTable.setVariableExtensions("gl_StorageSemanticsBuffer", 1, &E_GL_KHR_memory_scope_semantics);
+ symbolTable.setVariableExtensions("gl_StorageSemanticsShared", 1, &E_GL_KHR_memory_scope_semantics);
+ symbolTable.setVariableExtensions("gl_StorageSemanticsImage", 1, &E_GL_KHR_memory_scope_semantics);
+ symbolTable.setVariableExtensions("gl_StorageSemanticsOutput", 1, &E_GL_KHR_memory_scope_semantics);
+ }
+ break;
+
+ case EShLangCompute:
+ BuiltInVariable("gl_NumWorkGroups", EbvNumWorkGroups, symbolTable);
+ BuiltInVariable("gl_WorkGroupSize", EbvWorkGroupSize, symbolTable);
+ BuiltInVariable("gl_WorkGroupID", EbvWorkGroupId, symbolTable);
+ BuiltInVariable("gl_LocalInvocationID", EbvLocalInvocationId, symbolTable);
+ BuiltInVariable("gl_GlobalInvocationID", EbvGlobalInvocationId, symbolTable);
+ BuiltInVariable("gl_LocalInvocationIndex", EbvLocalInvocationIndex, symbolTable);
+
+ if (profile != EEsProfile && version < 430) {
+ symbolTable.setVariableExtensions("gl_NumWorkGroups", 1, &E_GL_ARB_compute_shader);
+ symbolTable.setVariableExtensions("gl_WorkGroupSize", 1, &E_GL_ARB_compute_shader);
+ symbolTable.setVariableExtensions("gl_WorkGroupID", 1, &E_GL_ARB_compute_shader);
+ symbolTable.setVariableExtensions("gl_LocalInvocationID", 1, &E_GL_ARB_compute_shader);
+ symbolTable.setVariableExtensions("gl_GlobalInvocationID", 1, &E_GL_ARB_compute_shader);
+ symbolTable.setVariableExtensions("gl_LocalInvocationIndex", 1, &E_GL_ARB_compute_shader);
+
+ symbolTable.setVariableExtensions("gl_MaxComputeWorkGroupCount", 1, &E_GL_ARB_compute_shader);
+ symbolTable.setVariableExtensions("gl_MaxComputeWorkGroupSize", 1, &E_GL_ARB_compute_shader);
+ symbolTable.setVariableExtensions("gl_MaxComputeUniformComponents", 1, &E_GL_ARB_compute_shader);
+ symbolTable.setVariableExtensions("gl_MaxComputeTextureImageUnits", 1, &E_GL_ARB_compute_shader);
+ symbolTable.setVariableExtensions("gl_MaxComputeImageUniforms", 1, &E_GL_ARB_compute_shader);
+ symbolTable.setVariableExtensions("gl_MaxComputeAtomicCounters", 1, &E_GL_ARB_compute_shader);
+ symbolTable.setVariableExtensions("gl_MaxComputeAtomicCounterBuffers", 1, &E_GL_ARB_compute_shader);
+
+ symbolTable.setFunctionExtensions("barrier", 1, &E_GL_ARB_compute_shader);
+ symbolTable.setFunctionExtensions("memoryBarrierAtomicCounter", 1, &E_GL_ARB_compute_shader);
+ symbolTable.setFunctionExtensions("memoryBarrierBuffer", 1, &E_GL_ARB_compute_shader);
+ symbolTable.setFunctionExtensions("memoryBarrierImage", 1, &E_GL_ARB_compute_shader);
+ symbolTable.setFunctionExtensions("memoryBarrierShared", 1, &E_GL_ARB_compute_shader);
+ symbolTable.setFunctionExtensions("groupMemoryBarrier", 1, &E_GL_ARB_compute_shader);
+ }
+
+ symbolTable.setFunctionExtensions("controlBarrier", 1, &E_GL_KHR_memory_scope_semantics);
+
+ // GL_ARB_shader_ballot
+ if (profile != EEsProfile) {
+ symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot);
+
+ BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable);
+ BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable);
+ BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable);
+ BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable);
+ BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable);
+ BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable);
+
+ if (spvVersion.vulkan > 0)
+ // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan
+ SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable);
+ else
+ BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable);
+ }
+
+ // GL_KHR_shader_subgroup
+ if ((profile == EEsProfile && version >= 310) ||
+ (profile != EEsProfile && version >= 140)) {
+ symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic);
+ symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic);
+ symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+
+ BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable);
+ BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable);
+ BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable);
+ BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable);
+ BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable);
+ BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable);
+ BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable);
+ }
+
+ if ((profile != EEsProfile && version >= 140) ||
+ (profile == EEsProfile && version >= 310)) {
+ symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group);
+ BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable);
+ symbolTable.setVariableExtensions("gl_ViewIndex", 1, &E_GL_EXT_multiview);
+ BuiltInVariable("gl_ViewIndex", EbvViewIndex, symbolTable);
+ }
+
+ // GL_KHR_shader_subgroup
+ if ((profile == EEsProfile && version >= 310) ||
+ (profile != EEsProfile && version >= 140)) {
+ symbolTable.setVariableExtensions("gl_NumSubgroups", 1, &E_GL_KHR_shader_subgroup_basic);
+ symbolTable.setVariableExtensions("gl_SubgroupID", 1, &E_GL_KHR_shader_subgroup_basic);
+
+ BuiltInVariable("gl_NumSubgroups", EbvNumSubgroups, symbolTable);
+ BuiltInVariable("gl_SubgroupID", EbvSubgroupID, symbolTable);
+
+ symbolTable.setFunctionExtensions("subgroupMemoryBarrierShared", 1, &E_GL_KHR_shader_subgroup_basic);
+ }
+
+ symbolTable.setFunctionExtensions("coopMatLoadNV", 1, &E_GL_NV_cooperative_matrix);
+ symbolTable.setFunctionExtensions("coopMatStoreNV", 1, &E_GL_NV_cooperative_matrix);
+ symbolTable.setFunctionExtensions("coopMatMulAddNV", 1, &E_GL_NV_cooperative_matrix);
+
+ break;
+#ifdef NV_EXTENSIONS
+ case EShLangRayGenNV:
+ case EShLangIntersectNV:
+ case EShLangAnyHitNV:
+ case EShLangClosestHitNV:
+ case EShLangMissNV:
+ case EShLangCallableNV:
+ if (profile != EEsProfile && version >= 460) {
+ symbolTable.setVariableExtensions("gl_LaunchIDNV", 1, &E_GL_NV_ray_tracing);
+ symbolTable.setVariableExtensions("gl_LaunchSizeNV", 1, &E_GL_NV_ray_tracing);
+ symbolTable.setVariableExtensions("gl_PrimitiveID", 1, &E_GL_NV_ray_tracing);
+ symbolTable.setVariableExtensions("gl_InstanceID", 1, &E_GL_NV_ray_tracing);
+ symbolTable.setVariableExtensions("gl_InstanceCustomIndexNV", 1, &E_GL_NV_ray_tracing);
+ symbolTable.setVariableExtensions("gl_WorldRayOriginNV", 1, &E_GL_NV_ray_tracing);
+ symbolTable.setVariableExtensions("gl_WorldRayDirectionNV", 1, &E_GL_NV_ray_tracing);
+ symbolTable.setVariableExtensions("gl_ObjectRayOriginNV", 1, &E_GL_NV_ray_tracing);
+ symbolTable.setVariableExtensions("gl_ObjectRayDirectionNV", 1, &E_GL_NV_ray_tracing);
+ symbolTable.setVariableExtensions("gl_RayTminNV", 1, &E_GL_NV_ray_tracing);
+ symbolTable.setVariableExtensions("gl_RayTmaxNV", 1, &E_GL_NV_ray_tracing);
+ symbolTable.setVariableExtensions("gl_HitTNV", 1, &E_GL_NV_ray_tracing);
+ symbolTable.setVariableExtensions("gl_HitKindNV", 1, &E_GL_NV_ray_tracing);
+ symbolTable.setVariableExtensions("gl_ObjectToWorldNV", 1, &E_GL_NV_ray_tracing);
+ symbolTable.setVariableExtensions("gl_WorldToObjectNV", 1, &E_GL_NV_ray_tracing);
+ symbolTable.setVariableExtensions("gl_IncomingRayFlagsNV", 1, &E_GL_NV_ray_tracing);
+
+ symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group);
+
+ BuiltInVariable("gl_LaunchIDNV", EbvLaunchIdNV, symbolTable);
+ BuiltInVariable("gl_LaunchSizeNV", EbvLaunchSizeNV, symbolTable);
+ BuiltInVariable("gl_PrimitiveID", EbvPrimitiveId, symbolTable);
+ BuiltInVariable("gl_InstanceID", EbvInstanceId, symbolTable);
+ BuiltInVariable("gl_InstanceCustomIndexNV", EbvInstanceCustomIndexNV,symbolTable);
+ BuiltInVariable("gl_WorldRayOriginNV", EbvWorldRayOriginNV, symbolTable);
+ BuiltInVariable("gl_WorldRayDirectionNV", EbvWorldRayDirectionNV, symbolTable);
+ BuiltInVariable("gl_ObjectRayOriginNV", EbvObjectRayOriginNV, symbolTable);
+ BuiltInVariable("gl_ObjectRayDirectionNV", EbvObjectRayDirectionNV, symbolTable);
+ BuiltInVariable("gl_RayTminNV", EbvRayTminNV, symbolTable);
+ BuiltInVariable("gl_RayTmaxNV", EbvRayTmaxNV, symbolTable);
+ BuiltInVariable("gl_HitTNV", EbvHitTNV, symbolTable);
+ BuiltInVariable("gl_HitKindNV", EbvHitKindNV, symbolTable);
+ BuiltInVariable("gl_ObjectToWorldNV", EbvObjectToWorldNV, symbolTable);
+ BuiltInVariable("gl_WorldToObjectNV", EbvWorldToObjectNV, symbolTable);
+ BuiltInVariable("gl_IncomingRayFlagsNV", EbvIncomingRayFlagsNV, symbolTable);
+ BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable);
+ }
+ break;
+ case EShLangMeshNV:
+ if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) {
+ // per-vertex builtins
+ symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_Position", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_PointSize", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_ClipDistance", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_CullDistance", 1, &E_GL_NV_mesh_shader);
+
+ BuiltInVariable("gl_MeshVerticesNV", "gl_Position", EbvPosition, symbolTable);
+ BuiltInVariable("gl_MeshVerticesNV", "gl_PointSize", EbvPointSize, symbolTable);
+ BuiltInVariable("gl_MeshVerticesNV", "gl_ClipDistance", EbvClipDistance, symbolTable);
+ BuiltInVariable("gl_MeshVerticesNV", "gl_CullDistance", EbvCullDistance, symbolTable);
+
+ symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_PositionPerViewNV", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_ClipDistancePerViewNV", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_CullDistancePerViewNV", 1, &E_GL_NV_mesh_shader);
+
+ BuiltInVariable("gl_MeshVerticesNV", "gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable);
+ BuiltInVariable("gl_MeshVerticesNV", "gl_ClipDistancePerViewNV", EbvClipDistancePerViewNV, symbolTable);
+ BuiltInVariable("gl_MeshVerticesNV", "gl_CullDistancePerViewNV", EbvCullDistancePerViewNV, symbolTable);
+
+ // per-primitive builtins
+ symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_PrimitiveID", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_Layer", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_ViewportIndex", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_ViewportMask", 1, &E_GL_NV_mesh_shader);
+
+ BuiltInVariable("gl_MeshPrimitivesNV", "gl_PrimitiveID", EbvPrimitiveId, symbolTable);
+ BuiltInVariable("gl_MeshPrimitivesNV", "gl_Layer", EbvLayer, symbolTable);
+ BuiltInVariable("gl_MeshPrimitivesNV", "gl_ViewportIndex", EbvViewportIndex, symbolTable);
+ BuiltInVariable("gl_MeshPrimitivesNV", "gl_ViewportMask", EbvViewportMaskNV, symbolTable);
+
+ // per-view per-primitive builtins
+ symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_LayerPerViewNV", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_ViewportMaskPerViewNV", 1, &E_GL_NV_mesh_shader);
+
+ BuiltInVariable("gl_MeshPrimitivesNV", "gl_LayerPerViewNV", EbvLayerPerViewNV, symbolTable);
+ BuiltInVariable("gl_MeshPrimitivesNV", "gl_ViewportMaskPerViewNV", EbvViewportMaskPerViewNV, symbolTable);
+
+ // other builtins
+ symbolTable.setVariableExtensions("gl_PrimitiveCountNV", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_PrimitiveIndicesNV", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_MeshViewCountNV", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_MeshViewIndicesNV", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_WorkGroupSize", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_WorkGroupID", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_LocalInvocationID", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_GlobalInvocationID", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_LocalInvocationIndex", 1, &E_GL_NV_mesh_shader);
+
+ BuiltInVariable("gl_PrimitiveCountNV", EbvPrimitiveCountNV, symbolTable);
+ BuiltInVariable("gl_PrimitiveIndicesNV", EbvPrimitiveIndicesNV, symbolTable);
+ BuiltInVariable("gl_MeshViewCountNV", EbvMeshViewCountNV, symbolTable);
+ BuiltInVariable("gl_MeshViewIndicesNV", EbvMeshViewIndicesNV, symbolTable);
+ BuiltInVariable("gl_WorkGroupSize", EbvWorkGroupSize, symbolTable);
+ BuiltInVariable("gl_WorkGroupID", EbvWorkGroupId, symbolTable);
+ BuiltInVariable("gl_LocalInvocationID", EbvLocalInvocationId, symbolTable);
+ BuiltInVariable("gl_GlobalInvocationID", EbvGlobalInvocationId, symbolTable);
+ BuiltInVariable("gl_LocalInvocationIndex", EbvLocalInvocationIndex, symbolTable);
+
+ // builtin constants
+ symbolTable.setVariableExtensions("gl_MaxMeshOutputVerticesNV", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_MaxMeshOutputPrimitivesNV", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_MaxMeshWorkGroupSizeNV", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_MaxMeshViewCountNV", 1, &E_GL_NV_mesh_shader);
+
+ // builtin functions
+ symbolTable.setFunctionExtensions("barrier", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setFunctionExtensions("memoryBarrierShared", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setFunctionExtensions("groupMemoryBarrier", 1, &E_GL_NV_mesh_shader);
+ }
+
+ if (profile != EEsProfile && version >= 450) {
+ // GL_EXT_device_group
+ symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group);
+ BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable);
+
+ // GL_ARB_shader_draw_parameters
+ symbolTable.setVariableExtensions("gl_DrawIDARB", 1, &E_GL_ARB_shader_draw_parameters);
+ BuiltInVariable("gl_DrawIDARB", EbvDrawId, symbolTable);
+ if (version >= 460) {
+ BuiltInVariable("gl_DrawID", EbvDrawId, symbolTable);
+ }
+
+ // GL_ARB_shader_ballot
+ symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot);
+
+ BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable);
+ BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable);
+ BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable);
+ BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable);
+ BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable);
+ BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable);
+
+ if (spvVersion.vulkan > 0)
+ // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan
+ SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable);
+ else
+ BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable);
+ }
+
+ // GL_KHR_shader_subgroup
+ if ((profile == EEsProfile && version >= 310) ||
+ (profile != EEsProfile && version >= 140)) {
+ symbolTable.setVariableExtensions("gl_NumSubgroups", 1, &E_GL_KHR_shader_subgroup_basic);
+ symbolTable.setVariableExtensions("gl_SubgroupID", 1, &E_GL_KHR_shader_subgroup_basic);
+ symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic);
+ symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic);
+ symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+
+ BuiltInVariable("gl_NumSubgroups", EbvNumSubgroups, symbolTable);
+ BuiltInVariable("gl_SubgroupID", EbvSubgroupID, symbolTable);
+ BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable);
+ BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable);
+ BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable);
+ BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable);
+ BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable);
+ BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable);
+ BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable);
+
+ symbolTable.setFunctionExtensions("subgroupMemoryBarrierShared", 1, &E_GL_KHR_shader_subgroup_basic);
+ }
+ break;
+
+ case EShLangTaskNV:
+ if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) {
+ symbolTable.setVariableExtensions("gl_TaskCountNV", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_WorkGroupSize", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_WorkGroupID", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_LocalInvocationID", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_GlobalInvocationID", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_LocalInvocationIndex", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_MeshViewCountNV", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_MeshViewIndicesNV", 1, &E_GL_NV_mesh_shader);
+
+ BuiltInVariable("gl_TaskCountNV", EbvTaskCountNV, symbolTable);
+ BuiltInVariable("gl_WorkGroupSize", EbvWorkGroupSize, symbolTable);
+ BuiltInVariable("gl_WorkGroupID", EbvWorkGroupId, symbolTable);
+ BuiltInVariable("gl_LocalInvocationID", EbvLocalInvocationId, symbolTable);
+ BuiltInVariable("gl_GlobalInvocationID", EbvGlobalInvocationId, symbolTable);
+ BuiltInVariable("gl_LocalInvocationIndex", EbvLocalInvocationIndex, symbolTable);
+ BuiltInVariable("gl_MeshViewCountNV", EbvMeshViewCountNV, symbolTable);
+ BuiltInVariable("gl_MeshViewIndicesNV", EbvMeshViewIndicesNV, symbolTable);
+
+ symbolTable.setVariableExtensions("gl_MaxTaskWorkGroupSizeNV", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setVariableExtensions("gl_MaxMeshViewCountNV", 1, &E_GL_NV_mesh_shader);
+
+ symbolTable.setFunctionExtensions("barrier", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setFunctionExtensions("memoryBarrierShared", 1, &E_GL_NV_mesh_shader);
+ symbolTable.setFunctionExtensions("groupMemoryBarrier", 1, &E_GL_NV_mesh_shader);
+ }
+
+ if (profile != EEsProfile && version >= 450) {
+ // GL_EXT_device_group
+ symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group);
+ BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable);
+
+ // GL_ARB_shader_draw_parameters
+ symbolTable.setVariableExtensions("gl_DrawIDARB", 1, &E_GL_ARB_shader_draw_parameters);
+ BuiltInVariable("gl_DrawIDARB", EbvDrawId, symbolTable);
+ if (version >= 460) {
+ BuiltInVariable("gl_DrawID", EbvDrawId, symbolTable);
+ }
+
+ // GL_ARB_shader_ballot
+ symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot);
+ symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot);
+
+ BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable);
+ BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable);
+ BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable);
+ BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable);
+ BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable);
+ BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable);
+
+ if (spvVersion.vulkan > 0)
+ // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan
+ SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable);
+ else
+ BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable);
+ }
+
+ // GL_KHR_shader_subgroup
+ if ((profile == EEsProfile && version >= 310) ||
+ (profile != EEsProfile && version >= 140)) {
+ symbolTable.setVariableExtensions("gl_NumSubgroups", 1, &E_GL_KHR_shader_subgroup_basic);
+ symbolTable.setVariableExtensions("gl_SubgroupID", 1, &E_GL_KHR_shader_subgroup_basic);
+ symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic);
+ symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic);
+ symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+ symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot);
+
+ BuiltInVariable("gl_NumSubgroups", EbvNumSubgroups, symbolTable);
+ BuiltInVariable("gl_SubgroupID", EbvSubgroupID, symbolTable);
+ BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable);
+ BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable);
+ BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable);
+ BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable);
+ BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable);
+ BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable);
+ BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable);
+
+ symbolTable.setFunctionExtensions("subgroupMemoryBarrierShared", 1, &E_GL_KHR_shader_subgroup_basic);
+ }
+ break;
+#endif
+
+ default:
+ assert(false && "Language not supported");
+ break;
+ }
+
+ //
+ // Next, identify which built-ins have a mapping to an operator.
+ // If PureOperatorBuiltins is false, those that are not identified as such are
+ // expected to be resolved through a library of functions, versus as
+ // operations.
+ //
+ symbolTable.relateToOperator("not", EOpVectorLogicalNot);
+
+ symbolTable.relateToOperator("matrixCompMult", EOpMul);
+ // 120 and 150 are correct for both ES and desktop
+ if (version >= 120) {
+ symbolTable.relateToOperator("outerProduct", EOpOuterProduct);
+ symbolTable.relateToOperator("transpose", EOpTranspose);
+ if (version >= 150) {
+ symbolTable.relateToOperator("determinant", EOpDeterminant);
+ symbolTable.relateToOperator("inverse", EOpMatrixInverse);
+ }
+ }
+
+ symbolTable.relateToOperator("mod", EOpMod);
+ symbolTable.relateToOperator("modf", EOpModf);
+
+ symbolTable.relateToOperator("equal", EOpVectorEqual);
+ symbolTable.relateToOperator("notEqual", EOpVectorNotEqual);
+ symbolTable.relateToOperator("lessThan", EOpLessThan);
+ symbolTable.relateToOperator("greaterThan", EOpGreaterThan);
+ symbolTable.relateToOperator("lessThanEqual", EOpLessThanEqual);
+ symbolTable.relateToOperator("greaterThanEqual", EOpGreaterThanEqual);
+
+ symbolTable.relateToOperator("radians", EOpRadians);
+ symbolTable.relateToOperator("degrees", EOpDegrees);
+ symbolTable.relateToOperator("sin", EOpSin);
+ symbolTable.relateToOperator("cos", EOpCos);
+ symbolTable.relateToOperator("tan", EOpTan);
+ symbolTable.relateToOperator("asin", EOpAsin);
+ symbolTable.relateToOperator("acos", EOpAcos);
+ symbolTable.relateToOperator("atan", EOpAtan);
+ symbolTable.relateToOperator("sinh", EOpSinh);
+ symbolTable.relateToOperator("cosh", EOpCosh);
+ symbolTable.relateToOperator("tanh", EOpTanh);
+ symbolTable.relateToOperator("asinh", EOpAsinh);
+ symbolTable.relateToOperator("acosh", EOpAcosh);
+ symbolTable.relateToOperator("atanh", EOpAtanh);
+
+ symbolTable.relateToOperator("pow", EOpPow);
+ symbolTable.relateToOperator("exp2", EOpExp2);
+ symbolTable.relateToOperator("log", EOpLog);
+ symbolTable.relateToOperator("exp", EOpExp);
+ symbolTable.relateToOperator("log2", EOpLog2);
+ symbolTable.relateToOperator("sqrt", EOpSqrt);
+ symbolTable.relateToOperator("inversesqrt", EOpInverseSqrt);
+
+ symbolTable.relateToOperator("abs", EOpAbs);
+ symbolTable.relateToOperator("sign", EOpSign);
+ symbolTable.relateToOperator("floor", EOpFloor);
+ symbolTable.relateToOperator("trunc", EOpTrunc);
+ symbolTable.relateToOperator("round", EOpRound);
+ symbolTable.relateToOperator("roundEven", EOpRoundEven);
+ symbolTable.relateToOperator("ceil", EOpCeil);
+ symbolTable.relateToOperator("fract", EOpFract);
+ symbolTable.relateToOperator("min", EOpMin);
+ symbolTable.relateToOperator("max", EOpMax);
+ symbolTable.relateToOperator("clamp", EOpClamp);
+ symbolTable.relateToOperator("mix", EOpMix);
+ symbolTable.relateToOperator("step", EOpStep);
+ symbolTable.relateToOperator("smoothstep", EOpSmoothStep);
+
+ symbolTable.relateToOperator("isnan", EOpIsNan);
+ symbolTable.relateToOperator("isinf", EOpIsInf);
+
+ symbolTable.relateToOperator("floatBitsToInt", EOpFloatBitsToInt);
+ symbolTable.relateToOperator("floatBitsToUint", EOpFloatBitsToUint);
+ symbolTable.relateToOperator("intBitsToFloat", EOpIntBitsToFloat);
+ symbolTable.relateToOperator("uintBitsToFloat", EOpUintBitsToFloat);
+ symbolTable.relateToOperator("doubleBitsToInt64", EOpDoubleBitsToInt64);
+ symbolTable.relateToOperator("doubleBitsToUint64", EOpDoubleBitsToUint64);
+ symbolTable.relateToOperator("int64BitsToDouble", EOpInt64BitsToDouble);
+ symbolTable.relateToOperator("uint64BitsToDouble", EOpUint64BitsToDouble);
+ symbolTable.relateToOperator("halfBitsToInt16", EOpFloat16BitsToInt16);
+ symbolTable.relateToOperator("halfBitsToUint16", EOpFloat16BitsToUint16);
+ symbolTable.relateToOperator("float16BitsToInt16", EOpFloat16BitsToInt16);
+ symbolTable.relateToOperator("float16BitsToUint16", EOpFloat16BitsToUint16);
+ symbolTable.relateToOperator("int16BitsToFloat16", EOpInt16BitsToFloat16);
+ symbolTable.relateToOperator("uint16BitsToFloat16", EOpUint16BitsToFloat16);
+
+ symbolTable.relateToOperator("int16BitsToHalf", EOpInt16BitsToFloat16);
+ symbolTable.relateToOperator("uint16BitsToHalf", EOpUint16BitsToFloat16);
+
+ symbolTable.relateToOperator("packSnorm2x16", EOpPackSnorm2x16);
+ symbolTable.relateToOperator("unpackSnorm2x16", EOpUnpackSnorm2x16);
+ symbolTable.relateToOperator("packUnorm2x16", EOpPackUnorm2x16);
+ symbolTable.relateToOperator("unpackUnorm2x16", EOpUnpackUnorm2x16);
+
+ symbolTable.relateToOperator("packSnorm4x8", EOpPackSnorm4x8);
+ symbolTable.relateToOperator("unpackSnorm4x8", EOpUnpackSnorm4x8);
+ symbolTable.relateToOperator("packUnorm4x8", EOpPackUnorm4x8);
+ symbolTable.relateToOperator("unpackUnorm4x8", EOpUnpackUnorm4x8);
+
+ symbolTable.relateToOperator("packDouble2x32", EOpPackDouble2x32);
+ symbolTable.relateToOperator("unpackDouble2x32", EOpUnpackDouble2x32);
+
+ symbolTable.relateToOperator("packHalf2x16", EOpPackHalf2x16);
+ symbolTable.relateToOperator("unpackHalf2x16", EOpUnpackHalf2x16);
+
+ symbolTable.relateToOperator("packInt2x32", EOpPackInt2x32);
+ symbolTable.relateToOperator("unpackInt2x32", EOpUnpackInt2x32);
+ symbolTable.relateToOperator("packUint2x32", EOpPackUint2x32);
+ symbolTable.relateToOperator("unpackUint2x32", EOpUnpackUint2x32);
+
+ symbolTable.relateToOperator("packInt2x16", EOpPackInt2x16);
+ symbolTable.relateToOperator("unpackInt2x16", EOpUnpackInt2x16);
+ symbolTable.relateToOperator("packUint2x16", EOpPackUint2x16);
+ symbolTable.relateToOperator("unpackUint2x16", EOpUnpackUint2x16);
+
+ symbolTable.relateToOperator("packInt4x16", EOpPackInt4x16);
+ symbolTable.relateToOperator("unpackInt4x16", EOpUnpackInt4x16);
+ symbolTable.relateToOperator("packUint4x16", EOpPackUint4x16);
+ symbolTable.relateToOperator("unpackUint4x16", EOpUnpackUint4x16);
+ symbolTable.relateToOperator("packFloat2x16", EOpPackFloat2x16);
+ symbolTable.relateToOperator("unpackFloat2x16", EOpUnpackFloat2x16);
+
+ symbolTable.relateToOperator("pack16", EOpPack16);
+ symbolTable.relateToOperator("pack32", EOpPack32);
+ symbolTable.relateToOperator("pack64", EOpPack64);
+
+ symbolTable.relateToOperator("unpack32", EOpUnpack32);
+ symbolTable.relateToOperator("unpack16", EOpUnpack16);
+ symbolTable.relateToOperator("unpack8", EOpUnpack8);
+
+ symbolTable.relateToOperator("length", EOpLength);
+ symbolTable.relateToOperator("distance", EOpDistance);
+ symbolTable.relateToOperator("dot", EOpDot);
+ symbolTable.relateToOperator("cross", EOpCross);
+ symbolTable.relateToOperator("normalize", EOpNormalize);
+ symbolTable.relateToOperator("faceforward", EOpFaceForward);
+ symbolTable.relateToOperator("reflect", EOpReflect);
+ symbolTable.relateToOperator("refract", EOpRefract);
+
+ symbolTable.relateToOperator("any", EOpAny);
+ symbolTable.relateToOperator("all", EOpAll);
+
+ symbolTable.relateToOperator("barrier", EOpBarrier);
+ symbolTable.relateToOperator("controlBarrier", EOpBarrier);
+ symbolTable.relateToOperator("memoryBarrier", EOpMemoryBarrier);
+ symbolTable.relateToOperator("memoryBarrierAtomicCounter", EOpMemoryBarrierAtomicCounter);
+ symbolTable.relateToOperator("memoryBarrierBuffer", EOpMemoryBarrierBuffer);
+ symbolTable.relateToOperator("memoryBarrierImage", EOpMemoryBarrierImage);
+
+ symbolTable.relateToOperator("atomicAdd", EOpAtomicAdd);
+ symbolTable.relateToOperator("atomicMin", EOpAtomicMin);
+ symbolTable.relateToOperator("atomicMax", EOpAtomicMax);
+ symbolTable.relateToOperator("atomicAnd", EOpAtomicAnd);
+ symbolTable.relateToOperator("atomicOr", EOpAtomicOr);
+ symbolTable.relateToOperator("atomicXor", EOpAtomicXor);
+ symbolTable.relateToOperator("atomicExchange", EOpAtomicExchange);
+ symbolTable.relateToOperator("atomicCompSwap", EOpAtomicCompSwap);
+ symbolTable.relateToOperator("atomicLoad", EOpAtomicLoad);
+ symbolTable.relateToOperator("atomicStore", EOpAtomicStore);
+
+ symbolTable.relateToOperator("atomicCounterIncrement", EOpAtomicCounterIncrement);
+ symbolTable.relateToOperator("atomicCounterDecrement", EOpAtomicCounterDecrement);
+ symbolTable.relateToOperator("atomicCounter", EOpAtomicCounter);
+
+ if (profile != EEsProfile && version >= 460) {
+ symbolTable.relateToOperator("atomicCounterAdd", EOpAtomicCounterAdd);
+ symbolTable.relateToOperator("atomicCounterSubtract", EOpAtomicCounterSubtract);
+ symbolTable.relateToOperator("atomicCounterMin", EOpAtomicCounterMin);
+ symbolTable.relateToOperator("atomicCounterMax", EOpAtomicCounterMax);
+ symbolTable.relateToOperator("atomicCounterAnd", EOpAtomicCounterAnd);
+ symbolTable.relateToOperator("atomicCounterOr", EOpAtomicCounterOr);
+ symbolTable.relateToOperator("atomicCounterXor", EOpAtomicCounterXor);
+ symbolTable.relateToOperator("atomicCounterExchange", EOpAtomicCounterExchange);
+ symbolTable.relateToOperator("atomicCounterCompSwap", EOpAtomicCounterCompSwap);
+ }
+
+ symbolTable.relateToOperator("fma", EOpFma);
+ symbolTable.relateToOperator("frexp", EOpFrexp);
+ symbolTable.relateToOperator("ldexp", EOpLdexp);
+ symbolTable.relateToOperator("uaddCarry", EOpAddCarry);
+ symbolTable.relateToOperator("usubBorrow", EOpSubBorrow);
+ symbolTable.relateToOperator("umulExtended", EOpUMulExtended);
+ symbolTable.relateToOperator("imulExtended", EOpIMulExtended);
+ symbolTable.relateToOperator("bitfieldExtract", EOpBitfieldExtract);
+ symbolTable.relateToOperator("bitfieldInsert", EOpBitfieldInsert);
+ symbolTable.relateToOperator("bitfieldReverse", EOpBitFieldReverse);
+ symbolTable.relateToOperator("bitCount", EOpBitCount);
+ symbolTable.relateToOperator("findLSB", EOpFindLSB);
+ symbolTable.relateToOperator("findMSB", EOpFindMSB);
+
+ if (PureOperatorBuiltins) {
+ symbolTable.relateToOperator("imageSize", EOpImageQuerySize);
+ symbolTable.relateToOperator("imageSamples", EOpImageQuerySamples);
+ symbolTable.relateToOperator("imageLoad", EOpImageLoad);
+ symbolTable.relateToOperator("imageStore", EOpImageStore);
+ symbolTable.relateToOperator("imageAtomicAdd", EOpImageAtomicAdd);
+ symbolTable.relateToOperator("imageAtomicMin", EOpImageAtomicMin);
+ symbolTable.relateToOperator("imageAtomicMax", EOpImageAtomicMax);
+ symbolTable.relateToOperator("imageAtomicAnd", EOpImageAtomicAnd);
+ symbolTable.relateToOperator("imageAtomicOr", EOpImageAtomicOr);
+ symbolTable.relateToOperator("imageAtomicXor", EOpImageAtomicXor);
+ symbolTable.relateToOperator("imageAtomicExchange", EOpImageAtomicExchange);
+ symbolTable.relateToOperator("imageAtomicCompSwap", EOpImageAtomicCompSwap);
+ symbolTable.relateToOperator("imageAtomicLoad", EOpImageAtomicLoad);
+ symbolTable.relateToOperator("imageAtomicStore", EOpImageAtomicStore);
+
+ symbolTable.relateToOperator("subpassLoad", EOpSubpassLoad);
+ symbolTable.relateToOperator("subpassLoadMS", EOpSubpassLoadMS);
+
+ symbolTable.relateToOperator("textureSize", EOpTextureQuerySize);
+ symbolTable.relateToOperator("textureQueryLod", EOpTextureQueryLod);
+ symbolTable.relateToOperator("textureQueryLevels", EOpTextureQueryLevels);
+ symbolTable.relateToOperator("textureSamples", EOpTextureQuerySamples);
+ symbolTable.relateToOperator("texture", EOpTexture);
+ symbolTable.relateToOperator("textureProj", EOpTextureProj);
+ symbolTable.relateToOperator("textureLod", EOpTextureLod);
+ symbolTable.relateToOperator("textureOffset", EOpTextureOffset);
+ symbolTable.relateToOperator("texelFetch", EOpTextureFetch);
+ symbolTable.relateToOperator("texelFetchOffset", EOpTextureFetchOffset);
+ symbolTable.relateToOperator("textureProjOffset", EOpTextureProjOffset);
+ symbolTable.relateToOperator("textureLodOffset", EOpTextureLodOffset);
+ symbolTable.relateToOperator("textureProjLod", EOpTextureProjLod);
+ symbolTable.relateToOperator("textureProjLodOffset", EOpTextureProjLodOffset);
+ symbolTable.relateToOperator("textureGrad", EOpTextureGrad);
+ symbolTable.relateToOperator("textureGradOffset", EOpTextureGradOffset);
+ symbolTable.relateToOperator("textureProjGrad", EOpTextureProjGrad);
+ symbolTable.relateToOperator("textureProjGradOffset", EOpTextureProjGradOffset);
+ symbolTable.relateToOperator("textureGather", EOpTextureGather);
+ symbolTable.relateToOperator("textureGatherOffset", EOpTextureGatherOffset);
+ symbolTable.relateToOperator("textureGatherOffsets", EOpTextureGatherOffsets);
+
+ symbolTable.relateToOperator("noise1", EOpNoise);
+ symbolTable.relateToOperator("noise2", EOpNoise);
+ symbolTable.relateToOperator("noise3", EOpNoise);
+ symbolTable.relateToOperator("noise4", EOpNoise);
+
+#ifdef NV_EXTENSIONS
+ symbolTable.relateToOperator("textureFootprintNV", EOpImageSampleFootprintNV);
+ symbolTable.relateToOperator("textureFootprintClampNV", EOpImageSampleFootprintClampNV);
+ symbolTable.relateToOperator("textureFootprintLodNV", EOpImageSampleFootprintLodNV);
+ symbolTable.relateToOperator("textureFootprintGradNV", EOpImageSampleFootprintGradNV);
+ symbolTable.relateToOperator("textureFootprintGradClampNV", EOpImageSampleFootprintGradClampNV);
+#endif
+
+ if (spvVersion.spv == 0 && (IncludeLegacy(version, profile, spvVersion) ||
+ (profile == EEsProfile && version == 100))) {
+ symbolTable.relateToOperator("ftransform", EOpFtransform);
+
+ symbolTable.relateToOperator("texture1D", EOpTexture);
+ symbolTable.relateToOperator("texture1DGradARB", EOpTextureGrad);
+ symbolTable.relateToOperator("texture1DProj", EOpTextureProj);
+ symbolTable.relateToOperator("texture1DProjGradARB", EOpTextureProjGrad);
+ symbolTable.relateToOperator("texture1DLod", EOpTextureLod);
+ symbolTable.relateToOperator("texture1DProjLod", EOpTextureProjLod);
+
+ symbolTable.relateToOperator("texture2DRect", EOpTexture);
+ symbolTable.relateToOperator("texture2DRectProj", EOpTextureProj);
+ symbolTable.relateToOperator("texture2DRectGradARB", EOpTextureGrad);
+ symbolTable.relateToOperator("texture2DRectProjGradARB", EOpTextureProjGrad);
+ symbolTable.relateToOperator("shadow2DRect", EOpTexture);
+ symbolTable.relateToOperator("shadow2DRectProj", EOpTextureProj);
+ symbolTable.relateToOperator("shadow2DRectGradARB", EOpTextureGrad);
+ symbolTable.relateToOperator("shadow2DRectProjGradARB", EOpTextureProjGrad);
+
+ symbolTable.relateToOperator("texture2D", EOpTexture);
+ symbolTable.relateToOperator("texture2DProj", EOpTextureProj);
+ symbolTable.relateToOperator("texture2DGradEXT", EOpTextureGrad);
+ symbolTable.relateToOperator("texture2DGradARB", EOpTextureGrad);
+ symbolTable.relateToOperator("texture2DProjGradEXT", EOpTextureProjGrad);
+ symbolTable.relateToOperator("texture2DProjGradARB", EOpTextureProjGrad);
+ symbolTable.relateToOperator("texture2DLod", EOpTextureLod);
+ symbolTable.relateToOperator("texture2DLodEXT", EOpTextureLod);
+ symbolTable.relateToOperator("texture2DProjLod", EOpTextureProjLod);
+ symbolTable.relateToOperator("texture2DProjLodEXT", EOpTextureProjLod);
+
+ symbolTable.relateToOperator("texture3D", EOpTexture);
+ symbolTable.relateToOperator("texture3DGradARB", EOpTextureGrad);
+ symbolTable.relateToOperator("texture3DProj", EOpTextureProj);
+ symbolTable.relateToOperator("texture3DProjGradARB", EOpTextureProjGrad);
+ symbolTable.relateToOperator("texture3DLod", EOpTextureLod);
+ symbolTable.relateToOperator("texture3DProjLod", EOpTextureProjLod);
+ symbolTable.relateToOperator("textureCube", EOpTexture);
+ symbolTable.relateToOperator("textureCubeGradEXT", EOpTextureGrad);
+ symbolTable.relateToOperator("textureCubeGradARB", EOpTextureGrad);
+ symbolTable.relateToOperator("textureCubeLod", EOpTextureLod);
+ symbolTable.relateToOperator("textureCubeLodEXT", EOpTextureLod);
+ symbolTable.relateToOperator("shadow1D", EOpTexture);
+ symbolTable.relateToOperator("shadow1DGradARB", EOpTextureGrad);
+ symbolTable.relateToOperator("shadow2D", EOpTexture);
+ symbolTable.relateToOperator("shadow2DGradARB", EOpTextureGrad);
+ symbolTable.relateToOperator("shadow1DProj", EOpTextureProj);
+ symbolTable.relateToOperator("shadow2DProj", EOpTextureProj);
+ symbolTable.relateToOperator("shadow1DProjGradARB", EOpTextureProjGrad);
+ symbolTable.relateToOperator("shadow2DProjGradARB", EOpTextureProjGrad);
+ symbolTable.relateToOperator("shadow1DLod", EOpTextureLod);
+ symbolTable.relateToOperator("shadow2DLod", EOpTextureLod);
+ symbolTable.relateToOperator("shadow1DProjLod", EOpTextureProjLod);
+ symbolTable.relateToOperator("shadow2DProjLod", EOpTextureProjLod);
+ }
+
+ if (profile != EEsProfile) {
+ symbolTable.relateToOperator("sparseTextureARB", EOpSparseTexture);
+ symbolTable.relateToOperator("sparseTextureLodARB", EOpSparseTextureLod);
+ symbolTable.relateToOperator("sparseTextureOffsetARB", EOpSparseTextureOffset);
+ symbolTable.relateToOperator("sparseTexelFetchARB", EOpSparseTextureFetch);
+ symbolTable.relateToOperator("sparseTexelFetchOffsetARB", EOpSparseTextureFetchOffset);
+ symbolTable.relateToOperator("sparseTextureLodOffsetARB", EOpSparseTextureLodOffset);
+ symbolTable.relateToOperator("sparseTextureGradARB", EOpSparseTextureGrad);
+ symbolTable.relateToOperator("sparseTextureGradOffsetARB", EOpSparseTextureGradOffset);
+ symbolTable.relateToOperator("sparseTextureGatherARB", EOpSparseTextureGather);
+ symbolTable.relateToOperator("sparseTextureGatherOffsetARB", EOpSparseTextureGatherOffset);
+ symbolTable.relateToOperator("sparseTextureGatherOffsetsARB", EOpSparseTextureGatherOffsets);
+ symbolTable.relateToOperator("sparseImageLoadARB", EOpSparseImageLoad);
+ symbolTable.relateToOperator("sparseTexelsResidentARB", EOpSparseTexelsResident);
+
+ symbolTable.relateToOperator("sparseTextureClampARB", EOpSparseTextureClamp);
+ symbolTable.relateToOperator("sparseTextureOffsetClampARB", EOpSparseTextureOffsetClamp);
+ symbolTable.relateToOperator("sparseTextureGradClampARB", EOpSparseTextureGradClamp);
+ symbolTable.relateToOperator("sparseTextureGradOffsetClampARB", EOpSparseTextureGradOffsetClamp);
+ symbolTable.relateToOperator("textureClampARB", EOpTextureClamp);
+ symbolTable.relateToOperator("textureOffsetClampARB", EOpTextureOffsetClamp);
+ symbolTable.relateToOperator("textureGradClampARB", EOpTextureGradClamp);
+ symbolTable.relateToOperator("textureGradOffsetClampARB", EOpTextureGradOffsetClamp);
+
+ symbolTable.relateToOperator("ballotARB", EOpBallot);
+ symbolTable.relateToOperator("readInvocationARB", EOpReadInvocation);
+ symbolTable.relateToOperator("readFirstInvocationARB", EOpReadFirstInvocation);
+
+ if (version >= 430) {
+ symbolTable.relateToOperator("anyInvocationARB", EOpAnyInvocation);
+ symbolTable.relateToOperator("allInvocationsARB", EOpAllInvocations);
+ symbolTable.relateToOperator("allInvocationsEqualARB", EOpAllInvocationsEqual);
+ }
+ if (version >= 460) {
+ symbolTable.relateToOperator("anyInvocation", EOpAnyInvocation);
+ symbolTable.relateToOperator("allInvocations", EOpAllInvocations);
+ symbolTable.relateToOperator("allInvocationsEqual", EOpAllInvocationsEqual);
+ }
+#ifdef AMD_EXTENSIONS
+ symbolTable.relateToOperator("minInvocationsAMD", EOpMinInvocations);
+ symbolTable.relateToOperator("maxInvocationsAMD", EOpMaxInvocations);
+ symbolTable.relateToOperator("addInvocationsAMD", EOpAddInvocations);
+ symbolTable.relateToOperator("minInvocationsNonUniformAMD", EOpMinInvocationsNonUniform);
+ symbolTable.relateToOperator("maxInvocationsNonUniformAMD", EOpMaxInvocationsNonUniform);
+ symbolTable.relateToOperator("addInvocationsNonUniformAMD", EOpAddInvocationsNonUniform);
+ symbolTable.relateToOperator("minInvocationsInclusiveScanAMD", EOpMinInvocationsInclusiveScan);
+ symbolTable.relateToOperator("maxInvocationsInclusiveScanAMD", EOpMaxInvocationsInclusiveScan);
+ symbolTable.relateToOperator("addInvocationsInclusiveScanAMD", EOpAddInvocationsInclusiveScan);
+ symbolTable.relateToOperator("minInvocationsInclusiveScanNonUniformAMD", EOpMinInvocationsInclusiveScanNonUniform);
+ symbolTable.relateToOperator("maxInvocationsInclusiveScanNonUniformAMD", EOpMaxInvocationsInclusiveScanNonUniform);
+ symbolTable.relateToOperator("addInvocationsInclusiveScanNonUniformAMD", EOpAddInvocationsInclusiveScanNonUniform);
+ symbolTable.relateToOperator("minInvocationsExclusiveScanAMD", EOpMinInvocationsExclusiveScan);
+ symbolTable.relateToOperator("maxInvocationsExclusiveScanAMD", EOpMaxInvocationsExclusiveScan);
+ symbolTable.relateToOperator("addInvocationsExclusiveScanAMD", EOpAddInvocationsExclusiveScan);
+ symbolTable.relateToOperator("minInvocationsExclusiveScanNonUniformAMD", EOpMinInvocationsExclusiveScanNonUniform);
+ symbolTable.relateToOperator("maxInvocationsExclusiveScanNonUniformAMD", EOpMaxInvocationsExclusiveScanNonUniform);
+ symbolTable.relateToOperator("addInvocationsExclusiveScanNonUniformAMD", EOpAddInvocationsExclusiveScanNonUniform);
+ symbolTable.relateToOperator("swizzleInvocationsAMD", EOpSwizzleInvocations);
+ symbolTable.relateToOperator("swizzleInvocationsMaskedAMD", EOpSwizzleInvocationsMasked);
+ symbolTable.relateToOperator("writeInvocationAMD", EOpWriteInvocation);
+ symbolTable.relateToOperator("mbcntAMD", EOpMbcnt);
+
+ symbolTable.relateToOperator("min3", EOpMin3);
+ symbolTable.relateToOperator("max3", EOpMax3);
+ symbolTable.relateToOperator("mid3", EOpMid3);
+
+ symbolTable.relateToOperator("cubeFaceIndexAMD", EOpCubeFaceIndex);
+ symbolTable.relateToOperator("cubeFaceCoordAMD", EOpCubeFaceCoord);
+ symbolTable.relateToOperator("timeAMD", EOpTime);
+
+ symbolTable.relateToOperator("textureGatherLodAMD", EOpTextureGatherLod);
+ symbolTable.relateToOperator("textureGatherLodOffsetAMD", EOpTextureGatherLodOffset);
+ symbolTable.relateToOperator("textureGatherLodOffsetsAMD", EOpTextureGatherLodOffsets);
+ symbolTable.relateToOperator("sparseTextureGatherLodAMD", EOpSparseTextureGatherLod);
+ symbolTable.relateToOperator("sparseTextureGatherLodOffsetAMD", EOpSparseTextureGatherLodOffset);
+ symbolTable.relateToOperator("sparseTextureGatherLodOffsetsAMD", EOpSparseTextureGatherLodOffsets);
+
+ symbolTable.relateToOperator("imageLoadLodAMD", EOpImageLoadLod);
+ symbolTable.relateToOperator("imageStoreLodAMD", EOpImageStoreLod);
+ symbolTable.relateToOperator("sparseImageLoadLodAMD", EOpSparseImageLoadLod);
+
+ symbolTable.relateToOperator("fragmentMaskFetchAMD", EOpFragmentMaskFetch);
+ symbolTable.relateToOperator("fragmentFetchAMD", EOpFragmentFetch);
+#endif
+ }
+
+ // GL_KHR_shader_subgroup
+ if ((profile == EEsProfile && version >= 310) ||
+ (profile != EEsProfile && version >= 140)) {
+ symbolTable.relateToOperator("subgroupBarrier", EOpSubgroupBarrier);
+ symbolTable.relateToOperator("subgroupMemoryBarrier", EOpSubgroupMemoryBarrier);
+ symbolTable.relateToOperator("subgroupMemoryBarrierBuffer", EOpSubgroupMemoryBarrierBuffer);
+ symbolTable.relateToOperator("subgroupMemoryBarrierImage", EOpSubgroupMemoryBarrierImage);
+ symbolTable.relateToOperator("subgroupElect", EOpSubgroupElect);
+ symbolTable.relateToOperator("subgroupAll", EOpSubgroupAll);
+ symbolTable.relateToOperator("subgroupAny", EOpSubgroupAny);
+ symbolTable.relateToOperator("subgroupAllEqual", EOpSubgroupAllEqual);
+ symbolTable.relateToOperator("subgroupBroadcast", EOpSubgroupBroadcast);
+ symbolTable.relateToOperator("subgroupBroadcastFirst", EOpSubgroupBroadcastFirst);
+ symbolTable.relateToOperator("subgroupBallot", EOpSubgroupBallot);
+ symbolTable.relateToOperator("subgroupInverseBallot", EOpSubgroupInverseBallot);
+ symbolTable.relateToOperator("subgroupBallotBitExtract", EOpSubgroupBallotBitExtract);
+ symbolTable.relateToOperator("subgroupBallotBitCount", EOpSubgroupBallotBitCount);
+ symbolTable.relateToOperator("subgroupBallotInclusiveBitCount", EOpSubgroupBallotInclusiveBitCount);
+ symbolTable.relateToOperator("subgroupBallotExclusiveBitCount", EOpSubgroupBallotExclusiveBitCount);
+ symbolTable.relateToOperator("subgroupBallotFindLSB", EOpSubgroupBallotFindLSB);
+ symbolTable.relateToOperator("subgroupBallotFindMSB", EOpSubgroupBallotFindMSB);
+ symbolTable.relateToOperator("subgroupShuffle", EOpSubgroupShuffle);
+ symbolTable.relateToOperator("subgroupShuffleXor", EOpSubgroupShuffleXor);
+ symbolTable.relateToOperator("subgroupShuffleUp", EOpSubgroupShuffleUp);
+ symbolTable.relateToOperator("subgroupShuffleDown", EOpSubgroupShuffleDown);
+ symbolTable.relateToOperator("subgroupAdd", EOpSubgroupAdd);
+ symbolTable.relateToOperator("subgroupMul", EOpSubgroupMul);
+ symbolTable.relateToOperator("subgroupMin", EOpSubgroupMin);
+ symbolTable.relateToOperator("subgroupMax", EOpSubgroupMax);
+ symbolTable.relateToOperator("subgroupAnd", EOpSubgroupAnd);
+ symbolTable.relateToOperator("subgroupOr", EOpSubgroupOr);
+ symbolTable.relateToOperator("subgroupXor", EOpSubgroupXor);
+ symbolTable.relateToOperator("subgroupInclusiveAdd", EOpSubgroupInclusiveAdd);
+ symbolTable.relateToOperator("subgroupInclusiveMul", EOpSubgroupInclusiveMul);
+ symbolTable.relateToOperator("subgroupInclusiveMin", EOpSubgroupInclusiveMin);
+ symbolTable.relateToOperator("subgroupInclusiveMax", EOpSubgroupInclusiveMax);
+ symbolTable.relateToOperator("subgroupInclusiveAnd", EOpSubgroupInclusiveAnd);
+ symbolTable.relateToOperator("subgroupInclusiveOr", EOpSubgroupInclusiveOr);
+ symbolTable.relateToOperator("subgroupInclusiveXor", EOpSubgroupInclusiveXor);
+ symbolTable.relateToOperator("subgroupExclusiveAdd", EOpSubgroupExclusiveAdd);
+ symbolTable.relateToOperator("subgroupExclusiveMul", EOpSubgroupExclusiveMul);
+ symbolTable.relateToOperator("subgroupExclusiveMin", EOpSubgroupExclusiveMin);
+ symbolTable.relateToOperator("subgroupExclusiveMax", EOpSubgroupExclusiveMax);
+ symbolTable.relateToOperator("subgroupExclusiveAnd", EOpSubgroupExclusiveAnd);
+ symbolTable.relateToOperator("subgroupExclusiveOr", EOpSubgroupExclusiveOr);
+ symbolTable.relateToOperator("subgroupExclusiveXor", EOpSubgroupExclusiveXor);
+ symbolTable.relateToOperator("subgroupClusteredAdd", EOpSubgroupClusteredAdd);
+ symbolTable.relateToOperator("subgroupClusteredMul", EOpSubgroupClusteredMul);
+ symbolTable.relateToOperator("subgroupClusteredMin", EOpSubgroupClusteredMin);
+ symbolTable.relateToOperator("subgroupClusteredMax", EOpSubgroupClusteredMax);
+ symbolTable.relateToOperator("subgroupClusteredAnd", EOpSubgroupClusteredAnd);
+ symbolTable.relateToOperator("subgroupClusteredOr", EOpSubgroupClusteredOr);
+ symbolTable.relateToOperator("subgroupClusteredXor", EOpSubgroupClusteredXor);
+ symbolTable.relateToOperator("subgroupQuadBroadcast", EOpSubgroupQuadBroadcast);
+ symbolTable.relateToOperator("subgroupQuadSwapHorizontal", EOpSubgroupQuadSwapHorizontal);
+ symbolTable.relateToOperator("subgroupQuadSwapVertical", EOpSubgroupQuadSwapVertical);
+ symbolTable.relateToOperator("subgroupQuadSwapDiagonal", EOpSubgroupQuadSwapDiagonal);
+
+#ifdef NV_EXTENSIONS
+ symbolTable.relateToOperator("subgroupPartitionNV", EOpSubgroupPartition);
+ symbolTable.relateToOperator("subgroupPartitionedAddNV", EOpSubgroupPartitionedAdd);
+ symbolTable.relateToOperator("subgroupPartitionedMulNV", EOpSubgroupPartitionedMul);
+ symbolTable.relateToOperator("subgroupPartitionedMinNV", EOpSubgroupPartitionedMin);
+ symbolTable.relateToOperator("subgroupPartitionedMaxNV", EOpSubgroupPartitionedMax);
+ symbolTable.relateToOperator("subgroupPartitionedAndNV", EOpSubgroupPartitionedAnd);
+ symbolTable.relateToOperator("subgroupPartitionedOrNV", EOpSubgroupPartitionedOr);
+ symbolTable.relateToOperator("subgroupPartitionedXorNV", EOpSubgroupPartitionedXor);
+ symbolTable.relateToOperator("subgroupPartitionedInclusiveAddNV", EOpSubgroupPartitionedInclusiveAdd);
+ symbolTable.relateToOperator("subgroupPartitionedInclusiveMulNV", EOpSubgroupPartitionedInclusiveMul);
+ symbolTable.relateToOperator("subgroupPartitionedInclusiveMinNV", EOpSubgroupPartitionedInclusiveMin);
+ symbolTable.relateToOperator("subgroupPartitionedInclusiveMaxNV", EOpSubgroupPartitionedInclusiveMax);
+ symbolTable.relateToOperator("subgroupPartitionedInclusiveAndNV", EOpSubgroupPartitionedInclusiveAnd);
+ symbolTable.relateToOperator("subgroupPartitionedInclusiveOrNV", EOpSubgroupPartitionedInclusiveOr);
+ symbolTable.relateToOperator("subgroupPartitionedInclusiveXorNV", EOpSubgroupPartitionedInclusiveXor);
+ symbolTable.relateToOperator("subgroupPartitionedExclusiveAddNV", EOpSubgroupPartitionedExclusiveAdd);
+ symbolTable.relateToOperator("subgroupPartitionedExclusiveMulNV", EOpSubgroupPartitionedExclusiveMul);
+ symbolTable.relateToOperator("subgroupPartitionedExclusiveMinNV", EOpSubgroupPartitionedExclusiveMin);
+ symbolTable.relateToOperator("subgroupPartitionedExclusiveMaxNV", EOpSubgroupPartitionedExclusiveMax);
+ symbolTable.relateToOperator("subgroupPartitionedExclusiveAndNV", EOpSubgroupPartitionedExclusiveAnd);
+ symbolTable.relateToOperator("subgroupPartitionedExclusiveOrNV", EOpSubgroupPartitionedExclusiveOr);
+ symbolTable.relateToOperator("subgroupPartitionedExclusiveXorNV", EOpSubgroupPartitionedExclusiveXor);
+#endif
+ }
+
+ if (profile == EEsProfile) {
+ symbolTable.relateToOperator("shadow2DEXT", EOpTexture);
+ symbolTable.relateToOperator("shadow2DProjEXT", EOpTextureProj);
+ }
+ }
+
+ switch(language) {
+ case EShLangVertex:
+ break;
+
+ case EShLangTessControl:
+ case EShLangTessEvaluation:
+ break;
+
+ case EShLangGeometry:
+ symbolTable.relateToOperator("EmitStreamVertex", EOpEmitStreamVertex);
+ symbolTable.relateToOperator("EndStreamPrimitive", EOpEndStreamPrimitive);
+ symbolTable.relateToOperator("EmitVertex", EOpEmitVertex);
+ symbolTable.relateToOperator("EndPrimitive", EOpEndPrimitive);
+ break;
+
+ case EShLangFragment:
+ symbolTable.relateToOperator("dFdx", EOpDPdx);
+ symbolTable.relateToOperator("dFdy", EOpDPdy);
+ symbolTable.relateToOperator("fwidth", EOpFwidth);
+ if (profile != EEsProfile && version >= 400) {
+ symbolTable.relateToOperator("dFdxFine", EOpDPdxFine);
+ symbolTable.relateToOperator("dFdyFine", EOpDPdyFine);
+ symbolTable.relateToOperator("fwidthFine", EOpFwidthFine);
+ symbolTable.relateToOperator("dFdxCoarse", EOpDPdxCoarse);
+ symbolTable.relateToOperator("dFdyCoarse", EOpDPdyCoarse);
+ symbolTable.relateToOperator("fwidthCoarse", EOpFwidthCoarse);
+ }
+ symbolTable.relateToOperator("interpolateAtCentroid", EOpInterpolateAtCentroid);
+ symbolTable.relateToOperator("interpolateAtSample", EOpInterpolateAtSample);
+ symbolTable.relateToOperator("interpolateAtOffset", EOpInterpolateAtOffset);
+
+#ifdef AMD_EXTENSIONS
+ if (profile != EEsProfile)
+ symbolTable.relateToOperator("interpolateAtVertexAMD", EOpInterpolateAtVertex);
+#endif
+ break;
+
+ case EShLangCompute:
+ symbolTable.relateToOperator("memoryBarrierShared", EOpMemoryBarrierShared);
+ symbolTable.relateToOperator("groupMemoryBarrier", EOpGroupMemoryBarrier);
+ symbolTable.relateToOperator("subgroupMemoryBarrierShared", EOpSubgroupMemoryBarrierShared);
+#ifdef NV_EXTENSIONS
+ if ((profile != EEsProfile && version >= 450) ||
+ (profile == EEsProfile && version >= 320)) {
+ symbolTable.relateToOperator("dFdx", EOpDPdx);
+ symbolTable.relateToOperator("dFdy", EOpDPdy);
+ symbolTable.relateToOperator("fwidth", EOpFwidth);
+ symbolTable.relateToOperator("dFdxFine", EOpDPdxFine);
+ symbolTable.relateToOperator("dFdyFine", EOpDPdyFine);
+ symbolTable.relateToOperator("fwidthFine", EOpFwidthFine);
+ symbolTable.relateToOperator("dFdxCoarse", EOpDPdxCoarse);
+ symbolTable.relateToOperator("dFdyCoarse", EOpDPdyCoarse);
+ symbolTable.relateToOperator("fwidthCoarse",EOpFwidthCoarse);
+ }
+#endif
+ symbolTable.relateToOperator("coopMatLoadNV", EOpCooperativeMatrixLoad);
+ symbolTable.relateToOperator("coopMatStoreNV", EOpCooperativeMatrixStore);
+ symbolTable.relateToOperator("coopMatMulAddNV", EOpCooperativeMatrixMulAdd);
+ break;
+
+#ifdef NV_EXTENSIONS
+ case EShLangRayGenNV:
+ case EShLangClosestHitNV:
+ case EShLangMissNV:
+ if (profile != EEsProfile && version >= 460) {
+ symbolTable.relateToOperator("traceNV", EOpTraceNV);
+ symbolTable.relateToOperator("executeCallableNV", EOpExecuteCallableNV);
+ }
+ break;
+ case EShLangIntersectNV:
+ if (profile != EEsProfile && version >= 460)
+ symbolTable.relateToOperator("reportIntersectionNV", EOpReportIntersectionNV);
+ break;
+ case EShLangAnyHitNV:
+ if (profile != EEsProfile && version >= 460) {
+ symbolTable.relateToOperator("ignoreIntersectionNV", EOpIgnoreIntersectionNV);
+ symbolTable.relateToOperator("terminateRayNV", EOpTerminateRayNV);
+ }
+ break;
+ case EShLangCallableNV:
+ if (profile != EEsProfile && version >= 460) {
+ symbolTable.relateToOperator("executeCallableNV", EOpExecuteCallableNV);
+ }
+ break;
+ case EShLangMeshNV:
+ if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) {
+ symbolTable.relateToOperator("writePackedPrimitiveIndices4x8NV", EOpWritePackedPrimitiveIndices4x8NV);
+ }
+ // fall through
+ case EShLangTaskNV:
+ if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) {
+ symbolTable.relateToOperator("memoryBarrierShared", EOpMemoryBarrierShared);
+ symbolTable.relateToOperator("groupMemoryBarrier", EOpGroupMemoryBarrier);
+ }
+ break;
+#endif
+
+ default:
+ assert(false && "Language not supported");
+ }
+}
+
+//
+// Add context-dependent (resource-specific) built-ins not handled by the above. These
+// would be ones that need to be programmatically added because they cannot
+// be added by simple text strings. For these, also
+// 1) Map built-in functions to operators, for those that will turn into an operation node
+// instead of remaining a function call.
+// 2) Tag extension-related symbols added to their base version with their extensions, so
+// that if an early version has the extension turned off, there is an error reported on use.
+//
+void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable, const TBuiltInResource &resources)
+{
+ if (profile != EEsProfile && version >= 430 && version < 440) {
+ symbolTable.setVariableExtensions("gl_MaxTransformFeedbackBuffers", 1, &E_GL_ARB_enhanced_layouts);
+ symbolTable.setVariableExtensions("gl_MaxTransformFeedbackInterleavedComponents", 1, &E_GL_ARB_enhanced_layouts);
+ }
+ if (profile != EEsProfile && version >= 130 && version < 420) {
+ symbolTable.setVariableExtensions("gl_MinProgramTexelOffset", 1, &E_GL_ARB_shading_language_420pack);
+ symbolTable.setVariableExtensions("gl_MaxProgramTexelOffset", 1, &E_GL_ARB_shading_language_420pack);
+ }
+ if (profile != EEsProfile && version >= 150 && version < 410)
+ symbolTable.setVariableExtensions("gl_MaxViewports", 1, &E_GL_ARB_viewport_array);
+
+ switch(language) {
+ case EShLangFragment:
+ // Set up gl_FragData based on current array size.
+ if (version == 100 || IncludeLegacy(version, profile, spvVersion) || (! ForwardCompatibility && profile != EEsProfile && version < 420)) {
+ TPrecisionQualifier pq = profile == EEsProfile ? EpqMedium : EpqNone;
+ TType fragData(EbtFloat, EvqFragColor, pq, 4);
+ TArraySizes* arraySizes = new TArraySizes;
+ arraySizes->addInnerSize(resources.maxDrawBuffers);
+ fragData.transferArraySizes(arraySizes);
+ symbolTable.insert(*new TVariable(NewPoolTString("gl_FragData"), fragData));
+ SpecialQualifier("gl_FragData", EvqFragColor, EbvFragData, symbolTable);
+ }
+ break;
+
+ case EShLangTessControl:
+ case EShLangTessEvaluation:
+ // Because of the context-dependent array size (gl_MaxPatchVertices),
+ // these variables were added later than the others and need to be mapped now.
+
+ // standard members
+ BuiltInVariable("gl_in", "gl_Position", EbvPosition, symbolTable);
+ BuiltInVariable("gl_in", "gl_PointSize", EbvPointSize, symbolTable);
+ BuiltInVariable("gl_in", "gl_ClipDistance", EbvClipDistance, symbolTable);
+ BuiltInVariable("gl_in", "gl_CullDistance", EbvCullDistance, symbolTable);
+
+ // compatibility members
+ BuiltInVariable("gl_in", "gl_ClipVertex", EbvClipVertex, symbolTable);
+ BuiltInVariable("gl_in", "gl_FrontColor", EbvFrontColor, symbolTable);
+ BuiltInVariable("gl_in", "gl_BackColor", EbvBackColor, symbolTable);
+ BuiltInVariable("gl_in", "gl_FrontSecondaryColor", EbvFrontSecondaryColor, symbolTable);
+ BuiltInVariable("gl_in", "gl_BackSecondaryColor", EbvBackSecondaryColor, symbolTable);
+ BuiltInVariable("gl_in", "gl_TexCoord", EbvTexCoord, symbolTable);
+ BuiltInVariable("gl_in", "gl_FogFragCoord", EbvFogFragCoord, symbolTable);
+
+ // extension requirements
+ if (profile == EEsProfile) {
+ symbolTable.setVariableExtensions("gl_in", "gl_PointSize", Num_AEP_tessellation_point_size, AEP_tessellation_point_size);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/Initialize.h b/src/3rdparty/glslang/glslang/MachineIndependent/Initialize.h
new file mode 100644
index 0000000..b5de324
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/Initialize.h
@@ -0,0 +1,110 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2013-2016 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef _INITIALIZE_INCLUDED_
+#define _INITIALIZE_INCLUDED_
+
+#include "../Include/ResourceLimits.h"
+#include "../Include/Common.h"
+#include "../Include/ShHandle.h"
+#include "SymbolTable.h"
+#include "Versions.h"
+
+namespace glslang {
+
+//
+// This is made to hold parseable strings for almost all the built-in
+// functions and variables for one specific combination of version
+// and profile. (Some still need to be added programmatically.)
+// This is a base class for language-specific derivations, which
+// can be used for language independent builtins.
+//
+// The strings are organized by
+// commonBuiltins: intersection of all stages' built-ins, processed just once
+// stageBuiltins[]: anything a stage needs that's not in commonBuiltins
+//
+class TBuiltInParseables {
+public:
+ POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator())
+ TBuiltInParseables();
+ virtual ~TBuiltInParseables();
+ virtual void initialize(int version, EProfile, const SpvVersion& spvVersion) = 0;
+ virtual void initialize(const TBuiltInResource& resources, int version, EProfile, const SpvVersion& spvVersion, EShLanguage) = 0;
+ virtual const TString& getCommonString() const { return commonBuiltins; }
+ virtual const TString& getStageString(EShLanguage language) const { return stageBuiltins[language]; }
+
+ virtual void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable) = 0;
+ virtual void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable, const TBuiltInResource &resources) = 0;
+
+protected:
+ TString commonBuiltins;
+ TString stageBuiltins[EShLangCount];
+};
+
+//
+// This is a GLSL specific derivation of TBuiltInParseables. To present a stable
+// interface and match other similar code, it is called TBuiltIns, rather
+// than TBuiltInParseablesGlsl.
+//
+class TBuiltIns : public TBuiltInParseables {
+public:
+ POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator())
+ TBuiltIns();
+ virtual ~TBuiltIns();
+ void initialize(int version, EProfile, const SpvVersion& spvVersion);
+ void initialize(const TBuiltInResource& resources, int version, EProfile, const SpvVersion& spvVersion, EShLanguage);
+
+ void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable);
+ void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable, const TBuiltInResource &resources);
+
+protected:
+ void add2ndGenerationSamplingImaging(int version, EProfile profile, const SpvVersion& spvVersion);
+ void addSubpassSampling(TSampler, const TString& typeName, int version, EProfile profile);
+ void addQueryFunctions(TSampler, const TString& typeName, int version, EProfile profile);
+ void addImageFunctions(TSampler, const TString& typeName, int version, EProfile profile);
+ void addSamplingFunctions(TSampler, const TString& typeName, int version, EProfile profile);
+ void addGatherFunctions(TSampler, const TString& typeName, int version, EProfile profile);
+
+ // Helpers for making textual representations of the permutations
+ // of texturing/imaging functions.
+ const char* postfixes[5];
+ const char* prefixes[EbtNumTypes];
+ int dimMap[EsdNumDims];
+};
+
+} // end namespace glslang
+
+#endif // _INITIALIZE_INCLUDED_
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/IntermTraverse.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/IntermTraverse.cpp
new file mode 100644
index 0000000..f46010b
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/IntermTraverse.cpp
@@ -0,0 +1,302 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2013 LunarG, Inc.
+// Copyright (c) 2002-2010 The ANGLE Project Authors.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "../Include/intermediate.h"
+
+namespace glslang {
+
+//
+// Traverse the intermediate representation tree, and
+// call a node type specific function for each node.
+// Done recursively through the member function Traverse().
+// Node types can be skipped if their function to call is 0,
+// but their subtree will still be traversed.
+// Nodes with children can have their whole subtree skipped
+// if preVisit is turned on and the type specific function
+// returns false.
+//
+// preVisit, postVisit, and rightToLeft control what order
+// nodes are visited in.
+//
+
+//
+// Traversal functions for terminals are straightforward....
+//
+void TIntermMethod::traverse(TIntermTraverser*)
+{
+ // Tree should always resolve all methods as a non-method.
+}
+
+void TIntermSymbol::traverse(TIntermTraverser *it)
+{
+ it->visitSymbol(this);
+}
+
+void TIntermConstantUnion::traverse(TIntermTraverser *it)
+{
+ it->visitConstantUnion(this);
+}
+
+//
+// Traverse a binary node.
+//
+void TIntermBinary::traverse(TIntermTraverser *it)
+{
+ bool visit = true;
+
+ //
+ // visit the node before children if pre-visiting.
+ //
+ if (it->preVisit)
+ visit = it->visitBinary(EvPreVisit, this);
+
+ //
+ // Visit the children, in the right order.
+ //
+ if (visit) {
+ it->incrementDepth(this);
+
+ if (it->rightToLeft) {
+ if (right)
+ right->traverse(it);
+
+ if (it->inVisit)
+ visit = it->visitBinary(EvInVisit, this);
+
+ if (visit && left)
+ left->traverse(it);
+ } else {
+ if (left)
+ left->traverse(it);
+
+ if (it->inVisit)
+ visit = it->visitBinary(EvInVisit, this);
+
+ if (visit && right)
+ right->traverse(it);
+ }
+
+ it->decrementDepth();
+ }
+
+ //
+ // Visit the node after the children, if requested and the traversal
+ // hasn't been canceled yet.
+ //
+ if (visit && it->postVisit)
+ it->visitBinary(EvPostVisit, this);
+}
+
+//
+// Traverse a unary node. Same comments in binary node apply here.
+//
+void TIntermUnary::traverse(TIntermTraverser *it)
+{
+ bool visit = true;
+
+ if (it->preVisit)
+ visit = it->visitUnary(EvPreVisit, this);
+
+ if (visit) {
+ it->incrementDepth(this);
+ operand->traverse(it);
+ it->decrementDepth();
+ }
+
+ if (visit && it->postVisit)
+ it->visitUnary(EvPostVisit, this);
+}
+
+//
+// Traverse an aggregate node. Same comments in binary node apply here.
+//
+void TIntermAggregate::traverse(TIntermTraverser *it)
+{
+ bool visit = true;
+
+ if (it->preVisit)
+ visit = it->visitAggregate(EvPreVisit, this);
+
+ if (visit) {
+ it->incrementDepth(this);
+
+ if (it->rightToLeft) {
+ for (TIntermSequence::reverse_iterator sit = sequence.rbegin(); sit != sequence.rend(); sit++) {
+ (*sit)->traverse(it);
+
+ if (visit && it->inVisit) {
+ if (*sit != sequence.front())
+ visit = it->visitAggregate(EvInVisit, this);
+ }
+ }
+ } else {
+ for (TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); sit++) {
+ (*sit)->traverse(it);
+
+ if (visit && it->inVisit) {
+ if (*sit != sequence.back())
+ visit = it->visitAggregate(EvInVisit, this);
+ }
+ }
+ }
+
+ it->decrementDepth();
+ }
+
+ if (visit && it->postVisit)
+ it->visitAggregate(EvPostVisit, this);
+}
+
+//
+// Traverse a selection node. Same comments in binary node apply here.
+//
+void TIntermSelection::traverse(TIntermTraverser *it)
+{
+ bool visit = true;
+
+ if (it->preVisit)
+ visit = it->visitSelection(EvPreVisit, this);
+
+ if (visit) {
+ it->incrementDepth(this);
+ if (it->rightToLeft) {
+ if (falseBlock)
+ falseBlock->traverse(it);
+ if (trueBlock)
+ trueBlock->traverse(it);
+ condition->traverse(it);
+ } else {
+ condition->traverse(it);
+ if (trueBlock)
+ trueBlock->traverse(it);
+ if (falseBlock)
+ falseBlock->traverse(it);
+ }
+ it->decrementDepth();
+ }
+
+ if (visit && it->postVisit)
+ it->visitSelection(EvPostVisit, this);
+}
+
+//
+// Traverse a loop node. Same comments in binary node apply here.
+//
+void TIntermLoop::traverse(TIntermTraverser *it)
+{
+ bool visit = true;
+
+ if (it->preVisit)
+ visit = it->visitLoop(EvPreVisit, this);
+
+ if (visit) {
+ it->incrementDepth(this);
+
+ if (it->rightToLeft) {
+ if (terminal)
+ terminal->traverse(it);
+
+ if (body)
+ body->traverse(it);
+
+ if (test)
+ test->traverse(it);
+ } else {
+ if (test)
+ test->traverse(it);
+
+ if (body)
+ body->traverse(it);
+
+ if (terminal)
+ terminal->traverse(it);
+ }
+
+ it->decrementDepth();
+ }
+
+ if (visit && it->postVisit)
+ it->visitLoop(EvPostVisit, this);
+}
+
+//
+// Traverse a branch node. Same comments in binary node apply here.
+//
+void TIntermBranch::traverse(TIntermTraverser *it)
+{
+ bool visit = true;
+
+ if (it->preVisit)
+ visit = it->visitBranch(EvPreVisit, this);
+
+ if (visit && expression) {
+ it->incrementDepth(this);
+ expression->traverse(it);
+ it->decrementDepth();
+ }
+
+ if (visit && it->postVisit)
+ it->visitBranch(EvPostVisit, this);
+}
+
+//
+// Traverse a switch node.
+//
+void TIntermSwitch::traverse(TIntermTraverser* it)
+{
+ bool visit = true;
+
+ if (it->preVisit)
+ visit = it->visitSwitch(EvPreVisit, this);
+
+ if (visit) {
+ it->incrementDepth(this);
+ if (it->rightToLeft) {
+ body->traverse(it);
+ condition->traverse(it);
+ } else {
+ condition->traverse(it);
+ body->traverse(it);
+ }
+ it->decrementDepth();
+ }
+
+ if (visit && it->postVisit)
+ it->visitSwitch(EvPostVisit, this);
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/Intermediate.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/Intermediate.cpp
new file mode 100644
index 0000000..5e9c784
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/Intermediate.cpp
@@ -0,0 +1,3967 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2012-2015 LunarG, Inc.
+// Copyright (C) 2015-2018 Google, Inc.
+// Copyright (C) 2017 ARM Limited.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+//
+// Build the intermediate representation.
+//
+
+#include "localintermediate.h"
+#include "RemoveTree.h"
+#include "SymbolTable.h"
+#include "propagateNoContraction.h"
+
+#include <cfloat>
+#include <utility>
+#include <tuple>
+
+namespace glslang {
+
+////////////////////////////////////////////////////////////////////////////
+//
+// First set of functions are to help build the intermediate representation.
+// These functions are not member functions of the nodes.
+// They are called from parser productions.
+//
+/////////////////////////////////////////////////////////////////////////////
+
+//
+// Add a terminal node for an identifier in an expression.
+//
+// Returns the added node.
+//
+
+TIntermSymbol* TIntermediate::addSymbol(int id, const TString& name, const TType& type, const TConstUnionArray& constArray,
+ TIntermTyped* constSubtree, const TSourceLoc& loc)
+{
+ TIntermSymbol* node = new TIntermSymbol(id, name, type);
+ node->setLoc(loc);
+ node->setConstArray(constArray);
+ node->setConstSubtree(constSubtree);
+
+ return node;
+}
+
+TIntermSymbol* TIntermediate::addSymbol(const TIntermSymbol& intermSymbol)
+{
+ return addSymbol(intermSymbol.getId(),
+ intermSymbol.getName(),
+ intermSymbol.getType(),
+ intermSymbol.getConstArray(),
+ intermSymbol.getConstSubtree(),
+ intermSymbol.getLoc());
+}
+
+TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable)
+{
+ glslang::TSourceLoc loc; // just a null location
+ loc.init();
+
+ return addSymbol(variable, loc);
+}
+
+TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable, const TSourceLoc& loc)
+{
+ return addSymbol(variable.getUniqueId(), variable.getName(), variable.getType(), variable.getConstArray(), variable.getConstSubtree(), loc);
+}
+
+TIntermSymbol* TIntermediate::addSymbol(const TType& type, const TSourceLoc& loc)
+{
+ TConstUnionArray unionArray; // just a null constant
+
+ return addSymbol(0, "", type, unionArray, nullptr, loc);
+}
+
+//
+// Connect two nodes with a new parent that does a binary operation on the nodes.
+//
+// Returns the added node.
+//
+// Returns nullptr if the working conversions and promotions could not be found.
+//
+TIntermTyped* TIntermediate::addBinaryMath(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc loc)
+{
+ // No operations work on blocks
+ if (left->getType().getBasicType() == EbtBlock || right->getType().getBasicType() == EbtBlock)
+ return nullptr;
+
+ // Try converting the children's base types to compatible types.
+ auto children = addConversion(op, left, right);
+ left = std::get<0>(children);
+ right = std::get<1>(children);
+
+ if (left == nullptr || right == nullptr)
+ return nullptr;
+
+ // Convert the children's type shape to be compatible.
+ addBiShapeConversion(op, left, right);
+ if (left == nullptr || right == nullptr)
+ return nullptr;
+
+ //
+ // Need a new node holding things together. Make
+ // one and promote it to the right type.
+ //
+ TIntermBinary* node = addBinaryNode(op, left, right, loc);
+ if (! promote(node))
+ return nullptr;
+
+ node->updatePrecision();
+
+ //
+ // If they are both (non-specialization) constants, they must be folded.
+ // (Unless it's the sequence (comma) operator, but that's handled in addComma().)
+ //
+ TIntermConstantUnion *leftTempConstant = node->getLeft()->getAsConstantUnion();
+ TIntermConstantUnion *rightTempConstant = node->getRight()->getAsConstantUnion();
+ if (leftTempConstant && rightTempConstant) {
+ TIntermTyped* folded = leftTempConstant->fold(node->getOp(), rightTempConstant);
+ if (folded)
+ return folded;
+ }
+
+ // If can propagate spec-constantness and if the operation is an allowed
+ // specialization-constant operation, make a spec-constant.
+ if (specConstantPropagates(*node->getLeft(), *node->getRight()) && isSpecializationOperation(*node))
+ node->getWritableType().getQualifier().makeSpecConstant();
+
+ // If must propagate nonuniform, make a nonuniform.
+ if ((node->getLeft()->getQualifier().nonUniform || node->getRight()->getQualifier().nonUniform) &&
+ isNonuniformPropagating(node->getOp()))
+ node->getWritableType().getQualifier().nonUniform = true;
+
+ return node;
+}
+
+//
+// Low level: add binary node (no promotions or other argument modifications)
+//
+TIntermBinary* TIntermediate::addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc loc) const
+{
+ // build the node
+ TIntermBinary* node = new TIntermBinary(op);
+ if (loc.line == 0)
+ loc = left->getLoc();
+ node->setLoc(loc);
+ node->setLeft(left);
+ node->setRight(right);
+
+ return node;
+}
+
+//
+// like non-type form, but sets node's type.
+//
+TIntermBinary* TIntermediate::addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc loc, const TType& type) const
+{
+ TIntermBinary* node = addBinaryNode(op, left, right, loc);
+ node->setType(type);
+ return node;
+}
+
+//
+// Low level: add unary node (no promotions or other argument modifications)
+//
+TIntermUnary* TIntermediate::addUnaryNode(TOperator op, TIntermTyped* child, TSourceLoc loc) const
+{
+ TIntermUnary* node = new TIntermUnary(op);
+ if (loc.line == 0)
+ loc = child->getLoc();
+ node->setLoc(loc);
+ node->setOperand(child);
+
+ return node;
+}
+
+//
+// like non-type form, but sets node's type.
+//
+TIntermUnary* TIntermediate::addUnaryNode(TOperator op, TIntermTyped* child, TSourceLoc loc, const TType& type) const
+{
+ TIntermUnary* node = addUnaryNode(op, child, loc);
+ node->setType(type);
+ return node;
+}
+
+//
+// Connect two nodes through an assignment.
+//
+// Returns the added node.
+//
+// Returns nullptr if the 'right' type could not be converted to match the 'left' type,
+// or the resulting operation cannot be properly promoted.
+//
+TIntermTyped* TIntermediate::addAssign(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc loc)
+{
+ // No block assignment
+ if (left->getType().getBasicType() == EbtBlock || right->getType().getBasicType() == EbtBlock)
+ return nullptr;
+
+ //
+ // Like adding binary math, except the conversion can only go
+ // from right to left.
+ //
+
+ // convert base types, nullptr return means not possible
+ right = addConversion(op, left->getType(), right);
+ if (right == nullptr)
+ return nullptr;
+
+ // convert shape
+ right = addUniShapeConversion(op, left->getType(), right);
+
+ // build the node
+ TIntermBinary* node = addBinaryNode(op, left, right, loc);
+
+ if (! promote(node))
+ return nullptr;
+
+ node->updatePrecision();
+
+ return node;
+}
+
+//
+// Connect two nodes through an index operator, where the left node is the base
+// of an array or struct, and the right node is a direct or indirect offset.
+//
+// Returns the added node.
+// The caller should set the type of the returned node.
+//
+TIntermTyped* TIntermediate::addIndex(TOperator op, TIntermTyped* base, TIntermTyped* index, TSourceLoc loc)
+{
+ // caller should set the type
+ return addBinaryNode(op, base, index, loc);
+}
+
+//
+// Add one node as the parent of another that it operates on.
+//
+// Returns the added node.
+//
+TIntermTyped* TIntermediate::addUnaryMath(TOperator op, TIntermTyped* child, TSourceLoc loc)
+{
+ if (child == 0)
+ return nullptr;
+
+ if (child->getType().getBasicType() == EbtBlock)
+ return nullptr;
+
+ switch (op) {
+ case EOpLogicalNot:
+ if (source == EShSourceHlsl) {
+ break; // HLSL can promote logical not
+ }
+
+ if (child->getType().getBasicType() != EbtBool || child->getType().isMatrix() || child->getType().isArray() || child->getType().isVector()) {
+ return nullptr;
+ }
+ break;
+
+ case EOpPostIncrement:
+ case EOpPreIncrement:
+ case EOpPostDecrement:
+ case EOpPreDecrement:
+ case EOpNegative:
+ if (child->getType().getBasicType() == EbtStruct || child->getType().isArray())
+ return nullptr;
+ default: break; // some compilers want this
+ }
+
+ //
+ // Do we need to promote the operand?
+ //
+ TBasicType newType = EbtVoid;
+ switch (op) {
+ case EOpConstructInt8: newType = EbtInt8; break;
+ case EOpConstructUint8: newType = EbtUint8; break;
+ case EOpConstructInt16: newType = EbtInt16; break;
+ case EOpConstructUint16: newType = EbtUint16; break;
+ case EOpConstructInt: newType = EbtInt; break;
+ case EOpConstructUint: newType = EbtUint; break;
+ case EOpConstructInt64: newType = EbtInt64; break;
+ case EOpConstructUint64: newType = EbtUint64; break;
+ case EOpConstructBool: newType = EbtBool; break;
+ case EOpConstructFloat: newType = EbtFloat; break;
+ case EOpConstructDouble: newType = EbtDouble; break;
+ case EOpConstructFloat16: newType = EbtFloat16; break;
+ default: break; // some compilers want this
+ }
+
+ if (newType != EbtVoid) {
+ child = addConversion(op, TType(newType, EvqTemporary, child->getVectorSize(),
+ child->getMatrixCols(),
+ child->getMatrixRows(),
+ child->isVector()),
+ child);
+ if (child == nullptr)
+ return nullptr;
+ }
+
+ //
+ // For constructors, we are now done, it was all in the conversion.
+ // TODO: but, did this bypass constant folding?
+ //
+ switch (op) {
+ case EOpConstructInt8:
+ case EOpConstructUint8:
+ case EOpConstructInt16:
+ case EOpConstructUint16:
+ case EOpConstructInt:
+ case EOpConstructUint:
+ case EOpConstructInt64:
+ case EOpConstructUint64:
+ case EOpConstructBool:
+ case EOpConstructFloat:
+ case EOpConstructDouble:
+ case EOpConstructFloat16:
+ return child;
+ default: break; // some compilers want this
+ }
+
+ //
+ // Make a new node for the operator.
+ //
+ TIntermUnary* node = addUnaryNode(op, child, loc);
+
+ if (! promote(node))
+ return nullptr;
+
+ node->updatePrecision();
+
+ // If it's a (non-specialization) constant, it must be folded.
+ if (node->getOperand()->getAsConstantUnion())
+ return node->getOperand()->getAsConstantUnion()->fold(op, node->getType());
+
+ // If it's a specialization constant, the result is too,
+ // if the operation is allowed for specialization constants.
+ if (node->getOperand()->getType().getQualifier().isSpecConstant() && isSpecializationOperation(*node))
+ node->getWritableType().getQualifier().makeSpecConstant();
+
+ // If must propagate nonuniform, make a nonuniform.
+ if (node->getOperand()->getQualifier().nonUniform && isNonuniformPropagating(node->getOp()))
+ node->getWritableType().getQualifier().nonUniform = true;
+
+ return node;
+}
+
+TIntermTyped* TIntermediate::addBuiltInFunctionCall(const TSourceLoc& loc, TOperator op, bool unary,
+ TIntermNode* childNode, const TType& returnType)
+{
+ if (unary) {
+ //
+ // Treat it like a unary operator.
+ // addUnaryMath() should get the type correct on its own;
+ // including constness (which would differ from the prototype).
+ //
+ TIntermTyped* child = childNode->getAsTyped();
+ if (child == nullptr)
+ return nullptr;
+
+ if (child->getAsConstantUnion()) {
+ TIntermTyped* folded = child->getAsConstantUnion()->fold(op, returnType);
+ if (folded)
+ return folded;
+ }
+
+ return addUnaryNode(op, child, child->getLoc(), returnType);
+ } else {
+ // setAggregateOperater() calls fold() for constant folding
+ TIntermTyped* node = setAggregateOperator(childNode, op, returnType, loc);
+
+ return node;
+ }
+}
+
+//
+// This is the safe way to change the operator on an aggregate, as it
+// does lots of error checking and fixing. Especially for establishing
+// a function call's operation on it's set of parameters. Sequences
+// of instructions are also aggregates, but they just directly set
+// their operator to EOpSequence.
+//
+// Returns an aggregate node, which could be the one passed in if
+// it was already an aggregate.
+//
+TIntermTyped* TIntermediate::setAggregateOperator(TIntermNode* node, TOperator op, const TType& type, TSourceLoc loc)
+{
+ TIntermAggregate* aggNode;
+
+ //
+ // Make sure we have an aggregate. If not turn it into one.
+ //
+ if (node != nullptr) {
+ aggNode = node->getAsAggregate();
+ if (aggNode == nullptr || aggNode->getOp() != EOpNull) {
+ //
+ // Make an aggregate containing this node.
+ //
+ aggNode = new TIntermAggregate();
+ aggNode->getSequence().push_back(node);
+ if (loc.line == 0)
+ loc = node->getLoc();
+ }
+ } else
+ aggNode = new TIntermAggregate();
+
+ //
+ // Set the operator.
+ //
+ aggNode->setOperator(op);
+ if (loc.line != 0)
+ aggNode->setLoc(loc);
+
+ aggNode->setType(type);
+
+ return fold(aggNode);
+}
+
+bool TIntermediate::isConversionAllowed(TOperator op, TIntermTyped* node) const
+{
+ //
+ // Does the base type even allow the operation?
+ //
+ switch (node->getBasicType()) {
+ case EbtVoid:
+ return false;
+ case EbtAtomicUint:
+ case EbtSampler:
+#ifdef NV_EXTENSIONS
+ case EbtAccStructNV:
+#endif
+ // opaque types can be passed to functions
+ if (op == EOpFunction)
+ break;
+
+ // HLSL can assign samplers directly (no constructor)
+ if (source == EShSourceHlsl && node->getBasicType() == EbtSampler)
+ break;
+
+ // samplers can get assigned via a sampler constructor
+ // (well, not yet, but code in the rest of this function is ready for it)
+ if (node->getBasicType() == EbtSampler && op == EOpAssign &&
+ node->getAsOperator() != nullptr && node->getAsOperator()->getOp() == EOpConstructTextureSampler)
+ break;
+
+ // otherwise, opaque types can't even be operated on, let alone converted
+ return false;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+// This is 'mechanism' here, it does any conversion told.
+// It is about basic type, not about shape.
+// The policy comes from the shader or the calling code.
+TIntermTyped* TIntermediate::createConversion(TBasicType convertTo, TIntermTyped* node) const
+{
+ //
+ // Add a new newNode for the conversion.
+ //
+ TIntermUnary* newNode = nullptr;
+
+ TOperator newOp = EOpNull;
+
+ switch (convertTo) {
+ case EbtDouble:
+ switch (node->getBasicType()) {
+ case EbtInt8: newOp = EOpConvInt8ToDouble; break;
+ case EbtUint8: newOp = EOpConvUint8ToDouble; break;
+ case EbtInt16: newOp = EOpConvInt16ToDouble; break;
+ case EbtUint16: newOp = EOpConvUint16ToDouble; break;
+ case EbtInt: newOp = EOpConvIntToDouble; break;
+ case EbtUint: newOp = EOpConvUintToDouble; break;
+ case EbtBool: newOp = EOpConvBoolToDouble; break;
+ case EbtFloat: newOp = EOpConvFloatToDouble; break;
+ case EbtFloat16: newOp = EOpConvFloat16ToDouble; break;
+ case EbtInt64: newOp = EOpConvInt64ToDouble; break;
+ case EbtUint64: newOp = EOpConvUint64ToDouble; break;
+ default:
+ return nullptr;
+ }
+ break;
+ case EbtFloat:
+ switch (node->getBasicType()) {
+ case EbtInt8: newOp = EOpConvInt8ToFloat; break;
+ case EbtUint8: newOp = EOpConvUint8ToFloat; break;
+ case EbtInt16: newOp = EOpConvInt16ToFloat; break;
+ case EbtUint16: newOp = EOpConvUint16ToFloat; break;
+ case EbtInt: newOp = EOpConvIntToFloat; break;
+ case EbtUint: newOp = EOpConvUintToFloat; break;
+ case EbtBool: newOp = EOpConvBoolToFloat; break;
+ case EbtDouble: newOp = EOpConvDoubleToFloat; break;
+ case EbtFloat16: newOp = EOpConvFloat16ToFloat; break;
+ case EbtInt64: newOp = EOpConvInt64ToFloat; break;
+ case EbtUint64: newOp = EOpConvUint64ToFloat; break;
+ default:
+ return nullptr;
+ }
+ break;
+ case EbtFloat16:
+ switch (node->getBasicType()) {
+ case EbtInt8: newOp = EOpConvInt8ToFloat16; break;
+ case EbtUint8: newOp = EOpConvUint8ToFloat16; break;
+ case EbtInt16: newOp = EOpConvInt16ToFloat16; break;
+ case EbtUint16: newOp = EOpConvUint16ToFloat16; break;
+ case EbtInt: newOp = EOpConvIntToFloat16; break;
+ case EbtUint: newOp = EOpConvUintToFloat16; break;
+ case EbtBool: newOp = EOpConvBoolToFloat16; break;
+ case EbtFloat: newOp = EOpConvFloatToFloat16; break;
+ case EbtDouble: newOp = EOpConvDoubleToFloat16; break;
+ case EbtInt64: newOp = EOpConvInt64ToFloat16; break;
+ case EbtUint64: newOp = EOpConvUint64ToFloat16; break;
+ default:
+ return nullptr;
+ }
+ break;
+ case EbtBool:
+ switch (node->getBasicType()) {
+ case EbtInt8: newOp = EOpConvInt8ToBool; break;
+ case EbtUint8: newOp = EOpConvUint8ToBool; break;
+ case EbtInt16: newOp = EOpConvInt16ToBool; break;
+ case EbtUint16: newOp = EOpConvUint16ToBool; break;
+ case EbtInt: newOp = EOpConvIntToBool; break;
+ case EbtUint: newOp = EOpConvUintToBool; break;
+ case EbtFloat: newOp = EOpConvFloatToBool; break;
+ case EbtDouble: newOp = EOpConvDoubleToBool; break;
+ case EbtFloat16: newOp = EOpConvFloat16ToBool; break;
+ case EbtInt64: newOp = EOpConvInt64ToBool; break;
+ case EbtUint64: newOp = EOpConvUint64ToBool; break;
+ default:
+ return nullptr;
+ }
+ break;
+ case EbtInt8:
+ switch (node->getBasicType()) {
+ case EbtUint8: newOp = EOpConvUint8ToInt8; break;
+ case EbtInt16: newOp = EOpConvInt16ToInt8; break;
+ case EbtUint16: newOp = EOpConvUint16ToInt8; break;
+ case EbtInt: newOp = EOpConvIntToInt8; break;
+ case EbtUint: newOp = EOpConvUintToInt8; break;
+ case EbtInt64: newOp = EOpConvInt64ToInt8; break;
+ case EbtUint64: newOp = EOpConvUint64ToInt8; break;
+ case EbtBool: newOp = EOpConvBoolToInt8; break;
+ case EbtFloat: newOp = EOpConvFloatToInt8; break;
+ case EbtDouble: newOp = EOpConvDoubleToInt8; break;
+ case EbtFloat16: newOp = EOpConvFloat16ToInt8; break;
+ default:
+ return nullptr;
+ }
+ break;
+ case EbtUint8:
+ switch (node->getBasicType()) {
+ case EbtInt8: newOp = EOpConvInt8ToUint8; break;
+ case EbtInt16: newOp = EOpConvInt16ToUint8; break;
+ case EbtUint16: newOp = EOpConvUint16ToUint8; break;
+ case EbtInt: newOp = EOpConvIntToUint8; break;
+ case EbtUint: newOp = EOpConvUintToUint8; break;
+ case EbtInt64: newOp = EOpConvInt64ToUint8; break;
+ case EbtUint64: newOp = EOpConvUint64ToUint8; break;
+ case EbtBool: newOp = EOpConvBoolToUint8; break;
+ case EbtFloat: newOp = EOpConvFloatToUint8; break;
+ case EbtDouble: newOp = EOpConvDoubleToUint8; break;
+ case EbtFloat16: newOp = EOpConvFloat16ToUint8; break;
+ default:
+ return nullptr;
+ }
+ break;
+
+ case EbtInt16:
+ switch (node->getBasicType()) {
+ case EbtUint8: newOp = EOpConvUint8ToInt16; break;
+ case EbtInt8: newOp = EOpConvInt8ToInt16; break;
+ case EbtUint16: newOp = EOpConvUint16ToInt16; break;
+ case EbtInt: newOp = EOpConvIntToInt16; break;
+ case EbtUint: newOp = EOpConvUintToInt16; break;
+ case EbtInt64: newOp = EOpConvInt64ToInt16; break;
+ case EbtUint64: newOp = EOpConvUint64ToInt16; break;
+ case EbtBool: newOp = EOpConvBoolToInt16; break;
+ case EbtFloat: newOp = EOpConvFloatToInt16; break;
+ case EbtDouble: newOp = EOpConvDoubleToInt16; break;
+ case EbtFloat16: newOp = EOpConvFloat16ToInt16; break;
+ default:
+ return nullptr;
+ }
+ break;
+ case EbtUint16:
+ switch (node->getBasicType()) {
+ case EbtInt8: newOp = EOpConvInt8ToUint16; break;
+ case EbtUint8: newOp = EOpConvUint8ToUint16; break;
+ case EbtInt16: newOp = EOpConvInt16ToUint16; break;
+ case EbtInt: newOp = EOpConvIntToUint16; break;
+ case EbtUint: newOp = EOpConvUintToUint16; break;
+ case EbtInt64: newOp = EOpConvInt64ToUint16; break;
+ case EbtUint64: newOp = EOpConvUint64ToUint16; break;
+ case EbtBool: newOp = EOpConvBoolToUint16; break;
+ case EbtFloat: newOp = EOpConvFloatToUint16; break;
+ case EbtDouble: newOp = EOpConvDoubleToUint16; break;
+ case EbtFloat16: newOp = EOpConvFloat16ToUint16; break;
+ default:
+ return nullptr;
+ }
+ break;
+
+ case EbtInt:
+ switch (node->getBasicType()) {
+ case EbtInt8: newOp = EOpConvInt8ToInt; break;
+ case EbtUint8: newOp = EOpConvUint8ToInt; break;
+ case EbtInt16: newOp = EOpConvInt16ToInt; break;
+ case EbtUint16: newOp = EOpConvUint16ToInt; break;
+ case EbtUint: newOp = EOpConvUintToInt; break;
+ case EbtBool: newOp = EOpConvBoolToInt; break;
+ case EbtFloat: newOp = EOpConvFloatToInt; break;
+ case EbtDouble: newOp = EOpConvDoubleToInt; break;
+ case EbtFloat16: newOp = EOpConvFloat16ToInt; break;
+ case EbtInt64: newOp = EOpConvInt64ToInt; break;
+ case EbtUint64: newOp = EOpConvUint64ToInt; break;
+ default:
+ return nullptr;
+ }
+ break;
+ case EbtUint:
+ switch (node->getBasicType()) {
+ case EbtInt8: newOp = EOpConvInt8ToUint; break;
+ case EbtUint8: newOp = EOpConvUint8ToUint; break;
+ case EbtInt16: newOp = EOpConvInt16ToUint; break;
+ case EbtUint16: newOp = EOpConvUint16ToUint; break;
+ case EbtInt: newOp = EOpConvIntToUint; break;
+ case EbtBool: newOp = EOpConvBoolToUint; break;
+ case EbtFloat: newOp = EOpConvFloatToUint; break;
+ case EbtDouble: newOp = EOpConvDoubleToUint; break;
+ case EbtFloat16: newOp = EOpConvFloat16ToUint; break;
+ case EbtInt64: newOp = EOpConvInt64ToUint; break;
+ case EbtUint64: newOp = EOpConvUint64ToUint; break;
+ default:
+ return nullptr;
+ }
+ break;
+ case EbtInt64:
+ switch (node->getBasicType()) {
+ case EbtInt8: newOp = EOpConvInt8ToInt64; break;
+ case EbtUint8: newOp = EOpConvUint8ToInt64; break;
+ case EbtInt16: newOp = EOpConvInt16ToInt64; break;
+ case EbtUint16: newOp = EOpConvUint16ToInt64; break;
+ case EbtInt: newOp = EOpConvIntToInt64; break;
+ case EbtUint: newOp = EOpConvUintToInt64; break;
+ case EbtBool: newOp = EOpConvBoolToInt64; break;
+ case EbtFloat: newOp = EOpConvFloatToInt64; break;
+ case EbtDouble: newOp = EOpConvDoubleToInt64; break;
+ case EbtFloat16: newOp = EOpConvFloat16ToInt64; break;
+ case EbtUint64: newOp = EOpConvUint64ToInt64; break;
+ default:
+ return nullptr;
+ }
+ break;
+ case EbtUint64:
+ switch (node->getBasicType()) {
+ case EbtInt8: newOp = EOpConvInt8ToUint64; break;
+ case EbtUint8: newOp = EOpConvUint8ToUint64; break;
+ case EbtInt16: newOp = EOpConvInt16ToUint64; break;
+ case EbtUint16: newOp = EOpConvUint16ToUint64; break;
+ case EbtInt: newOp = EOpConvIntToUint64; break;
+ case EbtUint: newOp = EOpConvUintToUint64; break;
+ case EbtBool: newOp = EOpConvBoolToUint64; break;
+ case EbtFloat: newOp = EOpConvFloatToUint64; break;
+ case EbtDouble: newOp = EOpConvDoubleToUint64; break;
+ case EbtFloat16: newOp = EOpConvFloat16ToUint64; break;
+ case EbtInt64: newOp = EOpConvInt64ToUint64; break;
+ default:
+ return nullptr;
+ }
+ break;
+ default:
+ return nullptr;
+ }
+
+ TType newType(convertTo, EvqTemporary, node->getVectorSize(), node->getMatrixCols(), node->getMatrixRows());
+ newNode = addUnaryNode(newOp, node, node->getLoc(), newType);
+
+ if (node->getAsConstantUnion()) {
+ TIntermTyped* folded = node->getAsConstantUnion()->fold(newOp, newType);
+ if (folded)
+ return folded;
+ }
+
+ // Propagate specialization-constant-ness, if allowed
+ if (node->getType().getQualifier().isSpecConstant() && isSpecializationOperation(*newNode))
+ newNode->getWritableType().getQualifier().makeSpecConstant();
+
+ return newNode;
+}
+
+TIntermTyped* TIntermediate::addConversion(TBasicType convertTo, TIntermTyped* node) const
+{
+ return createConversion(convertTo, node);
+}
+
+// For converting a pair of operands to a binary operation to compatible
+// types with each other, relative to the operation in 'op'.
+// This does not cover assignment operations, which is asymmetric in that the
+// left type is not changeable.
+// See addConversion(op, type, node) for assignments and unary operation
+// conversions.
+//
+// Generally, this is focused on basic type conversion, not shape conversion.
+// See addShapeConversion() for shape conversions.
+//
+// Returns the converted pair of nodes.
+// Returns <nullptr, nullptr> when there is no conversion.
+std::tuple<TIntermTyped*, TIntermTyped*>
+TIntermediate::addConversion(TOperator op, TIntermTyped* node0, TIntermTyped* node1)
+{
+ if (!isConversionAllowed(op, node0) || !isConversionAllowed(op, node1))
+ return std::make_tuple(nullptr, nullptr);
+
+ if (node0->getType() != node1->getType()) {
+ // If differing structure, then no conversions.
+ if (node0->isStruct() || node1->isStruct())
+ return std::make_tuple(nullptr, nullptr);
+
+ // If differing arrays, then no conversions.
+ if (node0->getType().isArray() || node1->getType().isArray())
+ return std::make_tuple(nullptr, nullptr);
+
+ // No implicit conversions for operations involving cooperative matrices
+ if (node0->getType().isCoopMat() || node1->getType().isCoopMat())
+ return std::make_tuple(node0, node1);
+ }
+
+ auto promoteTo = std::make_tuple(EbtNumTypes, EbtNumTypes);
+
+ switch (op) {
+ //
+ // List all the binary ops that can implicitly convert one operand to the other's type;
+ // This implements the 'policy' for implicit type conversion.
+ //
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+ case EOpEqual:
+ case EOpNotEqual:
+
+ case EOpAdd:
+ case EOpSub:
+ case EOpMul:
+ case EOpDiv:
+ case EOpMod:
+
+ case EOpVectorTimesScalar:
+ case EOpVectorTimesMatrix:
+ case EOpMatrixTimesVector:
+ case EOpMatrixTimesScalar:
+
+ case EOpAnd:
+ case EOpInclusiveOr:
+ case EOpExclusiveOr:
+
+ case EOpSequence: // used by ?:
+
+ if (node0->getBasicType() == node1->getBasicType())
+ return std::make_tuple(node0, node1);
+
+ promoteTo = getConversionDestinatonType(node0->getBasicType(), node1->getBasicType(), op);
+ if (std::get<0>(promoteTo) == EbtNumTypes || std::get<1>(promoteTo) == EbtNumTypes)
+ return std::make_tuple(nullptr, nullptr);
+
+ break;
+
+ case EOpLogicalAnd:
+ case EOpLogicalOr:
+ case EOpLogicalXor:
+ if (source == EShSourceHlsl)
+ promoteTo = std::make_tuple(EbtBool, EbtBool);
+ else
+ return std::make_tuple(node0, node1);
+ break;
+
+ // There are no conversions needed for GLSL; the shift amount just needs to be an
+ // integer type, as does the base.
+ // HLSL can promote bools to ints to make this work.
+ case EOpLeftShift:
+ case EOpRightShift:
+ if (source == EShSourceHlsl) {
+ TBasicType node0BasicType = node0->getBasicType();
+ if (node0BasicType == EbtBool)
+ node0BasicType = EbtInt;
+ if (node1->getBasicType() == EbtBool)
+ promoteTo = std::make_tuple(node0BasicType, EbtInt);
+ else
+ promoteTo = std::make_tuple(node0BasicType, node1->getBasicType());
+ } else {
+ if (isTypeInt(node0->getBasicType()) && isTypeInt(node1->getBasicType()))
+ return std::make_tuple(node0, node1);
+ else
+ return std::make_tuple(nullptr, nullptr);
+ }
+ break;
+
+ default:
+ if (node0->getType() == node1->getType())
+ return std::make_tuple(node0, node1);
+
+ return std::make_tuple(nullptr, nullptr);
+ }
+
+ TIntermTyped* newNode0;
+ TIntermTyped* newNode1;
+
+ if (std::get<0>(promoteTo) != node0->getType().getBasicType()) {
+ if (node0->getAsConstantUnion())
+ newNode0 = promoteConstantUnion(std::get<0>(promoteTo), node0->getAsConstantUnion());
+ else
+ newNode0 = createConversion(std::get<0>(promoteTo), node0);
+ } else
+ newNode0 = node0;
+
+ if (std::get<1>(promoteTo) != node1->getType().getBasicType()) {
+ if (node1->getAsConstantUnion())
+ newNode1 = promoteConstantUnion(std::get<1>(promoteTo), node1->getAsConstantUnion());
+ else
+ newNode1 = createConversion(std::get<1>(promoteTo), node1);
+ } else
+ newNode1 = node1;
+
+ return std::make_tuple(newNode0, newNode1);
+}
+
+//
+// Convert the node's type to the given type, as allowed by the operation involved: 'op'.
+// For implicit conversions, 'op' is not the requested conversion, it is the explicit
+// operation requiring the implicit conversion.
+//
+// Binary operation conversions should be handled by addConversion(op, node, node), not here.
+//
+// Returns a node representing the conversion, which could be the same
+// node passed in if no conversion was needed.
+//
+// Generally, this is focused on basic type conversion, not shape conversion.
+// See addShapeConversion() for shape conversions.
+//
+// Return nullptr if a conversion can't be done.
+//
+TIntermTyped* TIntermediate::addConversion(TOperator op, const TType& type, TIntermTyped* node)
+{
+ if (!isConversionAllowed(op, node))
+ return nullptr;
+
+ // Otherwise, if types are identical, no problem
+ if (type == node->getType())
+ return node;
+
+ // If one's a structure, then no conversions.
+ if (type.isStruct() || node->isStruct())
+ return nullptr;
+
+ // If one's an array, then no conversions.
+ if (type.isArray() || node->getType().isArray())
+ return nullptr;
+
+ // Note: callers are responsible for other aspects of shape,
+ // like vector and matrix sizes.
+
+ TBasicType promoteTo;
+ // GL_EXT_shader_16bit_storage can't do OpConstantComposite with
+ // 16-bit types, so disable promotion for those types.
+ bool canPromoteConstant = true;
+
+ switch (op) {
+ //
+ // Explicit conversions (unary operations)
+ //
+ case EOpConstructBool:
+ promoteTo = EbtBool;
+ break;
+ case EOpConstructFloat:
+ promoteTo = EbtFloat;
+ break;
+ case EOpConstructDouble:
+ promoteTo = EbtDouble;
+ break;
+ case EOpConstructFloat16:
+ promoteTo = EbtFloat16;
+ canPromoteConstant = extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types) ||
+ extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_float16);
+ break;
+ case EOpConstructInt8:
+ promoteTo = EbtInt8;
+ canPromoteConstant = extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types) ||
+ extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_int8);
+ break;
+ case EOpConstructUint8:
+ promoteTo = EbtUint8;
+ canPromoteConstant = extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types) ||
+ extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_int8);
+ break;
+ case EOpConstructInt16:
+ promoteTo = EbtInt16;
+ canPromoteConstant = extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types) ||
+ extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_int16);
+ break;
+ case EOpConstructUint16:
+ promoteTo = EbtUint16;
+ canPromoteConstant = extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types) ||
+ extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_int16);
+ break;
+ case EOpConstructInt:
+ promoteTo = EbtInt;
+ break;
+ case EOpConstructUint:
+ promoteTo = EbtUint;
+ break;
+ case EOpConstructInt64:
+ promoteTo = EbtInt64;
+ break;
+ case EOpConstructUint64:
+ promoteTo = EbtUint64;
+ break;
+
+ case EOpLogicalNot:
+
+ case EOpFunctionCall:
+
+ case EOpReturn:
+ case EOpAssign:
+ case EOpAddAssign:
+ case EOpSubAssign:
+ case EOpMulAssign:
+ case EOpVectorTimesScalarAssign:
+ case EOpMatrixTimesScalarAssign:
+ case EOpDivAssign:
+ case EOpModAssign:
+ case EOpAndAssign:
+ case EOpInclusiveOrAssign:
+ case EOpExclusiveOrAssign:
+
+ case EOpAtan:
+ case EOpClamp:
+ case EOpCross:
+ case EOpDistance:
+ case EOpDot:
+ case EOpDst:
+ case EOpFaceForward:
+ case EOpFma:
+ case EOpFrexp:
+ case EOpLdexp:
+ case EOpMix:
+ case EOpLit:
+ case EOpMax:
+ case EOpMin:
+ case EOpModf:
+ case EOpPow:
+ case EOpReflect:
+ case EOpRefract:
+ case EOpSmoothStep:
+ case EOpStep:
+
+ case EOpSequence:
+ case EOpConstructStruct:
+ case EOpConstructCooperativeMatrix:
+
+ if (type.getBasicType() == EbtReference || node->getType().getBasicType() == EbtReference) {
+ // types must match to assign a reference
+ if (type == node->getType())
+ return node;
+ else
+ return nullptr;
+ }
+
+ if (type.getBasicType() == node->getType().getBasicType())
+ return node;
+
+ if (canImplicitlyPromote(node->getBasicType(), type.getBasicType(), op))
+ promoteTo = type.getBasicType();
+ else
+ return nullptr;
+ break;
+
+ // For GLSL, there are no conversions needed; the shift amount just needs to be an
+ // integer type, as do the base/result.
+ // HLSL can convert the shift from a bool to an int.
+ case EOpLeftShiftAssign:
+ case EOpRightShiftAssign:
+ {
+ if (source == EShSourceHlsl && node->getType().getBasicType() == EbtBool)
+ promoteTo = type.getBasicType();
+ else {
+ if (isTypeInt(type.getBasicType()) && isTypeInt(node->getBasicType()))
+ return node;
+ else
+ return nullptr;
+ }
+ break;
+ }
+
+ default:
+ // default is to require a match; all exceptions should have case statements above
+
+ if (type.getBasicType() == node->getType().getBasicType())
+ return node;
+ else
+ return nullptr;
+ }
+
+ if (canPromoteConstant && node->getAsConstantUnion())
+ return promoteConstantUnion(promoteTo, node->getAsConstantUnion());
+
+ //
+ // Add a new newNode for the conversion.
+ //
+ TIntermTyped* newNode = createConversion(promoteTo, node);
+
+ return newNode;
+}
+
+// Convert the node's shape of type for the given type, as allowed by the
+// operation involved: 'op'. This is for situations where there is only one
+// direction to consider doing the shape conversion.
+//
+// This implements policy, it call addShapeConversion() for the mechanism.
+//
+// Generally, the AST represents allowed GLSL shapes, so this isn't needed
+// for GLSL. Bad shapes are caught in conversion or promotion.
+//
+// Return 'node' if no conversion was done. Promotion handles final shape
+// checking.
+//
+TIntermTyped* TIntermediate::addUniShapeConversion(TOperator op, const TType& type, TIntermTyped* node)
+{
+ // some source languages don't do this
+ switch (source) {
+ case EShSourceHlsl:
+ break;
+ case EShSourceGlsl:
+ default:
+ return node;
+ }
+
+ // some operations don't do this
+ switch (op) {
+ case EOpFunctionCall:
+ case EOpReturn:
+ break;
+
+ case EOpMulAssign:
+ // want to support vector *= scalar native ops in AST and lower, not smear, similarly for
+ // matrix *= scalar, etc.
+
+ case EOpAddAssign:
+ case EOpSubAssign:
+ case EOpDivAssign:
+ case EOpAndAssign:
+ case EOpInclusiveOrAssign:
+ case EOpExclusiveOrAssign:
+ case EOpRightShiftAssign:
+ case EOpLeftShiftAssign:
+ if (node->getVectorSize() == 1)
+ return node;
+ break;
+
+ case EOpAssign:
+ break;
+
+ case EOpMix:
+ break;
+
+ default:
+ return node;
+ }
+
+ return addShapeConversion(type, node);
+}
+
+// Convert the nodes' shapes to be compatible for the operation 'op'.
+//
+// This implements policy, it call addShapeConversion() for the mechanism.
+//
+// Generally, the AST represents allowed GLSL shapes, so this isn't needed
+// for GLSL. Bad shapes are caught in conversion or promotion.
+//
+void TIntermediate::addBiShapeConversion(TOperator op, TIntermTyped*& lhsNode, TIntermTyped*& rhsNode)
+{
+ // some source languages don't do this
+ switch (source) {
+ case EShSourceHlsl:
+ break;
+ case EShSourceGlsl:
+ default:
+ return;
+ }
+
+ // some operations don't do this
+ // 'break' will mean attempt bidirectional conversion
+ switch (op) {
+ case EOpMulAssign:
+ case EOpAssign:
+ case EOpAddAssign:
+ case EOpSubAssign:
+ case EOpDivAssign:
+ case EOpAndAssign:
+ case EOpInclusiveOrAssign:
+ case EOpExclusiveOrAssign:
+ case EOpRightShiftAssign:
+ case EOpLeftShiftAssign:
+ // switch to unidirectional conversion (the lhs can't change)
+ rhsNode = addUniShapeConversion(op, lhsNode->getType(), rhsNode);
+ return;
+
+ case EOpMul:
+ // matrix multiply does not change shapes
+ if (lhsNode->isMatrix() && rhsNode->isMatrix())
+ return;
+ case EOpAdd:
+ case EOpSub:
+ case EOpDiv:
+ // want to support vector * scalar native ops in AST and lower, not smear, similarly for
+ // matrix * vector, etc.
+ if (lhsNode->getVectorSize() == 1 || rhsNode->getVectorSize() == 1)
+ return;
+ break;
+
+ case EOpRightShift:
+ case EOpLeftShift:
+ // can natively support the right operand being a scalar and the left a vector,
+ // but not the reverse
+ if (rhsNode->getVectorSize() == 1)
+ return;
+ break;
+
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+
+ case EOpEqual:
+ case EOpNotEqual:
+
+ case EOpLogicalAnd:
+ case EOpLogicalOr:
+ case EOpLogicalXor:
+
+ case EOpAnd:
+ case EOpInclusiveOr:
+ case EOpExclusiveOr:
+
+ case EOpMix:
+ break;
+
+ default:
+ return;
+ }
+
+ // Do bidirectional conversions
+ if (lhsNode->getType().isScalarOrVec1() || rhsNode->getType().isScalarOrVec1()) {
+ if (lhsNode->getType().isScalarOrVec1())
+ lhsNode = addShapeConversion(rhsNode->getType(), lhsNode);
+ else
+ rhsNode = addShapeConversion(lhsNode->getType(), rhsNode);
+ }
+ lhsNode = addShapeConversion(rhsNode->getType(), lhsNode);
+ rhsNode = addShapeConversion(lhsNode->getType(), rhsNode);
+}
+
+// Convert the node's shape of type for the given type, as allowed by the
+// operation involved: 'op'.
+//
+// Generally, the AST represents allowed GLSL shapes, so this isn't needed
+// for GLSL. Bad shapes are caught in conversion or promotion.
+//
+// Return 'node' if no conversion was done. Promotion handles final shape
+// checking.
+//
+TIntermTyped* TIntermediate::addShapeConversion(const TType& type, TIntermTyped* node)
+{
+ // no conversion needed
+ if (node->getType() == type)
+ return node;
+
+ // structures and arrays don't change shape, either to or from
+ if (node->getType().isStruct() || node->getType().isArray() ||
+ type.isStruct() || type.isArray())
+ return node;
+
+ // The new node that handles the conversion
+ TOperator constructorOp = mapTypeToConstructorOp(type);
+
+ if (source == EShSourceHlsl) {
+ // HLSL rules for scalar, vector and matrix conversions:
+ // 1) scalar can become anything, initializing every component with its value
+ // 2) vector and matrix can become scalar, first element is used (warning: truncation)
+ // 3) matrix can become matrix with less rows and/or columns (warning: truncation)
+ // 4) vector can become vector with less rows size (warning: truncation)
+ // 5a) vector 4 can become 2x2 matrix (special case) (same packing layout, its a reinterpret)
+ // 5b) 2x2 matrix can become vector 4 (special case) (same packing layout, its a reinterpret)
+
+ const TType &sourceType = node->getType();
+
+ // rule 1 for scalar to matrix is special
+ if (sourceType.isScalarOrVec1() && type.isMatrix()) {
+
+ // HLSL semantics: the scalar (or vec1) is replicated to every component of the matrix. Left to its
+ // own devices, the constructor from a scalar would populate the diagonal. This forces replication
+ // to every matrix element.
+
+ // Note that if the node is complex (e.g, a function call), we don't want to duplicate it here
+ // repeatedly, so we copy it to a temp, then use the temp.
+ const int matSize = type.computeNumComponents();
+ TIntermAggregate* rhsAggregate = new TIntermAggregate();
+
+ const bool isSimple = (node->getAsSymbolNode() != nullptr) || (node->getAsConstantUnion() != nullptr);
+
+ if (!isSimple) {
+ assert(0); // TODO: use node replicator service when available.
+ }
+
+ for (int x = 0; x < matSize; ++x)
+ rhsAggregate->getSequence().push_back(node);
+
+ return setAggregateOperator(rhsAggregate, constructorOp, type, node->getLoc());
+ }
+
+ // rule 1 and 2
+ if ((sourceType.isScalar() && !type.isScalar()) || (!sourceType.isScalar() && type.isScalar()))
+ return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc());
+
+ // rule 3 and 5b
+ if (sourceType.isMatrix()) {
+ // rule 3
+ if (type.isMatrix()) {
+ if ((sourceType.getMatrixCols() != type.getMatrixCols() || sourceType.getMatrixRows() != type.getMatrixRows()) &&
+ sourceType.getMatrixCols() >= type.getMatrixCols() && sourceType.getMatrixRows() >= type.getMatrixRows())
+ return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc());
+ // rule 5b
+ } else if (type.isVector()) {
+ if (type.getVectorSize() == 4 && sourceType.getMatrixCols() == 2 && sourceType.getMatrixRows() == 2)
+ return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc());
+ }
+ }
+
+ // rule 4 and 5a
+ if (sourceType.isVector()) {
+ // rule 4
+ if (type.isVector())
+ {
+ if (sourceType.getVectorSize() > type.getVectorSize())
+ return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc());
+ // rule 5a
+ } else if (type.isMatrix()) {
+ if (sourceType.getVectorSize() == 4 && type.getMatrixCols() == 2 && type.getMatrixRows() == 2)
+ return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc());
+ }
+ }
+ }
+
+ // scalar -> vector or vec1 -> vector or
+ // vector -> scalar or
+ // bigger vector -> smaller vector
+ if ((node->getType().isScalarOrVec1() && type.isVector()) ||
+ (node->getType().isVector() && type.isScalar()) ||
+ (node->isVector() && type.isVector() && node->getVectorSize() > type.getVectorSize()))
+ return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc());
+
+ return node;
+}
+
+bool TIntermediate::isIntegralPromotion(TBasicType from, TBasicType to) const
+{
+ // integral promotions
+ if (to == EbtInt) {
+ switch(from) {
+ case EbtInt8:
+ case EbtInt16:
+ case EbtUint8:
+ case EbtUint16:
+ return true;
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+bool TIntermediate::isFPPromotion(TBasicType from, TBasicType to) const
+{
+ // floating-point promotions
+ if (to == EbtDouble) {
+ switch(from) {
+ case EbtFloat16:
+ case EbtFloat:
+ return true;
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+bool TIntermediate::isIntegralConversion(TBasicType from, TBasicType to) const
+{
+ switch (from) {
+ case EbtInt8:
+ switch (to) {
+ case EbtUint8:
+ case EbtInt16:
+ case EbtUint16:
+ case EbtUint:
+ case EbtInt64:
+ case EbtUint64:
+ return true;
+ default:
+ break;
+ }
+ break;
+ case EbtUint8:
+ switch (to) {
+ case EbtInt16:
+ case EbtUint16:
+ case EbtUint:
+ case EbtInt64:
+ case EbtUint64:
+ return true;
+ default:
+ break;
+ }
+ break;
+ case EbtInt16:
+ switch(to) {
+ case EbtUint16:
+ case EbtUint:
+ case EbtInt64:
+ case EbtUint64:
+ return true;
+ default:
+ break;
+ }
+ break;
+ case EbtUint16:
+ switch(to) {
+ case EbtUint:
+ case EbtInt64:
+ case EbtUint64:
+ return true;
+ default:
+ break;
+ }
+ break;
+ case EbtInt:
+ switch(to) {
+ case EbtUint:
+ return version >= 400 || (source == EShSourceHlsl);
+ case EbtInt64:
+ case EbtUint64:
+ return true;
+ default:
+ break;
+ }
+ break;
+ case EbtUint:
+ switch(to) {
+ case EbtInt64:
+ case EbtUint64:
+ return true;
+ default:
+ break;
+ }
+ break;
+ case EbtInt64:
+ if (to == EbtUint64) {
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+bool TIntermediate::isFPConversion(TBasicType from, TBasicType to) const
+{
+ if (to == EbtFloat && from == EbtFloat16) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool TIntermediate::isFPIntegralConversion(TBasicType from, TBasicType to) const
+{
+ switch (from) {
+ case EbtInt8:
+ case EbtUint8:
+ case EbtInt16:
+ case EbtUint16:
+ switch (to) {
+ case EbtFloat16:
+ case EbtFloat:
+ case EbtDouble:
+ return true;
+ default:
+ break;
+ }
+ break;
+ case EbtInt:
+ case EbtUint:
+ switch(to) {
+ case EbtFloat:
+ case EbtDouble:
+ return true;
+ default:
+ break;
+ }
+ break;
+ case EbtInt64:
+ case EbtUint64:
+ if (to == EbtDouble) {
+ return true;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return false;
+}
+
+//
+// See if the 'from' type is allowed to be implicitly converted to the
+// 'to' type. This is not about vector/array/struct, only about basic type.
+//
+bool TIntermediate::canImplicitlyPromote(TBasicType from, TBasicType to, TOperator op) const
+{
+ if (profile == EEsProfile || version == 110)
+ return false;
+
+ if (from == to)
+ return true;
+
+ // TODO: Move more policies into language-specific handlers.
+ // Some languages allow more general (or potentially, more specific) conversions under some conditions.
+ if (source == EShSourceHlsl) {
+ const bool fromConvertable = (from == EbtFloat || from == EbtDouble || from == EbtInt || from == EbtUint || from == EbtBool);
+ const bool toConvertable = (to == EbtFloat || to == EbtDouble || to == EbtInt || to == EbtUint || to == EbtBool);
+
+ if (fromConvertable && toConvertable) {
+ switch (op) {
+ case EOpAndAssign: // assignments can perform arbitrary conversions
+ case EOpInclusiveOrAssign: // ...
+ case EOpExclusiveOrAssign: // ...
+ case EOpAssign: // ...
+ case EOpAddAssign: // ...
+ case EOpSubAssign: // ...
+ case EOpMulAssign: // ...
+ case EOpVectorTimesScalarAssign: // ...
+ case EOpMatrixTimesScalarAssign: // ...
+ case EOpDivAssign: // ...
+ case EOpModAssign: // ...
+ case EOpReturn: // function returns can also perform arbitrary conversions
+ case EOpFunctionCall: // conversion of a calling parameter
+ case EOpLogicalNot:
+ case EOpLogicalAnd:
+ case EOpLogicalOr:
+ case EOpLogicalXor:
+ case EOpConstructStruct:
+ return true;
+ default:
+ break;
+ }
+ }
+ }
+
+ bool explicitTypesEnabled = extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types) ||
+ extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_int8) ||
+ extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_int16) ||
+ extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_int32) ||
+ extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_int64) ||
+ extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_float16) ||
+ extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_float32) ||
+ extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_float64);
+
+ if (explicitTypesEnabled) {
+ // integral promotions
+ if (isIntegralPromotion(from, to)) {
+ return true;
+ }
+
+ // floating-point promotions
+ if (isFPPromotion(from, to)) {
+ return true;
+ }
+
+ // integral conversions
+ if (isIntegralConversion(from, to)) {
+ return true;
+ }
+
+ // floating-point conversions
+ if (isFPConversion(from, to)) {
+ return true;
+ }
+
+ // floating-integral conversions
+ if (isFPIntegralConversion(from, to)) {
+ return true;
+ }
+
+ // hlsl supported conversions
+ if (source == EShSourceHlsl) {
+ if (from == EbtBool && (to == EbtInt || to == EbtUint || to == EbtFloat))
+ return true;
+ }
+ } else {
+ switch (to) {
+ case EbtDouble:
+ switch (from) {
+ case EbtInt:
+ case EbtUint:
+ case EbtInt64:
+ case EbtUint64:
+ case EbtFloat:
+ case EbtDouble:
+ return true;
+#ifdef AMD_EXTENSIONS
+ case EbtInt16:
+ case EbtUint16:
+ return extensionRequested(E_GL_AMD_gpu_shader_int16);
+ case EbtFloat16:
+ return extensionRequested(E_GL_AMD_gpu_shader_half_float);
+#endif
+ default:
+ return false;
+ }
+ case EbtFloat:
+ switch (from) {
+ case EbtInt:
+ case EbtUint:
+ case EbtFloat:
+ return true;
+ case EbtBool:
+ return (source == EShSourceHlsl);
+#ifdef AMD_EXTENSIONS
+ case EbtInt16:
+ case EbtUint16:
+ return extensionRequested(E_GL_AMD_gpu_shader_int16);
+#endif
+ case EbtFloat16:
+ return
+#ifdef AMD_EXTENSIONS
+ extensionRequested(E_GL_AMD_gpu_shader_half_float) ||
+#endif
+ (source == EShSourceHlsl);
+ default:
+ return false;
+ }
+ case EbtUint:
+ switch (from) {
+ case EbtInt:
+ return version >= 400 || (source == EShSourceHlsl);
+ case EbtUint:
+ return true;
+ case EbtBool:
+ return (source == EShSourceHlsl);
+#ifdef AMD_EXTENSIONS
+ case EbtInt16:
+ case EbtUint16:
+ return extensionRequested(E_GL_AMD_gpu_shader_int16);
+#endif
+ default:
+ return false;
+ }
+ case EbtInt:
+ switch (from) {
+ case EbtInt:
+ return true;
+ case EbtBool:
+ return (source == EShSourceHlsl);
+#ifdef AMD_EXTENSIONS
+ case EbtInt16:
+ return extensionRequested(E_GL_AMD_gpu_shader_int16);
+#endif
+ default:
+ return false;
+ }
+ case EbtUint64:
+ switch (from) {
+ case EbtInt:
+ case EbtUint:
+ case EbtInt64:
+ case EbtUint64:
+ return true;
+#ifdef AMD_EXTENSIONS
+ case EbtInt16:
+ case EbtUint16:
+ return extensionRequested(E_GL_AMD_gpu_shader_int16);
+#endif
+ default:
+ return false;
+ }
+ case EbtInt64:
+ switch (from) {
+ case EbtInt:
+ case EbtInt64:
+ return true;
+#ifdef AMD_EXTENSIONS
+ case EbtInt16:
+ return extensionRequested(E_GL_AMD_gpu_shader_int16);
+#endif
+ default:
+ return false;
+ }
+ case EbtFloat16:
+#ifdef AMD_EXTENSIONS
+ switch (from) {
+ case EbtInt16:
+ case EbtUint16:
+ return extensionRequested(E_GL_AMD_gpu_shader_int16);
+ case EbtFloat16:
+ return extensionRequested(E_GL_AMD_gpu_shader_half_float);
+ default:
+ break;
+ }
+#endif
+ return false;
+ case EbtUint16:
+#ifdef AMD_EXTENSIONS
+ switch (from) {
+ case EbtInt16:
+ case EbtUint16:
+ return extensionRequested(E_GL_AMD_gpu_shader_int16);
+ default:
+ break;
+ }
+#endif
+ return false;
+ default:
+ return false;
+ }
+ }
+
+ return false;
+}
+
+static bool canSignedIntTypeRepresentAllUnsignedValues(TBasicType sintType, TBasicType uintType) {
+ switch(sintType) {
+ case EbtInt8:
+ switch(uintType) {
+ case EbtUint8:
+ case EbtUint16:
+ case EbtUint:
+ case EbtUint64:
+ return false;
+ default:
+ assert(false);
+ return false;
+ }
+ break;
+ case EbtInt16:
+ switch(uintType) {
+ case EbtUint8:
+ return true;
+ case EbtUint16:
+ case EbtUint:
+ case EbtUint64:
+ return false;
+ default:
+ assert(false);
+ return false;
+ }
+ break;
+ case EbtInt:
+ switch(uintType) {
+ case EbtUint8:
+ case EbtUint16:
+ return true;
+ case EbtUint:
+ return false;
+ default:
+ assert(false);
+ return false;
+ }
+ break;
+ case EbtInt64:
+ switch(uintType) {
+ case EbtUint8:
+ case EbtUint16:
+ case EbtUint:
+ return true;
+ case EbtUint64:
+ return false;
+ default:
+ assert(false);
+ return false;
+ }
+ break;
+ default:
+ assert(false);
+ return false;
+ }
+}
+
+
+static TBasicType getCorrespondingUnsignedType(TBasicType type) {
+ switch(type) {
+ case EbtInt8:
+ return EbtUint8;
+ case EbtInt16:
+ return EbtUint16;
+ case EbtInt:
+ return EbtUint;
+ case EbtInt64:
+ return EbtUint64;
+ default:
+ assert(false);
+ return EbtNumTypes;
+ }
+}
+
+// Implements the following rules
+// - If either operand has type float64_t or derived from float64_t,
+// the other shall be converted to float64_t or derived type.
+// - Otherwise, if either operand has type float32_t or derived from
+// float32_t, the other shall be converted to float32_t or derived type.
+// - Otherwise, if either operand has type float16_t or derived from
+// float16_t, the other shall be converted to float16_t or derived type.
+// - Otherwise, if both operands have integer types the following rules
+// shall be applied to the operands:
+// - If both operands have the same type, no further conversion
+// is needed.
+// - Otherwise, if both operands have signed integer types or both
+// have unsigned integer types, the operand with the type of lesser
+// integer conversion rank shall be converted to the type of the
+// operand with greater rank.
+// - Otherwise, if the operand that has unsigned integer type has rank
+// greater than or equal to the rank of the type of the other
+// operand, the operand with signed integer type shall be converted
+// to the type of the operand with unsigned integer type.
+// - Otherwise, if the type of the operand with signed integer type can
+// represent all of the values of the type of the operand with
+// unsigned integer type, the operand with unsigned integer type
+// shall be converted to the type of the operand with signed
+// integer type.
+// - Otherwise, both operands shall be converted to the unsigned
+// integer type corresponding to the type of the operand with signed
+// integer type.
+
+std::tuple<TBasicType, TBasicType> TIntermediate::getConversionDestinatonType(TBasicType type0, TBasicType type1, TOperator op) const
+{
+ TBasicType res0 = EbtNumTypes;
+ TBasicType res1 = EbtNumTypes;
+
+ if (profile == EEsProfile || version == 110)
+ return std::make_tuple(res0, res1);;
+
+ if (source == EShSourceHlsl) {
+ if (canImplicitlyPromote(type1, type0, op)) {
+ res0 = type0;
+ res1 = type0;
+ } else if (canImplicitlyPromote(type0, type1, op)) {
+ res0 = type1;
+ res1 = type1;
+ }
+ return std::make_tuple(res0, res1);
+ }
+
+ if ((type0 == EbtDouble && canImplicitlyPromote(type1, EbtDouble, op)) ||
+ (type1 == EbtDouble && canImplicitlyPromote(type0, EbtDouble, op)) ) {
+ res0 = EbtDouble;
+ res1 = EbtDouble;
+ } else if ((type0 == EbtFloat && canImplicitlyPromote(type1, EbtFloat, op)) ||
+ (type1 == EbtFloat && canImplicitlyPromote(type0, EbtFloat, op)) ) {
+ res0 = EbtFloat;
+ res1 = EbtFloat;
+ } else if ((type0 == EbtFloat16 && canImplicitlyPromote(type1, EbtFloat16, op)) ||
+ (type1 == EbtFloat16 && canImplicitlyPromote(type0, EbtFloat16, op)) ) {
+ res0 = EbtFloat16;
+ res1 = EbtFloat16;
+ } else if (isTypeInt(type0) && isTypeInt(type1) &&
+ (canImplicitlyPromote(type0, type1, op) || canImplicitlyPromote(type1, type0, op))) {
+ if ((isTypeSignedInt(type0) && isTypeSignedInt(type1)) ||
+ (isTypeUnsignedInt(type0) && isTypeUnsignedInt(type1))) {
+ if (getTypeRank(type0) < getTypeRank(type1)) {
+ res0 = type1;
+ res1 = type1;
+ } else {
+ res0 = type0;
+ res1 = type0;
+ }
+ } else if (isTypeUnsignedInt(type0) && (getTypeRank(type0) > getTypeRank(type1))) {
+ res0 = type0;
+ res1 = type0;
+ } else if (isTypeUnsignedInt(type1) && (getTypeRank(type1) > getTypeRank(type0))) {
+ res0 = type1;
+ res1 = type1;
+ } else if (isTypeSignedInt(type0)) {
+ if (canSignedIntTypeRepresentAllUnsignedValues(type0, type1)) {
+ res0 = type0;
+ res1 = type0;
+ } else {
+ res0 = getCorrespondingUnsignedType(type0);
+ res1 = getCorrespondingUnsignedType(type0);
+ }
+ } else if (isTypeSignedInt(type1)) {
+ if (canSignedIntTypeRepresentAllUnsignedValues(type1, type0)) {
+ res0 = type1;
+ res1 = type1;
+ } else {
+ res0 = getCorrespondingUnsignedType(type1);
+ res1 = getCorrespondingUnsignedType(type1);
+ }
+ }
+ }
+
+ return std::make_tuple(res0, res1);
+}
+
+//
+// Given a type, find what operation would fully construct it.
+//
+TOperator TIntermediate::mapTypeToConstructorOp(const TType& type) const
+{
+ TOperator op = EOpNull;
+
+ if (type.getQualifier().nonUniform)
+ return EOpConstructNonuniform;
+
+ if (type.isCoopMat())
+ return EOpConstructCooperativeMatrix;
+
+ switch (type.getBasicType()) {
+ case EbtStruct:
+ op = EOpConstructStruct;
+ break;
+ case EbtSampler:
+ if (type.getSampler().combined)
+ op = EOpConstructTextureSampler;
+ break;
+ case EbtFloat:
+ if (type.isMatrix()) {
+ switch (type.getMatrixCols()) {
+ case 2:
+ switch (type.getMatrixRows()) {
+ case 2: op = EOpConstructMat2x2; break;
+ case 3: op = EOpConstructMat2x3; break;
+ case 4: op = EOpConstructMat2x4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ case 3:
+ switch (type.getMatrixRows()) {
+ case 2: op = EOpConstructMat3x2; break;
+ case 3: op = EOpConstructMat3x3; break;
+ case 4: op = EOpConstructMat3x4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ case 4:
+ switch (type.getMatrixRows()) {
+ case 2: op = EOpConstructMat4x2; break;
+ case 3: op = EOpConstructMat4x3; break;
+ case 4: op = EOpConstructMat4x4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ default: break; // some compilers want this
+ }
+ } else {
+ switch(type.getVectorSize()) {
+ case 1: op = EOpConstructFloat; break;
+ case 2: op = EOpConstructVec2; break;
+ case 3: op = EOpConstructVec3; break;
+ case 4: op = EOpConstructVec4; break;
+ default: break; // some compilers want this
+ }
+ }
+ break;
+ case EbtDouble:
+ if (type.getMatrixCols()) {
+ switch (type.getMatrixCols()) {
+ case 2:
+ switch (type.getMatrixRows()) {
+ case 2: op = EOpConstructDMat2x2; break;
+ case 3: op = EOpConstructDMat2x3; break;
+ case 4: op = EOpConstructDMat2x4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ case 3:
+ switch (type.getMatrixRows()) {
+ case 2: op = EOpConstructDMat3x2; break;
+ case 3: op = EOpConstructDMat3x3; break;
+ case 4: op = EOpConstructDMat3x4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ case 4:
+ switch (type.getMatrixRows()) {
+ case 2: op = EOpConstructDMat4x2; break;
+ case 3: op = EOpConstructDMat4x3; break;
+ case 4: op = EOpConstructDMat4x4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ }
+ } else {
+ switch(type.getVectorSize()) {
+ case 1: op = EOpConstructDouble; break;
+ case 2: op = EOpConstructDVec2; break;
+ case 3: op = EOpConstructDVec3; break;
+ case 4: op = EOpConstructDVec4; break;
+ default: break; // some compilers want this
+ }
+ }
+ break;
+ case EbtFloat16:
+ if (type.getMatrixCols()) {
+ switch (type.getMatrixCols()) {
+ case 2:
+ switch (type.getMatrixRows()) {
+ case 2: op = EOpConstructF16Mat2x2; break;
+ case 3: op = EOpConstructF16Mat2x3; break;
+ case 4: op = EOpConstructF16Mat2x4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ case 3:
+ switch (type.getMatrixRows()) {
+ case 2: op = EOpConstructF16Mat3x2; break;
+ case 3: op = EOpConstructF16Mat3x3; break;
+ case 4: op = EOpConstructF16Mat3x4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ case 4:
+ switch (type.getMatrixRows()) {
+ case 2: op = EOpConstructF16Mat4x2; break;
+ case 3: op = EOpConstructF16Mat4x3; break;
+ case 4: op = EOpConstructF16Mat4x4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ }
+ }
+ else {
+ switch (type.getVectorSize()) {
+ case 1: op = EOpConstructFloat16; break;
+ case 2: op = EOpConstructF16Vec2; break;
+ case 3: op = EOpConstructF16Vec3; break;
+ case 4: op = EOpConstructF16Vec4; break;
+ default: break; // some compilers want this
+ }
+ }
+ break;
+ case EbtInt8:
+ switch(type.getVectorSize()) {
+ case 1: op = EOpConstructInt8; break;
+ case 2: op = EOpConstructI8Vec2; break;
+ case 3: op = EOpConstructI8Vec3; break;
+ case 4: op = EOpConstructI8Vec4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ case EbtUint8:
+ switch(type.getVectorSize()) {
+ case 1: op = EOpConstructUint8; break;
+ case 2: op = EOpConstructU8Vec2; break;
+ case 3: op = EOpConstructU8Vec3; break;
+ case 4: op = EOpConstructU8Vec4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ case EbtInt16:
+ switch(type.getVectorSize()) {
+ case 1: op = EOpConstructInt16; break;
+ case 2: op = EOpConstructI16Vec2; break;
+ case 3: op = EOpConstructI16Vec3; break;
+ case 4: op = EOpConstructI16Vec4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ case EbtUint16:
+ switch(type.getVectorSize()) {
+ case 1: op = EOpConstructUint16; break;
+ case 2: op = EOpConstructU16Vec2; break;
+ case 3: op = EOpConstructU16Vec3; break;
+ case 4: op = EOpConstructU16Vec4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ case EbtInt:
+ if (type.getMatrixCols()) {
+ switch (type.getMatrixCols()) {
+ case 2:
+ switch (type.getMatrixRows()) {
+ case 2: op = EOpConstructIMat2x2; break;
+ case 3: op = EOpConstructIMat2x3; break;
+ case 4: op = EOpConstructIMat2x4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ case 3:
+ switch (type.getMatrixRows()) {
+ case 2: op = EOpConstructIMat3x2; break;
+ case 3: op = EOpConstructIMat3x3; break;
+ case 4: op = EOpConstructIMat3x4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ case 4:
+ switch (type.getMatrixRows()) {
+ case 2: op = EOpConstructIMat4x2; break;
+ case 3: op = EOpConstructIMat4x3; break;
+ case 4: op = EOpConstructIMat4x4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ }
+ } else {
+ switch(type.getVectorSize()) {
+ case 1: op = EOpConstructInt; break;
+ case 2: op = EOpConstructIVec2; break;
+ case 3: op = EOpConstructIVec3; break;
+ case 4: op = EOpConstructIVec4; break;
+ default: break; // some compilers want this
+ }
+ }
+ break;
+ case EbtUint:
+ if (type.getMatrixCols()) {
+ switch (type.getMatrixCols()) {
+ case 2:
+ switch (type.getMatrixRows()) {
+ case 2: op = EOpConstructUMat2x2; break;
+ case 3: op = EOpConstructUMat2x3; break;
+ case 4: op = EOpConstructUMat2x4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ case 3:
+ switch (type.getMatrixRows()) {
+ case 2: op = EOpConstructUMat3x2; break;
+ case 3: op = EOpConstructUMat3x3; break;
+ case 4: op = EOpConstructUMat3x4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ case 4:
+ switch (type.getMatrixRows()) {
+ case 2: op = EOpConstructUMat4x2; break;
+ case 3: op = EOpConstructUMat4x3; break;
+ case 4: op = EOpConstructUMat4x4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ }
+ } else {
+ switch(type.getVectorSize()) {
+ case 1: op = EOpConstructUint; break;
+ case 2: op = EOpConstructUVec2; break;
+ case 3: op = EOpConstructUVec3; break;
+ case 4: op = EOpConstructUVec4; break;
+ default: break; // some compilers want this
+ }
+ }
+ break;
+ case EbtInt64:
+ switch(type.getVectorSize()) {
+ case 1: op = EOpConstructInt64; break;
+ case 2: op = EOpConstructI64Vec2; break;
+ case 3: op = EOpConstructI64Vec3; break;
+ case 4: op = EOpConstructI64Vec4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ case EbtUint64:
+ switch(type.getVectorSize()) {
+ case 1: op = EOpConstructUint64; break;
+ case 2: op = EOpConstructU64Vec2; break;
+ case 3: op = EOpConstructU64Vec3; break;
+ case 4: op = EOpConstructU64Vec4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ case EbtBool:
+ if (type.getMatrixCols()) {
+ switch (type.getMatrixCols()) {
+ case 2:
+ switch (type.getMatrixRows()) {
+ case 2: op = EOpConstructBMat2x2; break;
+ case 3: op = EOpConstructBMat2x3; break;
+ case 4: op = EOpConstructBMat2x4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ case 3:
+ switch (type.getMatrixRows()) {
+ case 2: op = EOpConstructBMat3x2; break;
+ case 3: op = EOpConstructBMat3x3; break;
+ case 4: op = EOpConstructBMat3x4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ case 4:
+ switch (type.getMatrixRows()) {
+ case 2: op = EOpConstructBMat4x2; break;
+ case 3: op = EOpConstructBMat4x3; break;
+ case 4: op = EOpConstructBMat4x4; break;
+ default: break; // some compilers want this
+ }
+ break;
+ }
+ } else {
+ switch(type.getVectorSize()) {
+ case 1: op = EOpConstructBool; break;
+ case 2: op = EOpConstructBVec2; break;
+ case 3: op = EOpConstructBVec3; break;
+ case 4: op = EOpConstructBVec4; break;
+ default: break; // some compilers want this
+ }
+ }
+ break;
+ case EbtReference:
+ op = EOpConstructReference;
+ break;
+ default:
+ break;
+ }
+
+ return op;
+}
+
+//
+// Safe way to combine two nodes into an aggregate. Works with null pointers,
+// a node that's not a aggregate yet, etc.
+//
+// Returns the resulting aggregate, unless nullptr was passed in for
+// both existing nodes.
+//
+TIntermAggregate* TIntermediate::growAggregate(TIntermNode* left, TIntermNode* right)
+{
+ if (left == nullptr && right == nullptr)
+ return nullptr;
+
+ TIntermAggregate* aggNode = nullptr;
+ if (left != nullptr)
+ aggNode = left->getAsAggregate();
+ if (aggNode == nullptr || aggNode->getOp() != EOpNull) {
+ aggNode = new TIntermAggregate;
+ if (left != nullptr)
+ aggNode->getSequence().push_back(left);
+ }
+
+ if (right != nullptr)
+ aggNode->getSequence().push_back(right);
+
+ return aggNode;
+}
+
+TIntermAggregate* TIntermediate::growAggregate(TIntermNode* left, TIntermNode* right, const TSourceLoc& loc)
+{
+ TIntermAggregate* aggNode = growAggregate(left, right);
+ if (aggNode)
+ aggNode->setLoc(loc);
+
+ return aggNode;
+}
+
+//
+// Turn an existing node into an aggregate.
+//
+// Returns an aggregate, unless nullptr was passed in for the existing node.
+//
+TIntermAggregate* TIntermediate::makeAggregate(TIntermNode* node)
+{
+ if (node == nullptr)
+ return nullptr;
+
+ TIntermAggregate* aggNode = new TIntermAggregate;
+ aggNode->getSequence().push_back(node);
+ aggNode->setLoc(node->getLoc());
+
+ return aggNode;
+}
+
+TIntermAggregate* TIntermediate::makeAggregate(TIntermNode* node, const TSourceLoc& loc)
+{
+ if (node == nullptr)
+ return nullptr;
+
+ TIntermAggregate* aggNode = new TIntermAggregate;
+ aggNode->getSequence().push_back(node);
+ aggNode->setLoc(loc);
+
+ return aggNode;
+}
+
+//
+// Make an aggregate with an empty sequence.
+//
+TIntermAggregate* TIntermediate::makeAggregate(const TSourceLoc& loc)
+{
+ TIntermAggregate* aggNode = new TIntermAggregate;
+ aggNode->setLoc(loc);
+
+ return aggNode;
+}
+
+//
+// For "if" test nodes. There are three children; a condition,
+// a true path, and a false path. The two paths are in the
+// nodePair.
+//
+// Returns the selection node created.
+//
+TIntermSelection* TIntermediate::addSelection(TIntermTyped* cond, TIntermNodePair nodePair, const TSourceLoc& loc)
+{
+ //
+ // Don't prune the false path for compile-time constants; it's needed
+ // for static access analysis.
+ //
+
+ TIntermSelection* node = new TIntermSelection(cond, nodePair.node1, nodePair.node2);
+ node->setLoc(loc);
+
+ return node;
+}
+
+TIntermTyped* TIntermediate::addComma(TIntermTyped* left, TIntermTyped* right, const TSourceLoc& loc)
+{
+ // However, the lowest precedence operators of the sequence operator ( , ) and the assignment operators
+ // ... are not included in the operators that can create a constant expression.
+ //
+ // if (left->getType().getQualifier().storage == EvqConst &&
+ // right->getType().getQualifier().storage == EvqConst) {
+
+ // return right;
+ //}
+
+ TIntermTyped *commaAggregate = growAggregate(left, right, loc);
+ commaAggregate->getAsAggregate()->setOperator(EOpComma);
+ commaAggregate->setType(right->getType());
+ commaAggregate->getWritableType().getQualifier().makeTemporary();
+
+ return commaAggregate;
+}
+
+TIntermTyped* TIntermediate::addMethod(TIntermTyped* object, const TType& type, const TString* name, const TSourceLoc& loc)
+{
+ TIntermMethod* method = new TIntermMethod(object, type, *name);
+ method->setLoc(loc);
+
+ return method;
+}
+
+//
+// For "?:" test nodes. There are three children; a condition,
+// a true path, and a false path. The two paths are specified
+// as separate parameters. For vector 'cond', the true and false
+// are not paths, but vectors to mix.
+//
+// Specialization constant operations include
+// - The ternary operator ( ? : )
+//
+// Returns the selection node created, or nullptr if one could not be.
+//
+TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock,
+ const TSourceLoc& loc)
+{
+ // If it's void, go to the if-then-else selection()
+ if (trueBlock->getBasicType() == EbtVoid && falseBlock->getBasicType() == EbtVoid) {
+ TIntermNodePair pair = { trueBlock, falseBlock };
+ TIntermSelection* selection = addSelection(cond, pair, loc);
+ if (getSource() == EShSourceHlsl)
+ selection->setNoShortCircuit();
+
+ return selection;
+ }
+
+ //
+ // Get compatible types.
+ //
+ auto children = addConversion(EOpSequence, trueBlock, falseBlock);
+ trueBlock = std::get<0>(children);
+ falseBlock = std::get<1>(children);
+
+ if (trueBlock == nullptr || falseBlock == nullptr)
+ return nullptr;
+
+ // Handle a vector condition as a mix
+ if (!cond->getType().isScalarOrVec1()) {
+ TType targetVectorType(trueBlock->getType().getBasicType(), EvqTemporary,
+ cond->getType().getVectorSize());
+ // smear true/false operands as needed
+ trueBlock = addUniShapeConversion(EOpMix, targetVectorType, trueBlock);
+ falseBlock = addUniShapeConversion(EOpMix, targetVectorType, falseBlock);
+
+ // After conversion, types have to match.
+ if (falseBlock->getType() != trueBlock->getType())
+ return nullptr;
+
+ // make the mix operation
+ TIntermAggregate* mix = makeAggregate(loc);
+ mix = growAggregate(mix, falseBlock);
+ mix = growAggregate(mix, trueBlock);
+ mix = growAggregate(mix, cond);
+ mix->setType(targetVectorType);
+ mix->setOp(EOpMix);
+
+ return mix;
+ }
+
+ // Now have a scalar condition...
+
+ // Convert true and false expressions to matching types
+ addBiShapeConversion(EOpMix, trueBlock, falseBlock);
+
+ // After conversion, types have to match.
+ if (falseBlock->getType() != trueBlock->getType())
+ return nullptr;
+
+ // Eliminate the selection when the condition is a scalar and all operands are constant.
+ if (cond->getAsConstantUnion() && trueBlock->getAsConstantUnion() && falseBlock->getAsConstantUnion()) {
+ if (cond->getAsConstantUnion()->getConstArray()[0].getBConst())
+ return trueBlock;
+ else
+ return falseBlock;
+ }
+
+ //
+ // Make a selection node.
+ //
+ TIntermSelection* node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType());
+ node->setLoc(loc);
+ node->getQualifier().precision = std::max(trueBlock->getQualifier().precision, falseBlock->getQualifier().precision);
+
+ if ((cond->getQualifier().isConstant() && specConstantPropagates(*trueBlock, *falseBlock)) ||
+ (cond->getQualifier().isSpecConstant() && trueBlock->getQualifier().isConstant() &&
+ falseBlock->getQualifier().isConstant()))
+ node->getQualifier().makeSpecConstant();
+ else
+ node->getQualifier().makeTemporary();
+
+ if (getSource() == EShSourceHlsl)
+ node->setNoShortCircuit();
+
+ return node;
+}
+
+//
+// Constant terminal nodes. Has a union that contains bool, float or int constants
+//
+// Returns the constant union node created.
+//
+
+TIntermConstantUnion* TIntermediate::addConstantUnion(const TConstUnionArray& unionArray, const TType& t, const TSourceLoc& loc, bool literal) const
+{
+ TIntermConstantUnion* node = new TIntermConstantUnion(unionArray, t);
+ node->getQualifier().storage = EvqConst;
+ node->setLoc(loc);
+ if (literal)
+ node->setLiteral();
+
+ return node;
+}
+TIntermConstantUnion* TIntermediate::addConstantUnion(signed char i8, const TSourceLoc& loc, bool literal) const
+{
+ TConstUnionArray unionArray(1);
+ unionArray[0].setI8Const(i8);
+
+ return addConstantUnion(unionArray, TType(EbtInt8, EvqConst), loc, literal);
+}
+
+TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned char u8, const TSourceLoc& loc, bool literal) const
+{
+ TConstUnionArray unionArray(1);
+ unionArray[0].setUConst(u8);
+
+ return addConstantUnion(unionArray, TType(EbtUint8, EvqConst), loc, literal);
+}
+
+TIntermConstantUnion* TIntermediate::addConstantUnion(signed short i16, const TSourceLoc& loc, bool literal) const
+{
+ TConstUnionArray unionArray(1);
+ unionArray[0].setI16Const(i16);
+
+ return addConstantUnion(unionArray, TType(EbtInt16, EvqConst), loc, literal);
+}
+
+TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned short u16, const TSourceLoc& loc, bool literal) const
+{
+ TConstUnionArray unionArray(1);
+ unionArray[0].setU16Const(u16);
+
+ return addConstantUnion(unionArray, TType(EbtUint16, EvqConst), loc, literal);
+}
+
+TIntermConstantUnion* TIntermediate::addConstantUnion(int i, const TSourceLoc& loc, bool literal) const
+{
+ TConstUnionArray unionArray(1);
+ unionArray[0].setIConst(i);
+
+ return addConstantUnion(unionArray, TType(EbtInt, EvqConst), loc, literal);
+}
+
+TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned int u, const TSourceLoc& loc, bool literal) const
+{
+ TConstUnionArray unionArray(1);
+ unionArray[0].setUConst(u);
+
+ return addConstantUnion(unionArray, TType(EbtUint, EvqConst), loc, literal);
+}
+
+TIntermConstantUnion* TIntermediate::addConstantUnion(long long i64, const TSourceLoc& loc, bool literal) const
+{
+ TConstUnionArray unionArray(1);
+ unionArray[0].setI64Const(i64);
+
+ return addConstantUnion(unionArray, TType(EbtInt64, EvqConst), loc, literal);
+}
+
+TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned long long u64, const TSourceLoc& loc, bool literal) const
+{
+ TConstUnionArray unionArray(1);
+ unionArray[0].setU64Const(u64);
+
+ return addConstantUnion(unionArray, TType(EbtUint64, EvqConst), loc, literal);
+}
+
+TIntermConstantUnion* TIntermediate::addConstantUnion(bool b, const TSourceLoc& loc, bool literal) const
+{
+ TConstUnionArray unionArray(1);
+ unionArray[0].setBConst(b);
+
+ return addConstantUnion(unionArray, TType(EbtBool, EvqConst), loc, literal);
+}
+
+TIntermConstantUnion* TIntermediate::addConstantUnion(double d, TBasicType baseType, const TSourceLoc& loc, bool literal) const
+{
+ assert(baseType == EbtFloat || baseType == EbtDouble || baseType == EbtFloat16);
+
+ TConstUnionArray unionArray(1);
+ unionArray[0].setDConst(d);
+
+ return addConstantUnion(unionArray, TType(baseType, EvqConst), loc, literal);
+}
+
+TIntermConstantUnion* TIntermediate::addConstantUnion(const TString* s, const TSourceLoc& loc, bool literal) const
+{
+ TConstUnionArray unionArray(1);
+ unionArray[0].setSConst(s);
+
+ return addConstantUnion(unionArray, TType(EbtString, EvqConst), loc, literal);
+}
+
+// Put vector swizzle selectors onto the given sequence
+void TIntermediate::pushSelector(TIntermSequence& sequence, const TVectorSelector& selector, const TSourceLoc& loc)
+{
+ TIntermConstantUnion* constIntNode = addConstantUnion(selector, loc);
+ sequence.push_back(constIntNode);
+}
+
+// Put matrix swizzle selectors onto the given sequence
+void TIntermediate::pushSelector(TIntermSequence& sequence, const TMatrixSelector& selector, const TSourceLoc& loc)
+{
+ TIntermConstantUnion* constIntNode = addConstantUnion(selector.coord1, loc);
+ sequence.push_back(constIntNode);
+ constIntNode = addConstantUnion(selector.coord2, loc);
+ sequence.push_back(constIntNode);
+}
+
+// Make an aggregate node that has a sequence of all selectors.
+template TIntermTyped* TIntermediate::addSwizzle<TVectorSelector>(TSwizzleSelectors<TVectorSelector>& selector, const TSourceLoc& loc);
+template TIntermTyped* TIntermediate::addSwizzle<TMatrixSelector>(TSwizzleSelectors<TMatrixSelector>& selector, const TSourceLoc& loc);
+template<typename selectorType>
+TIntermTyped* TIntermediate::addSwizzle(TSwizzleSelectors<selectorType>& selector, const TSourceLoc& loc)
+{
+ TIntermAggregate* node = new TIntermAggregate(EOpSequence);
+
+ node->setLoc(loc);
+ TIntermSequence &sequenceVector = node->getSequence();
+
+ for (int i = 0; i < selector.size(); i++)
+ pushSelector(sequenceVector, selector[i], loc);
+
+ return node;
+}
+
+//
+// Follow the left branches down to the root of an l-value
+// expression (just "." and []).
+//
+// Return the base of the l-value (where following indexing quits working).
+// Return nullptr if a chain following dereferences cannot be followed.
+//
+// 'swizzleOkay' says whether or not it is okay to consider a swizzle
+// a valid part of the dereference chain.
+//
+const TIntermTyped* TIntermediate::findLValueBase(const TIntermTyped* node, bool swizzleOkay)
+{
+ do {
+ const TIntermBinary* binary = node->getAsBinaryNode();
+ if (binary == nullptr)
+ return node;
+ TOperator op = binary->getOp();
+ if (op != EOpIndexDirect && op != EOpIndexIndirect && op != EOpIndexDirectStruct && op != EOpVectorSwizzle && op != EOpMatrixSwizzle)
+ return nullptr;
+ if (! swizzleOkay) {
+ if (op == EOpVectorSwizzle || op == EOpMatrixSwizzle)
+ return nullptr;
+ if ((op == EOpIndexDirect || op == EOpIndexIndirect) &&
+ (binary->getLeft()->getType().isVector() || binary->getLeft()->getType().isScalar()) &&
+ ! binary->getLeft()->getType().isArray())
+ return nullptr;
+ }
+ node = node->getAsBinaryNode()->getLeft();
+ } while (true);
+}
+
+//
+// Create while and do-while loop nodes.
+//
+TIntermLoop* TIntermediate::addLoop(TIntermNode* body, TIntermTyped* test, TIntermTyped* terminal, bool testFirst,
+ const TSourceLoc& loc)
+{
+ TIntermLoop* node = new TIntermLoop(body, test, terminal, testFirst);
+ node->setLoc(loc);
+
+ return node;
+}
+
+//
+// Create a for-loop sequence.
+//
+TIntermAggregate* TIntermediate::addForLoop(TIntermNode* body, TIntermNode* initializer, TIntermTyped* test,
+ TIntermTyped* terminal, bool testFirst, const TSourceLoc& loc, TIntermLoop*& node)
+{
+ node = new TIntermLoop(body, test, terminal, testFirst);
+ node->setLoc(loc);
+
+ // make a sequence of the initializer and statement, but try to reuse the
+ // aggregate already created for whatever is in the initializer, if there is one
+ TIntermAggregate* loopSequence = (initializer == nullptr ||
+ initializer->getAsAggregate() == nullptr) ? makeAggregate(initializer, loc)
+ : initializer->getAsAggregate();
+ if (loopSequence != nullptr && loopSequence->getOp() == EOpSequence)
+ loopSequence->setOp(EOpNull);
+ loopSequence = growAggregate(loopSequence, node);
+ loopSequence->setOperator(EOpSequence);
+
+ return loopSequence;
+}
+
+//
+// Add branches.
+//
+TIntermBranch* TIntermediate::addBranch(TOperator branchOp, const TSourceLoc& loc)
+{
+ return addBranch(branchOp, nullptr, loc);
+}
+
+TIntermBranch* TIntermediate::addBranch(TOperator branchOp, TIntermTyped* expression, const TSourceLoc& loc)
+{
+ TIntermBranch* node = new TIntermBranch(branchOp, expression);
+ node->setLoc(loc);
+
+ return node;
+}
+
+//
+// This is to be executed after the final root is put on top by the parsing
+// process.
+//
+bool TIntermediate::postProcess(TIntermNode* root, EShLanguage /*language*/)
+{
+ if (root == nullptr)
+ return true;
+
+ // Finish off the top-level sequence
+ TIntermAggregate* aggRoot = root->getAsAggregate();
+ if (aggRoot && aggRoot->getOp() == EOpNull)
+ aggRoot->setOperator(EOpSequence);
+
+ // Propagate 'noContraction' label in backward from 'precise' variables.
+ glslang::PropagateNoContraction(*this);
+
+ switch (textureSamplerTransformMode) {
+ case EShTexSampTransKeep:
+ break;
+ case EShTexSampTransUpgradeTextureRemoveSampler:
+ performTextureUpgradeAndSamplerRemovalTransformation(root);
+ break;
+ }
+
+ return true;
+}
+
+void TIntermediate::addSymbolLinkageNodes(TIntermAggregate*& linkage, EShLanguage language, TSymbolTable& symbolTable)
+{
+ // Add top-level nodes for declarations that must be checked cross
+ // compilation unit by a linker, yet might not have been referenced
+ // by the AST.
+ //
+ // Almost entirely, translation of symbols is driven by what's present
+ // in the AST traversal, not by translating the symbol table.
+ //
+ // However, there are some special cases:
+ // - From the specification: "Special built-in inputs gl_VertexID and
+ // gl_InstanceID are also considered active vertex attributes."
+ // - Linker-based type mismatch error reporting needs to see all
+ // uniforms/ins/outs variables and blocks.
+ // - ftransform() can make gl_Vertex and gl_ModelViewProjectionMatrix active.
+ //
+
+ // if (ftransformUsed) {
+ // TODO: 1.1 lowering functionality: track ftransform() usage
+ // addSymbolLinkageNode(root, symbolTable, "gl_Vertex");
+ // addSymbolLinkageNode(root, symbolTable, "gl_ModelViewProjectionMatrix");
+ //}
+
+ if (language == EShLangVertex) {
+ // the names won't be found in the symbol table unless the versions are right,
+ // so version logic does not need to be repeated here
+ addSymbolLinkageNode(linkage, symbolTable, "gl_VertexID");
+ addSymbolLinkageNode(linkage, symbolTable, "gl_InstanceID");
+ }
+
+ // Add a child to the root node for the linker objects
+ linkage->setOperator(EOpLinkerObjects);
+ treeRoot = growAggregate(treeRoot, linkage);
+}
+
+//
+// Add the given name or symbol to the list of nodes at the end of the tree used
+// for link-time checking and external linkage.
+//
+
+void TIntermediate::addSymbolLinkageNode(TIntermAggregate*& linkage, TSymbolTable& symbolTable, const TString& name)
+{
+ TSymbol* symbol = symbolTable.find(name);
+ if (symbol)
+ addSymbolLinkageNode(linkage, *symbol->getAsVariable());
+}
+
+void TIntermediate::addSymbolLinkageNode(TIntermAggregate*& linkage, const TSymbol& symbol)
+{
+ const TVariable* variable = symbol.getAsVariable();
+ if (! variable) {
+ // This must be a member of an anonymous block, and we need to add the whole block
+ const TAnonMember* anon = symbol.getAsAnonMember();
+ variable = &anon->getAnonContainer();
+ }
+ TIntermSymbol* node = addSymbol(*variable);
+ linkage = growAggregate(linkage, node);
+}
+
+//
+// Add a caller->callee relationship to the call graph.
+// Assumes the strings are unique per signature.
+//
+void TIntermediate::addToCallGraph(TInfoSink& /*infoSink*/, const TString& caller, const TString& callee)
+{
+ // Duplicates are okay, but faster to not keep them, and they come grouped by caller,
+ // as long as new ones are push on the same end we check on for duplicates
+ for (TGraph::const_iterator call = callGraph.begin(); call != callGraph.end(); ++call) {
+ if (call->caller != caller)
+ break;
+ if (call->callee == callee)
+ return;
+ }
+
+ callGraph.push_front(TCall(caller, callee));
+}
+
+//
+// This deletes the tree.
+//
+void TIntermediate::removeTree()
+{
+ if (treeRoot)
+ RemoveAllTreeNodes(treeRoot);
+}
+
+//
+// Implement the part of KHR_vulkan_glsl that lists the set of operations
+// that can result in a specialization constant operation.
+//
+// "5.x Specialization Constant Operations"
+//
+// Only some operations discussed in this section may be applied to a
+// specialization constant and still yield a result that is as
+// specialization constant. The operations allowed are listed below.
+// When a specialization constant is operated on with one of these
+// operators and with another constant or specialization constant, the
+// result is implicitly a specialization constant.
+//
+// - int(), uint(), and bool() constructors for type conversions
+// from any of the following types to any of the following types:
+// * int
+// * uint
+// * bool
+// - vector versions of the above conversion constructors
+// - allowed implicit conversions of the above
+// - swizzles (e.g., foo.yx)
+// - The following when applied to integer or unsigned integer types:
+// * unary negative ( - )
+// * binary operations ( + , - , * , / , % )
+// * shift ( <<, >> )
+// * bitwise operations ( & , | , ^ )
+// - The following when applied to integer or unsigned integer scalar types:
+// * comparison ( == , != , > , >= , < , <= )
+// - The following when applied to the Boolean scalar type:
+// * not ( ! )
+// * logical operations ( && , || , ^^ )
+// * comparison ( == , != )"
+//
+// This function just handles binary and unary nodes. Construction
+// rules are handled in construction paths that are not covered by the unary
+// and binary paths, while required conversions will still show up here
+// as unary converters in the from a construction operator.
+//
+bool TIntermediate::isSpecializationOperation(const TIntermOperator& node) const
+{
+ // The operations resulting in floating point are quite limited
+ // (However, some floating-point operations result in bool, like ">",
+ // so are handled later.)
+ if (node.getType().isFloatingDomain()) {
+ switch (node.getOp()) {
+ case EOpIndexDirect:
+ case EOpIndexIndirect:
+ case EOpIndexDirectStruct:
+ case EOpVectorSwizzle:
+ case EOpConvFloatToDouble:
+ case EOpConvDoubleToFloat:
+ case EOpConvFloat16ToFloat:
+ case EOpConvFloatToFloat16:
+ case EOpConvFloat16ToDouble:
+ case EOpConvDoubleToFloat16:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ // Check for floating-point arguments
+ if (const TIntermBinary* bin = node.getAsBinaryNode())
+ if (bin->getLeft() ->getType().isFloatingDomain() ||
+ bin->getRight()->getType().isFloatingDomain())
+ return false;
+
+ // So, for now, we can assume everything left is non-floating-point...
+
+ // Now check for integer/bool-based operations
+ switch (node.getOp()) {
+
+ // dereference/swizzle
+ case EOpIndexDirect:
+ case EOpIndexIndirect:
+ case EOpIndexDirectStruct:
+ case EOpVectorSwizzle:
+
+ // (u)int* -> bool
+ case EOpConvInt8ToBool:
+ case EOpConvInt16ToBool:
+ case EOpConvIntToBool:
+ case EOpConvInt64ToBool:
+ case EOpConvUint8ToBool:
+ case EOpConvUint16ToBool:
+ case EOpConvUintToBool:
+ case EOpConvUint64ToBool:
+
+ // bool -> (u)int*
+ case EOpConvBoolToInt8:
+ case EOpConvBoolToInt16:
+ case EOpConvBoolToInt:
+ case EOpConvBoolToInt64:
+ case EOpConvBoolToUint8:
+ case EOpConvBoolToUint16:
+ case EOpConvBoolToUint:
+ case EOpConvBoolToUint64:
+
+ // int8_t -> (u)int*
+ case EOpConvInt8ToInt16:
+ case EOpConvInt8ToInt:
+ case EOpConvInt8ToInt64:
+ case EOpConvInt8ToUint8:
+ case EOpConvInt8ToUint16:
+ case EOpConvInt8ToUint:
+ case EOpConvInt8ToUint64:
+
+ // int16_t -> (u)int*
+ case EOpConvInt16ToInt8:
+ case EOpConvInt16ToInt:
+ case EOpConvInt16ToInt64:
+ case EOpConvInt16ToUint8:
+ case EOpConvInt16ToUint16:
+ case EOpConvInt16ToUint:
+ case EOpConvInt16ToUint64:
+
+ // int32_t -> (u)int*
+ case EOpConvIntToInt8:
+ case EOpConvIntToInt16:
+ case EOpConvIntToInt64:
+ case EOpConvIntToUint8:
+ case EOpConvIntToUint16:
+ case EOpConvIntToUint:
+ case EOpConvIntToUint64:
+
+ // int64_t -> (u)int*
+ case EOpConvInt64ToInt8:
+ case EOpConvInt64ToInt16:
+ case EOpConvInt64ToInt:
+ case EOpConvInt64ToUint8:
+ case EOpConvInt64ToUint16:
+ case EOpConvInt64ToUint:
+ case EOpConvInt64ToUint64:
+
+ // uint8_t -> (u)int*
+ case EOpConvUint8ToInt8:
+ case EOpConvUint8ToInt16:
+ case EOpConvUint8ToInt:
+ case EOpConvUint8ToInt64:
+ case EOpConvUint8ToUint16:
+ case EOpConvUint8ToUint:
+ case EOpConvUint8ToUint64:
+
+ // uint16_t -> (u)int*
+ case EOpConvUint16ToInt8:
+ case EOpConvUint16ToInt16:
+ case EOpConvUint16ToInt:
+ case EOpConvUint16ToInt64:
+ case EOpConvUint16ToUint8:
+ case EOpConvUint16ToUint:
+ case EOpConvUint16ToUint64:
+
+ // uint32_t -> (u)int*
+ case EOpConvUintToInt8:
+ case EOpConvUintToInt16:
+ case EOpConvUintToInt:
+ case EOpConvUintToInt64:
+ case EOpConvUintToUint8:
+ case EOpConvUintToUint16:
+ case EOpConvUintToUint64:
+
+ // uint64_t -> (u)int*
+ case EOpConvUint64ToInt8:
+ case EOpConvUint64ToInt16:
+ case EOpConvUint64ToInt:
+ case EOpConvUint64ToInt64:
+ case EOpConvUint64ToUint8:
+ case EOpConvUint64ToUint16:
+ case EOpConvUint64ToUint:
+
+ // unary operations
+ case EOpNegative:
+ case EOpLogicalNot:
+ case EOpBitwiseNot:
+
+ // binary operations
+ case EOpAdd:
+ case EOpSub:
+ case EOpMul:
+ case EOpVectorTimesScalar:
+ case EOpDiv:
+ case EOpMod:
+ case EOpRightShift:
+ case EOpLeftShift:
+ case EOpAnd:
+ case EOpInclusiveOr:
+ case EOpExclusiveOr:
+ case EOpLogicalOr:
+ case EOpLogicalXor:
+ case EOpLogicalAnd:
+ case EOpEqual:
+ case EOpNotEqual:
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Is the operation one that must propagate nonuniform?
+bool TIntermediate::isNonuniformPropagating(TOperator op) const
+{
+ // "* All Operators in Section 5.1 (Operators), except for assignment,
+ // arithmetic assignment, and sequence
+ // * Component selection in Section 5.5
+ // * Matrix components in Section 5.6
+ // * Structure and Array Operations in Section 5.7, except for the length
+ // method."
+ switch (op) {
+ case EOpPostIncrement:
+ case EOpPostDecrement:
+ case EOpPreIncrement:
+ case EOpPreDecrement:
+
+ case EOpNegative:
+ case EOpLogicalNot:
+ case EOpVectorLogicalNot:
+ case EOpBitwiseNot:
+
+ case EOpAdd:
+ case EOpSub:
+ case EOpMul:
+ case EOpDiv:
+ case EOpMod:
+ case EOpRightShift:
+ case EOpLeftShift:
+ case EOpAnd:
+ case EOpInclusiveOr:
+ case EOpExclusiveOr:
+ case EOpEqual:
+ case EOpNotEqual:
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+ case EOpVectorTimesScalar:
+ case EOpVectorTimesMatrix:
+ case EOpMatrixTimesVector:
+ case EOpMatrixTimesScalar:
+
+ case EOpLogicalOr:
+ case EOpLogicalXor:
+ case EOpLogicalAnd:
+
+ case EOpIndexDirect:
+ case EOpIndexIndirect:
+ case EOpIndexDirectStruct:
+ case EOpVectorSwizzle:
+ return true;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+////////////////////////////////////////////////////////////////
+//
+// Member functions of the nodes used for building the tree.
+//
+////////////////////////////////////////////////////////////////
+
+//
+// Say whether or not an operation node changes the value of a variable.
+//
+// Returns true if state is modified.
+//
+bool TIntermOperator::modifiesState() const
+{
+ switch (op) {
+ case EOpPostIncrement:
+ case EOpPostDecrement:
+ case EOpPreIncrement:
+ case EOpPreDecrement:
+ case EOpAssign:
+ case EOpAddAssign:
+ case EOpSubAssign:
+ case EOpMulAssign:
+ case EOpVectorTimesMatrixAssign:
+ case EOpVectorTimesScalarAssign:
+ case EOpMatrixTimesScalarAssign:
+ case EOpMatrixTimesMatrixAssign:
+ case EOpDivAssign:
+ case EOpModAssign:
+ case EOpAndAssign:
+ case EOpInclusiveOrAssign:
+ case EOpExclusiveOrAssign:
+ case EOpLeftShiftAssign:
+ case EOpRightShiftAssign:
+ return true;
+ default:
+ return false;
+ }
+}
+
+//
+// returns true if the operator is for one of the constructors
+//
+bool TIntermOperator::isConstructor() const
+{
+ return op > EOpConstructGuardStart && op < EOpConstructGuardEnd;
+}
+
+//
+// Make sure the type of an operator is appropriate for its
+// combination of operation and operand type. This will invoke
+// promoteUnary, promoteBinary, etc as needed.
+//
+// Returns false if nothing makes sense.
+//
+bool TIntermediate::promote(TIntermOperator* node)
+{
+ if (node == nullptr)
+ return false;
+
+ if (node->getAsUnaryNode())
+ return promoteUnary(*node->getAsUnaryNode());
+
+ if (node->getAsBinaryNode())
+ return promoteBinary(*node->getAsBinaryNode());
+
+ if (node->getAsAggregate())
+ return promoteAggregate(*node->getAsAggregate());
+
+ return false;
+}
+
+//
+// See TIntermediate::promote
+//
+bool TIntermediate::promoteUnary(TIntermUnary& node)
+{
+ const TOperator op = node.getOp();
+ TIntermTyped* operand = node.getOperand();
+
+ switch (op) {
+ case EOpLogicalNot:
+ // Convert operand to a boolean type
+ if (operand->getBasicType() != EbtBool) {
+ // Add constructor to boolean type. If that fails, we can't do it, so return false.
+ TIntermTyped* converted = addConversion(op, TType(EbtBool), operand);
+ if (converted == nullptr)
+ return false;
+
+ // Use the result of converting the node to a bool.
+ node.setOperand(operand = converted); // also updates stack variable
+ }
+ break;
+ case EOpBitwiseNot:
+ if (!isTypeInt(operand->getBasicType()))
+ return false;
+ break;
+ case EOpNegative:
+ case EOpPostIncrement:
+ case EOpPostDecrement:
+ case EOpPreIncrement:
+ case EOpPreDecrement:
+ if (!isTypeInt(operand->getBasicType()) &&
+ operand->getBasicType() != EbtFloat &&
+ operand->getBasicType() != EbtFloat16 &&
+ operand->getBasicType() != EbtDouble)
+
+ return false;
+ break;
+
+ default:
+ if (operand->getBasicType() != EbtFloat)
+
+ return false;
+ }
+
+ node.setType(operand->getType());
+ node.getWritableType().getQualifier().makeTemporary();
+
+ return true;
+}
+
+void TIntermUnary::updatePrecision()
+{
+ if (getBasicType() == EbtInt || getBasicType() == EbtUint || getBasicType() == EbtFloat || getBasicType() == EbtFloat16) {
+ if (operand->getQualifier().precision > getQualifier().precision)
+ getQualifier().precision = operand->getQualifier().precision;
+ }
+}
+
+//
+// See TIntermediate::promote
+//
+bool TIntermediate::promoteBinary(TIntermBinary& node)
+{
+ TOperator op = node.getOp();
+ TIntermTyped* left = node.getLeft();
+ TIntermTyped* right = node.getRight();
+
+ // Arrays and structures have to be exact matches.
+ if ((left->isArray() || right->isArray() || left->getBasicType() == EbtStruct || right->getBasicType() == EbtStruct)
+ && left->getType() != right->getType())
+ return false;
+
+ // Base assumption: just make the type the same as the left
+ // operand. Only deviations from this will be coded.
+ node.setType(left->getType());
+ node.getWritableType().getQualifier().clear();
+
+ // Composite and opaque types don't having pending operator changes, e.g.,
+ // array, structure, and samplers. Just establish final type and correctness.
+ if (left->isArray() || left->getBasicType() == EbtStruct || left->getBasicType() == EbtSampler) {
+ switch (op) {
+ case EOpEqual:
+ case EOpNotEqual:
+ if (left->getBasicType() == EbtSampler) {
+ // can't compare samplers
+ return false;
+ } else {
+ // Promote to conditional
+ node.setType(TType(EbtBool));
+ }
+
+ return true;
+
+ case EOpAssign:
+ // Keep type from above
+
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ //
+ // We now have only scalars, vectors, and matrices to worry about.
+ //
+
+ // HLSL implicitly promotes bool -> int for numeric operations.
+ // (Implicit conversions to make the operands match each other's types were already done.)
+ if (getSource() == EShSourceHlsl &&
+ (left->getBasicType() == EbtBool || right->getBasicType() == EbtBool)) {
+ switch (op) {
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+
+ case EOpRightShift:
+ case EOpLeftShift:
+
+ case EOpMod:
+
+ case EOpAnd:
+ case EOpInclusiveOr:
+ case EOpExclusiveOr:
+
+ case EOpAdd:
+ case EOpSub:
+ case EOpDiv:
+ case EOpMul:
+ if (left->getBasicType() == EbtBool)
+ left = createConversion(EbtInt, left);
+ if (right->getBasicType() == EbtBool)
+ right = createConversion(EbtInt, right);
+ if (left == nullptr || right == nullptr)
+ return false;
+ node.setLeft(left);
+ node.setRight(right);
+
+ // Update the original base assumption on result type..
+ node.setType(left->getType());
+ node.getWritableType().getQualifier().clear();
+
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // Do general type checks against individual operands (comparing left and right is coming up, checking mixed shapes after that)
+ switch (op) {
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+ // Relational comparisons need numeric types and will promote to scalar Boolean.
+ if (left->getBasicType() == EbtBool)
+ return false;
+
+ node.setType(TType(EbtBool, EvqTemporary, left->getVectorSize()));
+ break;
+
+ case EOpEqual:
+ case EOpNotEqual:
+ if (getSource() == EShSourceHlsl) {
+ const int resultWidth = std::max(left->getVectorSize(), right->getVectorSize());
+
+ // In HLSL, == or != on vectors means component-wise comparison.
+ if (resultWidth > 1) {
+ op = (op == EOpEqual) ? EOpVectorEqual : EOpVectorNotEqual;
+ node.setOp(op);
+ }
+
+ node.setType(TType(EbtBool, EvqTemporary, resultWidth));
+ } else {
+ // All the above comparisons result in a bool (but not the vector compares)
+ node.setType(TType(EbtBool));
+ }
+ break;
+
+ case EOpLogicalAnd:
+ case EOpLogicalOr:
+ case EOpLogicalXor:
+ // logical ops operate only on Booleans or vectors of Booleans.
+ if (left->getBasicType() != EbtBool || left->isMatrix())
+ return false;
+
+ if (getSource() == EShSourceGlsl) {
+ // logical ops operate only on scalar Booleans and will promote to scalar Boolean.
+ if (left->isVector())
+ return false;
+ }
+
+ node.setType(TType(EbtBool, EvqTemporary, left->getVectorSize()));
+ break;
+
+ case EOpRightShift:
+ case EOpLeftShift:
+ case EOpRightShiftAssign:
+ case EOpLeftShiftAssign:
+
+ case EOpMod:
+ case EOpModAssign:
+
+ case EOpAnd:
+ case EOpInclusiveOr:
+ case EOpExclusiveOr:
+ case EOpAndAssign:
+ case EOpInclusiveOrAssign:
+ case EOpExclusiveOrAssign:
+ if (getSource() == EShSourceHlsl)
+ break;
+
+ // Check for integer-only operands.
+ if (!isTypeInt(left->getBasicType()) && !isTypeInt(right->getBasicType()))
+ return false;
+ if (left->isMatrix() || right->isMatrix())
+ return false;
+
+ break;
+
+ case EOpAdd:
+ case EOpSub:
+ case EOpDiv:
+ case EOpMul:
+ case EOpAddAssign:
+ case EOpSubAssign:
+ case EOpMulAssign:
+ case EOpDivAssign:
+ // check for non-Boolean operands
+ if (left->getBasicType() == EbtBool || right->getBasicType() == EbtBool)
+ return false;
+
+ default:
+ break;
+ }
+
+ // Compare left and right, and finish with the cases where the operand types must match
+ switch (op) {
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+
+ case EOpEqual:
+ case EOpNotEqual:
+ case EOpVectorEqual:
+ case EOpVectorNotEqual:
+
+ case EOpLogicalAnd:
+ case EOpLogicalOr:
+ case EOpLogicalXor:
+ return left->getType() == right->getType();
+
+ case EOpMod:
+ case EOpModAssign:
+
+ case EOpAnd:
+ case EOpInclusiveOr:
+ case EOpExclusiveOr:
+ case EOpAndAssign:
+ case EOpInclusiveOrAssign:
+ case EOpExclusiveOrAssign:
+
+ case EOpAdd:
+ case EOpSub:
+ case EOpDiv:
+
+ case EOpAddAssign:
+ case EOpSubAssign:
+ case EOpDivAssign:
+ // Quick out in case the types do match
+ if (left->getType() == right->getType())
+ return true;
+
+ // Fall through
+
+ case EOpMul:
+ case EOpMulAssign:
+ // At least the basic type has to match
+ if (left->getBasicType() != right->getBasicType())
+ return false;
+
+ default:
+ break;
+ }
+
+ if (left->getType().isCoopMat() || right->getType().isCoopMat()) {
+ if (left->getType().isCoopMat() && right->getType().isCoopMat() &&
+ *left->getType().getTypeParameters() != *right->getType().getTypeParameters()) {
+ return false;
+ }
+ switch (op) {
+ case EOpMul:
+ case EOpMulAssign:
+ if (left->getType().isCoopMat() && right->getType().isCoopMat()) {
+ return false;
+ }
+ if (op == EOpMulAssign && right->getType().isCoopMat()) {
+ return false;
+ }
+ node.setOp(op == EOpMulAssign ? EOpMatrixTimesScalarAssign : EOpMatrixTimesScalar);
+ if (right->getType().isCoopMat()) {
+ node.setType(right->getType());
+ }
+ return true;
+ case EOpAdd:
+ case EOpSub:
+ case EOpDiv:
+ case EOpAssign:
+ // These require both to be cooperative matrices
+ if (!left->getType().isCoopMat() || !right->getType().isCoopMat()) {
+ return false;
+ }
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }
+
+ // Finish handling the case, for all ops, where both operands are scalars.
+ if (left->isScalar() && right->isScalar())
+ return true;
+
+ // Finish handling the case, for all ops, where there are two vectors of different sizes
+ if (left->isVector() && right->isVector() && left->getVectorSize() != right->getVectorSize() && right->getVectorSize() > 1)
+ return false;
+
+ //
+ // We now have a mix of scalars, vectors, or matrices, for non-relational operations.
+ //
+
+ // Can these two operands be combined, what is the resulting type?
+ TBasicType basicType = left->getBasicType();
+ switch (op) {
+ case EOpMul:
+ if (!left->isMatrix() && right->isMatrix()) {
+ if (left->isVector()) {
+ if (left->getVectorSize() != right->getMatrixRows())
+ return false;
+ node.setOp(op = EOpVectorTimesMatrix);
+ node.setType(TType(basicType, EvqTemporary, right->getMatrixCols()));
+ } else {
+ node.setOp(op = EOpMatrixTimesScalar);
+ node.setType(TType(basicType, EvqTemporary, 0, right->getMatrixCols(), right->getMatrixRows()));
+ }
+ } else if (left->isMatrix() && !right->isMatrix()) {
+ if (right->isVector()) {
+ if (left->getMatrixCols() != right->getVectorSize())
+ return false;
+ node.setOp(op = EOpMatrixTimesVector);
+ node.setType(TType(basicType, EvqTemporary, left->getMatrixRows()));
+ } else {
+ node.setOp(op = EOpMatrixTimesScalar);
+ }
+ } else if (left->isMatrix() && right->isMatrix()) {
+ if (left->getMatrixCols() != right->getMatrixRows())
+ return false;
+ node.setOp(op = EOpMatrixTimesMatrix);
+ node.setType(TType(basicType, EvqTemporary, 0, right->getMatrixCols(), left->getMatrixRows()));
+ } else if (! left->isMatrix() && ! right->isMatrix()) {
+ if (left->isVector() && right->isVector()) {
+ ; // leave as component product
+ } else if (left->isVector() || right->isVector()) {
+ node.setOp(op = EOpVectorTimesScalar);
+ if (right->isVector())
+ node.setType(TType(basicType, EvqTemporary, right->getVectorSize()));
+ }
+ } else {
+ return false;
+ }
+ break;
+ case EOpMulAssign:
+ if (! left->isMatrix() && right->isMatrix()) {
+ if (left->isVector()) {
+ if (left->getVectorSize() != right->getMatrixRows() || left->getVectorSize() != right->getMatrixCols())
+ return false;
+ node.setOp(op = EOpVectorTimesMatrixAssign);
+ } else {
+ return false;
+ }
+ } else if (left->isMatrix() && !right->isMatrix()) {
+ if (right->isVector()) {
+ return false;
+ } else {
+ node.setOp(op = EOpMatrixTimesScalarAssign);
+ }
+ } else if (left->isMatrix() && right->isMatrix()) {
+ if (left->getMatrixCols() != right->getMatrixCols() || left->getMatrixCols() != right->getMatrixRows())
+ return false;
+ node.setOp(op = EOpMatrixTimesMatrixAssign);
+ } else if (!left->isMatrix() && !right->isMatrix()) {
+ if (left->isVector() && right->isVector()) {
+ // leave as component product
+ } else if (left->isVector() || right->isVector()) {
+ if (! left->isVector())
+ return false;
+ node.setOp(op = EOpVectorTimesScalarAssign);
+ }
+ } else {
+ return false;
+ }
+ break;
+
+ case EOpRightShift:
+ case EOpLeftShift:
+ case EOpRightShiftAssign:
+ case EOpLeftShiftAssign:
+ if (right->isVector() && (! left->isVector() || right->getVectorSize() != left->getVectorSize()))
+ return false;
+ break;
+
+ case EOpAssign:
+ if (left->getVectorSize() != right->getVectorSize() || left->getMatrixCols() != right->getMatrixCols() || left->getMatrixRows() != right->getMatrixRows())
+ return false;
+ // fall through
+
+ case EOpAdd:
+ case EOpSub:
+ case EOpDiv:
+ case EOpMod:
+ case EOpAnd:
+ case EOpInclusiveOr:
+ case EOpExclusiveOr:
+ case EOpAddAssign:
+ case EOpSubAssign:
+ case EOpDivAssign:
+ case EOpModAssign:
+ case EOpAndAssign:
+ case EOpInclusiveOrAssign:
+ case EOpExclusiveOrAssign:
+
+ if ((left->isMatrix() && right->isVector()) ||
+ (left->isVector() && right->isMatrix()) ||
+ left->getBasicType() != right->getBasicType())
+ return false;
+ if (left->isMatrix() && right->isMatrix() && (left->getMatrixCols() != right->getMatrixCols() || left->getMatrixRows() != right->getMatrixRows()))
+ return false;
+ if (left->isVector() && right->isVector() && left->getVectorSize() != right->getVectorSize())
+ return false;
+ if (right->isVector() || right->isMatrix()) {
+ node.getWritableType().shallowCopy(right->getType());
+ node.getWritableType().getQualifier().makeTemporary();
+ }
+ break;
+
+ default:
+ return false;
+ }
+
+ //
+ // One more check for assignment.
+ //
+ switch (op) {
+ // The resulting type has to match the left operand.
+ case EOpAssign:
+ case EOpAddAssign:
+ case EOpSubAssign:
+ case EOpMulAssign:
+ case EOpDivAssign:
+ case EOpModAssign:
+ case EOpAndAssign:
+ case EOpInclusiveOrAssign:
+ case EOpExclusiveOrAssign:
+ case EOpLeftShiftAssign:
+ case EOpRightShiftAssign:
+ if (node.getType() != left->getType())
+ return false;
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+//
+// See TIntermediate::promote
+//
+bool TIntermediate::promoteAggregate(TIntermAggregate& node)
+{
+ TOperator op = node.getOp();
+ TIntermSequence& args = node.getSequence();
+ const int numArgs = static_cast<int>(args.size());
+
+ // Presently, only hlsl does intrinsic promotions.
+ if (getSource() != EShSourceHlsl)
+ return true;
+
+ // set of opcodes that can be promoted in this manner.
+ switch (op) {
+ case EOpAtan:
+ case EOpClamp:
+ case EOpCross:
+ case EOpDistance:
+ case EOpDot:
+ case EOpDst:
+ case EOpFaceForward:
+ // case EOpFindMSB: TODO:
+ // case EOpFindLSB: TODO:
+ case EOpFma:
+ case EOpMod:
+ case EOpFrexp:
+ case EOpLdexp:
+ case EOpMix:
+ case EOpLit:
+ case EOpMax:
+ case EOpMin:
+ case EOpModf:
+ // case EOpGenMul: TODO:
+ case EOpPow:
+ case EOpReflect:
+ case EOpRefract:
+ // case EOpSinCos: TODO:
+ case EOpSmoothStep:
+ case EOpStep:
+ break;
+ default:
+ return true;
+ }
+
+ // TODO: array and struct behavior
+
+ // Try converting all nodes to the given node's type
+ TIntermSequence convertedArgs(numArgs, nullptr);
+
+ // Try to convert all types to the nonConvArg type.
+ for (int nonConvArg = 0; nonConvArg < numArgs; ++nonConvArg) {
+ // Try converting all args to this arg's type
+ for (int convArg = 0; convArg < numArgs; ++convArg) {
+ convertedArgs[convArg] = addConversion(op, args[nonConvArg]->getAsTyped()->getType(),
+ args[convArg]->getAsTyped());
+ }
+
+ // If we successfully converted all the args, use the result.
+ if (std::all_of(convertedArgs.begin(), convertedArgs.end(),
+ [](const TIntermNode* node) { return node != nullptr; })) {
+
+ std::swap(args, convertedArgs);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void TIntermBinary::updatePrecision()
+{
+ if (getBasicType() == EbtInt || getBasicType() == EbtUint || getBasicType() == EbtFloat || getBasicType() == EbtFloat16) {
+ getQualifier().precision = std::max(right->getQualifier().precision, left->getQualifier().precision);
+ if (getQualifier().precision != EpqNone) {
+ left->propagatePrecision(getQualifier().precision);
+ right->propagatePrecision(getQualifier().precision);
+ }
+ }
+}
+
+void TIntermTyped::propagatePrecision(TPrecisionQualifier newPrecision)
+{
+ if (getQualifier().precision != EpqNone || (getBasicType() != EbtInt && getBasicType() != EbtUint && getBasicType() != EbtFloat && getBasicType() != EbtFloat16))
+ return;
+
+ getQualifier().precision = newPrecision;
+
+ TIntermBinary* binaryNode = getAsBinaryNode();
+ if (binaryNode) {
+ binaryNode->getLeft()->propagatePrecision(newPrecision);
+ binaryNode->getRight()->propagatePrecision(newPrecision);
+
+ return;
+ }
+
+ TIntermUnary* unaryNode = getAsUnaryNode();
+ if (unaryNode) {
+ unaryNode->getOperand()->propagatePrecision(newPrecision);
+
+ return;
+ }
+
+ TIntermAggregate* aggregateNode = getAsAggregate();
+ if (aggregateNode) {
+ TIntermSequence operands = aggregateNode->getSequence();
+ for (unsigned int i = 0; i < operands.size(); ++i) {
+ TIntermTyped* typedNode = operands[i]->getAsTyped();
+ if (! typedNode)
+ break;
+ typedNode->propagatePrecision(newPrecision);
+ }
+
+ return;
+ }
+
+ TIntermSelection* selectionNode = getAsSelectionNode();
+ if (selectionNode) {
+ TIntermTyped* typedNode = selectionNode->getTrueBlock()->getAsTyped();
+ if (typedNode) {
+ typedNode->propagatePrecision(newPrecision);
+ typedNode = selectionNode->getFalseBlock()->getAsTyped();
+ if (typedNode)
+ typedNode->propagatePrecision(newPrecision);
+ }
+
+ return;
+ }
+}
+
+TIntermTyped* TIntermediate::promoteConstantUnion(TBasicType promoteTo, TIntermConstantUnion* node) const
+{
+ const TConstUnionArray& rightUnionArray = node->getConstArray();
+ int size = node->getType().computeNumComponents();
+
+ TConstUnionArray leftUnionArray(size);
+
+ for (int i=0; i < size; i++) {
+ switch (promoteTo) {
+ case EbtFloat:
+ switch (node->getType().getBasicType()) {
+ case EbtInt:
+ leftUnionArray[i].setDConst(static_cast<double>(rightUnionArray[i].getIConst()));
+ break;
+ case EbtUint:
+ leftUnionArray[i].setDConst(static_cast<double>(rightUnionArray[i].getUConst()));
+ break;
+ case EbtInt64:
+ leftUnionArray[i].setDConst(static_cast<double>(rightUnionArray[i].getI64Const()));
+ break;
+ case EbtUint64:
+ leftUnionArray[i].setDConst(static_cast<double>(rightUnionArray[i].getU64Const()));
+ break;
+ case EbtBool:
+ leftUnionArray[i].setDConst(static_cast<double>(rightUnionArray[i].getBConst()));
+ break;
+ case EbtFloat:
+ case EbtDouble:
+ case EbtFloat16:
+ leftUnionArray[i] = rightUnionArray[i];
+ break;
+ default:
+ return node;
+ }
+ break;
+ case EbtDouble:
+ switch (node->getType().getBasicType()) {
+ case EbtInt:
+ leftUnionArray[i].setDConst(static_cast<double>(rightUnionArray[i].getIConst()));
+ break;
+ case EbtUint:
+ leftUnionArray[i].setDConst(static_cast<double>(rightUnionArray[i].getUConst()));
+ break;
+ case EbtInt64:
+ leftUnionArray[i].setDConst(static_cast<double>(rightUnionArray[i].getI64Const()));
+ break;
+ case EbtUint64:
+ leftUnionArray[i].setDConst(static_cast<double>(rightUnionArray[i].getU64Const()));
+ break;
+ case EbtBool:
+ leftUnionArray[i].setDConst(static_cast<double>(rightUnionArray[i].getBConst()));
+ break;
+ case EbtFloat:
+ case EbtDouble:
+ case EbtFloat16:
+ leftUnionArray[i] = rightUnionArray[i];
+ break;
+ default:
+ return node;
+ }
+ break;
+ case EbtFloat16:
+ switch (node->getType().getBasicType()) {
+ case EbtInt:
+ leftUnionArray[i].setDConst(static_cast<double>(rightUnionArray[i].getIConst()));
+ break;
+ case EbtUint:
+ leftUnionArray[i].setDConst(static_cast<double>(rightUnionArray[i].getUConst()));
+ break;
+ case EbtInt64:
+ leftUnionArray[i].setDConst(static_cast<double>(rightUnionArray[i].getI64Const()));
+ break;
+ case EbtUint64:
+ leftUnionArray[i].setDConst(static_cast<double>(rightUnionArray[i].getU64Const()));
+ break;
+ case EbtBool:
+ leftUnionArray[i].setDConst(static_cast<double>(rightUnionArray[i].getBConst()));
+ break;
+ case EbtFloat:
+ case EbtDouble:
+ case EbtFloat16:
+ leftUnionArray[i] = rightUnionArray[i];
+ break;
+ default:
+ return node;
+ }
+ break;
+ case EbtInt:
+ switch (node->getType().getBasicType()) {
+ case EbtInt:
+ leftUnionArray[i] = rightUnionArray[i];
+ break;
+ case EbtUint:
+ leftUnionArray[i].setIConst(static_cast<int>(rightUnionArray[i].getUConst()));
+ break;
+ case EbtInt64:
+ leftUnionArray[i].setIConst(static_cast<int>(rightUnionArray[i].getI64Const()));
+ break;
+ case EbtUint64:
+ leftUnionArray[i].setIConst(static_cast<int>(rightUnionArray[i].getU64Const()));
+ break;
+ case EbtBool:
+ leftUnionArray[i].setIConst(static_cast<int>(rightUnionArray[i].getBConst()));
+ break;
+ case EbtFloat:
+ case EbtDouble:
+ case EbtFloat16:
+ leftUnionArray[i].setIConst(static_cast<int>(rightUnionArray[i].getDConst()));
+ break;
+ default:
+ return node;
+ }
+ break;
+ case EbtUint:
+ switch (node->getType().getBasicType()) {
+ case EbtInt:
+ leftUnionArray[i].setUConst(static_cast<unsigned int>(rightUnionArray[i].getIConst()));
+ break;
+ case EbtUint:
+ leftUnionArray[i] = rightUnionArray[i];
+ break;
+ case EbtInt64:
+ leftUnionArray[i].setUConst(static_cast<unsigned int>(rightUnionArray[i].getI64Const()));
+ break;
+ case EbtUint64:
+ leftUnionArray[i].setUConst(static_cast<unsigned int>(rightUnionArray[i].getU64Const()));
+ break;
+ case EbtBool:
+ leftUnionArray[i].setUConst(static_cast<unsigned int>(rightUnionArray[i].getBConst()));
+ break;
+ case EbtFloat:
+ case EbtDouble:
+ case EbtFloat16:
+ leftUnionArray[i].setUConst(static_cast<unsigned int>(rightUnionArray[i].getDConst()));
+ break;
+ default:
+ return node;
+ }
+ break;
+ case EbtBool:
+ switch (node->getType().getBasicType()) {
+ case EbtInt:
+ leftUnionArray[i].setBConst(rightUnionArray[i].getIConst() != 0);
+ break;
+ case EbtUint:
+ leftUnionArray[i].setBConst(rightUnionArray[i].getUConst() != 0);
+ break;
+ case EbtInt64:
+ leftUnionArray[i].setBConst(rightUnionArray[i].getI64Const() != 0);
+ break;
+ case EbtUint64:
+ leftUnionArray[i].setBConst(rightUnionArray[i].getU64Const() != 0);
+ break;
+ case EbtBool:
+ leftUnionArray[i] = rightUnionArray[i];
+ break;
+ case EbtFloat:
+ case EbtDouble:
+ case EbtFloat16:
+ leftUnionArray[i].setBConst(rightUnionArray[i].getDConst() != 0.0);
+ break;
+ default:
+ return node;
+ }
+ break;
+ case EbtInt64:
+ switch (node->getType().getBasicType()) {
+ case EbtInt:
+ leftUnionArray[i].setI64Const(static_cast<long long>(rightUnionArray[i].getIConst()));
+ break;
+ case EbtUint:
+ leftUnionArray[i].setI64Const(static_cast<long long>(rightUnionArray[i].getUConst()));
+ break;
+ case EbtInt64:
+ leftUnionArray[i] = rightUnionArray[i];
+ break;
+ case EbtUint64:
+ leftUnionArray[i].setI64Const(static_cast<long long>(rightUnionArray[i].getU64Const()));
+ break;
+ case EbtBool:
+ leftUnionArray[i].setI64Const(static_cast<long long>(rightUnionArray[i].getBConst()));
+ break;
+ case EbtFloat:
+ case EbtDouble:
+ case EbtFloat16:
+ leftUnionArray[i].setI64Const(static_cast<long long>(rightUnionArray[i].getDConst()));
+ break;
+ default:
+ return node;
+ }
+ break;
+ case EbtUint64:
+ switch (node->getType().getBasicType()) {
+ case EbtInt:
+ leftUnionArray[i].setU64Const(static_cast<unsigned long long>(rightUnionArray[i].getIConst()));
+ break;
+ case EbtUint:
+ leftUnionArray[i].setU64Const(static_cast<unsigned long long>(rightUnionArray[i].getUConst()));
+ break;
+ case EbtInt64:
+ leftUnionArray[i].setU64Const(static_cast<unsigned long long>(rightUnionArray[i].getI64Const()));
+ break;
+ case EbtUint64:
+ leftUnionArray[i] = rightUnionArray[i];
+ break;
+ case EbtBool:
+ leftUnionArray[i].setU64Const(static_cast<unsigned long long>(rightUnionArray[i].getBConst()));
+ break;
+ case EbtFloat:
+ case EbtDouble:
+ case EbtFloat16:
+ leftUnionArray[i].setU64Const(static_cast<unsigned long long>(rightUnionArray[i].getDConst()));
+ break;
+ default:
+ return node;
+ }
+ break;
+ default:
+ return node;
+ }
+ }
+
+ const TType& t = node->getType();
+
+ return addConstantUnion(leftUnionArray, TType(promoteTo, t.getQualifier().storage, t.getVectorSize(), t.getMatrixCols(), t.getMatrixRows()),
+ node->getLoc());
+}
+
+void TIntermAggregate::setPragmaTable(const TPragmaTable& pTable)
+{
+ assert(pragmaTable == nullptr);
+ pragmaTable = new TPragmaTable;
+ *pragmaTable = pTable;
+}
+
+// If either node is a specialization constant, while the other is
+// a constant (or specialization constant), the result is still
+// a specialization constant.
+bool TIntermediate::specConstantPropagates(const TIntermTyped& node1, const TIntermTyped& node2)
+{
+ return (node1.getType().getQualifier().isSpecConstant() && node2.getType().getQualifier().isConstant()) ||
+ (node2.getType().getQualifier().isSpecConstant() && node1.getType().getQualifier().isConstant());
+}
+
+struct TextureUpgradeAndSamplerRemovalTransform : public TIntermTraverser {
+ void visitSymbol(TIntermSymbol* symbol) override {
+ if (symbol->getBasicType() == EbtSampler && symbol->getType().getSampler().isTexture()) {
+ symbol->getWritableType().getSampler().combined = true;
+ }
+ }
+ bool visitAggregate(TVisit, TIntermAggregate* ag) override {
+ using namespace std;
+ TIntermSequence& seq = ag->getSequence();
+ TQualifierList& qual = ag->getQualifierList();
+
+ // qual and seq are indexed using the same indices, so we have to modify both in lock-step
+ assert(seq.size() == qual.size() || qual.empty());
+
+ size_t write = 0;
+ for (size_t i = 0; i < seq.size(); ++i) {
+ TIntermSymbol* symbol = seq[i]->getAsSymbolNode();
+ if (symbol && symbol->getBasicType() == EbtSampler && symbol->getType().getSampler().isPureSampler()) {
+ // remove pure sampler variables
+ continue;
+ }
+
+ TIntermNode* result = seq[i];
+
+ // replace constructors with sampler/textures
+ TIntermAggregate *constructor = seq[i]->getAsAggregate();
+ if (constructor && constructor->getOp() == EOpConstructTextureSampler) {
+ if (!constructor->getSequence().empty())
+ result = constructor->getSequence()[0];
+ }
+
+ // write new node & qualifier
+ seq[write] = result;
+ if (!qual.empty())
+ qual[write] = qual[i];
+ write++;
+ }
+
+ seq.resize(write);
+ if (!qual.empty())
+ qual.resize(write);
+
+ return true;
+ }
+};
+
+void TIntermediate::performTextureUpgradeAndSamplerRemovalTransformation(TIntermNode* root)
+{
+ TextureUpgradeAndSamplerRemovalTransform transform;
+ root->traverse(&transform);
+}
+
+const char* TIntermediate::getResourceName(TResourceType res)
+{
+ switch (res) {
+ case EResSampler: return "shift-sampler-binding";
+ case EResTexture: return "shift-texture-binding";
+ case EResImage: return "shift-image-binding";
+ case EResUbo: return "shift-UBO-binding";
+ case EResSsbo: return "shift-ssbo-binding";
+ case EResUav: return "shift-uav-binding";
+ default:
+ assert(0); // internal error: should only be called with valid resource types.
+ return nullptr;
+ }
+}
+
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/LiveTraverser.h b/src/3rdparty/glslang/glslang/MachineIndependent/LiveTraverser.h
new file mode 100644
index 0000000..7333bc9
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/LiveTraverser.h
@@ -0,0 +1,138 @@
+//
+// Copyright (C) 2016 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#pragma once
+
+#include "../Include/Common.h"
+#include "reflection.h"
+#include "localintermediate.h"
+
+#include "gl_types.h"
+
+#include <list>
+#include <unordered_set>
+
+namespace glslang {
+
+//
+// The traverser: mostly pass through, except
+// - processing function-call nodes to push live functions onto the stack of functions to process
+// - processing selection nodes to trim semantically dead code
+//
+// This is in the glslang namespace directly so it can be a friend of TReflection.
+// This can be derived from to implement reflection database traversers or
+// binding mappers: anything that wants to traverse the live subset of the tree.
+//
+
+class TLiveTraverser : public TIntermTraverser {
+public:
+ TLiveTraverser(const TIntermediate& i, bool traverseAll = false,
+ bool preVisit = true, bool inVisit = false, bool postVisit = false) :
+ TIntermTraverser(preVisit, inVisit, postVisit),
+ intermediate(i), traverseAll(traverseAll)
+ { }
+
+ //
+ // Given a function name, find its subroot in the tree, and push it onto the stack of
+ // functions left to process.
+ //
+ void pushFunction(const TString& name)
+ {
+ TIntermSequence& globals = intermediate.getTreeRoot()->getAsAggregate()->getSequence();
+ for (unsigned int f = 0; f < globals.size(); ++f) {
+ TIntermAggregate* candidate = globals[f]->getAsAggregate();
+ if (candidate && candidate->getOp() == EOpFunction && candidate->getName() == name) {
+ functions.push_back(candidate);
+ break;
+ }
+ }
+ }
+
+ typedef std::list<TIntermAggregate*> TFunctionStack;
+ TFunctionStack functions;
+
+protected:
+ // To catch which function calls are not dead, and hence which functions must be visited.
+ virtual bool visitAggregate(TVisit, TIntermAggregate* node)
+ {
+ if (!traverseAll)
+ if (node->getOp() == EOpFunctionCall)
+ addFunctionCall(node);
+
+ return true; // traverse this subtree
+ }
+
+ // To prune semantically dead paths.
+ virtual bool visitSelection(TVisit /* visit */, TIntermSelection* node)
+ {
+ if (traverseAll)
+ return true; // traverse all code
+
+ TIntermConstantUnion* constant = node->getCondition()->getAsConstantUnion();
+ if (constant) {
+ // cull the path that is dead
+ if (constant->getConstArray()[0].getBConst() == true && node->getTrueBlock())
+ node->getTrueBlock()->traverse(this);
+ if (constant->getConstArray()[0].getBConst() == false && node->getFalseBlock())
+ node->getFalseBlock()->traverse(this);
+
+ return false; // don't traverse any more, we did it all above
+ } else
+ return true; // traverse the whole subtree
+ }
+
+ // Track live functions as well as uniforms, so that we don't visit dead functions
+ // and only visit each function once.
+ void addFunctionCall(TIntermAggregate* call)
+ {
+ // // just use the map to ensure we process each function at most once
+ if (liveFunctions.find(call->getName()) == liveFunctions.end()) {
+ liveFunctions.insert(call->getName());
+ pushFunction(call->getName());
+ }
+ }
+
+ const TIntermediate& intermediate;
+ typedef std::unordered_set<TString> TLiveFunctions;
+ TLiveFunctions liveFunctions;
+ bool traverseAll;
+
+private:
+ // prevent copy & copy construct
+ TLiveTraverser(TLiveTraverser&);
+ TLiveTraverser& operator=(TLiveTraverser&);
+};
+
+} // namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/ParseContextBase.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/ParseContextBase.cpp
new file mode 100644
index 0000000..c9ddaea
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/ParseContextBase.cpp
@@ -0,0 +1,628 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2016 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+// Implement the TParseContextBase class.
+
+#include <cstdarg>
+
+#include "ParseHelper.h"
+
+extern int yyparse(glslang::TParseContext*);
+
+namespace glslang {
+
+//
+// Used to output syntax, parsing, and semantic errors.
+//
+
+void TParseContextBase::outputMessage(const TSourceLoc& loc, const char* szReason,
+ const char* szToken,
+ const char* szExtraInfoFormat,
+ TPrefixType prefix, va_list args)
+{
+ const int maxSize = MaxTokenLength + 200;
+ char szExtraInfo[maxSize];
+
+ safe_vsprintf(szExtraInfo, maxSize, szExtraInfoFormat, args);
+
+ infoSink.info.prefix(prefix);
+ infoSink.info.location(loc);
+ infoSink.info << "'" << szToken << "' : " << szReason << " " << szExtraInfo << "\n";
+
+ if (prefix == EPrefixError) {
+ ++numErrors;
+ }
+}
+
+void C_DECL TParseContextBase::error(const TSourceLoc& loc, const char* szReason, const char* szToken,
+ const char* szExtraInfoFormat, ...)
+{
+ if (messages & EShMsgOnlyPreprocessor)
+ return;
+ va_list args;
+ va_start(args, szExtraInfoFormat);
+ outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixError, args);
+ va_end(args);
+
+ if ((messages & EShMsgCascadingErrors) == 0)
+ currentScanner->setEndOfInput();
+}
+
+void C_DECL TParseContextBase::warn(const TSourceLoc& loc, const char* szReason, const char* szToken,
+ const char* szExtraInfoFormat, ...)
+{
+ if (suppressWarnings())
+ return;
+ va_list args;
+ va_start(args, szExtraInfoFormat);
+ outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixWarning, args);
+ va_end(args);
+}
+
+void C_DECL TParseContextBase::ppError(const TSourceLoc& loc, const char* szReason, const char* szToken,
+ const char* szExtraInfoFormat, ...)
+{
+ va_list args;
+ va_start(args, szExtraInfoFormat);
+ outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixError, args);
+ va_end(args);
+
+ if ((messages & EShMsgCascadingErrors) == 0)
+ currentScanner->setEndOfInput();
+}
+
+void C_DECL TParseContextBase::ppWarn(const TSourceLoc& loc, const char* szReason, const char* szToken,
+ const char* szExtraInfoFormat, ...)
+{
+ va_list args;
+ va_start(args, szExtraInfoFormat);
+ outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixWarning, args);
+ va_end(args);
+}
+
+//
+// Both test and if necessary, spit out an error, to see if the node is really
+// an l-value that can be operated on this way.
+//
+// Returns true if there was an error.
+//
+bool TParseContextBase::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node)
+{
+ TIntermBinary* binaryNode = node->getAsBinaryNode();
+
+ if (binaryNode) {
+ switch(binaryNode->getOp()) {
+ case EOpIndexDirect:
+ case EOpIndexIndirect: // fall through
+ case EOpIndexDirectStruct: // fall through
+ case EOpVectorSwizzle:
+ case EOpMatrixSwizzle:
+ return lValueErrorCheck(loc, op, binaryNode->getLeft());
+ default:
+ break;
+ }
+ error(loc, " l-value required", op, "", "");
+
+ return true;
+ }
+
+ const char* symbol = nullptr;
+ TIntermSymbol* symNode = node->getAsSymbolNode();
+ if (symNode != nullptr)
+ symbol = symNode->getName().c_str();
+
+ const char* message = nullptr;
+ switch (node->getQualifier().storage) {
+ case EvqConst: message = "can't modify a const"; break;
+ case EvqConstReadOnly: message = "can't modify a const"; break;
+ case EvqUniform: message = "can't modify a uniform"; break;
+ case EvqBuffer:
+ if (node->getQualifier().readonly)
+ message = "can't modify a readonly buffer";
+#ifdef NV_EXTENSIONS
+ if (node->getQualifier().layoutShaderRecordNV)
+ message = "can't modify a shaderrecordnv qualified buffer";
+#endif
+ break;
+#ifdef NV_EXTENSIONS
+ case EvqHitAttrNV:
+ if (language != EShLangIntersectNV)
+ message = "cannot modify hitAttributeNV in this stage";
+ break;
+#endif
+
+ default:
+ //
+ // Type that can't be written to?
+ //
+ switch (node->getBasicType()) {
+ case EbtSampler:
+ message = "can't modify a sampler";
+ break;
+ case EbtAtomicUint:
+ message = "can't modify an atomic_uint";
+ break;
+ case EbtVoid:
+ message = "can't modify void";
+ break;
+#ifdef NV_EXTENSIONS
+ case EbtAccStructNV:
+ message = "can't modify accelerationStructureNV";
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+
+ if (message == nullptr && binaryNode == nullptr && symNode == nullptr) {
+ error(loc, " l-value required", op, "", "");
+
+ return true;
+ }
+
+ //
+ // Everything else is okay, no error.
+ //
+ if (message == nullptr)
+ return false;
+
+ //
+ // If we get here, we have an error and a message.
+ //
+ if (symNode)
+ error(loc, " l-value required", op, "\"%s\" (%s)", symbol, message);
+ else
+ error(loc, " l-value required", op, "(%s)", message);
+
+ return true;
+}
+
+// Test for and give an error if the node can't be read from.
+void TParseContextBase::rValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node)
+{
+ if (! node)
+ return;
+
+ TIntermBinary* binaryNode = node->getAsBinaryNode();
+ if (binaryNode) {
+ switch(binaryNode->getOp()) {
+ case EOpIndexDirect:
+ case EOpIndexIndirect:
+ case EOpIndexDirectStruct:
+ case EOpVectorSwizzle:
+ case EOpMatrixSwizzle:
+ rValueErrorCheck(loc, op, binaryNode->getLeft());
+ default:
+ break;
+ }
+
+ return;
+ }
+
+ TIntermSymbol* symNode = node->getAsSymbolNode();
+ if (symNode && symNode->getQualifier().writeonly)
+ error(loc, "can't read from writeonly object: ", op, symNode->getName().c_str());
+}
+
+// Add 'symbol' to the list of deferred linkage symbols, which
+// are later processed in finish(), at which point the symbol
+// must still be valid.
+// It is okay if the symbol's type will be subsequently edited;
+// the modifications will be tracked.
+// Order is preserved, to avoid creating novel forward references.
+void TParseContextBase::trackLinkage(TSymbol& symbol)
+{
+ if (!parsingBuiltins)
+ linkageSymbols.push_back(&symbol);
+}
+
+// Ensure index is in bounds, correct if necessary.
+// Give an error if not.
+void TParseContextBase::checkIndex(const TSourceLoc& loc, const TType& type, int& index)
+{
+ if (index < 0) {
+ error(loc, "", "[", "index out of range '%d'", index);
+ index = 0;
+ } else if (type.isArray()) {
+ if (type.isSizedArray() && index >= type.getOuterArraySize()) {
+ error(loc, "", "[", "array index out of range '%d'", index);
+ index = type.getOuterArraySize() - 1;
+ }
+ } else if (type.isVector()) {
+ if (index >= type.getVectorSize()) {
+ error(loc, "", "[", "vector index out of range '%d'", index);
+ index = type.getVectorSize() - 1;
+ }
+ } else if (type.isMatrix()) {
+ if (index >= type.getMatrixCols()) {
+ error(loc, "", "[", "matrix index out of range '%d'", index);
+ index = type.getMatrixCols() - 1;
+ }
+ }
+}
+
+// Make a shared symbol have a non-shared version that can be edited by the current
+// compile, such that editing its type will not change the shared version and will
+// effect all nodes already sharing it (non-shallow type),
+// or adopting its full type after being edited (shallow type).
+void TParseContextBase::makeEditable(TSymbol*& symbol)
+{
+ // copyUp() does a deep copy of the type.
+ symbol = symbolTable.copyUp(symbol);
+
+ // Save it (deferred, so it can be edited first) in the AST for linker use.
+ if (symbol)
+ trackLinkage(*symbol);
+}
+
+// Return a writable version of the variable 'name'.
+//
+// Return nullptr if 'name' is not found. This should mean
+// something is seriously wrong (e.g., compiler asking self for
+// built-in that doesn't exist).
+TVariable* TParseContextBase::getEditableVariable(const char* name)
+{
+ bool builtIn;
+ TSymbol* symbol = symbolTable.find(name, &builtIn);
+
+ assert(symbol != nullptr);
+ if (symbol == nullptr)
+ return nullptr;
+
+ if (builtIn)
+ makeEditable(symbol);
+
+ return symbol->getAsVariable();
+}
+
+// Select the best matching function for 'call' from 'candidateList'.
+//
+// Assumptions
+//
+// There is no exact match, so a selection algorithm needs to run. That is, the
+// language-specific handler should check for exact match first, to
+// decide what to do, before calling this selector.
+//
+// Input
+//
+// * list of candidate signatures to select from
+// * the call
+// * a predicate function convertible(from, to) that says whether or not type
+// 'from' can implicitly convert to type 'to' (it includes the case of what
+// the calling language would consider a matching type with no conversion
+// needed)
+// * a predicate function better(from1, from2, to1, to2) that says whether or
+// not a conversion from <-> to2 is considered better than a conversion
+// from <-> to1 (both in and out directions need testing, as declared by the
+// formal parameter)
+//
+// Output
+//
+// * best matching candidate (or none, if no viable candidates found)
+// * whether there was a tie for the best match (ambiguous overload selection,
+// caller's choice for how to report)
+//
+const TFunction* TParseContextBase::selectFunction(
+ const TVector<const TFunction*> candidateList,
+ const TFunction& call,
+ std::function<bool(const TType& from, const TType& to, TOperator op, int arg)> convertible,
+ std::function<bool(const TType& from, const TType& to1, const TType& to2)> better,
+ /* output */ bool& tie)
+{
+//
+// Operation
+//
+// 1. Prune the input list of candidates down to a list of viable candidates,
+// where each viable candidate has
+//
+// * at least as many parameters as there are calling arguments, with any
+// remaining parameters being optional or having default values
+// * each parameter is true under convertible(A, B), where A is the calling
+// type for in and B is the formal type, and in addition, for out B is the
+// calling type and A is the formal type
+//
+// 2. If there are no viable candidates, return with no match.
+//
+// 3. If there is only one viable candidate, it is the best match.
+//
+// 4. If there are multiple viable candidates, select the first viable candidate
+// as the incumbent. Compare the incumbent to the next viable candidate, and if
+// that candidate is better (bullets below), make it the incumbent. Repeat, with
+// a linear walk through the viable candidate list. The final incumbent will be
+// returned as the best match. A viable candidate is better than the incumbent if
+//
+// * it has a function argument with a better(...) conversion than the incumbent,
+// for all directions needed by in and out
+// * the incumbent has no argument with a better(...) conversion then the
+// candidate, for either in or out (as needed)
+//
+// 5. Check for ambiguity by comparing the best match against all other viable
+// candidates. If any other viable candidate has a function argument with a
+// better(...) conversion than the best candidate (for either in or out
+// directions), return that there was a tie for best.
+//
+
+ tie = false;
+
+ // 1. prune to viable...
+ TVector<const TFunction*> viableCandidates;
+ for (auto it = candidateList.begin(); it != candidateList.end(); ++it) {
+ const TFunction& candidate = *(*it);
+
+ // to even be a potential match, number of arguments must be >= the number of
+ // fixed (non-default) parameters, and <= the total (including parameter with defaults).
+ if (call.getParamCount() < candidate.getFixedParamCount() ||
+ call.getParamCount() > candidate.getParamCount())
+ continue;
+
+ // see if arguments are convertible
+ bool viable = true;
+
+ // The call can have fewer parameters than the candidate, if some have defaults.
+ const int paramCount = std::min(call.getParamCount(), candidate.getParamCount());
+ for (int param = 0; param < paramCount; ++param) {
+ if (candidate[param].type->getQualifier().isParamInput()) {
+ if (! convertible(*call[param].type, *candidate[param].type, candidate.getBuiltInOp(), param)) {
+ viable = false;
+ break;
+ }
+ }
+ if (candidate[param].type->getQualifier().isParamOutput()) {
+ if (! convertible(*candidate[param].type, *call[param].type, candidate.getBuiltInOp(), param)) {
+ viable = false;
+ break;
+ }
+ }
+ }
+
+ if (viable)
+ viableCandidates.push_back(&candidate);
+ }
+
+ // 2. none viable...
+ if (viableCandidates.size() == 0)
+ return nullptr;
+
+ // 3. only one viable...
+ if (viableCandidates.size() == 1)
+ return viableCandidates.front();
+
+ // 4. find best...
+ const auto betterParam = [&call, &better](const TFunction& can1, const TFunction& can2) -> bool {
+ // is call -> can2 better than call -> can1 for any parameter
+ bool hasBetterParam = false;
+ for (int param = 0; param < call.getParamCount(); ++param) {
+ if (better(*call[param].type, *can1[param].type, *can2[param].type)) {
+ hasBetterParam = true;
+ break;
+ }
+ }
+ return hasBetterParam;
+ };
+
+ const auto equivalentParams = [&call, &better](const TFunction& can1, const TFunction& can2) -> bool {
+ // is call -> can2 equivalent to call -> can1 for all the call parameters?
+ for (int param = 0; param < call.getParamCount(); ++param) {
+ if (better(*call[param].type, *can1[param].type, *can2[param].type) ||
+ better(*call[param].type, *can2[param].type, *can1[param].type))
+ return false;
+ }
+ return true;
+ };
+
+ const TFunction* incumbent = viableCandidates.front();
+ for (auto it = viableCandidates.begin() + 1; it != viableCandidates.end(); ++it) {
+ const TFunction& candidate = *(*it);
+ if (betterParam(*incumbent, candidate) && ! betterParam(candidate, *incumbent))
+ incumbent = &candidate;
+ }
+
+ // 5. ambiguity...
+ for (auto it = viableCandidates.begin(); it != viableCandidates.end(); ++it) {
+ if (incumbent == *it)
+ continue;
+ const TFunction& candidate = *(*it);
+
+ // In the case of default parameters, it may have an identical initial set, which is
+ // also ambiguous
+ if (betterParam(*incumbent, candidate) || equivalentParams(*incumbent, candidate))
+ tie = true;
+ }
+
+ return incumbent;
+}
+
+//
+// Look at a '.' field selector string and change it into numerical selectors
+// for a vector or scalar.
+//
+// Always return some form of swizzle, so the result is always usable.
+//
+void TParseContextBase::parseSwizzleSelector(const TSourceLoc& loc, const TString& compString, int vecSize,
+ TSwizzleSelectors<TVectorSelector>& selector)
+{
+ // Too long?
+ if (compString.size() > MaxSwizzleSelectors)
+ error(loc, "vector swizzle too long", compString.c_str(), "");
+
+ // Use this to test that all swizzle characters are from the same swizzle-namespace-set
+ enum {
+ exyzw,
+ ergba,
+ estpq,
+ } fieldSet[MaxSwizzleSelectors];
+
+ // Decode the swizzle string.
+ int size = std::min(MaxSwizzleSelectors, (int)compString.size());
+ for (int i = 0; i < size; ++i) {
+ switch (compString[i]) {
+ case 'x':
+ selector.push_back(0);
+ fieldSet[i] = exyzw;
+ break;
+ case 'r':
+ selector.push_back(0);
+ fieldSet[i] = ergba;
+ break;
+ case 's':
+ selector.push_back(0);
+ fieldSet[i] = estpq;
+ break;
+
+ case 'y':
+ selector.push_back(1);
+ fieldSet[i] = exyzw;
+ break;
+ case 'g':
+ selector.push_back(1);
+ fieldSet[i] = ergba;
+ break;
+ case 't':
+ selector.push_back(1);
+ fieldSet[i] = estpq;
+ break;
+
+ case 'z':
+ selector.push_back(2);
+ fieldSet[i] = exyzw;
+ break;
+ case 'b':
+ selector.push_back(2);
+ fieldSet[i] = ergba;
+ break;
+ case 'p':
+ selector.push_back(2);
+ fieldSet[i] = estpq;
+ break;
+
+ case 'w':
+ selector.push_back(3);
+ fieldSet[i] = exyzw;
+ break;
+ case 'a':
+ selector.push_back(3);
+ fieldSet[i] = ergba;
+ break;
+ case 'q':
+ selector.push_back(3);
+ fieldSet[i] = estpq;
+ break;
+
+ default:
+ error(loc, "unknown swizzle selection", compString.c_str(), "");
+ break;
+ }
+ }
+
+ // Additional error checking.
+ for (int i = 0; i < selector.size(); ++i) {
+ if (selector[i] >= vecSize) {
+ error(loc, "vector swizzle selection out of range", compString.c_str(), "");
+ selector.resize(i);
+ break;
+ }
+
+ if (i > 0 && fieldSet[i] != fieldSet[i-1]) {
+ error(loc, "vector swizzle selectors not from the same set", compString.c_str(), "");
+ selector.resize(i);
+ break;
+ }
+ }
+
+ // Ensure it is valid.
+ if (selector.size() == 0)
+ selector.push_back(0);
+}
+
+//
+// Make the passed-in variable information become a member of the
+// global uniform block. If this doesn't exist yet, make it.
+//
+void TParseContextBase::growGlobalUniformBlock(const TSourceLoc& loc, TType& memberType, const TString& memberName, TTypeList* typeList)
+{
+ // Make the global block, if not yet made.
+ if (globalUniformBlock == nullptr) {
+ TQualifier blockQualifier;
+ blockQualifier.clear();
+ blockQualifier.storage = EvqUniform;
+ TType blockType(new TTypeList, *NewPoolTString(getGlobalUniformBlockName()), blockQualifier);
+ setUniformBlockDefaults(blockType);
+ globalUniformBlock = new TVariable(NewPoolTString(""), blockType, true);
+ firstNewMember = 0;
+ }
+
+ // Update with binding and set
+ globalUniformBlock->getWritableType().getQualifier().layoutBinding = globalUniformBinding;
+ globalUniformBlock->getWritableType().getQualifier().layoutSet = globalUniformSet;
+
+ // Add the requested member as a member to the global block.
+ TType* type = new TType;
+ type->shallowCopy(memberType);
+ type->setFieldName(memberName);
+ if (typeList)
+ type->setStruct(typeList);
+ TTypeLoc typeLoc = {type, loc};
+ globalUniformBlock->getType().getWritableStruct()->push_back(typeLoc);
+
+ // Insert into the symbol table.
+ if (firstNewMember == 0) {
+ // This is the first request; we need a normal symbol table insert
+ if (symbolTable.insert(*globalUniformBlock))
+ trackLinkage(*globalUniformBlock);
+ else
+ error(loc, "failed to insert the global constant buffer", "uniform", "");
+ } else {
+ // This is a follow-on request; we need to amend the first insert
+ symbolTable.amend(*globalUniformBlock, firstNewMember);
+ }
+
+ ++firstNewMember;
+}
+
+void TParseContextBase::finish()
+{
+ if (parsingBuiltins)
+ return;
+
+ // Transfer the linkage symbols to AST nodes, preserving order.
+ TIntermAggregate* linkage = new TIntermAggregate;
+ for (auto i = linkageSymbols.begin(); i != linkageSymbols.end(); ++i)
+ intermediate.addSymbolLinkageNode(linkage, **i);
+ intermediate.addSymbolLinkageNodes(linkage, getLanguage(), symbolTable);
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/ParseHelper.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/ParseHelper.cpp
new file mode 100644
index 0000000..e1e2ee9
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/ParseHelper.cpp
@@ -0,0 +1,7997 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2012-2015 LunarG, Inc.
+// Copyright (C) 2015-2018 Google, Inc.
+// Copyright (C) 2017 ARM Limited.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "ParseHelper.h"
+#include "Scan.h"
+
+#include "../OSDependent/osinclude.h"
+#include <algorithm>
+
+#include "preprocessor/PpContext.h"
+
+extern int yyparse(glslang::TParseContext*);
+
+namespace glslang {
+
+TParseContext::TParseContext(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins,
+ int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language,
+ TInfoSink& infoSink, bool forwardCompatible, EShMessages messages,
+ const TString* entryPoint) :
+ TParseContextBase(symbolTable, interm, parsingBuiltins, version, profile, spvVersion, language,
+ infoSink, forwardCompatible, messages, entryPoint),
+ inMain(false),
+ blockName(nullptr),
+ limits(resources.limits),
+ atomicUintOffsets(nullptr), anyIndexLimits(false)
+{
+ // decide whether precision qualifiers should be ignored or respected
+ if (profile == EEsProfile || spvVersion.vulkan > 0) {
+ precisionManager.respectPrecisionQualifiers();
+ if (! parsingBuiltins && language == EShLangFragment && profile != EEsProfile && spvVersion.vulkan > 0)
+ precisionManager.warnAboutDefaults();
+ }
+
+ setPrecisionDefaults();
+
+ globalUniformDefaults.clear();
+ globalUniformDefaults.layoutMatrix = ElmColumnMajor;
+ globalUniformDefaults.layoutPacking = spvVersion.spv != 0 ? ElpStd140 : ElpShared;
+
+ globalBufferDefaults.clear();
+ globalBufferDefaults.layoutMatrix = ElmColumnMajor;
+ globalBufferDefaults.layoutPacking = spvVersion.spv != 0 ? ElpStd430 : ElpShared;
+
+ // use storage buffer on SPIR-V 1.3 and up
+ if (spvVersion.spv >= EShTargetSpv_1_3)
+ intermediate.setUseStorageBuffer();
+
+ globalInputDefaults.clear();
+ globalOutputDefaults.clear();
+
+ // "Shaders in the transform
+ // feedback capturing mode have an initial global default of
+ // layout(xfb_buffer = 0) out;"
+ if (language == EShLangVertex ||
+ language == EShLangTessControl ||
+ language == EShLangTessEvaluation ||
+ language == EShLangGeometry)
+ globalOutputDefaults.layoutXfbBuffer = 0;
+
+ if (language == EShLangGeometry)
+ globalOutputDefaults.layoutStream = 0;
+
+ if (entryPoint != nullptr && entryPoint->size() > 0 && *entryPoint != "main")
+ infoSink.info.message(EPrefixError, "Source entry point must be \"main\"");
+}
+
+TParseContext::~TParseContext()
+{
+ delete [] atomicUintOffsets;
+}
+
+// Set up all default precisions as needed by the current environment.
+// Intended just as a TParseContext constructor helper.
+void TParseContext::setPrecisionDefaults()
+{
+ // Set all precision defaults to EpqNone, which is correct for all types
+ // when not obeying precision qualifiers, and correct for types that don't
+ // have defaults (thus getting an error on use) when obeying precision
+ // qualifiers.
+
+ for (int type = 0; type < EbtNumTypes; ++type)
+ defaultPrecision[type] = EpqNone;
+
+ for (int type = 0; type < maxSamplerIndex; ++type)
+ defaultSamplerPrecision[type] = EpqNone;
+
+ // replace with real precision defaults for those that have them
+ if (obeyPrecisionQualifiers()) {
+ if (profile == EEsProfile) {
+ // Most don't have defaults, a few default to lowp.
+ TSampler sampler;
+ sampler.set(EbtFloat, Esd2D);
+ defaultSamplerPrecision[computeSamplerTypeIndex(sampler)] = EpqLow;
+ sampler.set(EbtFloat, EsdCube);
+ defaultSamplerPrecision[computeSamplerTypeIndex(sampler)] = EpqLow;
+ sampler.set(EbtFloat, Esd2D);
+ sampler.external = true;
+ defaultSamplerPrecision[computeSamplerTypeIndex(sampler)] = EpqLow;
+ }
+
+ // If we are parsing built-in computational variables/functions, it is meaningful to record
+ // whether the built-in has no precision qualifier, as that ambiguity
+ // is used to resolve the precision from the supplied arguments/operands instead.
+ // So, we don't actually want to replace EpqNone with a default precision for built-ins.
+ if (! parsingBuiltins) {
+ if (profile == EEsProfile && language == EShLangFragment) {
+ defaultPrecision[EbtInt] = EpqMedium;
+ defaultPrecision[EbtUint] = EpqMedium;
+ } else {
+ defaultPrecision[EbtInt] = EpqHigh;
+ defaultPrecision[EbtUint] = EpqHigh;
+ defaultPrecision[EbtFloat] = EpqHigh;
+ }
+
+ if (profile != EEsProfile) {
+ // Non-ES profile
+ // All sampler precisions default to highp.
+ for (int type = 0; type < maxSamplerIndex; ++type)
+ defaultSamplerPrecision[type] = EpqHigh;
+ }
+ }
+
+ defaultPrecision[EbtSampler] = EpqLow;
+ defaultPrecision[EbtAtomicUint] = EpqHigh;
+ }
+}
+
+void TParseContext::setLimits(const TBuiltInResource& r)
+{
+ resources = r;
+
+ anyIndexLimits = ! limits.generalAttributeMatrixVectorIndexing ||
+ ! limits.generalConstantMatrixVectorIndexing ||
+ ! limits.generalSamplerIndexing ||
+ ! limits.generalUniformIndexing ||
+ ! limits.generalVariableIndexing ||
+ ! limits.generalVaryingIndexing;
+
+ intermediate.setLimits(resources);
+
+ // "Each binding point tracks its own current default offset for
+ // inheritance of subsequent variables using the same binding. The initial state of compilation is that all
+ // binding points have an offset of 0."
+ atomicUintOffsets = new int[resources.maxAtomicCounterBindings];
+ for (int b = 0; b < resources.maxAtomicCounterBindings; ++b)
+ atomicUintOffsets[b] = 0;
+}
+
+//
+// Parse an array of strings using yyparse, going through the
+// preprocessor to tokenize the shader strings, then through
+// the GLSL scanner.
+//
+// Returns true for successful acceptance of the shader, false if any errors.
+//
+bool TParseContext::parseShaderStrings(TPpContext& ppContext, TInputScanner& input, bool versionWillBeError)
+{
+ currentScanner = &input;
+ ppContext.setInput(input, versionWillBeError);
+ yyparse(this);
+
+ finish();
+
+ return numErrors == 0;
+}
+
+// This is called from bison when it has a parse (syntax) error
+// Note though that to stop cascading errors, we set EOF, which
+// will usually cause a syntax error, so be more accurate that
+// compilation is terminating.
+void TParseContext::parserError(const char* s)
+{
+ if (! getScanner()->atEndOfInput() || numErrors == 0)
+ error(getCurrentLoc(), "", "", s, "");
+ else
+ error(getCurrentLoc(), "compilation terminated", "", "");
+}
+
+void TParseContext::handlePragma(const TSourceLoc& loc, const TVector<TString>& tokens)
+{
+ if (pragmaCallback)
+ pragmaCallback(loc.line, tokens);
+
+ if (tokens.size() == 0)
+ return;
+
+ if (tokens[0].compare("optimize") == 0) {
+ if (tokens.size() != 4) {
+ error(loc, "optimize pragma syntax is incorrect", "#pragma", "");
+ return;
+ }
+
+ if (tokens[1].compare("(") != 0) {
+ error(loc, "\"(\" expected after 'optimize' keyword", "#pragma", "");
+ return;
+ }
+
+ if (tokens[2].compare("on") == 0)
+ contextPragma.optimize = true;
+ else if (tokens[2].compare("off") == 0)
+ contextPragma.optimize = false;
+ else {
+ error(loc, "\"on\" or \"off\" expected after '(' for 'optimize' pragma", "#pragma", "");
+ return;
+ }
+
+ if (tokens[3].compare(")") != 0) {
+ error(loc, "\")\" expected to end 'optimize' pragma", "#pragma", "");
+ return;
+ }
+ } else if (tokens[0].compare("debug") == 0) {
+ if (tokens.size() != 4) {
+ error(loc, "debug pragma syntax is incorrect", "#pragma", "");
+ return;
+ }
+
+ if (tokens[1].compare("(") != 0) {
+ error(loc, "\"(\" expected after 'debug' keyword", "#pragma", "");
+ return;
+ }
+
+ if (tokens[2].compare("on") == 0)
+ contextPragma.debug = true;
+ else if (tokens[2].compare("off") == 0)
+ contextPragma.debug = false;
+ else {
+ error(loc, "\"on\" or \"off\" expected after '(' for 'debug' pragma", "#pragma", "");
+ return;
+ }
+
+ if (tokens[3].compare(")") != 0) {
+ error(loc, "\")\" expected to end 'debug' pragma", "#pragma", "");
+ return;
+ }
+ } else if (spvVersion.spv > 0 && tokens[0].compare("use_storage_buffer") == 0) {
+ if (tokens.size() != 1)
+ error(loc, "extra tokens", "#pragma", "");
+ intermediate.setUseStorageBuffer();
+ } else if (spvVersion.spv > 0 && tokens[0].compare("use_vulkan_memory_model") == 0) {
+ if (tokens.size() != 1)
+ error(loc, "extra tokens", "#pragma", "");
+ intermediate.setUseVulkanMemoryModel();
+ } else if (spvVersion.spv > 0 && tokens[0].compare("use_variable_pointers") == 0) {
+ if (tokens.size() != 1)
+ error(loc, "extra tokens", "#pragma", "");
+ if (spvVersion.spv < glslang::EShTargetSpv_1_3)
+ error(loc, "requires SPIR-V 1.3", "#pragma use_variable_pointers", "");
+ intermediate.setUseVariablePointers();
+ } else if (tokens[0].compare("once") == 0) {
+ warn(loc, "not implemented", "#pragma once", "");
+ } else if (tokens[0].compare("glslang_binary_double_output") == 0)
+ intermediate.setBinaryDoubleOutput();
+}
+
+//
+// Handle seeing a variable identifier in the grammar.
+//
+TIntermTyped* TParseContext::handleVariable(const TSourceLoc& loc, TSymbol* symbol, const TString* string)
+{
+ TIntermTyped* node = nullptr;
+
+ // Error check for requiring specific extensions present.
+ if (symbol && symbol->getNumExtensions())
+ requireExtensions(loc, symbol->getNumExtensions(), symbol->getExtensions(), symbol->getName().c_str());
+
+ if (symbol && symbol->isReadOnly()) {
+ // All shared things containing an unsized array must be copied up
+ // on first use, so that all future references will share its array structure,
+ // so that editing the implicit size will effect all nodes consuming it,
+ // and so that editing the implicit size won't change the shared one.
+ //
+ // If this is a variable or a block, check it and all it contains, but if this
+ // is a member of an anonymous block, check the whole block, as the whole block
+ // will need to be copied up if it contains an unsized array.
+ if (symbol->getType().containsUnsizedArray() ||
+ (symbol->getAsAnonMember() &&
+ symbol->getAsAnonMember()->getAnonContainer().getType().containsUnsizedArray()))
+ makeEditable(symbol);
+ }
+
+ const TVariable* variable;
+ const TAnonMember* anon = symbol ? symbol->getAsAnonMember() : nullptr;
+ if (anon) {
+ // It was a member of an anonymous container.
+
+ // Create a subtree for its dereference.
+ variable = anon->getAnonContainer().getAsVariable();
+ TIntermTyped* container = intermediate.addSymbol(*variable, loc);
+ TIntermTyped* constNode = intermediate.addConstantUnion(anon->getMemberNumber(), loc);
+ node = intermediate.addIndex(EOpIndexDirectStruct, container, constNode, loc);
+
+ node->setType(*(*variable->getType().getStruct())[anon->getMemberNumber()].type);
+ if (node->getType().hiddenMember())
+ error(loc, "member of nameless block was not redeclared", string->c_str(), "");
+ } else {
+ // Not a member of an anonymous container.
+
+ // The symbol table search was done in the lexical phase.
+ // See if it was a variable.
+ variable = symbol ? symbol->getAsVariable() : nullptr;
+ if (variable) {
+ if ((variable->getType().getBasicType() == EbtBlock ||
+ variable->getType().getBasicType() == EbtStruct) && variable->getType().getStruct() == nullptr) {
+ error(loc, "cannot be used (maybe an instance name is needed)", string->c_str(), "");
+ variable = nullptr;
+ }
+ } else {
+ if (symbol)
+ error(loc, "variable name expected", string->c_str(), "");
+ }
+
+ // Recovery, if it wasn't found or was not a variable.
+ if (! variable)
+ variable = new TVariable(string, TType(EbtVoid));
+
+ if (variable->getType().getQualifier().isFrontEndConstant())
+ node = intermediate.addConstantUnion(variable->getConstArray(), variable->getType(), loc);
+ else
+ node = intermediate.addSymbol(*variable, loc);
+ }
+
+ if (variable->getType().getQualifier().isIo())
+ intermediate.addIoAccessed(*string);
+
+ if (variable->getType().getBasicType() == EbtReference &&
+ variable->getType().getQualifier().bufferReferenceNeedsVulkanMemoryModel()) {
+ intermediate.setUseVulkanMemoryModel();
+ }
+
+ return node;
+}
+
+//
+// Handle seeing a base[index] dereference in the grammar.
+//
+TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index)
+{
+ int indexValue = 0;
+ if (index->getQualifier().isFrontEndConstant())
+ indexValue = index->getAsConstantUnion()->getConstArray()[0].getIConst();
+
+ // basic type checks...
+ variableCheck(base);
+
+ if (! base->isArray() && ! base->isMatrix() && ! base->isVector() && ! base->getType().isCoopMat()) {
+ if (base->getAsSymbolNode())
+ error(loc, " left of '[' is not of type array, matrix, or vector ", base->getAsSymbolNode()->getName().c_str(), "");
+ else
+ error(loc, " left of '[' is not of type array, matrix, or vector ", "expression", "");
+
+ // Insert dummy error-recovery result
+ return intermediate.addConstantUnion(0.0, EbtFloat, loc);
+ }
+
+ if (!base->isArray() && base->isVector()) {
+ if (base->getType().containsBasicType(EbtFloat16))
+ requireFloat16Arithmetic(loc, "[", "does not operate on types containing float16");
+ if (base->getType().contains16BitInt())
+ requireInt16Arithmetic(loc, "[", "does not operate on types containing (u)int16");
+ if (base->getType().contains8BitInt())
+ requireInt8Arithmetic(loc, "[", "does not operate on types containing (u)int8");
+ }
+
+ // check for constant folding
+ if (base->getType().getQualifier().isFrontEndConstant() && index->getQualifier().isFrontEndConstant()) {
+ // both base and index are front-end constants
+ checkIndex(loc, base->getType(), indexValue);
+ return intermediate.foldDereference(base, indexValue, loc);
+ }
+
+ // at least one of base and index is not a front-end constant variable...
+ TIntermTyped* result = nullptr;
+ if (index->getQualifier().isFrontEndConstant())
+ checkIndex(loc, base->getType(), indexValue);
+
+ if (base->getAsSymbolNode() && isIoResizeArray(base->getType()))
+ handleIoResizeArrayAccess(loc, base);
+
+ if (index->getQualifier().isFrontEndConstant()) {
+ if (base->getType().isUnsizedArray()) {
+ base->getWritableType().updateImplicitArraySize(indexValue + 1);
+#ifdef NV_EXTENSIONS
+ // For 2D per-view builtin arrays, update the inner dimension size in parent type
+ if (base->getQualifier().isPerView() && base->getQualifier().builtIn != EbvNone) {
+ TIntermBinary* binaryNode = base->getAsBinaryNode();
+ if (binaryNode) {
+ TType& leftType = binaryNode->getLeft()->getWritableType();
+ TArraySizes& arraySizes = *leftType.getArraySizes();
+ assert(arraySizes.getNumDims() == 2);
+ arraySizes.setDimSize(1, std::max(arraySizes.getDimSize(1), indexValue + 1));
+ }
+ }
+#endif
+ } else
+ checkIndex(loc, base->getType(), indexValue);
+ result = intermediate.addIndex(EOpIndexDirect, base, index, loc);
+ } else {
+ if (base->getType().isUnsizedArray()) {
+ // we have a variable index into an unsized array, which is okay,
+ // depending on the situation
+ if (base->getAsSymbolNode() && isIoResizeArray(base->getType()))
+ error(loc, "", "[", "array must be sized by a redeclaration or layout qualifier before being indexed with a variable");
+ else {
+ // it is okay for a run-time sized array
+ checkRuntimeSizable(loc, *base);
+ }
+ base->getWritableType().setArrayVariablyIndexed();
+ }
+ if (base->getBasicType() == EbtBlock) {
+ if (base->getQualifier().storage == EvqBuffer)
+ requireProfile(base->getLoc(), ~EEsProfile, "variable indexing buffer block array");
+ else if (base->getQualifier().storage == EvqUniform)
+ profileRequires(base->getLoc(), EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5,
+ "variable indexing uniform block array");
+ else {
+ // input/output blocks either don't exist or can be variable indexed
+ }
+ } else if (language == EShLangFragment && base->getQualifier().isPipeOutput())
+ requireProfile(base->getLoc(), ~EEsProfile, "variable indexing fragment shader output array");
+ else if (base->getBasicType() == EbtSampler && version >= 130) {
+ const char* explanation = "variable indexing sampler array";
+ requireProfile(base->getLoc(), EEsProfile | ECoreProfile | ECompatibilityProfile, explanation);
+ profileRequires(base->getLoc(), EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, explanation);
+ profileRequires(base->getLoc(), ECoreProfile | ECompatibilityProfile, 400, nullptr, explanation);
+ }
+
+ result = intermediate.addIndex(EOpIndexIndirect, base, index, loc);
+ }
+
+ // Insert valid dereferenced result
+ TType newType(base->getType(), 0); // dereferenced type
+ if (base->getType().getQualifier().isConstant() && index->getQualifier().isConstant()) {
+ newType.getQualifier().storage = EvqConst;
+ // If base or index is a specialization constant, the result should also be a specialization constant.
+ if (base->getType().getQualifier().isSpecConstant() || index->getQualifier().isSpecConstant()) {
+ newType.getQualifier().makeSpecConstant();
+ }
+ } else {
+ newType.getQualifier().makePartialTemporary();
+ }
+ result->setType(newType);
+
+ // Propagate nonuniform
+ if (base->getQualifier().isNonUniform() || index->getQualifier().isNonUniform())
+ result->getWritableType().getQualifier().nonUniform = true;
+
+ if (anyIndexLimits)
+ handleIndexLimits(loc, base, index);
+
+ return result;
+}
+
+// for ES 2.0 (version 100) limitations for almost all index operations except vertex-shader uniforms
+void TParseContext::handleIndexLimits(const TSourceLoc& /*loc*/, TIntermTyped* base, TIntermTyped* index)
+{
+ if ((! limits.generalSamplerIndexing && base->getBasicType() == EbtSampler) ||
+ (! limits.generalUniformIndexing && base->getQualifier().isUniformOrBuffer() && language != EShLangVertex) ||
+ (! limits.generalAttributeMatrixVectorIndexing && base->getQualifier().isPipeInput() && language == EShLangVertex && (base->getType().isMatrix() || base->getType().isVector())) ||
+ (! limits.generalConstantMatrixVectorIndexing && base->getAsConstantUnion()) ||
+ (! limits.generalVariableIndexing && ! base->getType().getQualifier().isUniformOrBuffer() &&
+ ! base->getType().getQualifier().isPipeInput() &&
+ ! base->getType().getQualifier().isPipeOutput() &&
+ ! base->getType().getQualifier().isConstant()) ||
+ (! limits.generalVaryingIndexing && (base->getType().getQualifier().isPipeInput() ||
+ base->getType().getQualifier().isPipeOutput()))) {
+ // it's too early to know what the inductive variables are, save it for post processing
+ needsIndexLimitationChecking.push_back(index);
+ }
+}
+
+// Make a shared symbol have a non-shared version that can be edited by the current
+// compile, such that editing its type will not change the shared version and will
+// effect all nodes sharing it.
+void TParseContext::makeEditable(TSymbol*& symbol)
+{
+ TParseContextBase::makeEditable(symbol);
+
+ // See if it's tied to IO resizing
+ if (isIoResizeArray(symbol->getType()))
+ ioArraySymbolResizeList.push_back(symbol);
+}
+
+// Return true if this is a geometry shader input array or tessellation control output array
+// or mesh shader output array.
+bool TParseContext::isIoResizeArray(const TType& type) const
+{
+ return type.isArray() &&
+ ((language == EShLangGeometry && type.getQualifier().storage == EvqVaryingIn) ||
+ (language == EShLangTessControl && type.getQualifier().storage == EvqVaryingOut && ! type.getQualifier().patch)
+#ifdef NV_EXTENSIONS
+ ||
+ (language == EShLangFragment && type.getQualifier().storage == EvqVaryingIn && type.getQualifier().pervertexNV) ||
+ (language == EShLangMeshNV && type.getQualifier().storage == EvqVaryingOut && !type.getQualifier().perTaskNV)
+
+#endif
+ );
+}
+
+// If an array is not isIoResizeArray() but is an io array, make sure it has the right size
+void TParseContext::fixIoArraySize(const TSourceLoc& loc, TType& type)
+{
+ if (! type.isArray() || type.getQualifier().patch || symbolTable.atBuiltInLevel())
+ return;
+
+ assert(! isIoResizeArray(type));
+
+ if (type.getQualifier().storage != EvqVaryingIn || type.getQualifier().patch)
+ return;
+
+ if (language == EShLangTessControl || language == EShLangTessEvaluation) {
+ if (type.getOuterArraySize() != resources.maxPatchVertices) {
+ if (type.isSizedArray())
+ error(loc, "tessellation input array size must be gl_MaxPatchVertices or implicitly sized", "[]", "");
+ type.changeOuterArraySize(resources.maxPatchVertices);
+ }
+ }
+}
+
+// Issue any errors if the non-array object is missing arrayness WRT
+// shader I/O that has array requirements.
+// All arrayness checking is handled in array paths, this is for
+void TParseContext::ioArrayCheck(const TSourceLoc& loc, const TType& type, const TString& identifier)
+{
+ if (! type.isArray() && ! symbolTable.atBuiltInLevel()) {
+ if (type.getQualifier().isArrayedIo(language)
+#ifdef NV_EXTENSIONS
+ && !type.getQualifier().layoutPassthrough
+#endif
+ )
+ error(loc, "type must be an array:", type.getStorageQualifierString(), identifier.c_str());
+ }
+}
+
+// Handle a dereference of a geometry shader input array or tessellation control output array.
+// See ioArraySymbolResizeList comment in ParseHelper.h.
+//
+void TParseContext::handleIoResizeArrayAccess(const TSourceLoc& /*loc*/, TIntermTyped* base)
+{
+ TIntermSymbol* symbolNode = base->getAsSymbolNode();
+ assert(symbolNode);
+ if (! symbolNode)
+ return;
+
+ // fix array size, if it can be fixed and needs to be fixed (will allow variable indexing)
+ if (symbolNode->getType().isUnsizedArray()) {
+ int newSize = getIoArrayImplicitSize(symbolNode->getType().getQualifier());
+ if (newSize > 0)
+ symbolNode->getWritableType().changeOuterArraySize(newSize);
+ }
+}
+
+// If there has been an input primitive declaration (geometry shader) or an output
+// number of vertices declaration(tessellation shader), make sure all input array types
+// match it in size. Types come either from nodes in the AST or symbols in the
+// symbol table.
+//
+// Types without an array size will be given one.
+// Types already having a size that is wrong will get an error.
+//
+void TParseContext::checkIoArraysConsistency(const TSourceLoc &loc, bool tailOnly)
+{
+ int requiredSize = 0;
+ TString featureString;
+ size_t listSize = ioArraySymbolResizeList.size();
+ size_t i = 0;
+
+ // If tailOnly = true, only check the last array symbol in the list.
+ if (tailOnly) {
+ i = listSize - 1;
+ }
+ for (bool firstIteration = true; i < listSize; ++i) {
+ TType &type = ioArraySymbolResizeList[i]->getWritableType();
+
+ // As I/O array sizes don't change, fetch requiredSize only once,
+ // except for mesh shaders which could have different I/O array sizes based on type qualifiers.
+ if (firstIteration
+#ifdef NV_EXTENSIONS
+ || (language == EShLangMeshNV)
+#endif
+ )
+ {
+ requiredSize = getIoArrayImplicitSize(type.getQualifier(), &featureString);
+ if (requiredSize == 0)
+ break;
+ firstIteration = false;
+ }
+
+ checkIoArrayConsistency(loc, requiredSize, featureString.c_str(), type,
+ ioArraySymbolResizeList[i]->getName());
+ }
+}
+
+int TParseContext::getIoArrayImplicitSize(const TQualifier &qualifier, TString *featureString) const
+{
+ int expectedSize = 0;
+ TString str = "unknown";
+ unsigned int maxVertices = intermediate.getVertices() != TQualifier::layoutNotSet ? intermediate.getVertices() : 0;
+
+ if (language == EShLangGeometry) {
+ expectedSize = TQualifier::mapGeometryToSize(intermediate.getInputPrimitive());
+ str = TQualifier::getGeometryString(intermediate.getInputPrimitive());
+ }
+ else if (language == EShLangTessControl) {
+ expectedSize = maxVertices;
+ str = "vertices";
+ }
+#ifdef NV_EXTENSIONS
+ else if (language == EShLangFragment) {
+ // Number of vertices for Fragment shader is always three.
+ expectedSize = 3;
+ str = "vertices";
+ }
+ else if (language == EShLangMeshNV) {
+ unsigned int maxPrimitives =
+ intermediate.getPrimitives() != TQualifier::layoutNotSet ? intermediate.getPrimitives() : 0;
+ if (qualifier.builtIn == EbvPrimitiveIndicesNV) {
+ expectedSize = maxPrimitives * TQualifier::mapGeometryToSize(intermediate.getOutputPrimitive());
+ str = "max_primitives*";
+ str += TQualifier::getGeometryString(intermediate.getOutputPrimitive());
+ }
+ else if (qualifier.isPerPrimitive()) {
+ expectedSize = maxPrimitives;
+ str = "max_primitives";
+ }
+ else {
+ expectedSize = maxVertices;
+ str = "max_vertices";
+ }
+ }
+#endif
+ if (featureString)
+ *featureString = str;
+ return expectedSize;
+}
+
+void TParseContext::checkIoArrayConsistency(const TSourceLoc& loc, int requiredSize, const char* feature, TType& type, const TString& name)
+{
+ if (type.isUnsizedArray())
+ type.changeOuterArraySize(requiredSize);
+ else if (type.getOuterArraySize() != requiredSize) {
+ if (language == EShLangGeometry)
+ error(loc, "inconsistent input primitive for array size of", feature, name.c_str());
+ else if (language == EShLangTessControl)
+ error(loc, "inconsistent output number of vertices for array size of", feature, name.c_str());
+#ifdef NV_EXTENSIONS
+ else if (language == EShLangFragment) {
+ if (type.getOuterArraySize() > requiredSize)
+ error(loc, " cannot be greater than 3 for pervertexNV", feature, name.c_str());
+ }
+ else if (language == EShLangMeshNV)
+ error(loc, "inconsistent output array size of", feature, name.c_str());
+#endif
+ else
+ assert(0);
+ }
+}
+
+// Handle seeing a binary node with a math operation.
+// Returns nullptr if not semantically allowed.
+TIntermTyped* TParseContext::handleBinaryMath(const TSourceLoc& loc, const char* str, TOperator op, TIntermTyped* left, TIntermTyped* right)
+{
+ rValueErrorCheck(loc, str, left->getAsTyped());
+ rValueErrorCheck(loc, str, right->getAsTyped());
+
+ bool allowed = true;
+ switch (op) {
+ // TODO: Bring more source language-specific checks up from intermediate.cpp
+ // to the specific parse helpers for that source language.
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+ if (! left->isScalar() || ! right->isScalar())
+ allowed = false;
+ break;
+ default:
+ break;
+ }
+
+ if (((left->getType().containsBasicType(EbtFloat16) || right->getType().containsBasicType(EbtFloat16)) && !float16Arithmetic()) ||
+ ((left->getType().contains16BitInt() || right->getType().contains16BitInt()) && !int16Arithmetic()) ||
+ ((left->getType().contains8BitInt() || right->getType().contains8BitInt()) && !int8Arithmetic())) {
+ allowed = false;
+ }
+
+ TIntermTyped* result = nullptr;
+ if (allowed)
+ result = intermediate.addBinaryMath(op, left, right, loc);
+
+ if (result == nullptr)
+ binaryOpError(loc, str, left->getCompleteString(), right->getCompleteString());
+
+ return result;
+}
+
+// Handle seeing a unary node with a math operation.
+TIntermTyped* TParseContext::handleUnaryMath(const TSourceLoc& loc, const char* str, TOperator op, TIntermTyped* childNode)
+{
+ rValueErrorCheck(loc, str, childNode);
+
+ bool allowed = true;
+ if ((childNode->getType().containsBasicType(EbtFloat16) && !float16Arithmetic()) ||
+ (childNode->getType().contains16BitInt() && !int16Arithmetic()) ||
+ (childNode->getType().contains8BitInt() && !int8Arithmetic())) {
+ allowed = false;
+ }
+
+ TIntermTyped* result = nullptr;
+
+ if (allowed)
+ result = intermediate.addUnaryMath(op, childNode, loc);
+
+ if (result)
+ return result;
+ else
+ unaryOpError(loc, str, childNode->getCompleteString());
+
+ return childNode;
+}
+
+//
+// Handle seeing a base.field dereference in the grammar.
+//
+TIntermTyped* TParseContext::handleDotDereference(const TSourceLoc& loc, TIntermTyped* base, const TString& field)
+{
+ variableCheck(base);
+
+ //
+ // .length() can't be resolved until we later see the function-calling syntax.
+ // Save away the name in the AST for now. Processing is completed in
+ // handleLengthMethod().
+ //
+ if (field == "length") {
+ if (base->isArray()) {
+ profileRequires(loc, ENoProfile, 120, E_GL_3DL_array_objects, ".length");
+ profileRequires(loc, EEsProfile, 300, nullptr, ".length");
+ } else if (base->isVector() || base->isMatrix()) {
+ const char* feature = ".length() on vectors and matrices";
+ requireProfile(loc, ~EEsProfile, feature);
+ profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, feature);
+ } else if (!base->getType().isCoopMat()) {
+ error(loc, "does not operate on this type:", field.c_str(), base->getType().getCompleteString().c_str());
+
+ return base;
+ }
+
+ return intermediate.addMethod(base, TType(EbtInt), &field, loc);
+ }
+
+ // It's not .length() if we get to here.
+
+ if (base->isArray()) {
+ error(loc, "cannot apply to an array:", ".", field.c_str());
+
+ return base;
+ }
+
+ if (base->getType().isCoopMat()) {
+ error(loc, "cannot apply to a cooperative matrix type:", ".", field.c_str());
+ return base;
+ }
+
+ // It's neither an array nor .length() if we get here,
+ // leaving swizzles and struct/block dereferences.
+
+ TIntermTyped* result = base;
+ if ((base->isVector() || base->isScalar()) &&
+ (base->isFloatingDomain() || base->isIntegerDomain() || base->getBasicType() == EbtBool)) {
+ if (base->isScalar()) {
+ const char* dotFeature = "scalar swizzle";
+ requireProfile(loc, ~EEsProfile, dotFeature);
+ profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, dotFeature);
+ }
+
+ TSwizzleSelectors<TVectorSelector> selectors;
+ parseSwizzleSelector(loc, field, base->getVectorSize(), selectors);
+
+ if (base->isVector() && selectors.size() != 1 && base->getType().containsBasicType(EbtFloat16))
+ requireFloat16Arithmetic(loc, ".", "can't swizzle types containing float16");
+ if (base->isVector() && selectors.size() != 1 && base->getType().contains16BitInt())
+ requireInt16Arithmetic(loc, ".", "can't swizzle types containing (u)int16");
+ if (base->isVector() && selectors.size() != 1 && base->getType().contains8BitInt())
+ requireInt8Arithmetic(loc, ".", "can't swizzle types containing (u)int8");
+
+ if (base->isScalar()) {
+ if (selectors.size() == 1)
+ return result;
+ else {
+ TType type(base->getBasicType(), EvqTemporary, selectors.size());
+ // Swizzle operations propagate specialization-constantness
+ if (base->getQualifier().isSpecConstant())
+ type.getQualifier().makeSpecConstant();
+ return addConstructor(loc, base, type);
+ }
+ }
+
+ if (base->getType().getQualifier().isFrontEndConstant())
+ result = intermediate.foldSwizzle(base, selectors, loc);
+ else {
+ if (selectors.size() == 1) {
+ TIntermTyped* index = intermediate.addConstantUnion(selectors[0], loc);
+ result = intermediate.addIndex(EOpIndexDirect, base, index, loc);
+ result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision));
+ } else {
+ TIntermTyped* index = intermediate.addSwizzle(selectors, loc);
+ result = intermediate.addIndex(EOpVectorSwizzle, base, index, loc);
+ result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision, selectors.size()));
+ }
+ // Swizzle operations propagate specialization-constantness
+ if (base->getType().getQualifier().isSpecConstant())
+ result->getWritableType().getQualifier().makeSpecConstant();
+ }
+ } else if (base->getBasicType() == EbtStruct ||
+ base->getBasicType() == EbtBlock ||
+ base->getBasicType() == EbtReference) {
+ const TTypeList* fields = base->getBasicType() == EbtReference ?
+ base->getType().getReferentType()->getStruct() :
+ base->getType().getStruct();
+ bool fieldFound = false;
+ int member;
+ for (member = 0; member < (int)fields->size(); ++member) {
+ if ((*fields)[member].type->getFieldName() == field) {
+ fieldFound = true;
+ break;
+ }
+ }
+ if (fieldFound) {
+ if (base->getType().getQualifier().isFrontEndConstant())
+ result = intermediate.foldDereference(base, member, loc);
+ else {
+ blockMemberExtensionCheck(loc, base, member, field);
+ TIntermTyped* index = intermediate.addConstantUnion(member, loc);
+ result = intermediate.addIndex(EOpIndexDirectStruct, base, index, loc);
+ result->setType(*(*fields)[member].type);
+ if ((*fields)[member].type->getQualifier().isIo())
+ intermediate.addIoAccessed(field);
+ }
+ } else
+ error(loc, "no such field in structure", field.c_str(), "");
+ } else
+ error(loc, "does not apply to this type:", field.c_str(), base->getType().getCompleteString().c_str());
+
+ // Propagate noContraction up the dereference chain
+ if (base->getQualifier().noContraction)
+ result->getWritableType().getQualifier().noContraction = true;
+
+ // Propagate nonuniform
+ if (base->getQualifier().isNonUniform())
+ result->getWritableType().getQualifier().nonUniform = true;
+
+ return result;
+}
+
+void TParseContext::blockMemberExtensionCheck(const TSourceLoc& loc, const TIntermTyped* base, int member, const TString& memberName)
+{
+ // a block that needs extension checking is either 'base', or if arrayed,
+ // one level removed to the left
+ const TIntermSymbol* baseSymbol = nullptr;
+ if (base->getAsBinaryNode() == nullptr)
+ baseSymbol = base->getAsSymbolNode();
+ else
+ baseSymbol = base->getAsBinaryNode()->getLeft()->getAsSymbolNode();
+ if (baseSymbol == nullptr)
+ return;
+ const TSymbol* symbol = symbolTable.find(baseSymbol->getName());
+ if (symbol == nullptr)
+ return;
+ const TVariable* variable = symbol->getAsVariable();
+ if (variable == nullptr)
+ return;
+ if (!variable->hasMemberExtensions())
+ return;
+
+ // We now have a variable that is the base of a dot reference
+ // with members that need extension checking.
+ if (variable->getNumMemberExtensions(member) > 0)
+ requireExtensions(loc, variable->getNumMemberExtensions(member), variable->getMemberExtensions(member), memberName.c_str());
+}
+
+//
+// Handle seeing a function declarator in the grammar. This is the precursor
+// to recognizing a function prototype or function definition.
+//
+TFunction* TParseContext::handleFunctionDeclarator(const TSourceLoc& loc, TFunction& function, bool prototype)
+{
+ // ES can't declare prototypes inside functions
+ if (! symbolTable.atGlobalLevel())
+ requireProfile(loc, ~EEsProfile, "local function declaration");
+
+ //
+ // Multiple declarations of the same function name are allowed.
+ //
+ // If this is a definition, the definition production code will check for redefinitions
+ // (we don't know at this point if it's a definition or not).
+ //
+ // Redeclarations (full signature match) are allowed. But, return types and parameter qualifiers must also match.
+ // - except ES 100, which only allows a single prototype
+ //
+ // ES 100 does not allow redefining, but does allow overloading of built-in functions.
+ // ES 300 does not allow redefining or overloading of built-in functions.
+ //
+ bool builtIn;
+ TSymbol* symbol = symbolTable.find(function.getMangledName(), &builtIn);
+ if (symbol && symbol->getAsFunction() && builtIn)
+ requireProfile(loc, ~EEsProfile, "redefinition of built-in function");
+ const TFunction* prevDec = symbol ? symbol->getAsFunction() : 0;
+ if (prevDec) {
+ if (prevDec->isPrototyped() && prototype)
+ profileRequires(loc, EEsProfile, 300, nullptr, "multiple prototypes for same function");
+ if (prevDec->getType() != function.getType())
+ error(loc, "overloaded functions must have the same return type", function.getName().c_str(), "");
+ for (int i = 0; i < prevDec->getParamCount(); ++i) {
+ if ((*prevDec)[i].type->getQualifier().storage != function[i].type->getQualifier().storage)
+ error(loc, "overloaded functions must have the same parameter storage qualifiers for argument", function[i].type->getStorageQualifierString(), "%d", i+1);
+
+ if ((*prevDec)[i].type->getQualifier().precision != function[i].type->getQualifier().precision)
+ error(loc, "overloaded functions must have the same parameter precision qualifiers for argument", function[i].type->getPrecisionQualifierString(), "%d", i+1);
+ }
+ }
+
+ arrayObjectCheck(loc, function.getType(), "array in function return type");
+
+ if (prototype) {
+ // All built-in functions are defined, even though they don't have a body.
+ // Count their prototype as a definition instead.
+ if (symbolTable.atBuiltInLevel())
+ function.setDefined();
+ else {
+ if (prevDec && ! builtIn)
+ symbol->getAsFunction()->setPrototyped(); // need a writable one, but like having prevDec as a const
+ function.setPrototyped();
+ }
+ }
+
+ // This insert won't actually insert it if it's a duplicate signature, but it will still check for
+ // other forms of name collisions.
+ if (! symbolTable.insert(function))
+ error(loc, "function name is redeclaration of existing name", function.getName().c_str(), "");
+
+ //
+ // If this is a redeclaration, it could also be a definition,
+ // in which case, we need to use the parameter names from this one, and not the one that's
+ // being redeclared. So, pass back this declaration, not the one in the symbol table.
+ //
+ return &function;
+}
+
+//
+// Handle seeing the function prototype in front of a function definition in the grammar.
+// The body is handled after this function returns.
+//
+TIntermAggregate* TParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function)
+{
+ currentCaller = function.getMangledName();
+ TSymbol* symbol = symbolTable.find(function.getMangledName());
+ TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr;
+
+ if (! prevDec)
+ error(loc, "can't find function", function.getName().c_str(), "");
+ // Note: 'prevDec' could be 'function' if this is the first time we've seen function
+ // as it would have just been put in the symbol table. Otherwise, we're looking up
+ // an earlier occurrence.
+
+ if (prevDec && prevDec->isDefined()) {
+ // Then this function already has a body.
+ error(loc, "function already has a body", function.getName().c_str(), "");
+ }
+ if (prevDec && ! prevDec->isDefined()) {
+ prevDec->setDefined();
+
+ // Remember the return type for later checking for RETURN statements.
+ currentFunctionType = &(prevDec->getType());
+ } else
+ currentFunctionType = new TType(EbtVoid);
+ functionReturnsValue = false;
+
+ // Check for entry point
+ if (function.getName().compare(intermediate.getEntryPointName().c_str()) == 0) {
+ intermediate.setEntryPointMangledName(function.getMangledName().c_str());
+ intermediate.incrementEntryPointCount();
+ inMain = true;
+ } else
+ inMain = false;
+
+ //
+ // Raise error message if main function takes any parameters or returns anything other than void
+ //
+ if (inMain) {
+ if (function.getParamCount() > 0)
+ error(loc, "function cannot take any parameter(s)", function.getName().c_str(), "");
+ if (function.getType().getBasicType() != EbtVoid)
+ error(loc, "", function.getType().getBasicTypeString().c_str(), "entry point cannot return a value");
+ }
+
+ //
+ // New symbol table scope for body of function plus its arguments
+ //
+ symbolTable.push();
+
+ //
+ // Insert parameters into the symbol table.
+ // If the parameter has no name, it's not an error, just don't insert it
+ // (could be used for unused args).
+ //
+ // Also, accumulate the list of parameters into the HIL, so lower level code
+ // knows where to find parameters.
+ //
+ TIntermAggregate* paramNodes = new TIntermAggregate;
+ for (int i = 0; i < function.getParamCount(); i++) {
+ TParameter& param = function[i];
+ if (param.name != nullptr) {
+ TVariable *variable = new TVariable(param.name, *param.type);
+
+ // Insert the parameters with name in the symbol table.
+ if (! symbolTable.insert(*variable))
+ error(loc, "redefinition", variable->getName().c_str(), "");
+ else {
+ // Transfer ownership of name pointer to symbol table.
+ param.name = nullptr;
+
+ // Add the parameter to the HIL
+ paramNodes = intermediate.growAggregate(paramNodes,
+ intermediate.addSymbol(*variable, loc),
+ loc);
+ }
+ } else
+ paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(*param.type, loc), loc);
+ }
+ intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), loc);
+ loopNestingLevel = 0;
+ statementNestingLevel = 0;
+ controlFlowNestingLevel = 0;
+ postEntryPointReturn = false;
+
+ return paramNodes;
+}
+
+//
+// Handle seeing function call syntax in the grammar, which could be any of
+// - .length() method
+// - constructor
+// - a call to a built-in function mapped to an operator
+// - a call to a built-in function that will remain a function call (e.g., texturing)
+// - user function
+// - subroutine call (not implemented yet)
+//
+TIntermTyped* TParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction* function, TIntermNode* arguments)
+{
+ TIntermTyped* result = nullptr;
+
+ if (function->getBuiltInOp() == EOpArrayLength)
+ result = handleLengthMethod(loc, function, arguments);
+ else if (function->getBuiltInOp() != EOpNull) {
+ //
+ // Then this should be a constructor.
+ // Don't go through the symbol table for constructors.
+ // Their parameters will be verified algorithmically.
+ //
+ TType type(EbtVoid); // use this to get the type back
+ if (! constructorError(loc, arguments, *function, function->getBuiltInOp(), type)) {
+ //
+ // It's a constructor, of type 'type'.
+ //
+ result = addConstructor(loc, arguments, type);
+ if (result == nullptr)
+ error(loc, "cannot construct with these arguments", type.getCompleteString().c_str(), "");
+ }
+ } else {
+ //
+ // Find it in the symbol table.
+ //
+ const TFunction* fnCandidate;
+ bool builtIn;
+ fnCandidate = findFunction(loc, *function, builtIn);
+ if (fnCandidate) {
+ // This is a declared function that might map to
+ // - a built-in operator,
+ // - a built-in function not mapped to an operator, or
+ // - a user function.
+
+ // Error check for a function requiring specific extensions present.
+ if (builtIn && fnCandidate->getNumExtensions())
+ requireExtensions(loc, fnCandidate->getNumExtensions(), fnCandidate->getExtensions(), fnCandidate->getName().c_str());
+
+ if (builtIn && fnCandidate->getType().containsBasicType(EbtFloat16))
+ requireFloat16Arithmetic(loc, "built-in function", "float16 types can only be in uniform block or buffer storage");
+ if (builtIn && fnCandidate->getType().contains16BitInt())
+ requireInt16Arithmetic(loc, "built-in function", "(u)int16 types can only be in uniform block or buffer storage");
+ if (builtIn && fnCandidate->getType().contains8BitInt())
+ requireInt8Arithmetic(loc, "built-in function", "(u)int8 types can only be in uniform block or buffer storage");
+
+ if (arguments != nullptr) {
+ // Make sure qualifications work for these arguments.
+ TIntermAggregate* aggregate = arguments->getAsAggregate();
+ for (int i = 0; i < fnCandidate->getParamCount(); ++i) {
+ // At this early point there is a slight ambiguity between whether an aggregate 'arguments'
+ // is the single argument itself or its children are the arguments. Only one argument
+ // means take 'arguments' itself as the one argument.
+ TIntermNode* arg = fnCandidate->getParamCount() == 1 ? arguments : (aggregate ? aggregate->getSequence()[i] : arguments);
+ TQualifier& formalQualifier = (*fnCandidate)[i].type->getQualifier();
+ if (formalQualifier.isParamOutput()) {
+ if (lValueErrorCheck(arguments->getLoc(), "assign", arg->getAsTyped()))
+ error(arguments->getLoc(), "Non-L-value cannot be passed for 'out' or 'inout' parameters.", "out", "");
+ }
+ TQualifier& argQualifier = arg->getAsTyped()->getQualifier();
+ if (argQualifier.isMemory()) {
+ const char* message = "argument cannot drop memory qualifier when passed to formal parameter";
+ if (argQualifier.volatil && ! formalQualifier.volatil)
+ error(arguments->getLoc(), message, "volatile", "");
+ if (argQualifier.coherent && ! (formalQualifier.devicecoherent || formalQualifier.coherent))
+ error(arguments->getLoc(), message, "coherent", "");
+ if (argQualifier.devicecoherent && ! (formalQualifier.devicecoherent || formalQualifier.coherent))
+ error(arguments->getLoc(), message, "devicecoherent", "");
+ if (argQualifier.queuefamilycoherent && ! (formalQualifier.queuefamilycoherent || formalQualifier.devicecoherent || formalQualifier.coherent))
+ error(arguments->getLoc(), message, "queuefamilycoherent", "");
+ if (argQualifier.workgroupcoherent && ! (formalQualifier.workgroupcoherent || formalQualifier.queuefamilycoherent || formalQualifier.devicecoherent || formalQualifier.coherent))
+ error(arguments->getLoc(), message, "workgroupcoherent", "");
+ if (argQualifier.subgroupcoherent && ! (formalQualifier.subgroupcoherent || formalQualifier.workgroupcoherent || formalQualifier.queuefamilycoherent || formalQualifier.devicecoherent || formalQualifier.coherent))
+ error(arguments->getLoc(), message, "subgroupcoherent", "");
+ if (argQualifier.readonly && ! formalQualifier.readonly)
+ error(arguments->getLoc(), message, "readonly", "");
+ if (argQualifier.writeonly && ! formalQualifier.writeonly)
+ error(arguments->getLoc(), message, "writeonly", "");
+ }
+
+ if (builtIn && arg->getAsTyped()->getType().containsBasicType(EbtFloat16))
+ requireFloat16Arithmetic(arguments->getLoc(), "built-in function", "float16 types can only be in uniform block or buffer storage");
+ if (builtIn && arg->getAsTyped()->getType().contains16BitInt())
+ requireInt16Arithmetic(arguments->getLoc(), "built-in function", "(u)int16 types can only be in uniform block or buffer storage");
+ if (builtIn && arg->getAsTyped()->getType().contains8BitInt())
+ requireInt8Arithmetic(arguments->getLoc(), "built-in function", "(u)int8 types can only be in uniform block or buffer storage");
+
+ // TODO 4.5 functionality: A shader will fail to compile
+ // if the value passed to the memargument of an atomic memory function does not correspond to a buffer or
+ // shared variable. It is acceptable to pass an element of an array or a single component of a vector to the
+ // memargument of an atomic memory function, as long as the underlying array or vector is a buffer or
+ // shared variable.
+ }
+
+ // Convert 'in' arguments
+ addInputArgumentConversions(*fnCandidate, arguments); // arguments may be modified if it's just a single argument node
+ }
+
+ if (builtIn && fnCandidate->getBuiltInOp() != EOpNull) {
+ // A function call mapped to a built-in operation.
+ result = handleBuiltInFunctionCall(loc, arguments, *fnCandidate);
+ } else {
+ // This is a function call not mapped to built-in operator.
+ // It could still be a built-in function, but only if PureOperatorBuiltins == false.
+ result = intermediate.setAggregateOperator(arguments, EOpFunctionCall, fnCandidate->getType(), loc);
+ TIntermAggregate* call = result->getAsAggregate();
+ call->setName(fnCandidate->getMangledName());
+
+ // this is how we know whether the given function is a built-in function or a user-defined function
+ // if builtIn == false, it's a userDefined -> could be an overloaded built-in function also
+ // if builtIn == true, it's definitely a built-in function with EOpNull
+ if (! builtIn) {
+ call->setUserDefined();
+ if (symbolTable.atGlobalLevel()) {
+ requireProfile(loc, ~EEsProfile, "calling user function from global scope");
+ intermediate.addToCallGraph(infoSink, "main(", fnCandidate->getMangledName());
+ } else
+ intermediate.addToCallGraph(infoSink, currentCaller, fnCandidate->getMangledName());
+ }
+
+ if (builtIn)
+ nonOpBuiltInCheck(loc, *fnCandidate, *call);
+ else
+ userFunctionCallCheck(loc, *call);
+ }
+
+ // Convert 'out' arguments. If it was a constant folded built-in, it won't be an aggregate anymore.
+ // Built-ins with a single argument aren't called with an aggregate, but they also don't have an output.
+ // Also, build the qualifier list for user function calls, which are always called with an aggregate.
+ if (result->getAsAggregate()) {
+ TQualifierList& qualifierList = result->getAsAggregate()->getQualifierList();
+ for (int i = 0; i < fnCandidate->getParamCount(); ++i) {
+ TStorageQualifier qual = (*fnCandidate)[i].type->getQualifier().storage;
+ qualifierList.push_back(qual);
+ }
+ result = addOutputArgumentConversions(*fnCandidate, *result->getAsAggregate());
+ }
+
+ if (result->getAsTyped()->getType().isCoopMat() &&
+ !result->getAsTyped()->getType().isParameterized()) {
+ assert(fnCandidate->getBuiltInOp() == EOpCooperativeMatrixMulAdd);
+
+ result->setType(result->getAsAggregate()->getSequence()[2]->getAsTyped()->getType());
+ }
+ }
+ }
+
+ // generic error recovery
+ // TODO: simplification: localize all the error recoveries that look like this, and taking type into account to reduce cascades
+ if (result == nullptr)
+ result = intermediate.addConstantUnion(0.0, EbtFloat, loc);
+
+ return result;
+}
+
+TIntermTyped* TParseContext::handleBuiltInFunctionCall(TSourceLoc loc, TIntermNode* arguments,
+ const TFunction& function)
+{
+ checkLocation(loc, function.getBuiltInOp());
+ TIntermTyped *result = intermediate.addBuiltInFunctionCall(loc, function.getBuiltInOp(),
+ function.getParamCount() == 1,
+ arguments, function.getType());
+ if (obeyPrecisionQualifiers())
+ computeBuiltinPrecisions(*result, function);
+
+ if (result == nullptr) {
+ if (arguments == nullptr)
+ error(loc, " wrong operand type", "Internal Error",
+ "built in unary operator function. Type: %s", "");
+ else
+ error(arguments->getLoc(), " wrong operand type", "Internal Error",
+ "built in unary operator function. Type: %s",
+ static_cast<TIntermTyped*>(arguments)->getCompleteString().c_str());
+ } else if (result->getAsOperator())
+ builtInOpCheck(loc, function, *result->getAsOperator());
+
+ return result;
+}
+
+// "The operation of a built-in function can have a different precision
+// qualification than the precision qualification of the resulting value.
+// These two precision qualifications are established as follows.
+//
+// The precision qualification of the operation of a built-in function is
+// based on the precision qualification of its input arguments and formal
+// parameters: When a formal parameter specifies a precision qualifier,
+// that is used, otherwise, the precision qualification of the calling
+// argument is used. The highest precision of these will be the precision
+// qualification of the operation of the built-in function. Generally,
+// this is applied across all arguments to a built-in function, with the
+// exceptions being:
+// - bitfieldExtract and bitfieldInsert ignore the 'offset' and 'bits'
+// arguments.
+// - interpolateAt* functions only look at the 'interpolant' argument.
+//
+// The precision qualification of the result of a built-in function is
+// determined in one of the following ways:
+//
+// - For the texture sampling, image load, and image store functions,
+// the precision of the return type matches the precision of the
+// sampler type
+//
+// Otherwise:
+//
+// - For prototypes that do not specify a resulting precision qualifier,
+// the precision will be the same as the precision of the operation.
+//
+// - For prototypes that do specify a resulting precision qualifier,
+// the specified precision qualifier is the precision qualification of
+// the result."
+//
+void TParseContext::computeBuiltinPrecisions(TIntermTyped& node, const TFunction& function)
+{
+ TPrecisionQualifier operationPrecision = EpqNone;
+ TPrecisionQualifier resultPrecision = EpqNone;
+
+ TIntermOperator* opNode = node.getAsOperator();
+ if (opNode == nullptr)
+ return;
+
+ if (TIntermUnary* unaryNode = node.getAsUnaryNode()) {
+ operationPrecision = std::max(function[0].type->getQualifier().precision,
+ unaryNode->getOperand()->getType().getQualifier().precision);
+ if (function.getType().getBasicType() != EbtBool)
+ resultPrecision = function.getType().getQualifier().precision == EpqNone ?
+ operationPrecision :
+ function.getType().getQualifier().precision;
+ } else if (TIntermAggregate* agg = node.getAsAggregate()) {
+ TIntermSequence& sequence = agg->getSequence();
+ unsigned int numArgs = (unsigned int)sequence.size();
+ switch (agg->getOp()) {
+ case EOpBitfieldExtract:
+ numArgs = 1;
+ break;
+ case EOpBitfieldInsert:
+ numArgs = 2;
+ break;
+ case EOpInterpolateAtCentroid:
+ case EOpInterpolateAtOffset:
+ case EOpInterpolateAtSample:
+ numArgs = 1;
+ break;
+ default:
+ break;
+ }
+ // find the maximum precision from the arguments and parameters
+ for (unsigned int arg = 0; arg < numArgs; ++arg) {
+ operationPrecision = std::max(operationPrecision, sequence[arg]->getAsTyped()->getQualifier().precision);
+ operationPrecision = std::max(operationPrecision, function[arg].type->getQualifier().precision);
+ }
+ // compute the result precision
+#ifdef AMD_EXTENSIONS
+ if (agg->isSampling() ||
+ agg->getOp() == EOpImageLoad || agg->getOp() == EOpImageStore ||
+ agg->getOp() == EOpImageLoadLod || agg->getOp() == EOpImageStoreLod)
+#else
+ if (agg->isSampling() || agg->getOp() == EOpImageLoad || agg->getOp() == EOpImageStore)
+#endif
+ resultPrecision = sequence[0]->getAsTyped()->getQualifier().precision;
+ else if (function.getType().getBasicType() != EbtBool)
+ resultPrecision = function.getType().getQualifier().precision == EpqNone ?
+ operationPrecision :
+ function.getType().getQualifier().precision;
+ }
+
+ // Propagate precision through this node and its children. That algorithm stops
+ // when a precision is found, so start by clearing this subroot precision
+ opNode->getQualifier().precision = EpqNone;
+ if (operationPrecision != EpqNone) {
+ opNode->propagatePrecision(operationPrecision);
+ opNode->setOperationPrecision(operationPrecision);
+ }
+ // Now, set the result precision, which might not match
+ opNode->getQualifier().precision = resultPrecision;
+}
+
+TIntermNode* TParseContext::handleReturnValue(const TSourceLoc& loc, TIntermTyped* value)
+{
+ storage16BitAssignmentCheck(loc, value->getType(), "return");
+
+ functionReturnsValue = true;
+ if (currentFunctionType->getBasicType() == EbtVoid) {
+ error(loc, "void function cannot return a value", "return", "");
+ return intermediate.addBranch(EOpReturn, loc);
+ } else if (*currentFunctionType != value->getType()) {
+ TIntermTyped* converted = intermediate.addConversion(EOpReturn, *currentFunctionType, value);
+ if (converted) {
+ if (*currentFunctionType != converted->getType())
+ error(loc, "cannot convert return value to function return type", "return", "");
+ if (version < 420)
+ warn(loc, "type conversion on return values was not explicitly allowed until version 420", "return", "");
+ return intermediate.addBranch(EOpReturn, converted, loc);
+ } else {
+ error(loc, "type does not match, or is not convertible to, the function's return type", "return", "");
+ return intermediate.addBranch(EOpReturn, value, loc);
+ }
+ } else
+ return intermediate.addBranch(EOpReturn, value, loc);
+}
+
+// See if the operation is being done in an illegal location.
+void TParseContext::checkLocation(const TSourceLoc& loc, TOperator op)
+{
+ switch (op) {
+ case EOpBarrier:
+ if (language == EShLangTessControl) {
+ if (controlFlowNestingLevel > 0)
+ error(loc, "tessellation control barrier() cannot be placed within flow control", "", "");
+ if (! inMain)
+ error(loc, "tessellation control barrier() must be in main()", "", "");
+ else if (postEntryPointReturn)
+ error(loc, "tessellation control barrier() cannot be placed after a return from main()", "", "");
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+// Finish processing object.length(). This started earlier in handleDotDereference(), where
+// the ".length" part was recognized and semantically checked, and finished here where the
+// function syntax "()" is recognized.
+//
+// Return resulting tree node.
+TIntermTyped* TParseContext::handleLengthMethod(const TSourceLoc& loc, TFunction* function, TIntermNode* intermNode)
+{
+ int length = 0;
+
+ if (function->getParamCount() > 0)
+ error(loc, "method does not accept any arguments", function->getName().c_str(), "");
+ else {
+ const TType& type = intermNode->getAsTyped()->getType();
+ if (type.isArray()) {
+ if (type.isUnsizedArray()) {
+ if (intermNode->getAsSymbolNode() && isIoResizeArray(type)) {
+ // We could be between a layout declaration that gives a built-in io array implicit size and
+ // a user redeclaration of that array, meaning we have to substitute its implicit size here
+ // without actually redeclaring the array. (It is an error to use a member before the
+ // redeclaration, but not an error to use the array name itself.)
+ const TString& name = intermNode->getAsSymbolNode()->getName();
+ if (name == "gl_in" || name == "gl_out"
+#ifdef NV_EXTENSIONS
+ || name == "gl_MeshVerticesNV"
+ || name == "gl_MeshPrimitivesNV"
+#endif
+ )
+ {
+ length = getIoArrayImplicitSize(type.getQualifier());
+ }
+ }
+ if (length == 0) {
+ if (intermNode->getAsSymbolNode() && isIoResizeArray(type))
+ error(loc, "", function->getName().c_str(), "array must first be sized by a redeclaration or layout qualifier");
+ else if (isRuntimeLength(*intermNode->getAsTyped())) {
+ // Create a unary op and let the back end handle it
+ return intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, intermNode, TType(EbtInt));
+ } else
+ error(loc, "", function->getName().c_str(), "array must be declared with a size before using this method");
+ }
+ } else if (type.getOuterArrayNode()) {
+ // If the array's outer size is specified by an intermediate node, it means the array's length
+ // was specified by a specialization constant. In such a case, we should return the node of the
+ // specialization constants to represent the length.
+ return type.getOuterArrayNode();
+ } else
+ length = type.getOuterArraySize();
+ } else if (type.isMatrix())
+ length = type.getMatrixCols();
+ else if (type.isVector())
+ length = type.getVectorSize();
+ else if (type.isCoopMat())
+ return intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, intermNode, TType(EbtInt));
+ else {
+ // we should not get here, because earlier semantic checking should have prevented this path
+ error(loc, ".length()", "unexpected use of .length()", "");
+ }
+ }
+
+ if (length == 0)
+ length = 1;
+
+ return intermediate.addConstantUnion(length, loc);
+}
+
+//
+// Add any needed implicit conversions for function-call arguments to input parameters.
+//
+void TParseContext::addInputArgumentConversions(const TFunction& function, TIntermNode*& arguments) const
+{
+ TIntermAggregate* aggregate = arguments->getAsAggregate();
+
+ // Process each argument's conversion
+ for (int i = 0; i < function.getParamCount(); ++i) {
+ // At this early point there is a slight ambiguity between whether an aggregate 'arguments'
+ // is the single argument itself or its children are the arguments. Only one argument
+ // means take 'arguments' itself as the one argument.
+ TIntermTyped* arg = function.getParamCount() == 1 ? arguments->getAsTyped() : (aggregate ? aggregate->getSequence()[i]->getAsTyped() : arguments->getAsTyped());
+ if (*function[i].type != arg->getType()) {
+ if (function[i].type->getQualifier().isParamInput() &&
+ !function[i].type->isCoopMat()) {
+ // In-qualified arguments just need an extra node added above the argument to
+ // convert to the correct type.
+ arg = intermediate.addConversion(EOpFunctionCall, *function[i].type, arg);
+ if (arg) {
+ if (function.getParamCount() == 1)
+ arguments = arg;
+ else {
+ if (aggregate)
+ aggregate->getSequence()[i] = arg;
+ else
+ arguments = arg;
+ }
+ }
+ }
+ }
+ }
+}
+
+//
+// Add any needed implicit output conversions for function-call arguments. This
+// can require a new tree topology, complicated further by whether the function
+// has a return value.
+//
+// Returns a node of a subtree that evaluates to the return value of the function.
+//
+TIntermTyped* TParseContext::addOutputArgumentConversions(const TFunction& function, TIntermAggregate& intermNode) const
+{
+ TIntermSequence& arguments = intermNode.getSequence();
+
+ // Will there be any output conversions?
+ bool outputConversions = false;
+ for (int i = 0; i < function.getParamCount(); ++i) {
+ if (*function[i].type != arguments[i]->getAsTyped()->getType() && function[i].type->getQualifier().isParamOutput()) {
+ outputConversions = true;
+ break;
+ }
+ }
+
+ if (! outputConversions)
+ return &intermNode;
+
+ // Setup for the new tree, if needed:
+ //
+ // Output conversions need a different tree topology.
+ // Out-qualified arguments need a temporary of the correct type, with the call
+ // followed by an assignment of the temporary to the original argument:
+ // void: function(arg, ...) -> ( function(tempArg, ...), arg = tempArg, ...)
+ // ret = function(arg, ...) -> ret = (tempRet = function(tempArg, ...), arg = tempArg, ..., tempRet)
+ // Where the "tempArg" type needs no conversion as an argument, but will convert on assignment.
+ TIntermTyped* conversionTree = nullptr;
+ TVariable* tempRet = nullptr;
+ if (intermNode.getBasicType() != EbtVoid) {
+ // do the "tempRet = function(...), " bit from above
+ tempRet = makeInternalVariable("tempReturn", intermNode.getType());
+ TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, intermNode.getLoc());
+ conversionTree = intermediate.addAssign(EOpAssign, tempRetNode, &intermNode, intermNode.getLoc());
+ } else
+ conversionTree = &intermNode;
+
+ conversionTree = intermediate.makeAggregate(conversionTree);
+
+ // Process each argument's conversion
+ for (int i = 0; i < function.getParamCount(); ++i) {
+ if (*function[i].type != arguments[i]->getAsTyped()->getType()) {
+ if (function[i].type->getQualifier().isParamOutput()) {
+ // Out-qualified arguments need to use the topology set up above.
+ // do the " ...(tempArg, ...), arg = tempArg" bit from above
+ TType paramType;
+ paramType.shallowCopy(*function[i].type);
+ if (arguments[i]->getAsTyped()->getType().isParameterized() &&
+ !paramType.isParameterized()) {
+ paramType.shallowCopy(arguments[i]->getAsTyped()->getType());
+ paramType.copyTypeParameters(*arguments[i]->getAsTyped()->getType().getTypeParameters());
+ }
+ TVariable* tempArg = makeInternalVariable("tempArg", paramType);
+ tempArg->getWritableType().getQualifier().makeTemporary();
+ TIntermSymbol* tempArgNode = intermediate.addSymbol(*tempArg, intermNode.getLoc());
+ TIntermTyped* tempAssign = intermediate.addAssign(EOpAssign, arguments[i]->getAsTyped(), tempArgNode, arguments[i]->getLoc());
+ conversionTree = intermediate.growAggregate(conversionTree, tempAssign, arguments[i]->getLoc());
+ // replace the argument with another node for the same tempArg variable
+ arguments[i] = intermediate.addSymbol(*tempArg, intermNode.getLoc());
+ }
+ }
+ }
+
+ // Finalize the tree topology (see bigger comment above).
+ if (tempRet) {
+ // do the "..., tempRet" bit from above
+ TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, intermNode.getLoc());
+ conversionTree = intermediate.growAggregate(conversionTree, tempRetNode, intermNode.getLoc());
+ }
+ conversionTree = intermediate.setAggregateOperator(conversionTree, EOpComma, intermNode.getType(), intermNode.getLoc());
+
+ return conversionTree;
+}
+
+void TParseContext::memorySemanticsCheck(const TSourceLoc& loc, const TFunction& fnCandidate, const TIntermOperator& callNode)
+{
+ const TIntermSequence* argp = &callNode.getAsAggregate()->getSequence();
+
+ //const int gl_SemanticsRelaxed = 0x0;
+ const int gl_SemanticsAcquire = 0x2;
+ const int gl_SemanticsRelease = 0x4;
+ const int gl_SemanticsAcquireRelease = 0x8;
+ const int gl_SemanticsMakeAvailable = 0x2000;
+ const int gl_SemanticsMakeVisible = 0x4000;
+
+ //const int gl_StorageSemanticsNone = 0x0;
+ const int gl_StorageSemanticsBuffer = 0x40;
+ const int gl_StorageSemanticsShared = 0x100;
+ const int gl_StorageSemanticsImage = 0x800;
+ const int gl_StorageSemanticsOutput = 0x1000;
+
+
+ unsigned int semantics = 0, storageClassSemantics = 0;
+ unsigned int semantics2 = 0, storageClassSemantics2 = 0;
+
+ // Grab the semantics and storage class semantics from the operands, based on opcode
+ switch (callNode.getOp()) {
+ case EOpAtomicAdd:
+ case EOpAtomicMin:
+ case EOpAtomicMax:
+ case EOpAtomicAnd:
+ case EOpAtomicOr:
+ case EOpAtomicXor:
+ case EOpAtomicExchange:
+ case EOpAtomicStore:
+ storageClassSemantics = (*argp)[3]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ semantics = (*argp)[4]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ break;
+ case EOpAtomicLoad:
+ storageClassSemantics = (*argp)[2]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ semantics = (*argp)[3]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ break;
+ case EOpAtomicCompSwap:
+ storageClassSemantics = (*argp)[4]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ semantics = (*argp)[5]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ storageClassSemantics2 = (*argp)[6]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ semantics2 = (*argp)[7]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ break;
+
+ case EOpImageAtomicAdd:
+ case EOpImageAtomicMin:
+ case EOpImageAtomicMax:
+ case EOpImageAtomicAnd:
+ case EOpImageAtomicOr:
+ case EOpImageAtomicXor:
+ case EOpImageAtomicExchange:
+ case EOpImageAtomicStore:
+ storageClassSemantics = (*argp)[4]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ semantics = (*argp)[5]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ break;
+ case EOpImageAtomicLoad:
+ storageClassSemantics = (*argp)[3]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ semantics = (*argp)[4]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ break;
+ case EOpImageAtomicCompSwap:
+ storageClassSemantics = (*argp)[5]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ semantics = (*argp)[6]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ storageClassSemantics2 = (*argp)[7]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ semantics2 = (*argp)[8]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ break;
+
+ case EOpBarrier:
+ storageClassSemantics = (*argp)[2]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ semantics = (*argp)[3]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ break;
+ case EOpMemoryBarrier:
+ storageClassSemantics = (*argp)[1]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ semantics = (*argp)[2]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ break;
+ default:
+ break;
+ }
+
+ if ((semantics & gl_SemanticsAcquire) &&
+ (callNode.getOp() == EOpAtomicStore || callNode.getOp() == EOpImageAtomicStore)) {
+ error(loc, "gl_SemanticsAcquire must not be used with (image) atomic store",
+ fnCandidate.getName().c_str(), "");
+ }
+ if ((semantics & gl_SemanticsRelease) &&
+ (callNode.getOp() == EOpAtomicLoad || callNode.getOp() == EOpImageAtomicLoad)) {
+ error(loc, "gl_SemanticsRelease must not be used with (image) atomic load",
+ fnCandidate.getName().c_str(), "");
+ }
+ if ((semantics & gl_SemanticsAcquireRelease) &&
+ (callNode.getOp() == EOpAtomicStore || callNode.getOp() == EOpImageAtomicStore ||
+ callNode.getOp() == EOpAtomicLoad || callNode.getOp() == EOpImageAtomicLoad)) {
+ error(loc, "gl_SemanticsAcquireRelease must not be used with (image) atomic load/store",
+ fnCandidate.getName().c_str(), "");
+ }
+ if (((semantics | semantics2) & ~(gl_SemanticsAcquire |
+ gl_SemanticsRelease |
+ gl_SemanticsAcquireRelease |
+ gl_SemanticsMakeAvailable |
+ gl_SemanticsMakeVisible))) {
+ error(loc, "Invalid semantics value", fnCandidate.getName().c_str(), "");
+ }
+ if (((storageClassSemantics | storageClassSemantics2) & ~(gl_StorageSemanticsBuffer |
+ gl_StorageSemanticsShared |
+ gl_StorageSemanticsImage |
+ gl_StorageSemanticsOutput))) {
+ error(loc, "Invalid storage class semantics value", fnCandidate.getName().c_str(), "");
+ }
+
+ if (callNode.getOp() == EOpMemoryBarrier) {
+ if (!IsPow2(semantics & (gl_SemanticsAcquire | gl_SemanticsRelease | gl_SemanticsAcquireRelease))) {
+ error(loc, "Semantics must include exactly one of gl_SemanticsRelease, gl_SemanticsAcquire, or "
+ "gl_SemanticsAcquireRelease", fnCandidate.getName().c_str(), "");
+ }
+ } else {
+ if (semantics & (gl_SemanticsAcquire | gl_SemanticsRelease | gl_SemanticsAcquireRelease)) {
+ if (!IsPow2(semantics & (gl_SemanticsAcquire | gl_SemanticsRelease | gl_SemanticsAcquireRelease))) {
+ error(loc, "Semantics must not include multiple of gl_SemanticsRelease, gl_SemanticsAcquire, or "
+ "gl_SemanticsAcquireRelease", fnCandidate.getName().c_str(), "");
+ }
+ }
+ if (semantics2 & (gl_SemanticsAcquire | gl_SemanticsRelease | gl_SemanticsAcquireRelease)) {
+ if (!IsPow2(semantics2 & (gl_SemanticsAcquire | gl_SemanticsRelease | gl_SemanticsAcquireRelease))) {
+ error(loc, "semUnequal must not include multiple of gl_SemanticsRelease, gl_SemanticsAcquire, or "
+ "gl_SemanticsAcquireRelease", fnCandidate.getName().c_str(), "");
+ }
+ }
+ }
+ if (callNode.getOp() == EOpMemoryBarrier) {
+ if (storageClassSemantics == 0) {
+ error(loc, "Storage class semantics must not be zero", fnCandidate.getName().c_str(), "");
+ }
+ }
+ if (callNode.getOp() == EOpBarrier && semantics != 0 && storageClassSemantics == 0) {
+ error(loc, "Storage class semantics must not be zero", fnCandidate.getName().c_str(), "");
+ }
+ if ((callNode.getOp() == EOpAtomicCompSwap || callNode.getOp() == EOpImageAtomicCompSwap) &&
+ (semantics2 & (gl_SemanticsRelease | gl_SemanticsAcquireRelease))) {
+ error(loc, "semUnequal must not be gl_SemanticsRelease or gl_SemanticsAcquireRelease",
+ fnCandidate.getName().c_str(), "");
+ }
+ if ((semantics & gl_SemanticsMakeAvailable) &&
+ !(semantics & (gl_SemanticsRelease | gl_SemanticsAcquireRelease))) {
+ error(loc, "gl_SemanticsMakeAvailable requires gl_SemanticsRelease or gl_SemanticsAcquireRelease",
+ fnCandidate.getName().c_str(), "");
+ }
+ if ((semantics & gl_SemanticsMakeVisible) &&
+ !(semantics & (gl_SemanticsAcquire | gl_SemanticsAcquireRelease))) {
+ error(loc, "gl_SemanticsMakeVisible requires gl_SemanticsAcquire or gl_SemanticsAcquireRelease",
+ fnCandidate.getName().c_str(), "");
+ }
+
+}
+
+
+//
+// Do additional checking of built-in function calls that is not caught
+// by normal semantic checks on argument type, extension tagging, etc.
+//
+// Assumes there has been a semantically correct match to a built-in function prototype.
+//
+void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermOperator& callNode)
+{
+ // Set up convenience accessors to the argument(s). There is almost always
+ // multiple arguments for the cases below, but when there might be one,
+ // check the unaryArg first.
+ const TIntermSequence* argp = nullptr; // confusing to use [] syntax on a pointer, so this is to help get a reference
+ const TIntermTyped* unaryArg = nullptr;
+ const TIntermTyped* arg0 = nullptr;
+ if (callNode.getAsAggregate()) {
+ argp = &callNode.getAsAggregate()->getSequence();
+ if (argp->size() > 0)
+ arg0 = (*argp)[0]->getAsTyped();
+ } else {
+ assert(callNode.getAsUnaryNode());
+ unaryArg = callNode.getAsUnaryNode()->getOperand();
+ arg0 = unaryArg;
+ }
+
+ TString featureString;
+ const char* feature = nullptr;
+ switch (callNode.getOp()) {
+ case EOpTextureGather:
+ case EOpTextureGatherOffset:
+ case EOpTextureGatherOffsets:
+ {
+ // Figure out which variants are allowed by what extensions,
+ // and what arguments must be constant for which situations.
+
+ featureString = fnCandidate.getName();
+ featureString += "(...)";
+ feature = featureString.c_str();
+ profileRequires(loc, EEsProfile, 310, nullptr, feature);
+ int compArg = -1; // track which argument, if any, is the constant component argument
+ switch (callNode.getOp()) {
+ case EOpTextureGather:
+ // More than two arguments needs gpu_shader5, and rectangular or shadow needs gpu_shader5,
+ // otherwise, need GL_ARB_texture_gather.
+ if (fnCandidate.getParamCount() > 2 || fnCandidate[0].type->getSampler().dim == EsdRect || fnCandidate[0].type->getSampler().shadow) {
+ profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature);
+ if (! fnCandidate[0].type->getSampler().shadow)
+ compArg = 2;
+ } else
+ profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_texture_gather, feature);
+ break;
+ case EOpTextureGatherOffset:
+ // GL_ARB_texture_gather is good enough for 2D non-shadow textures with no component argument
+ if (fnCandidate[0].type->getSampler().dim == Esd2D && ! fnCandidate[0].type->getSampler().shadow && fnCandidate.getParamCount() == 3)
+ profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_texture_gather, feature);
+ else
+ profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature);
+ if (! (*argp)[fnCandidate[0].type->getSampler().shadow ? 3 : 2]->getAsConstantUnion())
+ profileRequires(loc, EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5,
+ "non-constant offset argument");
+ if (! fnCandidate[0].type->getSampler().shadow)
+ compArg = 3;
+ break;
+ case EOpTextureGatherOffsets:
+ profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature);
+ if (! fnCandidate[0].type->getSampler().shadow)
+ compArg = 3;
+ // check for constant offsets
+ if (! (*argp)[fnCandidate[0].type->getSampler().shadow ? 3 : 2]->getAsConstantUnion())
+ error(loc, "must be a compile-time constant:", feature, "offsets argument");
+ break;
+ default:
+ break;
+ }
+
+ if (compArg > 0 && compArg < fnCandidate.getParamCount()) {
+ if ((*argp)[compArg]->getAsConstantUnion()) {
+ int value = (*argp)[compArg]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ if (value < 0 || value > 3)
+ error(loc, "must be 0, 1, 2, or 3:", feature, "component argument");
+ } else
+ error(loc, "must be a compile-time constant:", feature, "component argument");
+ }
+
+#ifdef AMD_EXTENSIONS
+ bool bias = false;
+ if (callNode.getOp() == EOpTextureGather)
+ bias = fnCandidate.getParamCount() > 3;
+ else if (callNode.getOp() == EOpTextureGatherOffset ||
+ callNode.getOp() == EOpTextureGatherOffsets)
+ bias = fnCandidate.getParamCount() > 4;
+
+ if (bias) {
+ featureString = fnCandidate.getName();
+ featureString += "with bias argument";
+ feature = featureString.c_str();
+ profileRequires(loc, ~EEsProfile, 450, nullptr, feature);
+ requireExtensions(loc, 1, &E_GL_AMD_texture_gather_bias_lod, feature);
+ }
+#endif
+
+ break;
+ }
+
+#ifdef AMD_EXTENSIONS
+ case EOpSparseTextureGather:
+ case EOpSparseTextureGatherOffset:
+ case EOpSparseTextureGatherOffsets:
+ {
+ bool bias = false;
+ if (callNode.getOp() == EOpSparseTextureGather)
+ bias = fnCandidate.getParamCount() > 4;
+ else if (callNode.getOp() == EOpSparseTextureGatherOffset ||
+ callNode.getOp() == EOpSparseTextureGatherOffsets)
+ bias = fnCandidate.getParamCount() > 5;
+
+ if (bias) {
+ featureString = fnCandidate.getName();
+ featureString += "with bias argument";
+ feature = featureString.c_str();
+ profileRequires(loc, ~EEsProfile, 450, nullptr, feature);
+ requireExtensions(loc, 1, &E_GL_AMD_texture_gather_bias_lod, feature);
+ }
+
+ break;
+ }
+
+ case EOpSparseTextureGatherLod:
+ case EOpSparseTextureGatherLodOffset:
+ case EOpSparseTextureGatherLodOffsets:
+ {
+ requireExtensions(loc, 1, &E_GL_ARB_sparse_texture2, fnCandidate.getName().c_str());
+ break;
+ }
+
+ case EOpSwizzleInvocations:
+ {
+ if (! (*argp)[1]->getAsConstantUnion())
+ error(loc, "argument must be compile-time constant", "offset", "");
+ else {
+ unsigned offset[4] = {};
+ offset[0] = (*argp)[1]->getAsConstantUnion()->getConstArray()[0].getUConst();
+ offset[1] = (*argp)[1]->getAsConstantUnion()->getConstArray()[1].getUConst();
+ offset[2] = (*argp)[1]->getAsConstantUnion()->getConstArray()[2].getUConst();
+ offset[3] = (*argp)[1]->getAsConstantUnion()->getConstArray()[3].getUConst();
+ if (offset[0] > 3 || offset[1] > 3 || offset[2] > 3 || offset[3] > 3)
+ error(loc, "components must be in the range [0, 3]", "offset", "");
+ }
+
+ break;
+ }
+
+ case EOpSwizzleInvocationsMasked:
+ {
+ if (! (*argp)[1]->getAsConstantUnion())
+ error(loc, "argument must be compile-time constant", "mask", "");
+ else {
+ unsigned mask[3] = {};
+ mask[0] = (*argp)[1]->getAsConstantUnion()->getConstArray()[0].getUConst();
+ mask[1] = (*argp)[1]->getAsConstantUnion()->getConstArray()[1].getUConst();
+ mask[2] = (*argp)[1]->getAsConstantUnion()->getConstArray()[2].getUConst();
+ if (mask[0] > 31 || mask[1] > 31 || mask[2] > 31)
+ error(loc, "components must be in the range [0, 31]", "mask", "");
+ }
+
+ break;
+ }
+#endif
+
+ case EOpTextureOffset:
+ case EOpTextureFetchOffset:
+ case EOpTextureProjOffset:
+ case EOpTextureLodOffset:
+ case EOpTextureProjLodOffset:
+ case EOpTextureGradOffset:
+ case EOpTextureProjGradOffset:
+ {
+ // Handle texture-offset limits checking
+ // Pick which argument has to hold constant offsets
+ int arg = -1;
+ switch (callNode.getOp()) {
+ case EOpTextureOffset: arg = 2; break;
+ case EOpTextureFetchOffset: arg = (arg0->getType().getSampler().dim != EsdRect) ? 3 : 2; break;
+ case EOpTextureProjOffset: arg = 2; break;
+ case EOpTextureLodOffset: arg = 3; break;
+ case EOpTextureProjLodOffset: arg = 3; break;
+ case EOpTextureGradOffset: arg = 4; break;
+ case EOpTextureProjGradOffset: arg = 4; break;
+ default:
+ assert(0);
+ break;
+ }
+
+ if (arg > 0) {
+
+#ifdef AMD_EXTENSIONS
+ bool f16ShadowCompare = (*argp)[1]->getAsTyped()->getBasicType() == EbtFloat16 && arg0->getType().getSampler().shadow;
+ if (f16ShadowCompare)
+ ++arg;
+#endif
+ if (! (*argp)[arg]->getAsConstantUnion())
+ error(loc, "argument must be compile-time constant", "texel offset", "");
+ else {
+ const TType& type = (*argp)[arg]->getAsTyped()->getType();
+ for (int c = 0; c < type.getVectorSize(); ++c) {
+ int offset = (*argp)[arg]->getAsConstantUnion()->getConstArray()[c].getIConst();
+ if (offset > resources.maxProgramTexelOffset || offset < resources.minProgramTexelOffset)
+ error(loc, "value is out of range:", "texel offset", "[gl_MinProgramTexelOffset, gl_MaxProgramTexelOffset]");
+ }
+ }
+ }
+
+ break;
+ }
+
+#ifdef NV_EXTENSIONS
+ case EOpTraceNV:
+ if (!(*argp)[10]->getAsConstantUnion())
+ error(loc, "argument must be compile-time constant", "payload number", "");
+ break;
+ case EOpExecuteCallableNV:
+ if (!(*argp)[1]->getAsConstantUnion())
+ error(loc, "argument must be compile-time constant", "callable data number", "");
+ break;
+#endif
+
+ case EOpTextureQuerySamples:
+ case EOpImageQuerySamples:
+ // GL_ARB_shader_texture_image_samples
+ profileRequires(loc, ~EEsProfile, 450, E_GL_ARB_shader_texture_image_samples, "textureSamples and imageSamples");
+ break;
+
+ case EOpImageAtomicAdd:
+ case EOpImageAtomicMin:
+ case EOpImageAtomicMax:
+ case EOpImageAtomicAnd:
+ case EOpImageAtomicOr:
+ case EOpImageAtomicXor:
+ case EOpImageAtomicExchange:
+ case EOpImageAtomicCompSwap:
+ case EOpImageAtomicLoad:
+ case EOpImageAtomicStore:
+ {
+ // Make sure the image types have the correct layout() format and correct argument types
+ const TType& imageType = arg0->getType();
+ if (imageType.getSampler().type == EbtInt || imageType.getSampler().type == EbtUint) {
+ if (imageType.getQualifier().layoutFormat != ElfR32i && imageType.getQualifier().layoutFormat != ElfR32ui)
+ error(loc, "only supported on image with format r32i or r32ui", fnCandidate.getName().c_str(), "");
+ } else {
+ if (fnCandidate.getName().compare(0, 19, "imageAtomicExchange") != 0)
+ error(loc, "only supported on integer images", fnCandidate.getName().c_str(), "");
+ else if (imageType.getQualifier().layoutFormat != ElfR32f && profile == EEsProfile)
+ error(loc, "only supported on image with format r32f", fnCandidate.getName().c_str(), "");
+ }
+
+ const size_t maxArgs = imageType.getSampler().isMultiSample() ? 5 : 4;
+ if (argp->size() > maxArgs) {
+ requireExtensions(loc, 1, &E_GL_KHR_memory_scope_semantics, fnCandidate.getName().c_str());
+ memorySemanticsCheck(loc, fnCandidate, callNode);
+ }
+
+ break;
+ }
+
+ case EOpAtomicAdd:
+ case EOpAtomicMin:
+ case EOpAtomicMax:
+ case EOpAtomicAnd:
+ case EOpAtomicOr:
+ case EOpAtomicXor:
+ case EOpAtomicExchange:
+ case EOpAtomicCompSwap:
+ case EOpAtomicLoad:
+ case EOpAtomicStore:
+ {
+ if (argp->size() > 3) {
+ requireExtensions(loc, 1, &E_GL_KHR_memory_scope_semantics, fnCandidate.getName().c_str());
+ memorySemanticsCheck(loc, fnCandidate, callNode);
+ } else if (arg0->getType().getBasicType() == EbtInt64 || arg0->getType().getBasicType() == EbtUint64) {
+#ifdef NV_EXTENSIONS
+ const char* const extensions[2] = { E_GL_NV_shader_atomic_int64,
+ E_GL_EXT_shader_atomic_int64 };
+ requireExtensions(loc, 2, extensions, fnCandidate.getName().c_str());
+#else
+ requireExtensions(loc, 1, &E_GL_EXT_shader_atomic_int64, fnCandidate.getName().c_str());
+#endif
+ }
+ break;
+ }
+
+ case EOpInterpolateAtCentroid:
+ case EOpInterpolateAtSample:
+ case EOpInterpolateAtOffset:
+#ifdef AMD_EXTENSIONS
+ case EOpInterpolateAtVertex:
+#endif
+ // Make sure the first argument is an interpolant, or an array element of an interpolant
+ if (arg0->getType().getQualifier().storage != EvqVaryingIn) {
+ // It might still be an array element.
+ //
+ // We could check more, but the semantics of the first argument are already met; the
+ // only way to turn an array into a float/vec* is array dereference and swizzle.
+ //
+ // ES and desktop 4.3 and earlier: swizzles may not be used
+ // desktop 4.4 and later: swizzles may be used
+ bool swizzleOkay = (profile != EEsProfile) && (version >= 440);
+ const TIntermTyped* base = TIntermediate::findLValueBase(arg0, swizzleOkay);
+ if (base == nullptr || base->getType().getQualifier().storage != EvqVaryingIn)
+ error(loc, "first argument must be an interpolant, or interpolant-array element", fnCandidate.getName().c_str(), "");
+ }
+
+#ifdef AMD_EXTENSIONS
+ if (callNode.getOp() == EOpInterpolateAtVertex) {
+ if (!arg0->getType().getQualifier().isExplicitInterpolation())
+ error(loc, "argument must be qualified as __explicitInterpAMD in", "interpolant", "");
+ else {
+ if (! (*argp)[1]->getAsConstantUnion())
+ error(loc, "argument must be compile-time constant", "vertex index", "");
+ else {
+ unsigned vertexIdx = (*argp)[1]->getAsConstantUnion()->getConstArray()[0].getUConst();
+ if (vertexIdx > 2)
+ error(loc, "must be in the range [0, 2]", "vertex index", "");
+ }
+ }
+ }
+#endif
+
+ break;
+
+ case EOpEmitStreamVertex:
+ case EOpEndStreamPrimitive:
+ intermediate.setMultiStream();
+ break;
+
+ case EOpSubgroupClusteredAdd:
+ case EOpSubgroupClusteredMul:
+ case EOpSubgroupClusteredMin:
+ case EOpSubgroupClusteredMax:
+ case EOpSubgroupClusteredAnd:
+ case EOpSubgroupClusteredOr:
+ case EOpSubgroupClusteredXor:
+ // The <clusterSize> as used in the subgroupClustered<op>() operations must be:
+ // - An integral constant expression.
+ // - At least 1.
+ // - A power of 2.
+ if ((*argp)[1]->getAsConstantUnion() == nullptr)
+ error(loc, "argument must be compile-time constant", "cluster size", "");
+ else {
+ int size = (*argp)[1]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ if (size < 1)
+ error(loc, "argument must be at least 1", "cluster size", "");
+ else if (!IsPow2(size))
+ error(loc, "argument must be a power of 2", "cluster size", "");
+ }
+ break;
+
+ case EOpSubgroupBroadcast:
+ // <id> must be an integral constant expression.
+ if ((*argp)[1]->getAsConstantUnion() == nullptr)
+ error(loc, "argument must be compile-time constant", "id", "");
+ break;
+
+ case EOpBarrier:
+ case EOpMemoryBarrier:
+ if (argp->size() > 0) {
+ requireExtensions(loc, 1, &E_GL_KHR_memory_scope_semantics, fnCandidate.getName().c_str());
+ memorySemanticsCheck(loc, fnCandidate, callNode);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Texture operations on texture objects (aside from texelFetch on a
+ // textureBuffer) require EXT_samplerless_texture_functions.
+ switch (callNode.getOp()) {
+ case EOpTextureQuerySize:
+ case EOpTextureQueryLevels:
+ case EOpTextureQuerySamples:
+ case EOpTextureFetch:
+ case EOpTextureFetchOffset:
+ {
+ const TSampler& sampler = fnCandidate[0].type->getSampler();
+
+ const bool isTexture = sampler.isTexture() && !sampler.isCombined();
+ const bool isBuffer = sampler.dim == EsdBuffer;
+ const bool isFetch = callNode.getOp() == EOpTextureFetch || callNode.getOp() == EOpTextureFetchOffset;
+
+ if (isTexture && (!isBuffer || !isFetch))
+ requireExtensions(loc, 1, &E_GL_EXT_samplerless_texture_functions, fnCandidate.getName().c_str());
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (callNode.getOp() > EOpSubgroupGuardStart && callNode.getOp() < EOpSubgroupGuardStop) {
+ // these require SPIR-V 1.3
+ if (spvVersion.spv > 0 && spvVersion.spv < EShTargetSpv_1_3)
+ error(loc, "requires SPIR-V 1.3", "subgroup op", "");
+ }
+}
+
+extern bool PureOperatorBuiltins;
+
+// Deprecated! Use PureOperatorBuiltins == true instead, in which case this
+// functionality is handled in builtInOpCheck() instead of here.
+//
+// Do additional checking of built-in function calls that were not mapped
+// to built-in operations (e.g., texturing functions).
+//
+// Assumes there has been a semantically correct match to a built-in function.
+//
+void TParseContext::nonOpBuiltInCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermAggregate& callNode)
+{
+ // Further maintenance of this function is deprecated, because the "correct"
+ // future-oriented design is to not have to do string compares on function names.
+
+ // If PureOperatorBuiltins == true, then all built-ins should be mapped
+ // to a TOperator, and this function would then never get called.
+
+ assert(PureOperatorBuiltins == false);
+
+ // built-in texturing functions get their return value precision from the precision of the sampler
+ if (fnCandidate.getType().getQualifier().precision == EpqNone &&
+ fnCandidate.getParamCount() > 0 && fnCandidate[0].type->getBasicType() == EbtSampler)
+ callNode.getQualifier().precision = callNode.getSequence()[0]->getAsTyped()->getQualifier().precision;
+
+ if (fnCandidate.getName().compare(0, 7, "texture") == 0) {
+ if (fnCandidate.getName().compare(0, 13, "textureGather") == 0) {
+ TString featureString = fnCandidate.getName() + "(...)";
+ const char* feature = featureString.c_str();
+ profileRequires(loc, EEsProfile, 310, nullptr, feature);
+
+ int compArg = -1; // track which argument, if any, is the constant component argument
+ if (fnCandidate.getName().compare("textureGatherOffset") == 0) {
+ // GL_ARB_texture_gather is good enough for 2D non-shadow textures with no component argument
+ if (fnCandidate[0].type->getSampler().dim == Esd2D && ! fnCandidate[0].type->getSampler().shadow && fnCandidate.getParamCount() == 3)
+ profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_texture_gather, feature);
+ else
+ profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature);
+ int offsetArg = fnCandidate[0].type->getSampler().shadow ? 3 : 2;
+ if (! callNode.getSequence()[offsetArg]->getAsConstantUnion())
+ profileRequires(loc, EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5,
+ "non-constant offset argument");
+ if (! fnCandidate[0].type->getSampler().shadow)
+ compArg = 3;
+ } else if (fnCandidate.getName().compare("textureGatherOffsets") == 0) {
+ profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature);
+ if (! fnCandidate[0].type->getSampler().shadow)
+ compArg = 3;
+ // check for constant offsets
+ int offsetArg = fnCandidate[0].type->getSampler().shadow ? 3 : 2;
+ if (! callNode.getSequence()[offsetArg]->getAsConstantUnion())
+ error(loc, "must be a compile-time constant:", feature, "offsets argument");
+ } else if (fnCandidate.getName().compare("textureGather") == 0) {
+ // More than two arguments needs gpu_shader5, and rectangular or shadow needs gpu_shader5,
+ // otherwise, need GL_ARB_texture_gather.
+ if (fnCandidate.getParamCount() > 2 || fnCandidate[0].type->getSampler().dim == EsdRect || fnCandidate[0].type->getSampler().shadow) {
+ profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature);
+ if (! fnCandidate[0].type->getSampler().shadow)
+ compArg = 2;
+ } else
+ profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_texture_gather, feature);
+ }
+
+ if (compArg > 0 && compArg < fnCandidate.getParamCount()) {
+ if (callNode.getSequence()[compArg]->getAsConstantUnion()) {
+ int value = callNode.getSequence()[compArg]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ if (value < 0 || value > 3)
+ error(loc, "must be 0, 1, 2, or 3:", feature, "component argument");
+ } else
+ error(loc, "must be a compile-time constant:", feature, "component argument");
+ }
+ } else {
+ // this is only for functions not starting "textureGather"...
+ if (fnCandidate.getName().find("Offset") != TString::npos) {
+
+ // Handle texture-offset limits checking
+ int arg = -1;
+ if (fnCandidate.getName().compare("textureOffset") == 0)
+ arg = 2;
+ else if (fnCandidate.getName().compare("texelFetchOffset") == 0)
+ arg = 3;
+ else if (fnCandidate.getName().compare("textureProjOffset") == 0)
+ arg = 2;
+ else if (fnCandidate.getName().compare("textureLodOffset") == 0)
+ arg = 3;
+ else if (fnCandidate.getName().compare("textureProjLodOffset") == 0)
+ arg = 3;
+ else if (fnCandidate.getName().compare("textureGradOffset") == 0)
+ arg = 4;
+ else if (fnCandidate.getName().compare("textureProjGradOffset") == 0)
+ arg = 4;
+
+ if (arg > 0) {
+ if (! callNode.getSequence()[arg]->getAsConstantUnion())
+ error(loc, "argument must be compile-time constant", "texel offset", "");
+ else {
+ const TType& type = callNode.getSequence()[arg]->getAsTyped()->getType();
+ for (int c = 0; c < type.getVectorSize(); ++c) {
+ int offset = callNode.getSequence()[arg]->getAsConstantUnion()->getConstArray()[c].getIConst();
+ if (offset > resources.maxProgramTexelOffset || offset < resources.minProgramTexelOffset)
+ error(loc, "value is out of range:", "texel offset", "[gl_MinProgramTexelOffset, gl_MaxProgramTexelOffset]");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // GL_ARB_shader_texture_image_samples
+ if (fnCandidate.getName().compare(0, 14, "textureSamples") == 0 || fnCandidate.getName().compare(0, 12, "imageSamples") == 0)
+ profileRequires(loc, ~EEsProfile, 450, E_GL_ARB_shader_texture_image_samples, "textureSamples and imageSamples");
+
+ if (fnCandidate.getName().compare(0, 11, "imageAtomic") == 0) {
+ const TType& imageType = callNode.getSequence()[0]->getAsTyped()->getType();
+ if (imageType.getSampler().type == EbtInt || imageType.getSampler().type == EbtUint) {
+ if (imageType.getQualifier().layoutFormat != ElfR32i && imageType.getQualifier().layoutFormat != ElfR32ui)
+ error(loc, "only supported on image with format r32i or r32ui", fnCandidate.getName().c_str(), "");
+ } else {
+ if (fnCandidate.getName().compare(0, 19, "imageAtomicExchange") != 0)
+ error(loc, "only supported on integer images", fnCandidate.getName().c_str(), "");
+ else if (imageType.getQualifier().layoutFormat != ElfR32f && profile == EEsProfile)
+ error(loc, "only supported on image with format r32f", fnCandidate.getName().c_str(), "");
+ }
+ }
+}
+
+//
+// Do any extra checking for a user function call.
+//
+void TParseContext::userFunctionCallCheck(const TSourceLoc& loc, TIntermAggregate& callNode)
+{
+ TIntermSequence& arguments = callNode.getSequence();
+
+ for (int i = 0; i < (int)arguments.size(); ++i)
+ samplerConstructorLocationCheck(loc, "call argument", arguments[i]);
+}
+
+//
+// Emit an error if this is a sampler constructor
+//
+void TParseContext::samplerConstructorLocationCheck(const TSourceLoc& loc, const char* token, TIntermNode* node)
+{
+ if (node->getAsOperator() && node->getAsOperator()->getOp() == EOpConstructTextureSampler)
+ error(loc, "sampler constructor must appear at point of use", token, "");
+}
+
+//
+// Handle seeing a built-in constructor in a grammar production.
+//
+TFunction* TParseContext::handleConstructorCall(const TSourceLoc& loc, const TPublicType& publicType)
+{
+ TType type(publicType);
+ type.getQualifier().precision = EpqNone;
+
+ if (type.isArray()) {
+ profileRequires(loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed constructor");
+ profileRequires(loc, EEsProfile, 300, nullptr, "arrayed constructor");
+ }
+
+ TOperator op = intermediate.mapTypeToConstructorOp(type);
+
+ if (op == EOpNull) {
+ error(loc, "cannot construct this type", type.getBasicString(), "");
+ op = EOpConstructFloat;
+ TType errorType(EbtFloat);
+ type.shallowCopy(errorType);
+ }
+
+ TString empty("");
+
+ return new TFunction(&empty, type, op);
+}
+
+// Handle seeing a precision qualifier in the grammar.
+void TParseContext::handlePrecisionQualifier(const TSourceLoc& /*loc*/, TQualifier& qualifier, TPrecisionQualifier precision)
+{
+ if (obeyPrecisionQualifiers())
+ qualifier.precision = precision;
+}
+
+// Check for messages to give on seeing a precision qualifier used in a
+// declaration in the grammar.
+void TParseContext::checkPrecisionQualifier(const TSourceLoc& loc, TPrecisionQualifier)
+{
+ if (precisionManager.shouldWarnAboutDefaults()) {
+ warn(loc, "all default precisions are highp; use precision statements to quiet warning, e.g.:\n"
+ " \"precision mediump int; precision highp float;\"", "", "");
+ precisionManager.defaultWarningGiven();
+ }
+}
+
+//
+// Same error message for all places assignments don't work.
+//
+void TParseContext::assignError(const TSourceLoc& loc, const char* op, TString left, TString right)
+{
+ error(loc, "", op, "cannot convert from '%s' to '%s'",
+ right.c_str(), left.c_str());
+}
+
+//
+// Same error message for all places unary operations don't work.
+//
+void TParseContext::unaryOpError(const TSourceLoc& loc, const char* op, TString operand)
+{
+ error(loc, " wrong operand type", op,
+ "no operation '%s' exists that takes an operand of type %s (or there is no acceptable conversion)",
+ op, operand.c_str());
+}
+
+//
+// Same error message for all binary operations don't work.
+//
+void TParseContext::binaryOpError(const TSourceLoc& loc, const char* op, TString left, TString right)
+{
+ error(loc, " wrong operand types:", op,
+ "no operation '%s' exists that takes a left-hand operand of type '%s' and "
+ "a right operand of type '%s' (or there is no acceptable conversion)",
+ op, left.c_str(), right.c_str());
+}
+
+//
+// A basic type of EbtVoid is a key that the name string was seen in the source, but
+// it was not found as a variable in the symbol table. If so, give the error
+// message and insert a dummy variable in the symbol table to prevent future errors.
+//
+void TParseContext::variableCheck(TIntermTyped*& nodePtr)
+{
+ TIntermSymbol* symbol = nodePtr->getAsSymbolNode();
+ if (! symbol)
+ return;
+
+ if (symbol->getType().getBasicType() == EbtVoid) {
+ const char *extraInfoFormat = "";
+ if (spvVersion.vulkan != 0 && symbol->getName() == "gl_VertexID") {
+ extraInfoFormat = "(Did you mean gl_VertexIndex?)";
+ } else if (spvVersion.vulkan != 0 && symbol->getName() == "gl_InstanceID") {
+ extraInfoFormat = "(Did you mean gl_InstanceIndex?)";
+ }
+ error(symbol->getLoc(), "undeclared identifier", symbol->getName().c_str(), extraInfoFormat);
+
+ // Add to symbol table to prevent future error messages on the same name
+ if (symbol->getName().size() > 0) {
+ TVariable* fakeVariable = new TVariable(&symbol->getName(), TType(EbtFloat));
+ symbolTable.insert(*fakeVariable);
+
+ // substitute a symbol node for this new variable
+ nodePtr = intermediate.addSymbol(*fakeVariable, symbol->getLoc());
+ }
+ } else {
+ switch (symbol->getQualifier().storage) {
+ case EvqPointCoord:
+ profileRequires(symbol->getLoc(), ENoProfile, 120, nullptr, "gl_PointCoord");
+ break;
+ default: break; // some compilers want this
+ }
+ }
+}
+
+//
+// Both test and if necessary, spit out an error, to see if the node is really
+// an l-value that can be operated on this way.
+//
+// Returns true if there was an error.
+//
+bool TParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node)
+{
+ TIntermBinary* binaryNode = node->getAsBinaryNode();
+
+ if (binaryNode) {
+ bool errorReturn = false;
+
+ switch(binaryNode->getOp()) {
+ case EOpIndexDirect:
+ case EOpIndexIndirect:
+ // ... tessellation control shader ...
+ // If a per-vertex output variable is used as an l-value, it is a
+ // compile-time or link-time error if the expression indicating the
+ // vertex index is not the identifier gl_InvocationID.
+ if (language == EShLangTessControl) {
+ const TType& leftType = binaryNode->getLeft()->getType();
+ if (leftType.getQualifier().storage == EvqVaryingOut && ! leftType.getQualifier().patch && binaryNode->getLeft()->getAsSymbolNode()) {
+ // we have a per-vertex output
+ const TIntermSymbol* rightSymbol = binaryNode->getRight()->getAsSymbolNode();
+ if (! rightSymbol || rightSymbol->getQualifier().builtIn != EbvInvocationId)
+ error(loc, "tessellation-control per-vertex output l-value must be indexed with gl_InvocationID", "[]", "");
+ }
+ }
+
+ break; // left node is checked by base class
+ case EOpIndexDirectStruct:
+ break; // left node is checked by base class
+ case EOpVectorSwizzle:
+ errorReturn = lValueErrorCheck(loc, op, binaryNode->getLeft());
+ if (!errorReturn) {
+ int offset[4] = {0,0,0,0};
+
+ TIntermTyped* rightNode = binaryNode->getRight();
+ TIntermAggregate *aggrNode = rightNode->getAsAggregate();
+
+ for (TIntermSequence::iterator p = aggrNode->getSequence().begin();
+ p != aggrNode->getSequence().end(); p++) {
+ int value = (*p)->getAsTyped()->getAsConstantUnion()->getConstArray()[0].getIConst();
+ offset[value]++;
+ if (offset[value] > 1) {
+ error(loc, " l-value of swizzle cannot have duplicate components", op, "", "");
+
+ return true;
+ }
+ }
+ }
+
+ return errorReturn;
+ default:
+ break;
+ }
+
+ if (errorReturn) {
+ error(loc, " l-value required", op, "", "");
+ return true;
+ }
+ }
+
+ if (binaryNode && binaryNode->getOp() == EOpIndexDirectStruct &&
+ binaryNode->getLeft()->getBasicType() == EbtReference)
+ return false;
+
+ // Let the base class check errors
+ if (TParseContextBase::lValueErrorCheck(loc, op, node))
+ return true;
+
+ const char* symbol = nullptr;
+ TIntermSymbol* symNode = node->getAsSymbolNode();
+ if (symNode != nullptr)
+ symbol = symNode->getName().c_str();
+
+ const char* message = nullptr;
+ switch (node->getQualifier().storage) {
+ case EvqVaryingIn: message = "can't modify shader input"; break;
+ case EvqInstanceId: message = "can't modify gl_InstanceID"; break;
+ case EvqVertexId: message = "can't modify gl_VertexID"; break;
+ case EvqFace: message = "can't modify gl_FrontFace"; break;
+ case EvqFragCoord: message = "can't modify gl_FragCoord"; break;
+ case EvqPointCoord: message = "can't modify gl_PointCoord"; break;
+ case EvqFragDepth:
+ intermediate.setDepthReplacing();
+ // "In addition, it is an error to statically write to gl_FragDepth in the fragment shader."
+ if (profile == EEsProfile && intermediate.getEarlyFragmentTests())
+ message = "can't modify gl_FragDepth if using early_fragment_tests";
+ break;
+
+ default:
+ break;
+ }
+
+ if (message == nullptr && binaryNode == nullptr && symNode == nullptr) {
+ error(loc, " l-value required", op, "", "");
+
+ return true;
+ }
+
+ //
+ // Everything else is okay, no error.
+ //
+ if (message == nullptr)
+ return false;
+
+ //
+ // If we get here, we have an error and a message.
+ //
+ if (symNode)
+ error(loc, " l-value required", op, "\"%s\" (%s)", symbol, message);
+ else
+ error(loc, " l-value required", op, "(%s)", message);
+
+ return true;
+}
+
+// Test for and give an error if the node can't be read from.
+void TParseContext::rValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node)
+{
+ // Let the base class check errors
+ TParseContextBase::rValueErrorCheck(loc, op, node);
+
+#ifdef AMD_EXTENSIONS
+ TIntermSymbol* symNode = node->getAsSymbolNode();
+ if (!(symNode && symNode->getQualifier().writeonly)) // base class checks
+ if (symNode && symNode->getQualifier().explicitInterp)
+ error(loc, "can't read from explicitly-interpolated object: ", op, symNode->getName().c_str());
+#endif
+}
+
+//
+// Both test, and if necessary spit out an error, to see if the node is really
+// a constant.
+//
+void TParseContext::constantValueCheck(TIntermTyped* node, const char* token)
+{
+ if (! node->getQualifier().isConstant())
+ error(node->getLoc(), "constant expression required", token, "");
+}
+
+//
+// Both test, and if necessary spit out an error, to see if the node is really
+// an integer.
+//
+void TParseContext::integerCheck(const TIntermTyped* node, const char* token)
+{
+ if ((node->getBasicType() == EbtInt || node->getBasicType() == EbtUint) && node->isScalar())
+ return;
+
+ error(node->getLoc(), "scalar integer expression required", token, "");
+}
+
+//
+// Both test, and if necessary spit out an error, to see if we are currently
+// globally scoped.
+//
+void TParseContext::globalCheck(const TSourceLoc& loc, const char* token)
+{
+ if (! symbolTable.atGlobalLevel())
+ error(loc, "not allowed in nested scope", token, "");
+}
+
+//
+// Reserved errors for GLSL.
+//
+void TParseContext::reservedErrorCheck(const TSourceLoc& loc, const TString& identifier)
+{
+ // "Identifiers starting with "gl_" are reserved for use by OpenGL, and may not be
+ // declared in a shader; this results in a compile-time error."
+ if (! symbolTable.atBuiltInLevel()) {
+ if (builtInName(identifier))
+ error(loc, "identifiers starting with \"gl_\" are reserved", identifier.c_str(), "");
+
+ // "__" are not supposed to be an error. ES 310 (and desktop) added the clarification:
+ // "In addition, all identifiers containing two consecutive underscores (__) are
+ // reserved; using such a name does not itself result in an error, but may result
+ // in undefined behavior."
+ // however, before that, ES tests required an error.
+ if (identifier.find("__") != TString::npos) {
+ if (profile == EEsProfile && version <= 300)
+ error(loc, "identifiers containing consecutive underscores (\"__\") are reserved, and an error if version <= 300", identifier.c_str(), "");
+ else
+ warn(loc, "identifiers containing consecutive underscores (\"__\") are reserved", identifier.c_str(), "");
+ }
+ }
+}
+
+//
+// Reserved errors for the preprocessor.
+//
+void TParseContext::reservedPpErrorCheck(const TSourceLoc& loc, const char* identifier, const char* op)
+{
+ // "__" are not supposed to be an error. ES 310 (and desktop) added the clarification:
+ // "All macro names containing two consecutive underscores ( __ ) are reserved;
+ // defining such a name does not itself result in an error, but may result in
+ // undefined behavior. All macro names prefixed with "GL_" ("GL" followed by a
+ // single underscore) are also reserved, and defining such a name results in a
+ // compile-time error."
+ // however, before that, ES tests required an error.
+ if (strncmp(identifier, "GL_", 3) == 0)
+ ppError(loc, "names beginning with \"GL_\" can't be (un)defined:", op, identifier);
+ else if (strncmp(identifier, "defined", 8) == 0)
+ ppError(loc, "\"defined\" can't be (un)defined:", op, identifier);
+ else if (strstr(identifier, "__") != 0) {
+ if (profile == EEsProfile && version >= 300 &&
+ (strcmp(identifier, "__LINE__") == 0 ||
+ strcmp(identifier, "__FILE__") == 0 ||
+ strcmp(identifier, "__VERSION__") == 0))
+ ppError(loc, "predefined names can't be (un)defined:", op, identifier);
+ else {
+ if (profile == EEsProfile && version <= 300)
+ ppError(loc, "names containing consecutive underscores are reserved, and an error if version <= 300:", op, identifier);
+ else
+ ppWarn(loc, "names containing consecutive underscores are reserved:", op, identifier);
+ }
+ }
+}
+
+//
+// See if this version/profile allows use of the line-continuation character '\'.
+//
+// Returns true if a line continuation should be done.
+//
+bool TParseContext::lineContinuationCheck(const TSourceLoc& loc, bool endOfComment)
+{
+ const char* message = "line continuation";
+
+ bool lineContinuationAllowed = (profile == EEsProfile && version >= 300) ||
+ (profile != EEsProfile && (version >= 420 || extensionTurnedOn(E_GL_ARB_shading_language_420pack)));
+
+ if (endOfComment) {
+ if (lineContinuationAllowed)
+ warn(loc, "used at end of comment; the following line is still part of the comment", message, "");
+ else
+ warn(loc, "used at end of comment, but this version does not provide line continuation", message, "");
+
+ return lineContinuationAllowed;
+ }
+
+ if (relaxedErrors()) {
+ if (! lineContinuationAllowed)
+ warn(loc, "not allowed in this version", message, "");
+ return true;
+ } else {
+ profileRequires(loc, EEsProfile, 300, nullptr, message);
+ profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, message);
+ }
+
+ return lineContinuationAllowed;
+}
+
+bool TParseContext::builtInName(const TString& identifier)
+{
+ return identifier.compare(0, 3, "gl_") == 0;
+}
+
+//
+// Make sure there is enough data and not too many arguments provided to the
+// constructor to build something of the type of the constructor. Also returns
+// the type of the constructor.
+//
+// Part of establishing type is establishing specialization-constness.
+// We don't yet know "top down" whether type is a specialization constant,
+// but a const constructor can becomes a specialization constant if any of
+// its children are, subject to KHR_vulkan_glsl rules:
+//
+// - int(), uint(), and bool() constructors for type conversions
+// from any of the following types to any of the following types:
+// * int
+// * uint
+// * bool
+// - vector versions of the above conversion constructors
+//
+// Returns true if there was an error in construction.
+//
+bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, TFunction& function, TOperator op, TType& type)
+{
+ type.shallowCopy(function.getType());
+
+ bool constructingMatrix = false;
+ switch(op) {
+ case EOpConstructTextureSampler:
+ return constructorTextureSamplerError(loc, function);
+ case EOpConstructMat2x2:
+ case EOpConstructMat2x3:
+ case EOpConstructMat2x4:
+ case EOpConstructMat3x2:
+ case EOpConstructMat3x3:
+ case EOpConstructMat3x4:
+ case EOpConstructMat4x2:
+ case EOpConstructMat4x3:
+ case EOpConstructMat4x4:
+ case EOpConstructDMat2x2:
+ case EOpConstructDMat2x3:
+ case EOpConstructDMat2x4:
+ case EOpConstructDMat3x2:
+ case EOpConstructDMat3x3:
+ case EOpConstructDMat3x4:
+ case EOpConstructDMat4x2:
+ case EOpConstructDMat4x3:
+ case EOpConstructDMat4x4:
+ case EOpConstructF16Mat2x2:
+ case EOpConstructF16Mat2x3:
+ case EOpConstructF16Mat2x4:
+ case EOpConstructF16Mat3x2:
+ case EOpConstructF16Mat3x3:
+ case EOpConstructF16Mat3x4:
+ case EOpConstructF16Mat4x2:
+ case EOpConstructF16Mat4x3:
+ case EOpConstructF16Mat4x4:
+ constructingMatrix = true;
+ break;
+ default:
+ break;
+ }
+
+ //
+ // Walk the arguments for first-pass checks and collection of information.
+ //
+
+ int size = 0;
+ bool constType = true;
+ bool specConstType = false; // value is only valid if constType is true
+ bool full = false;
+ bool overFull = false;
+ bool matrixInMatrix = false;
+ bool arrayArg = false;
+ bool floatArgument = false;
+ for (int arg = 0; arg < function.getParamCount(); ++arg) {
+ if (function[arg].type->isArray()) {
+ if (function[arg].type->isUnsizedArray()) {
+ // Can't construct from an unsized array.
+ error(loc, "array argument must be sized", "constructor", "");
+ return true;
+ }
+ arrayArg = true;
+ }
+ if (constructingMatrix && function[arg].type->isMatrix())
+ matrixInMatrix = true;
+
+ // 'full' will go to true when enough args have been seen. If we loop
+ // again, there is an extra argument.
+ if (full) {
+ // For vectors and matrices, it's okay to have too many components
+ // available, but not okay to have unused arguments.
+ overFull = true;
+ }
+
+ size += function[arg].type->computeNumComponents();
+ if (op != EOpConstructStruct && ! type.isArray() && size >= type.computeNumComponents())
+ full = true;
+
+ if (! function[arg].type->getQualifier().isConstant())
+ constType = false;
+ if (function[arg].type->getQualifier().isSpecConstant())
+ specConstType = true;
+ if (function[arg].type->isFloatingDomain())
+ floatArgument = true;
+ if (type.isStruct()) {
+ if (function[arg].type->containsBasicType(EbtFloat16)) {
+ requireFloat16Arithmetic(loc, "constructor", "can't construct structure containing 16-bit type");
+ }
+ if (function[arg].type->containsBasicType(EbtUint16) ||
+ function[arg].type->containsBasicType(EbtInt16)) {
+ requireInt16Arithmetic(loc, "constructor", "can't construct structure containing 16-bit type");
+ }
+ if (function[arg].type->containsBasicType(EbtUint8) ||
+ function[arg].type->containsBasicType(EbtInt8)) {
+ requireInt8Arithmetic(loc, "constructor", "can't construct structure containing 8-bit type");
+ }
+ }
+ }
+
+ switch (op) {
+ case EOpConstructFloat16:
+ case EOpConstructF16Vec2:
+ case EOpConstructF16Vec3:
+ case EOpConstructF16Vec4:
+ if (type.isArray())
+ requireFloat16Arithmetic(loc, "constructor", "16-bit arrays not supported");
+ if (type.isVector() && function.getParamCount() != 1)
+ requireFloat16Arithmetic(loc, "constructor", "16-bit vectors only take vector types");
+ break;
+ case EOpConstructUint16:
+ case EOpConstructU16Vec2:
+ case EOpConstructU16Vec3:
+ case EOpConstructU16Vec4:
+ case EOpConstructInt16:
+ case EOpConstructI16Vec2:
+ case EOpConstructI16Vec3:
+ case EOpConstructI16Vec4:
+ if (type.isArray())
+ requireInt16Arithmetic(loc, "constructor", "16-bit arrays not supported");
+ if (type.isVector() && function.getParamCount() != 1)
+ requireInt16Arithmetic(loc, "constructor", "16-bit vectors only take vector types");
+ break;
+ case EOpConstructUint8:
+ case EOpConstructU8Vec2:
+ case EOpConstructU8Vec3:
+ case EOpConstructU8Vec4:
+ case EOpConstructInt8:
+ case EOpConstructI8Vec2:
+ case EOpConstructI8Vec3:
+ case EOpConstructI8Vec4:
+ if (type.isArray())
+ requireInt8Arithmetic(loc, "constructor", "8-bit arrays not supported");
+ if (type.isVector() && function.getParamCount() != 1)
+ requireInt8Arithmetic(loc, "constructor", "8-bit vectors only take vector types");
+ break;
+ default:
+ break;
+ }
+
+ // inherit constness from children
+ if (constType) {
+ bool makeSpecConst;
+ // Finish pinning down spec-const semantics
+ if (specConstType) {
+ switch (op) {
+ case EOpConstructInt8:
+ case EOpConstructUint8:
+ case EOpConstructInt16:
+ case EOpConstructUint16:
+ case EOpConstructInt:
+ case EOpConstructUint:
+ case EOpConstructInt64:
+ case EOpConstructUint64:
+ case EOpConstructBool:
+ case EOpConstructBVec2:
+ case EOpConstructBVec3:
+ case EOpConstructBVec4:
+ case EOpConstructI8Vec2:
+ case EOpConstructI8Vec3:
+ case EOpConstructI8Vec4:
+ case EOpConstructU8Vec2:
+ case EOpConstructU8Vec3:
+ case EOpConstructU8Vec4:
+ case EOpConstructI16Vec2:
+ case EOpConstructI16Vec3:
+ case EOpConstructI16Vec4:
+ case EOpConstructU16Vec2:
+ case EOpConstructU16Vec3:
+ case EOpConstructU16Vec4:
+ case EOpConstructIVec2:
+ case EOpConstructIVec3:
+ case EOpConstructIVec4:
+ case EOpConstructUVec2:
+ case EOpConstructUVec3:
+ case EOpConstructUVec4:
+ case EOpConstructI64Vec2:
+ case EOpConstructI64Vec3:
+ case EOpConstructI64Vec4:
+ case EOpConstructU64Vec2:
+ case EOpConstructU64Vec3:
+ case EOpConstructU64Vec4:
+ // This was the list of valid ones, if they aren't converting from float
+ // and aren't making an array.
+ makeSpecConst = ! floatArgument && ! type.isArray();
+ break;
+ default:
+ // anything else wasn't white-listed in the spec as a conversion
+ makeSpecConst = false;
+ break;
+ }
+ } else
+ makeSpecConst = false;
+
+ if (makeSpecConst)
+ type.getQualifier().makeSpecConstant();
+ else if (specConstType)
+ type.getQualifier().makeTemporary();
+ else
+ type.getQualifier().storage = EvqConst;
+ }
+
+ if (type.isArray()) {
+ if (function.getParamCount() == 0) {
+ error(loc, "array constructor must have at least one argument", "constructor", "");
+ return true;
+ }
+
+ if (type.isUnsizedArray()) {
+ // auto adapt the constructor type to the number of arguments
+ type.changeOuterArraySize(function.getParamCount());
+ } else if (type.getOuterArraySize() != function.getParamCount()) {
+ error(loc, "array constructor needs one argument per array element", "constructor", "");
+ return true;
+ }
+
+ if (type.isArrayOfArrays()) {
+ // Types have to match, but we're still making the type.
+ // Finish making the type, and the comparison is done later
+ // when checking for conversion.
+ TArraySizes& arraySizes = *type.getArraySizes();
+
+ // At least the dimensionalities have to match.
+ if (! function[0].type->isArray() ||
+ arraySizes.getNumDims() != function[0].type->getArraySizes()->getNumDims() + 1) {
+ error(loc, "array constructor argument not correct type to construct array element", "constructor", "");
+ return true;
+ }
+
+ if (arraySizes.isInnerUnsized()) {
+ // "Arrays of arrays ..., and the size for any dimension is optional"
+ // That means we need to adopt (from the first argument) the other array sizes into the type.
+ for (int d = 1; d < arraySizes.getNumDims(); ++d) {
+ if (arraySizes.getDimSize(d) == UnsizedArraySize) {
+ arraySizes.setDimSize(d, function[0].type->getArraySizes()->getDimSize(d - 1));
+ }
+ }
+ }
+ }
+ }
+
+ if (arrayArg && op != EOpConstructStruct && ! type.isArrayOfArrays()) {
+ error(loc, "constructing non-array constituent from array argument", "constructor", "");
+ return true;
+ }
+
+ if (matrixInMatrix && ! type.isArray()) {
+ profileRequires(loc, ENoProfile, 120, nullptr, "constructing matrix from matrix");
+
+ // "If a matrix argument is given to a matrix constructor,
+ // it is a compile-time error to have any other arguments."
+ if (function.getParamCount() != 1)
+ error(loc, "matrix constructed from matrix can only have one argument", "constructor", "");
+ return false;
+ }
+
+ if (overFull) {
+ error(loc, "too many arguments", "constructor", "");
+ return true;
+ }
+
+ if (op == EOpConstructStruct && ! type.isArray() && (int)type.getStruct()->size() != function.getParamCount()) {
+ error(loc, "Number of constructor parameters does not match the number of structure fields", "constructor", "");
+ return true;
+ }
+
+ if ((op != EOpConstructStruct && size != 1 && size < type.computeNumComponents()) ||
+ (op == EOpConstructStruct && size < type.computeNumComponents())) {
+ error(loc, "not enough data provided for construction", "constructor", "");
+ return true;
+ }
+
+ if (type.isCoopMat() && function.getParamCount() != 1) {
+ error(loc, "wrong number of arguments", "constructor", "");
+ return true;
+ }
+ if (type.isCoopMat() &&
+ !(function[0].type->isScalar() || function[0].type->isCoopMat())) {
+ error(loc, "Cooperative matrix constructor argument must be scalar or cooperative matrix", "constructor", "");
+ return true;
+ }
+
+ TIntermTyped* typed = node->getAsTyped();
+ if (typed == nullptr) {
+ error(loc, "constructor argument does not have a type", "constructor", "");
+ return true;
+ }
+ if (op != EOpConstructStruct && typed->getBasicType() == EbtSampler) {
+ error(loc, "cannot convert a sampler", "constructor", "");
+ return true;
+ }
+ if (op != EOpConstructStruct && typed->getBasicType() == EbtAtomicUint) {
+ error(loc, "cannot convert an atomic_uint", "constructor", "");
+ return true;
+ }
+ if (typed->getBasicType() == EbtVoid) {
+ error(loc, "cannot convert a void", "constructor", "");
+ return true;
+ }
+
+ return false;
+}
+
+// Verify all the correct semantics for constructing a combined texture/sampler.
+// Return true if the semantics are incorrect.
+bool TParseContext::constructorTextureSamplerError(const TSourceLoc& loc, const TFunction& function)
+{
+ TString constructorName = function.getType().getBasicTypeString(); // TODO: performance: should not be making copy; interface needs to change
+ const char* token = constructorName.c_str();
+
+ // exactly two arguments needed
+ if (function.getParamCount() != 2) {
+ error(loc, "sampler-constructor requires two arguments", token, "");
+ return true;
+ }
+
+ // For now, not allowing arrayed constructors, the rest of this function
+ // is set up to allow them, if this test is removed:
+ if (function.getType().isArray()) {
+ error(loc, "sampler-constructor cannot make an array of samplers", token, "");
+ return true;
+ }
+
+ // first argument
+ // * the constructor's first argument must be a texture type
+ // * the dimensionality (1D, 2D, 3D, Cube, Rect, Buffer, MS, and Array)
+ // of the texture type must match that of the constructed sampler type
+ // (that is, the suffixes of the type of the first argument and the
+ // type of the constructor will be spelled the same way)
+ if (function[0].type->getBasicType() != EbtSampler ||
+ ! function[0].type->getSampler().isTexture() ||
+ function[0].type->isArray()) {
+ error(loc, "sampler-constructor first argument must be a scalar textureXXX type", token, "");
+ return true;
+ }
+ // simulate the first argument's impact on the result type, so it can be compared with the encapsulated operator!=()
+ TSampler texture = function.getType().getSampler();
+ texture.combined = false;
+ texture.shadow = false;
+ if (texture != function[0].type->getSampler()) {
+ error(loc, "sampler-constructor first argument must match type and dimensionality of constructor type", token, "");
+ return true;
+ }
+
+ // second argument
+ // * the constructor's second argument must be a scalar of type
+ // *sampler* or *samplerShadow*
+ if ( function[1].type->getBasicType() != EbtSampler ||
+ ! function[1].type->getSampler().isPureSampler() ||
+ function[1].type->isArray()) {
+ error(loc, "sampler-constructor second argument must be a scalar type 'sampler'", token, "");
+ return true;
+ }
+
+ return false;
+}
+
+// Checks to see if a void variable has been declared and raise an error message for such a case
+//
+// returns true in case of an error
+//
+bool TParseContext::voidErrorCheck(const TSourceLoc& loc, const TString& identifier, const TBasicType basicType)
+{
+ if (basicType == EbtVoid) {
+ error(loc, "illegal use of type 'void'", identifier.c_str(), "");
+ return true;
+ }
+
+ return false;
+}
+
+// Checks to see if the node (for the expression) contains a scalar boolean expression or not
+void TParseContext::boolCheck(const TSourceLoc& loc, const TIntermTyped* type)
+{
+ if (type->getBasicType() != EbtBool || type->isArray() || type->isMatrix() || type->isVector())
+ error(loc, "boolean expression expected", "", "");
+}
+
+// This function checks to see if the node (for the expression) contains a scalar boolean expression or not
+void TParseContext::boolCheck(const TSourceLoc& loc, const TPublicType& pType)
+{
+ if (pType.basicType != EbtBool || pType.arraySizes || pType.matrixCols > 1 || (pType.vectorSize > 1))
+ error(loc, "boolean expression expected", "", "");
+}
+
+void TParseContext::samplerCheck(const TSourceLoc& loc, const TType& type, const TString& identifier, TIntermTyped* /*initializer*/)
+{
+ // Check that the appropriate extension is enabled if external sampler is used.
+ // There are two extensions. The correct one must be used based on GLSL version.
+ if (type.getBasicType() == EbtSampler && type.getSampler().external) {
+ if (version < 300) {
+ requireExtensions(loc, 1, &E_GL_OES_EGL_image_external, "samplerExternalOES");
+ } else {
+ requireExtensions(loc, 1, &E_GL_OES_EGL_image_external_essl3, "samplerExternalOES");
+ }
+ }
+ if (type.getSampler().yuv) {
+ requireExtensions(loc, 1, &E_GL_EXT_YUV_target, "__samplerExternal2DY2YEXT");
+ }
+
+ if (type.getQualifier().storage == EvqUniform)
+ return;
+
+ if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtSampler))
+ error(loc, "non-uniform struct contains a sampler or image:", type.getBasicTypeString().c_str(), identifier.c_str());
+ else if (type.getBasicType() == EbtSampler && type.getQualifier().storage != EvqUniform) {
+ // non-uniform sampler
+ // not yet: okay if it has an initializer
+ // if (! initializer)
+ error(loc, "sampler/image types can only be used in uniform variables or function parameters:", type.getBasicTypeString().c_str(), identifier.c_str());
+ }
+}
+
+void TParseContext::atomicUintCheck(const TSourceLoc& loc, const TType& type, const TString& identifier)
+{
+ if (type.getQualifier().storage == EvqUniform)
+ return;
+
+ if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtAtomicUint))
+ error(loc, "non-uniform struct contains an atomic_uint:", type.getBasicTypeString().c_str(), identifier.c_str());
+ else if (type.getBasicType() == EbtAtomicUint && type.getQualifier().storage != EvqUniform)
+ error(loc, "atomic_uints can only be used in uniform variables or function parameters:", type.getBasicTypeString().c_str(), identifier.c_str());
+}
+#ifdef NV_EXTENSIONS
+void TParseContext::accStructNVCheck(const TSourceLoc& loc, const TType& type, const TString& identifier)
+{
+ if (type.getQualifier().storage == EvqUniform)
+ return;
+
+ if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtAccStructNV))
+ error(loc, "non-uniform struct contains an accelerationStructureNV:", type.getBasicTypeString().c_str(), identifier.c_str());
+ else if (type.getBasicType() == EbtAccStructNV && type.getQualifier().storage != EvqUniform)
+ error(loc, "accelerationStructureNV can only be used in uniform variables or function parameters:",
+ type.getBasicTypeString().c_str(), identifier.c_str());
+
+}
+#endif
+
+void TParseContext::transparentOpaqueCheck(const TSourceLoc& loc, const TType& type, const TString& identifier)
+{
+ if (parsingBuiltins)
+ return;
+
+ if (type.getQualifier().storage != EvqUniform)
+ return;
+
+ if (type.containsNonOpaque()) {
+ // Vulkan doesn't allow transparent uniforms outside of blocks
+ if (spvVersion.vulkan > 0)
+ vulkanRemoved(loc, "non-opaque uniforms outside a block");
+ // OpenGL wants locations on these (unless they are getting automapped)
+ if (spvVersion.openGl > 0 && !type.getQualifier().hasLocation() && !intermediate.getAutoMapLocations())
+ error(loc, "non-opaque uniform variables need a layout(location=L)", identifier.c_str(), "");
+ }
+}
+
+//
+// Qualifier checks knowing the qualifier and that it is a member of a struct/block.
+//
+void TParseContext::memberQualifierCheck(glslang::TPublicType& publicType)
+{
+ globalQualifierFixCheck(publicType.loc, publicType.qualifier);
+ checkNoShaderLayouts(publicType.loc, publicType.shaderQualifiers);
+ if (publicType.qualifier.isNonUniform()) {
+ error(publicType.loc, "not allowed on block or structure members", "nonuniformEXT", "");
+ publicType.qualifier.nonUniform = false;
+ }
+}
+
+//
+// Check/fix just a full qualifier (no variables or types yet, but qualifier is complete) at global level.
+//
+void TParseContext::globalQualifierFixCheck(const TSourceLoc& loc, TQualifier& qualifier)
+{
+ bool nonuniformOkay = false;
+
+ // move from parameter/unknown qualifiers to pipeline in/out qualifiers
+ switch (qualifier.storage) {
+ case EvqIn:
+ profileRequires(loc, ENoProfile, 130, nullptr, "in for stage inputs");
+ profileRequires(loc, EEsProfile, 300, nullptr, "in for stage inputs");
+ qualifier.storage = EvqVaryingIn;
+ nonuniformOkay = true;
+ break;
+ case EvqOut:
+ profileRequires(loc, ENoProfile, 130, nullptr, "out for stage outputs");
+ profileRequires(loc, EEsProfile, 300, nullptr, "out for stage outputs");
+ qualifier.storage = EvqVaryingOut;
+ break;
+ case EvqInOut:
+ qualifier.storage = EvqVaryingIn;
+ error(loc, "cannot use 'inout' at global scope", "", "");
+ break;
+ case EvqGlobal:
+ case EvqTemporary:
+ nonuniformOkay = true;
+ break;
+ default:
+ break;
+ }
+
+ if (!nonuniformOkay && qualifier.nonUniform)
+ error(loc, "for non-parameter, can only apply to 'in' or no storage qualifier", "nonuniformEXT", "");
+
+ invariantCheck(loc, qualifier);
+}
+
+//
+// Check a full qualifier and type (no variable yet) at global level.
+//
+void TParseContext::globalQualifierTypeCheck(const TSourceLoc& loc, const TQualifier& qualifier, const TPublicType& publicType)
+{
+ if (! symbolTable.atGlobalLevel())
+ return;
+
+ if (!(publicType.userDef && publicType.userDef->getBasicType() == EbtReference)) {
+ if (qualifier.isMemoryQualifierImageAndSSBOOnly() && ! publicType.isImage() && publicType.qualifier.storage != EvqBuffer) {
+ error(loc, "memory qualifiers cannot be used on this type", "", "");
+ } else if (qualifier.isMemory() && (publicType.basicType != EbtSampler) && !publicType.qualifier.isUniformOrBuffer()) {
+ error(loc, "memory qualifiers cannot be used on this type", "", "");
+ }
+ }
+
+ if (qualifier.storage == EvqBuffer &&
+ publicType.basicType != EbtBlock &&
+ !qualifier.layoutBufferReference)
+ error(loc, "buffers can be declared only as blocks", "buffer", "");
+
+ if (qualifier.storage != EvqVaryingIn && qualifier.storage != EvqVaryingOut)
+ return;
+
+ if (publicType.shaderQualifiers.blendEquation)
+ error(loc, "can only be applied to a standalone 'out'", "blend equation", "");
+
+ // now, knowing it is a shader in/out, do all the in/out semantic checks
+
+ if (publicType.basicType == EbtBool && !parsingBuiltins) {
+ error(loc, "cannot be bool", GetStorageQualifierString(qualifier.storage), "");
+ return;
+ }
+
+ if (isTypeInt(publicType.basicType) || publicType.basicType == EbtDouble)
+ profileRequires(loc, EEsProfile, 300, nullptr, "shader input/output");
+
+ if (!qualifier.flat
+#ifdef AMD_EXTENSIONS
+ && !qualifier.explicitInterp
+#endif
+#ifdef NV_EXTENSIONS
+ && !qualifier.pervertexNV
+#endif
+ ) {
+ if (isTypeInt(publicType.basicType) ||
+ publicType.basicType == EbtDouble ||
+ (publicType.userDef && (publicType.userDef->containsBasicType(EbtInt8) ||
+ publicType.userDef->containsBasicType(EbtUint8) ||
+ publicType.userDef->containsBasicType(EbtInt16) ||
+ publicType.userDef->containsBasicType(EbtUint16) ||
+ publicType.userDef->containsBasicType(EbtInt) ||
+ publicType.userDef->containsBasicType(EbtUint) ||
+ publicType.userDef->containsBasicType(EbtInt64) ||
+ publicType.userDef->containsBasicType(EbtUint64) ||
+ publicType.userDef->containsBasicType(EbtDouble)))) {
+ if (qualifier.storage == EvqVaryingIn && language == EShLangFragment)
+ error(loc, "must be qualified as flat", TType::getBasicString(publicType.basicType), GetStorageQualifierString(qualifier.storage));
+ else if (qualifier.storage == EvqVaryingOut && language == EShLangVertex && version == 300)
+ error(loc, "must be qualified as flat", TType::getBasicString(publicType.basicType), GetStorageQualifierString(qualifier.storage));
+ }
+ }
+
+ if (qualifier.patch && qualifier.isInterpolation())
+ error(loc, "cannot use interpolation qualifiers with patch", "patch", "");
+
+#ifdef NV_EXTENSIONS
+ if (qualifier.perTaskNV && publicType.basicType != EbtBlock)
+ error(loc, "taskNV variables can be declared only as blocks", "taskNV", "");
+#endif
+
+ if (qualifier.storage == EvqVaryingIn) {
+ switch (language) {
+ case EShLangVertex:
+ if (publicType.basicType == EbtStruct) {
+ error(loc, "cannot be a structure or array", GetStorageQualifierString(qualifier.storage), "");
+ return;
+ }
+ if (publicType.arraySizes) {
+ requireProfile(loc, ~EEsProfile, "vertex input arrays");
+ profileRequires(loc, ENoProfile, 150, nullptr, "vertex input arrays");
+ }
+ if (publicType.basicType == EbtDouble)
+ profileRequires(loc, ~EEsProfile, 410, nullptr, "vertex-shader `double` type input");
+ if (qualifier.isAuxiliary() || qualifier.isInterpolation() || qualifier.isMemory() || qualifier.invariant)
+ error(loc, "vertex input cannot be further qualified", "", "");
+ break;
+
+ case EShLangTessControl:
+ if (qualifier.patch)
+ error(loc, "can only use on output in tessellation-control shader", "patch", "");
+ break;
+
+ case EShLangTessEvaluation:
+ break;
+
+ case EShLangGeometry:
+ break;
+
+ case EShLangFragment:
+ if (publicType.userDef) {
+ profileRequires(loc, EEsProfile, 300, nullptr, "fragment-shader struct input");
+ profileRequires(loc, ~EEsProfile, 150, nullptr, "fragment-shader struct input");
+ if (publicType.userDef->containsStructure())
+ requireProfile(loc, ~EEsProfile, "fragment-shader struct input containing structure");
+ if (publicType.userDef->containsArray())
+ requireProfile(loc, ~EEsProfile, "fragment-shader struct input containing an array");
+ }
+ break;
+
+ case EShLangCompute:
+ if (! symbolTable.atBuiltInLevel())
+ error(loc, "global storage input qualifier cannot be used in a compute shader", "in", "");
+ break;
+
+ default:
+ break;
+ }
+ } else {
+ // qualifier.storage == EvqVaryingOut
+ switch (language) {
+ case EShLangVertex:
+ if (publicType.userDef) {
+ profileRequires(loc, EEsProfile, 300, nullptr, "vertex-shader struct output");
+ profileRequires(loc, ~EEsProfile, 150, nullptr, "vertex-shader struct output");
+ if (publicType.userDef->containsStructure())
+ requireProfile(loc, ~EEsProfile, "vertex-shader struct output containing structure");
+ if (publicType.userDef->containsArray())
+ requireProfile(loc, ~EEsProfile, "vertex-shader struct output containing an array");
+ }
+
+ break;
+
+ case EShLangTessControl:
+ break;
+
+ case EShLangTessEvaluation:
+ if (qualifier.patch)
+ error(loc, "can only use on input in tessellation-evaluation shader", "patch", "");
+ break;
+
+ case EShLangGeometry:
+ break;
+
+ case EShLangFragment:
+ profileRequires(loc, EEsProfile, 300, nullptr, "fragment shader output");
+ if (publicType.basicType == EbtStruct) {
+ error(loc, "cannot be a structure", GetStorageQualifierString(qualifier.storage), "");
+ return;
+ }
+ if (publicType.matrixRows > 0) {
+ error(loc, "cannot be a matrix", GetStorageQualifierString(qualifier.storage), "");
+ return;
+ }
+ if (qualifier.isAuxiliary())
+ error(loc, "can't use auxiliary qualifier on a fragment output", "centroid/sample/patch", "");
+ if (qualifier.isInterpolation())
+ error(loc, "can't use interpolation qualifier on a fragment output", "flat/smooth/noperspective", "");
+ if (publicType.basicType == EbtDouble || publicType.basicType == EbtInt64 || publicType.basicType == EbtUint64)
+ error(loc, "cannot contain a double, int64, or uint64", GetStorageQualifierString(qualifier.storage), "");
+ break;
+
+ case EShLangCompute:
+ error(loc, "global storage output qualifier cannot be used in a compute shader", "out", "");
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+//
+// Merge characteristics of the 'src' qualifier into the 'dst'.
+// If there is duplication, issue error messages, unless 'force'
+// is specified, which means to just override default settings.
+//
+// Also, when force is false, it will be assumed that 'src' follows
+// 'dst', for the purpose of error checking order for versions
+// that require specific orderings of qualifiers.
+//
+void TParseContext::mergeQualifiers(const TSourceLoc& loc, TQualifier& dst, const TQualifier& src, bool force)
+{
+ // Multiple auxiliary qualifiers (mostly done later by 'individual qualifiers')
+ if (src.isAuxiliary() && dst.isAuxiliary())
+ error(loc, "can only have one auxiliary qualifier (centroid, patch, and sample)", "", "");
+
+ // Multiple interpolation qualifiers (mostly done later by 'individual qualifiers')
+ if (src.isInterpolation() && dst.isInterpolation())
+#ifdef AMD_EXTENSIONS
+ error(loc, "can only have one interpolation qualifier (flat, smooth, noperspective, __explicitInterpAMD)", "", "");
+#else
+ error(loc, "can only have one interpolation qualifier (flat, smooth, noperspective)", "", "");
+#endif
+
+ // Ordering
+ if (! force && ((profile != EEsProfile && version < 420) ||
+ (profile == EEsProfile && version < 310))
+ && ! extensionTurnedOn(E_GL_ARB_shading_language_420pack)) {
+ // non-function parameters
+ if (src.noContraction && (dst.invariant || dst.isInterpolation() || dst.isAuxiliary() || dst.storage != EvqTemporary || dst.precision != EpqNone))
+ error(loc, "precise qualifier must appear first", "", "");
+ if (src.invariant && (dst.isInterpolation() || dst.isAuxiliary() || dst.storage != EvqTemporary || dst.precision != EpqNone))
+ error(loc, "invariant qualifier must appear before interpolation, storage, and precision qualifiers ", "", "");
+ else if (src.isInterpolation() && (dst.isAuxiliary() || dst.storage != EvqTemporary || dst.precision != EpqNone))
+ error(loc, "interpolation qualifiers must appear before storage and precision qualifiers", "", "");
+ else if (src.isAuxiliary() && (dst.storage != EvqTemporary || dst.precision != EpqNone))
+ error(loc, "Auxiliary qualifiers (centroid, patch, and sample) must appear before storage and precision qualifiers", "", "");
+ else if (src.storage != EvqTemporary && (dst.precision != EpqNone))
+ error(loc, "precision qualifier must appear as last qualifier", "", "");
+
+ // function parameters
+ if (src.noContraction && (dst.storage == EvqConst || dst.storage == EvqIn || dst.storage == EvqOut))
+ error(loc, "precise qualifier must appear first", "", "");
+ if (src.storage == EvqConst && (dst.storage == EvqIn || dst.storage == EvqOut))
+ error(loc, "in/out must appear before const", "", "");
+ }
+
+ // Storage qualification
+ if (dst.storage == EvqTemporary || dst.storage == EvqGlobal)
+ dst.storage = src.storage;
+ else if ((dst.storage == EvqIn && src.storage == EvqOut) ||
+ (dst.storage == EvqOut && src.storage == EvqIn))
+ dst.storage = EvqInOut;
+ else if ((dst.storage == EvqIn && src.storage == EvqConst) ||
+ (dst.storage == EvqConst && src.storage == EvqIn))
+ dst.storage = EvqConstReadOnly;
+ else if (src.storage != EvqTemporary &&
+ src.storage != EvqGlobal)
+ error(loc, "too many storage qualifiers", GetStorageQualifierString(src.storage), "");
+
+ // Precision qualifiers
+ if (! force && src.precision != EpqNone && dst.precision != EpqNone)
+ error(loc, "only one precision qualifier allowed", GetPrecisionQualifierString(src.precision), "");
+ if (dst.precision == EpqNone || (force && src.precision != EpqNone))
+ dst.precision = src.precision;
+
+ if (!force && ((src.coherent && (dst.devicecoherent || dst.queuefamilycoherent || dst.workgroupcoherent || dst.subgroupcoherent)) ||
+ (src.devicecoherent && (dst.coherent || dst.queuefamilycoherent || dst.workgroupcoherent || dst.subgroupcoherent)) ||
+ (src.queuefamilycoherent && (dst.coherent || dst.devicecoherent || dst.workgroupcoherent || dst.subgroupcoherent)) ||
+ (src.workgroupcoherent && (dst.coherent || dst.devicecoherent || dst.queuefamilycoherent || dst.subgroupcoherent)) ||
+ (src.subgroupcoherent && (dst.coherent || dst.devicecoherent || dst.queuefamilycoherent || dst.workgroupcoherent)))) {
+ error(loc, "only one coherent/devicecoherent/queuefamilycoherent/workgroupcoherent/subgroupcoherent qualifier allowed", GetPrecisionQualifierString(src.precision), "");
+ }
+ // Layout qualifiers
+ mergeObjectLayoutQualifiers(dst, src, false);
+
+ // individual qualifiers
+ bool repeated = false;
+ #define MERGE_SINGLETON(field) repeated |= dst.field && src.field; dst.field |= src.field;
+ MERGE_SINGLETON(invariant);
+ MERGE_SINGLETON(noContraction);
+ MERGE_SINGLETON(centroid);
+ MERGE_SINGLETON(smooth);
+ MERGE_SINGLETON(flat);
+ MERGE_SINGLETON(nopersp);
+#ifdef AMD_EXTENSIONS
+ MERGE_SINGLETON(explicitInterp);
+#endif
+#ifdef NV_EXTENSIONS
+ MERGE_SINGLETON(perPrimitiveNV);
+ MERGE_SINGLETON(perViewNV);
+ MERGE_SINGLETON(perTaskNV);
+#endif
+ MERGE_SINGLETON(patch);
+ MERGE_SINGLETON(sample);
+ MERGE_SINGLETON(coherent);
+ MERGE_SINGLETON(devicecoherent);
+ MERGE_SINGLETON(queuefamilycoherent);
+ MERGE_SINGLETON(workgroupcoherent);
+ MERGE_SINGLETON(subgroupcoherent);
+ MERGE_SINGLETON(nonprivate);
+ MERGE_SINGLETON(volatil);
+ MERGE_SINGLETON(restrict);
+ MERGE_SINGLETON(readonly);
+ MERGE_SINGLETON(writeonly);
+ MERGE_SINGLETON(specConstant);
+ MERGE_SINGLETON(nonUniform);
+
+ if (repeated)
+ error(loc, "replicated qualifiers", "", "");
+}
+
+void TParseContext::setDefaultPrecision(const TSourceLoc& loc, TPublicType& publicType, TPrecisionQualifier qualifier)
+{
+ TBasicType basicType = publicType.basicType;
+
+ if (basicType == EbtSampler) {
+ defaultSamplerPrecision[computeSamplerTypeIndex(publicType.sampler)] = qualifier;
+
+ return; // all is well
+ }
+
+ if (basicType == EbtInt || basicType == EbtFloat) {
+ if (publicType.isScalar()) {
+ defaultPrecision[basicType] = qualifier;
+ if (basicType == EbtInt) {
+ defaultPrecision[EbtUint] = qualifier;
+ precisionManager.explicitIntDefaultSeen();
+ } else
+ precisionManager.explicitFloatDefaultSeen();
+
+ return; // all is well
+ }
+ }
+
+ if (basicType == EbtAtomicUint) {
+ if (qualifier != EpqHigh)
+ error(loc, "can only apply highp to atomic_uint", "precision", "");
+
+ return;
+ }
+
+ error(loc, "cannot apply precision statement to this type; use 'float', 'int' or a sampler type", TType::getBasicString(basicType), "");
+}
+
+// used to flatten the sampler type space into a single dimension
+// correlates with the declaration of defaultSamplerPrecision[]
+int TParseContext::computeSamplerTypeIndex(TSampler& sampler)
+{
+ int arrayIndex = sampler.arrayed ? 1 : 0;
+ int shadowIndex = sampler.shadow ? 1 : 0;
+ int externalIndex = sampler.external? 1 : 0;
+ int imageIndex = sampler.image ? 1 : 0;
+ int msIndex = sampler.ms ? 1 : 0;
+
+ int flattened = EsdNumDims * (EbtNumTypes * (2 * (2 * (2 * (2 * arrayIndex + msIndex) + imageIndex) + shadowIndex) +
+ externalIndex) + sampler.type) + sampler.dim;
+ assert(flattened < maxSamplerIndex);
+
+ return flattened;
+}
+
+TPrecisionQualifier TParseContext::getDefaultPrecision(TPublicType& publicType)
+{
+ if (publicType.basicType == EbtSampler)
+ return defaultSamplerPrecision[computeSamplerTypeIndex(publicType.sampler)];
+ else
+ return defaultPrecision[publicType.basicType];
+}
+
+void TParseContext::precisionQualifierCheck(const TSourceLoc& loc, TBasicType baseType, TQualifier& qualifier)
+{
+ // Built-in symbols are allowed some ambiguous precisions, to be pinned down
+ // later by context.
+ if (! obeyPrecisionQualifiers() || parsingBuiltins)
+ return;
+
+ if (baseType == EbtAtomicUint && qualifier.precision != EpqNone && qualifier.precision != EpqHigh)
+ error(loc, "atomic counters can only be highp", "atomic_uint", "");
+
+ if (baseType == EbtFloat || baseType == EbtUint || baseType == EbtInt || baseType == EbtSampler || baseType == EbtAtomicUint) {
+ if (qualifier.precision == EpqNone) {
+ if (relaxedErrors())
+ warn(loc, "type requires declaration of default precision qualifier", TType::getBasicString(baseType), "substituting 'mediump'");
+ else
+ error(loc, "type requires declaration of default precision qualifier", TType::getBasicString(baseType), "");
+ qualifier.precision = EpqMedium;
+ defaultPrecision[baseType] = EpqMedium;
+ }
+ } else if (qualifier.precision != EpqNone)
+ error(loc, "type cannot have precision qualifier", TType::getBasicString(baseType), "");
+}
+
+void TParseContext::parameterTypeCheck(const TSourceLoc& loc, TStorageQualifier qualifier, const TType& type)
+{
+ if ((qualifier == EvqOut || qualifier == EvqInOut) && type.isOpaque())
+ error(loc, "samplers and atomic_uints cannot be output parameters", type.getBasicTypeString().c_str(), "");
+
+ if (!parsingBuiltins && type.containsBasicType(EbtFloat16))
+ requireFloat16Arithmetic(loc, type.getBasicTypeString().c_str(), "float16 types can only be in uniform block or buffer storage");
+ if (!parsingBuiltins && type.contains16BitInt())
+ requireInt16Arithmetic(loc, type.getBasicTypeString().c_str(), "(u)int16 types can only be in uniform block or buffer storage");
+ if (!parsingBuiltins && type.contains8BitInt())
+ requireInt8Arithmetic(loc, type.getBasicTypeString().c_str(), "(u)int8 types can only be in uniform block or buffer storage");
+}
+
+bool TParseContext::containsFieldWithBasicType(const TType& type, TBasicType basicType)
+{
+ if (type.getBasicType() == basicType)
+ return true;
+
+ if (type.getBasicType() == EbtStruct) {
+ const TTypeList& structure = *type.getStruct();
+ for (unsigned int i = 0; i < structure.size(); ++i) {
+ if (containsFieldWithBasicType(*structure[i].type, basicType))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//
+// Do size checking for an array type's size.
+//
+void TParseContext::arraySizeCheck(const TSourceLoc& loc, TIntermTyped* expr, TArraySize& sizePair, const char *sizeType)
+{
+ bool isConst = false;
+ sizePair.node = nullptr;
+
+ int size = 1;
+
+ TIntermConstantUnion* constant = expr->getAsConstantUnion();
+ if (constant) {
+ // handle true (non-specialization) constant
+ size = constant->getConstArray()[0].getIConst();
+ isConst = true;
+ } else {
+ // see if it's a specialization constant instead
+ if (expr->getQualifier().isSpecConstant()) {
+ isConst = true;
+ sizePair.node = expr;
+ TIntermSymbol* symbol = expr->getAsSymbolNode();
+ if (symbol && symbol->getConstArray().size() > 0)
+ size = symbol->getConstArray()[0].getIConst();
+ } else if (expr->getAsUnaryNode() &&
+ expr->getAsUnaryNode()->getOp() == glslang::EOpArrayLength &&
+ expr->getAsUnaryNode()->getOperand()->getType().isCoopMat()) {
+ isConst = true;
+ size = 1;
+ sizePair.node = expr->getAsUnaryNode();
+ }
+ }
+
+ sizePair.size = size;
+
+ if (! isConst || (expr->getBasicType() != EbtInt && expr->getBasicType() != EbtUint)) {
+ error(loc, sizeType, "", "must be a constant integer expression");
+ return;
+ }
+
+ if (size <= 0) {
+ error(loc, sizeType, "", "must be a positive integer");
+ return;
+ }
+}
+
+//
+// See if this qualifier can be an array.
+//
+// Returns true if there is an error.
+//
+bool TParseContext::arrayQualifierError(const TSourceLoc& loc, const TQualifier& qualifier)
+{
+ if (qualifier.storage == EvqConst) {
+ profileRequires(loc, ENoProfile, 120, E_GL_3DL_array_objects, "const array");
+ profileRequires(loc, EEsProfile, 300, nullptr, "const array");
+ }
+
+ if (qualifier.storage == EvqVaryingIn && language == EShLangVertex) {
+ requireProfile(loc, ~EEsProfile, "vertex input arrays");
+ profileRequires(loc, ENoProfile, 150, nullptr, "vertex input arrays");
+ }
+
+ return false;
+}
+
+//
+// See if this qualifier and type combination can be an array.
+// Assumes arrayQualifierError() was also called to catch the type-invariant tests.
+//
+// Returns true if there is an error.
+//
+bool TParseContext::arrayError(const TSourceLoc& loc, const TType& type)
+{
+ if (type.getQualifier().storage == EvqVaryingOut && language == EShLangVertex) {
+ if (type.isArrayOfArrays())
+ requireProfile(loc, ~EEsProfile, "vertex-shader array-of-array output");
+ else if (type.isStruct())
+ requireProfile(loc, ~EEsProfile, "vertex-shader array-of-struct output");
+ }
+ if (type.getQualifier().storage == EvqVaryingIn && language == EShLangFragment) {
+ if (type.isArrayOfArrays())
+ requireProfile(loc, ~EEsProfile, "fragment-shader array-of-array input");
+ else if (type.isStruct())
+ requireProfile(loc, ~EEsProfile, "fragment-shader array-of-struct input");
+ }
+ if (type.getQualifier().storage == EvqVaryingOut && language == EShLangFragment) {
+ if (type.isArrayOfArrays())
+ requireProfile(loc, ~EEsProfile, "fragment-shader array-of-array output");
+ }
+
+ return false;
+}
+
+//
+// Require array to be completely sized
+//
+void TParseContext::arraySizeRequiredCheck(const TSourceLoc& loc, const TArraySizes& arraySizes)
+{
+ if (!parsingBuiltins && arraySizes.hasUnsized())
+ error(loc, "array size required", "", "");
+}
+
+void TParseContext::structArrayCheck(const TSourceLoc& /*loc*/, const TType& type)
+{
+ const TTypeList& structure = *type.getStruct();
+ for (int m = 0; m < (int)structure.size(); ++m) {
+ const TType& member = *structure[m].type;
+ if (member.isArray())
+ arraySizeRequiredCheck(structure[m].loc, *member.getArraySizes());
+ }
+}
+
+void TParseContext::arraySizesCheck(const TSourceLoc& loc, const TQualifier& qualifier, TArraySizes* arraySizes,
+ const TIntermTyped* initializer, bool lastMember)
+{
+ assert(arraySizes);
+
+ // always allow special built-in ins/outs sized to topologies
+ if (parsingBuiltins)
+ return;
+
+ // initializer must be a sized array, in which case
+ // allow the initializer to set any unknown array sizes
+ if (initializer != nullptr) {
+ if (initializer->getType().isUnsizedArray())
+ error(loc, "array initializer must be sized", "[]", "");
+ return;
+ }
+
+ // No environment allows any non-outer-dimension to be implicitly sized
+ if (arraySizes->isInnerUnsized()) {
+ error(loc, "only outermost dimension of an array of arrays can be implicitly sized", "[]", "");
+ arraySizes->clearInnerUnsized();
+ }
+
+ if (arraySizes->isInnerSpecialization() &&
+ (qualifier.storage != EvqTemporary && qualifier.storage != EvqGlobal && qualifier.storage != EvqShared && qualifier.storage != EvqConst))
+ error(loc, "only outermost dimension of an array of arrays can be a specialization constant", "[]", "");
+
+ // desktop always allows outer-dimension-unsized variable arrays,
+ if (profile != EEsProfile)
+ return;
+
+ // for ES, if size isn't coming from an initializer, it has to be explicitly declared now,
+ // with very few exceptions
+
+ // last member of ssbo block exception:
+ if (qualifier.storage == EvqBuffer && lastMember)
+ return;
+
+ // implicitly-sized io exceptions:
+ switch (language) {
+ case EShLangGeometry:
+ if (qualifier.storage == EvqVaryingIn)
+ if ((profile == EEsProfile && version >= 320) ||
+ extensionsTurnedOn(Num_AEP_geometry_shader, AEP_geometry_shader))
+ return;
+ break;
+ case EShLangTessControl:
+ if ( qualifier.storage == EvqVaryingIn ||
+ (qualifier.storage == EvqVaryingOut && ! qualifier.patch))
+ if ((profile == EEsProfile && version >= 320) ||
+ extensionsTurnedOn(Num_AEP_tessellation_shader, AEP_tessellation_shader))
+ return;
+ break;
+ case EShLangTessEvaluation:
+ if ((qualifier.storage == EvqVaryingIn && ! qualifier.patch) ||
+ qualifier.storage == EvqVaryingOut)
+ if ((profile == EEsProfile && version >= 320) ||
+ extensionsTurnedOn(Num_AEP_tessellation_shader, AEP_tessellation_shader))
+ return;
+ break;
+#ifdef NV_EXTENSIONS
+ case EShLangMeshNV:
+ if (qualifier.storage == EvqVaryingOut)
+ if ((profile == EEsProfile && version >= 320) ||
+ extensionTurnedOn(E_GL_NV_mesh_shader))
+ return;
+ break;
+#endif
+ default:
+ break;
+ }
+
+ arraySizeRequiredCheck(loc, *arraySizes);
+}
+
+void TParseContext::arrayOfArrayVersionCheck(const TSourceLoc& loc, const TArraySizes* sizes)
+{
+ if (sizes == nullptr || sizes->getNumDims() == 1)
+ return;
+
+ const char* feature = "arrays of arrays";
+
+ requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, feature);
+ profileRequires(loc, EEsProfile, 310, nullptr, feature);
+ profileRequires(loc, ECoreProfile | ECompatibilityProfile, 430, nullptr, feature);
+}
+
+//
+// Do all the semantic checking for declaring or redeclaring an array, with and
+// without a size, and make the right changes to the symbol table.
+//
+void TParseContext::declareArray(const TSourceLoc& loc, const TString& identifier, const TType& type, TSymbol*& symbol)
+{
+ if (symbol == nullptr) {
+ bool currentScope;
+ symbol = symbolTable.find(identifier, nullptr, &currentScope);
+
+ if (symbol && builtInName(identifier) && ! symbolTable.atBuiltInLevel()) {
+ // bad shader (errors already reported) trying to redeclare a built-in name as an array
+ symbol = nullptr;
+ return;
+ }
+ if (symbol == nullptr || ! currentScope) {
+ //
+ // Successfully process a new definition.
+ // (Redeclarations have to take place at the same scope; otherwise they are hiding declarations)
+ //
+ symbol = new TVariable(&identifier, type);
+ symbolTable.insert(*symbol);
+ if (symbolTable.atGlobalLevel())
+ trackLinkage(*symbol);
+
+ if (! symbolTable.atBuiltInLevel()) {
+ if (isIoResizeArray(type)) {
+ ioArraySymbolResizeList.push_back(symbol);
+ checkIoArraysConsistency(loc, true);
+ } else
+ fixIoArraySize(loc, symbol->getWritableType());
+ }
+
+ return;
+ }
+ if (symbol->getAsAnonMember()) {
+ error(loc, "cannot redeclare a user-block member array", identifier.c_str(), "");
+ symbol = nullptr;
+ return;
+ }
+ }
+
+ //
+ // Process a redeclaration.
+ //
+
+ if (symbol == nullptr) {
+ error(loc, "array variable name expected", identifier.c_str(), "");
+ return;
+ }
+
+ // redeclareBuiltinVariable() should have already done the copyUp()
+ TType& existingType = symbol->getWritableType();
+
+ if (! existingType.isArray()) {
+ error(loc, "redeclaring non-array as array", identifier.c_str(), "");
+ return;
+ }
+
+ if (! existingType.sameElementType(type)) {
+ error(loc, "redeclaration of array with a different element type", identifier.c_str(), "");
+ return;
+ }
+
+ if (! existingType.sameInnerArrayness(type)) {
+ error(loc, "redeclaration of array with a different array dimensions or sizes", identifier.c_str(), "");
+ return;
+ }
+
+ if (existingType.isSizedArray()) {
+ // be more leniant for input arrays to geometry shaders and tessellation control outputs, where the redeclaration is the same size
+ if (! (isIoResizeArray(type) && existingType.getOuterArraySize() == type.getOuterArraySize()))
+ error(loc, "redeclaration of array with size", identifier.c_str(), "");
+ return;
+ }
+
+ arrayLimitCheck(loc, identifier, type.getOuterArraySize());
+
+ existingType.updateArraySizes(type);
+
+ if (isIoResizeArray(type))
+ checkIoArraysConsistency(loc);
+}
+
+// Policy and error check for needing a runtime sized array.
+void TParseContext::checkRuntimeSizable(const TSourceLoc& loc, const TIntermTyped& base)
+{
+ // runtime length implies runtime sizeable, so no problem
+ if (isRuntimeLength(base))
+ return;
+
+ // Check for last member of a bufferreference type, which is runtime sizeable
+ // but doesn't support runtime length
+ if (base.getType().getQualifier().storage == EvqBuffer) {
+ const TIntermBinary* binary = base.getAsBinaryNode();
+ if (binary != nullptr &&
+ binary->getOp() == EOpIndexDirectStruct &&
+ binary->getLeft()->getBasicType() == EbtReference) {
+
+ const int index = binary->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst();
+ const int memberCount = (int)binary->getLeft()->getType().getReferentType()->getStruct()->size();
+ if (index == memberCount - 1)
+ return;
+ }
+ }
+
+ // check for additional things allowed by GL_EXT_nonuniform_qualifier
+ if (base.getBasicType() == EbtSampler ||
+ (base.getBasicType() == EbtBlock && base.getType().getQualifier().isUniformOrBuffer()))
+ requireExtensions(loc, 1, &E_GL_EXT_nonuniform_qualifier, "variable index");
+ else
+ error(loc, "", "[", "array must be redeclared with a size before being indexed with a variable");
+}
+
+// Policy decision for whether a run-time .length() is allowed.
+bool TParseContext::isRuntimeLength(const TIntermTyped& base) const
+{
+ if (base.getType().getQualifier().storage == EvqBuffer) {
+ // in a buffer block
+ const TIntermBinary* binary = base.getAsBinaryNode();
+ if (binary != nullptr && binary->getOp() == EOpIndexDirectStruct) {
+ // is it the last member?
+ const int index = binary->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst();
+
+ if (binary->getLeft()->getBasicType() == EbtReference)
+ return false;
+
+ const int memberCount = (int)binary->getLeft()->getType().getStruct()->size();
+ if (index == memberCount - 1)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#ifdef NV_EXTENSIONS
+// Fix mesh view output array dimension
+void TParseContext::resizeMeshViewDimension(const TSourceLoc& loc, TType& type)
+{
+ // see if member is a per-view attribute
+ if (type.getQualifier().isPerView()) {
+ // since we don't have the maxMeshViewCountNV set during parsing builtins, we hardcode the value
+ int maxViewCount = parsingBuiltins ? 4 : resources.maxMeshViewCountNV;
+
+ if (! type.isArray()) {
+ error(loc, "requires an view array dimension", "perviewNV", "");
+ }
+ else if (!type.isUnsizedArray() && type.getOuterArraySize() != maxViewCount) {
+ error(loc, "mesh view output array size must be gl_MaxMeshViewCountNV or implicitly sized", "[]", "");
+ }
+ else if (type.isUnsizedArray()) {
+ type.changeOuterArraySize(maxViewCount);
+ }
+ }
+}
+#endif
+
+// Returns true if the first argument to the #line directive is the line number for the next line.
+//
+// Desktop, pre-version 3.30: "After processing this directive
+// (including its new-line), the implementation will behave as if it is compiling at line number line+1 and
+// source string number source-string-number."
+//
+// Desktop, version 3.30 and later, and ES: "After processing this directive
+// (including its new-line), the implementation will behave as if it is compiling at line number line and
+// source string number source-string-number.
+bool TParseContext::lineDirectiveShouldSetNextLine() const
+{
+ return profile == EEsProfile || version >= 330;
+}
+
+//
+// Enforce non-initializer type/qualifier rules.
+//
+void TParseContext::nonInitConstCheck(const TSourceLoc& loc, TString& identifier, TType& type)
+{
+ //
+ // Make the qualifier make sense, given that there is not an initializer.
+ //
+ if (type.getQualifier().storage == EvqConst ||
+ type.getQualifier().storage == EvqConstReadOnly) {
+ type.getQualifier().makeTemporary();
+ error(loc, "variables with qualifier 'const' must be initialized", identifier.c_str(), "");
+ }
+}
+
+//
+// See if the identifier is a built-in symbol that can be redeclared, and if so,
+// copy the symbol table's read-only built-in variable to the current
+// global level, where it can be modified based on the passed in type.
+//
+// Returns nullptr if no redeclaration took place; meaning a normal declaration still
+// needs to occur for it, not necessarily an error.
+//
+// Returns a redeclared and type-modified variable if a redeclarated occurred.
+//
+TSymbol* TParseContext::redeclareBuiltinVariable(const TSourceLoc& loc, const TString& identifier,
+ const TQualifier& qualifier, const TShaderQualifiers& publicType)
+{
+ if (! builtInName(identifier) || symbolTable.atBuiltInLevel() || ! symbolTable.atGlobalLevel())
+ return nullptr;
+
+ bool nonEsRedecls = (profile != EEsProfile && (version >= 130 || identifier == "gl_TexCoord"));
+ bool esRedecls = (profile == EEsProfile &&
+ (version >= 320 || extensionsTurnedOn(Num_AEP_shader_io_blocks, AEP_shader_io_blocks)));
+ if (! esRedecls && ! nonEsRedecls)
+ return nullptr;
+
+ // Special case when using GL_ARB_separate_shader_objects
+ bool ssoPre150 = false; // means the only reason this variable is redeclared is due to this combination
+ if (profile != EEsProfile && version <= 140 && extensionTurnedOn(E_GL_ARB_separate_shader_objects)) {
+ if (identifier == "gl_Position" ||
+ identifier == "gl_PointSize" ||
+ identifier == "gl_ClipVertex" ||
+ identifier == "gl_FogFragCoord")
+ ssoPre150 = true;
+ }
+
+ // Potentially redeclaring a built-in variable...
+
+ if (ssoPre150 ||
+ (identifier == "gl_FragDepth" && ((nonEsRedecls && version >= 420) || esRedecls)) ||
+ (identifier == "gl_FragCoord" && ((nonEsRedecls && version >= 150) || esRedecls)) ||
+ identifier == "gl_ClipDistance" ||
+ identifier == "gl_CullDistance" ||
+ identifier == "gl_FrontColor" ||
+ identifier == "gl_BackColor" ||
+ identifier == "gl_FrontSecondaryColor" ||
+ identifier == "gl_BackSecondaryColor" ||
+ identifier == "gl_SecondaryColor" ||
+ (identifier == "gl_Color" && language == EShLangFragment) ||
+ (identifier == "gl_FragStencilRefARB" && (nonEsRedecls && version >= 140)
+ && language == EShLangFragment) ||
+#ifdef NV_EXTENSIONS
+ identifier == "gl_SampleMask" ||
+ identifier == "gl_Layer" ||
+ identifier == "gl_PrimitiveIndicesNV" ||
+#endif
+ identifier == "gl_TexCoord") {
+
+ // Find the existing symbol, if any.
+ bool builtIn;
+ TSymbol* symbol = symbolTable.find(identifier, &builtIn);
+
+ // If the symbol was not found, this must be a version/profile/stage
+ // that doesn't have it.
+ if (! symbol)
+ return nullptr;
+
+ // If it wasn't at a built-in level, then it's already been redeclared;
+ // that is, this is a redeclaration of a redeclaration; reuse that initial
+ // redeclaration. Otherwise, make the new one.
+ if (builtIn)
+ makeEditable(symbol);
+
+ // Now, modify the type of the copy, as per the type of the current redeclaration.
+
+ TQualifier& symbolQualifier = symbol->getWritableType().getQualifier();
+ if (ssoPre150) {
+ if (intermediate.inIoAccessed(identifier))
+ error(loc, "cannot redeclare after use", identifier.c_str(), "");
+ if (qualifier.hasLayout())
+ error(loc, "cannot apply layout qualifier to", "redeclaration", symbol->getName().c_str());
+ if (qualifier.isMemory() || qualifier.isAuxiliary() || (language == EShLangVertex && qualifier.storage != EvqVaryingOut) ||
+ (language == EShLangFragment && qualifier.storage != EvqVaryingIn))
+ error(loc, "cannot change storage, memory, or auxiliary qualification of", "redeclaration", symbol->getName().c_str());
+ if (! qualifier.smooth)
+ error(loc, "cannot change interpolation qualification of", "redeclaration", symbol->getName().c_str());
+ } else if (identifier == "gl_FrontColor" ||
+ identifier == "gl_BackColor" ||
+ identifier == "gl_FrontSecondaryColor" ||
+ identifier == "gl_BackSecondaryColor" ||
+ identifier == "gl_SecondaryColor" ||
+ identifier == "gl_Color") {
+ symbolQualifier.flat = qualifier.flat;
+ symbolQualifier.smooth = qualifier.smooth;
+ symbolQualifier.nopersp = qualifier.nopersp;
+ if (qualifier.hasLayout())
+ error(loc, "cannot apply layout qualifier to", "redeclaration", symbol->getName().c_str());
+ if (qualifier.isMemory() || qualifier.isAuxiliary() || symbol->getType().getQualifier().storage != qualifier.storage)
+ error(loc, "cannot change storage, memory, or auxiliary qualification of", "redeclaration", symbol->getName().c_str());
+ } else if (identifier == "gl_TexCoord" ||
+ identifier == "gl_ClipDistance" ||
+ identifier == "gl_CullDistance") {
+ if (qualifier.hasLayout() || qualifier.isMemory() || qualifier.isAuxiliary() ||
+ qualifier.nopersp != symbolQualifier.nopersp || qualifier.flat != symbolQualifier.flat ||
+ symbolQualifier.storage != qualifier.storage)
+ error(loc, "cannot change qualification of", "redeclaration", symbol->getName().c_str());
+ } else if (identifier == "gl_FragCoord") {
+ if (intermediate.inIoAccessed("gl_FragCoord"))
+ error(loc, "cannot redeclare after use", "gl_FragCoord", "");
+ if (qualifier.nopersp != symbolQualifier.nopersp || qualifier.flat != symbolQualifier.flat ||
+ qualifier.isMemory() || qualifier.isAuxiliary())
+ error(loc, "can only change layout qualification of", "redeclaration", symbol->getName().c_str());
+ if (qualifier.storage != EvqVaryingIn)
+ error(loc, "cannot change input storage qualification of", "redeclaration", symbol->getName().c_str());
+ if (! builtIn && (publicType.pixelCenterInteger != intermediate.getPixelCenterInteger() ||
+ publicType.originUpperLeft != intermediate.getOriginUpperLeft()))
+ error(loc, "cannot redeclare with different qualification:", "redeclaration", symbol->getName().c_str());
+ if (publicType.pixelCenterInteger)
+ intermediate.setPixelCenterInteger();
+ if (publicType.originUpperLeft)
+ intermediate.setOriginUpperLeft();
+ } else if (identifier == "gl_FragDepth") {
+ if (qualifier.nopersp != symbolQualifier.nopersp || qualifier.flat != symbolQualifier.flat ||
+ qualifier.isMemory() || qualifier.isAuxiliary())
+ error(loc, "can only change layout qualification of", "redeclaration", symbol->getName().c_str());
+ if (qualifier.storage != EvqVaryingOut)
+ error(loc, "cannot change output storage qualification of", "redeclaration", symbol->getName().c_str());
+ if (publicType.layoutDepth != EldNone) {
+ if (intermediate.inIoAccessed("gl_FragDepth"))
+ error(loc, "cannot redeclare after use", "gl_FragDepth", "");
+ if (! intermediate.setDepth(publicType.layoutDepth))
+ error(loc, "all redeclarations must use the same depth layout on", "redeclaration", symbol->getName().c_str());
+ }
+ }
+ else if (
+#ifdef NV_EXTENSIONS
+ identifier == "gl_PrimitiveIndicesNV" ||
+#endif
+ identifier == "gl_FragStencilRefARB") {
+ if (qualifier.hasLayout())
+ error(loc, "cannot apply layout qualifier to", "redeclaration", symbol->getName().c_str());
+ if (qualifier.storage != EvqVaryingOut)
+ error(loc, "cannot change output storage qualification of", "redeclaration", symbol->getName().c_str());
+ }
+#ifdef NV_EXTENSIONS
+ else if (identifier == "gl_SampleMask") {
+ if (!publicType.layoutOverrideCoverage) {
+ error(loc, "redeclaration only allowed for override_coverage layout", "redeclaration", symbol->getName().c_str());
+ }
+ intermediate.setLayoutOverrideCoverage();
+ }
+ else if (identifier == "gl_Layer") {
+ if (!qualifier.layoutViewportRelative && qualifier.layoutSecondaryViewportRelativeOffset == -2048)
+ error(loc, "redeclaration only allowed for viewport_relative or secondary_view_offset layout", "redeclaration", symbol->getName().c_str());
+ symbolQualifier.layoutViewportRelative = qualifier.layoutViewportRelative;
+ symbolQualifier.layoutSecondaryViewportRelativeOffset = qualifier.layoutSecondaryViewportRelativeOffset;
+ }
+#endif
+
+ // TODO: semantics quality: separate smooth from nothing declared, then use IsInterpolation for several tests above
+
+ return symbol;
+ }
+
+ return nullptr;
+}
+
+//
+// Either redeclare the requested block, or give an error message why it can't be done.
+//
+// TODO: functionality: explicitly sizing members of redeclared blocks is not giving them an explicit size
+void TParseContext::redeclareBuiltinBlock(const TSourceLoc& loc, TTypeList& newTypeList, const TString& blockName,
+ const TString* instanceName, TArraySizes* arraySizes)
+{
+ const char* feature = "built-in block redeclaration";
+ profileRequires(loc, EEsProfile, 320, Num_AEP_shader_io_blocks, AEP_shader_io_blocks, feature);
+ profileRequires(loc, ~EEsProfile, 410, E_GL_ARB_separate_shader_objects, feature);
+
+ if (blockName != "gl_PerVertex" && blockName != "gl_PerFragment"
+#ifdef NV_EXTENSIONS
+ && blockName != "gl_MeshPerVertexNV" && blockName != "gl_MeshPerPrimitiveNV"
+#endif
+ )
+ {
+ error(loc, "cannot redeclare block: ", "block declaration", blockName.c_str());
+ return;
+ }
+
+ // Redeclaring a built-in block...
+
+ if (instanceName && ! builtInName(*instanceName)) {
+ error(loc, "cannot redeclare a built-in block with a user name", instanceName->c_str(), "");
+ return;
+ }
+
+ // Blocks with instance names are easy to find, lookup the instance name,
+ // Anonymous blocks need to be found via a member.
+ bool builtIn;
+ TSymbol* block;
+ if (instanceName)
+ block = symbolTable.find(*instanceName, &builtIn);
+ else
+ block = symbolTable.find(newTypeList.front().type->getFieldName(), &builtIn);
+
+ // If the block was not found, this must be a version/profile/stage
+ // that doesn't have it, or the instance name is wrong.
+ const char* errorName = instanceName ? instanceName->c_str() : newTypeList.front().type->getFieldName().c_str();
+ if (! block) {
+ error(loc, "no declaration found for redeclaration", errorName, "");
+ return;
+ }
+ // Built-in blocks cannot be redeclared more than once, which if happened,
+ // we'd be finding the already redeclared one here, rather than the built in.
+ if (! builtIn) {
+ error(loc, "can only redeclare a built-in block once, and before any use", blockName.c_str(), "");
+ return;
+ }
+
+ // Copy the block to make a writable version, to insert into the block table after editing.
+ block = symbolTable.copyUpDeferredInsert(block);
+
+ if (block->getType().getBasicType() != EbtBlock) {
+ error(loc, "cannot redeclare a non block as a block", errorName, "");
+ return;
+ }
+
+ // Fix XFB stuff up, it applies to the order of the redeclaration, not
+ // the order of the original members.
+ if (currentBlockQualifier.storage == EvqVaryingOut && globalOutputDefaults.hasXfbBuffer()) {
+ if (!currentBlockQualifier.hasXfbBuffer())
+ currentBlockQualifier.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer;
+ if (!currentBlockQualifier.hasStream())
+ currentBlockQualifier.layoutStream = globalOutputDefaults.layoutStream;
+ fixXfbOffsets(currentBlockQualifier, newTypeList);
+ }
+
+ // Edit and error check the container against the redeclaration
+ // - remove unused members
+ // - ensure remaining qualifiers/types match
+
+ TType& type = block->getWritableType();
+
+#ifdef NV_EXTENSIONS
+ // if gl_PerVertex is redeclared for the purpose of passing through "gl_Position"
+ // for passthrough purpose, the redeclared block should have the same qualifers as
+ // the current one
+ if (currentBlockQualifier.layoutPassthrough) {
+ type.getQualifier().layoutPassthrough = currentBlockQualifier.layoutPassthrough;
+ type.getQualifier().storage = currentBlockQualifier.storage;
+ type.getQualifier().layoutStream = currentBlockQualifier.layoutStream;
+ type.getQualifier().layoutXfbBuffer = currentBlockQualifier.layoutXfbBuffer;
+ }
+#endif
+
+ TTypeList::iterator member = type.getWritableStruct()->begin();
+ size_t numOriginalMembersFound = 0;
+ while (member != type.getStruct()->end()) {
+ // look for match
+ bool found = false;
+ TTypeList::const_iterator newMember;
+ TSourceLoc memberLoc;
+ memberLoc.init();
+ for (newMember = newTypeList.begin(); newMember != newTypeList.end(); ++newMember) {
+ if (member->type->getFieldName() == newMember->type->getFieldName()) {
+ found = true;
+ memberLoc = newMember->loc;
+ break;
+ }
+ }
+
+ if (found) {
+ ++numOriginalMembersFound;
+ // - ensure match between redeclared members' types
+ // - check for things that can't be changed
+ // - update things that can be changed
+ TType& oldType = *member->type;
+ const TType& newType = *newMember->type;
+ if (! newType.sameElementType(oldType))
+ error(memberLoc, "cannot redeclare block member with a different type", member->type->getFieldName().c_str(), "");
+ if (oldType.isArray() != newType.isArray())
+ error(memberLoc, "cannot change arrayness of redeclared block member", member->type->getFieldName().c_str(), "");
+ else if (! oldType.getQualifier().isPerView() && ! oldType.sameArrayness(newType) && oldType.isSizedArray())
+ error(memberLoc, "cannot change array size of redeclared block member", member->type->getFieldName().c_str(), "");
+ else if (! oldType.getQualifier().isPerView() && newType.isArray())
+ arrayLimitCheck(loc, member->type->getFieldName(), newType.getOuterArraySize());
+#ifdef NV_EXTENSIONS
+ if (oldType.getQualifier().isPerView() && ! newType.getQualifier().isPerView())
+ error(memberLoc, "missing perviewNV qualifier to redeclared block member", member->type->getFieldName().c_str(), "");
+ else if (! oldType.getQualifier().isPerView() && newType.getQualifier().isPerView())
+ error(memberLoc, "cannot add perviewNV qualifier to redeclared block member", member->type->getFieldName().c_str(), "");
+ else if (newType.getQualifier().isPerView()) {
+ if (oldType.getArraySizes()->getNumDims() != newType.getArraySizes()->getNumDims())
+ error(memberLoc, "cannot change arrayness of redeclared block member", member->type->getFieldName().c_str(), "");
+ else if (! newType.isUnsizedArray() && newType.getOuterArraySize() != resources.maxMeshViewCountNV)
+ error(loc, "mesh view output array size must be gl_MaxMeshViewCountNV or implicitly sized", "[]", "");
+ else if (newType.getArraySizes()->getNumDims() == 2) {
+ int innerDimSize = newType.getArraySizes()->getDimSize(1);
+ arrayLimitCheck(memberLoc, member->type->getFieldName(), innerDimSize);
+ oldType.getArraySizes()->setDimSize(1, innerDimSize);
+ }
+ }
+ if (oldType.getQualifier().isPerPrimitive() && ! newType.getQualifier().isPerPrimitive())
+ error(memberLoc, "missing perprimitiveNV qualifier to redeclared block member", member->type->getFieldName().c_str(), "");
+ else if (! oldType.getQualifier().isPerPrimitive() && newType.getQualifier().isPerPrimitive())
+ error(memberLoc, "cannot add perprimitiveNV qualifier to redeclared block member", member->type->getFieldName().c_str(), "");
+#endif
+ if (newType.getQualifier().isMemory())
+ error(memberLoc, "cannot add memory qualifier to redeclared block member", member->type->getFieldName().c_str(), "");
+ if (newType.getQualifier().hasNonXfbLayout())
+ error(memberLoc, "cannot add non-XFB layout to redeclared block member", member->type->getFieldName().c_str(), "");
+ if (newType.getQualifier().patch)
+ error(memberLoc, "cannot add patch to redeclared block member", member->type->getFieldName().c_str(), "");
+ if (newType.getQualifier().hasXfbBuffer() &&
+ newType.getQualifier().layoutXfbBuffer != currentBlockQualifier.layoutXfbBuffer)
+ error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_buffer", "");
+ if (newType.getQualifier().hasStream() &&
+ newType.getQualifier().layoutStream != currentBlockQualifier.layoutStream)
+ error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_stream", "");
+ oldType.getQualifier().centroid = newType.getQualifier().centroid;
+ oldType.getQualifier().sample = newType.getQualifier().sample;
+ oldType.getQualifier().invariant = newType.getQualifier().invariant;
+ oldType.getQualifier().noContraction = newType.getQualifier().noContraction;
+ oldType.getQualifier().smooth = newType.getQualifier().smooth;
+ oldType.getQualifier().flat = newType.getQualifier().flat;
+ oldType.getQualifier().nopersp = newType.getQualifier().nopersp;
+ oldType.getQualifier().layoutXfbOffset = newType.getQualifier().layoutXfbOffset;
+ oldType.getQualifier().layoutXfbBuffer = newType.getQualifier().layoutXfbBuffer;
+ oldType.getQualifier().layoutXfbStride = newType.getQualifier().layoutXfbStride;
+ if (oldType.getQualifier().layoutXfbOffset != TQualifier::layoutXfbBufferEnd) {
+ // If any member has an xfb_offset, then the block's xfb_buffer inherents current xfb_buffer,
+ // and for xfb processing, the member needs it as well, along with xfb_stride.
+ type.getQualifier().layoutXfbBuffer = currentBlockQualifier.layoutXfbBuffer;
+ oldType.getQualifier().layoutXfbBuffer = currentBlockQualifier.layoutXfbBuffer;
+ }
+ if (oldType.isUnsizedArray() && newType.isSizedArray())
+ oldType.changeOuterArraySize(newType.getOuterArraySize());
+
+ // check and process the member's type, which will include managing xfb information
+ layoutTypeCheck(loc, oldType);
+
+ // go to next member
+ ++member;
+ } else {
+ // For missing members of anonymous blocks that have been redeclared,
+ // hide the original (shared) declaration.
+ // Instance-named blocks can just have the member removed.
+ if (instanceName)
+ member = type.getWritableStruct()->erase(member);
+ else {
+ member->type->hideMember();
+ ++member;
+ }
+ }
+ }
+
+ if (spvVersion.vulkan > 0) {
+ // ...then streams apply to built-in blocks, instead of them being only on stream 0
+ type.getQualifier().layoutStream = currentBlockQualifier.layoutStream;
+ }
+
+ if (numOriginalMembersFound < newTypeList.size())
+ error(loc, "block redeclaration has extra members", blockName.c_str(), "");
+ if (type.isArray() != (arraySizes != nullptr) ||
+ (type.isArray() && arraySizes != nullptr && type.getArraySizes()->getNumDims() != arraySizes->getNumDims()))
+ error(loc, "cannot change arrayness of redeclared block", blockName.c_str(), "");
+ else if (type.isArray()) {
+ // At this point, we know both are arrays and both have the same number of dimensions.
+
+ // It is okay for a built-in block redeclaration to be unsized, and keep the size of the
+ // original block declaration.
+ if (!arraySizes->isSized() && type.isSizedArray())
+ arraySizes->changeOuterSize(type.getOuterArraySize());
+
+ // And, okay to be giving a size to the array, by the redeclaration
+ if (!type.isSizedArray() && arraySizes->isSized())
+ type.changeOuterArraySize(arraySizes->getOuterSize());
+
+ // Now, they must match in all dimensions.
+ if (type.isSizedArray() && *type.getArraySizes() != *arraySizes)
+ error(loc, "cannot change array size of redeclared block", blockName.c_str(), "");
+ }
+
+ symbolTable.insert(*block);
+
+ // Check for general layout qualifier errors
+ layoutObjectCheck(loc, *block);
+
+ // Tracking for implicit sizing of array
+ if (isIoResizeArray(block->getType())) {
+ ioArraySymbolResizeList.push_back(block);
+ checkIoArraysConsistency(loc, true);
+ } else if (block->getType().isArray())
+ fixIoArraySize(loc, block->getWritableType());
+
+ // Save it in the AST for linker use.
+ trackLinkage(*block);
+}
+
+void TParseContext::paramCheckFixStorage(const TSourceLoc& loc, const TStorageQualifier& qualifier, TType& type)
+{
+ switch (qualifier) {
+ case EvqConst:
+ case EvqConstReadOnly:
+ type.getQualifier().storage = EvqConstReadOnly;
+ break;
+ case EvqIn:
+ case EvqOut:
+ case EvqInOut:
+ type.getQualifier().storage = qualifier;
+ break;
+ case EvqGlobal:
+ case EvqTemporary:
+ type.getQualifier().storage = EvqIn;
+ break;
+ default:
+ type.getQualifier().storage = EvqIn;
+ error(loc, "storage qualifier not allowed on function parameter", GetStorageQualifierString(qualifier), "");
+ break;
+ }
+}
+
+void TParseContext::paramCheckFix(const TSourceLoc& loc, const TQualifier& qualifier, TType& type)
+{
+ if (qualifier.isMemory()) {
+ type.getQualifier().volatil = qualifier.volatil;
+ type.getQualifier().coherent = qualifier.coherent;
+ type.getQualifier().devicecoherent = qualifier.devicecoherent ;
+ type.getQualifier().queuefamilycoherent = qualifier.queuefamilycoherent;
+ type.getQualifier().workgroupcoherent = qualifier.workgroupcoherent;
+ type.getQualifier().subgroupcoherent = qualifier.subgroupcoherent;
+ type.getQualifier().nonprivate = qualifier.nonprivate;
+ type.getQualifier().readonly = qualifier.readonly;
+ type.getQualifier().writeonly = qualifier.writeonly;
+ type.getQualifier().restrict = qualifier.restrict;
+ }
+
+ if (qualifier.isAuxiliary() ||
+ qualifier.isInterpolation())
+ error(loc, "cannot use auxiliary or interpolation qualifiers on a function parameter", "", "");
+ if (qualifier.hasLayout())
+ error(loc, "cannot use layout qualifiers on a function parameter", "", "");
+ if (qualifier.invariant)
+ error(loc, "cannot use invariant qualifier on a function parameter", "", "");
+ if (qualifier.noContraction) {
+ if (qualifier.isParamOutput())
+ type.getQualifier().noContraction = true;
+ else
+ warn(loc, "qualifier has no effect on non-output parameters", "precise", "");
+ }
+ if (qualifier.isNonUniform())
+ type.getQualifier().nonUniform = qualifier.nonUniform;
+
+ paramCheckFixStorage(loc, qualifier.storage, type);
+}
+
+void TParseContext::nestedBlockCheck(const TSourceLoc& loc)
+{
+ if (structNestingLevel > 0)
+ error(loc, "cannot nest a block definition inside a structure or block", "", "");
+ ++structNestingLevel;
+}
+
+void TParseContext::nestedStructCheck(const TSourceLoc& loc)
+{
+ if (structNestingLevel > 0)
+ error(loc, "cannot nest a structure definition inside a structure or block", "", "");
+ ++structNestingLevel;
+}
+
+void TParseContext::arrayObjectCheck(const TSourceLoc& loc, const TType& type, const char* op)
+{
+ // Some versions don't allow comparing arrays or structures containing arrays
+ if (type.containsArray()) {
+ profileRequires(loc, ENoProfile, 120, E_GL_3DL_array_objects, op);
+ profileRequires(loc, EEsProfile, 300, nullptr, op);
+ }
+}
+
+void TParseContext::opaqueCheck(const TSourceLoc& loc, const TType& type, const char* op)
+{
+ if (containsFieldWithBasicType(type, EbtSampler))
+ error(loc, "can't use with samplers or structs containing samplers", op, "");
+}
+
+void TParseContext::referenceCheck(const TSourceLoc& loc, const TType& type, const char* op)
+{
+ if (containsFieldWithBasicType(type, EbtReference))
+ error(loc, "can't use with reference types", op, "");
+}
+
+void TParseContext::storage16BitAssignmentCheck(const TSourceLoc& loc, const TType& type, const char* op)
+{
+ if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtFloat16))
+ requireFloat16Arithmetic(loc, op, "can't use with structs containing float16");
+
+ if (type.isArray() && type.getBasicType() == EbtFloat16)
+ requireFloat16Arithmetic(loc, op, "can't use with arrays containing float16");
+
+ if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtInt16))
+ requireInt16Arithmetic(loc, op, "can't use with structs containing int16");
+
+ if (type.isArray() && type.getBasicType() == EbtInt16)
+ requireInt16Arithmetic(loc, op, "can't use with arrays containing int16");
+
+ if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtUint16))
+ requireInt16Arithmetic(loc, op, "can't use with structs containing uint16");
+
+ if (type.isArray() && type.getBasicType() == EbtUint16)
+ requireInt16Arithmetic(loc, op, "can't use with arrays containing uint16");
+
+ if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtInt8))
+ requireInt8Arithmetic(loc, op, "can't use with structs containing int8");
+
+ if (type.isArray() && type.getBasicType() == EbtInt8)
+ requireInt8Arithmetic(loc, op, "can't use with arrays containing int8");
+
+ if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtUint8))
+ requireInt8Arithmetic(loc, op, "can't use with structs containing uint8");
+
+ if (type.isArray() && type.getBasicType() == EbtUint8)
+ requireInt8Arithmetic(loc, op, "can't use with arrays containing uint8");
+}
+
+void TParseContext::specializationCheck(const TSourceLoc& loc, const TType& type, const char* op)
+{
+ if (type.containsSpecializationSize())
+ error(loc, "can't use with types containing arrays sized with a specialization constant", op, "");
+}
+
+void TParseContext::structTypeCheck(const TSourceLoc& /*loc*/, TPublicType& publicType)
+{
+ const TTypeList& typeList = *publicType.userDef->getStruct();
+
+ // fix and check for member storage qualifiers and types that don't belong within a structure
+ for (unsigned int member = 0; member < typeList.size(); ++member) {
+ TQualifier& memberQualifier = typeList[member].type->getQualifier();
+ const TSourceLoc& memberLoc = typeList[member].loc;
+ if (memberQualifier.isAuxiliary() ||
+ memberQualifier.isInterpolation() ||
+ (memberQualifier.storage != EvqTemporary && memberQualifier.storage != EvqGlobal))
+ error(memberLoc, "cannot use storage or interpolation qualifiers on structure members", typeList[member].type->getFieldName().c_str(), "");
+ if (memberQualifier.isMemory())
+ error(memberLoc, "cannot use memory qualifiers on structure members", typeList[member].type->getFieldName().c_str(), "");
+ if (memberQualifier.hasLayout()) {
+ error(memberLoc, "cannot use layout qualifiers on structure members", typeList[member].type->getFieldName().c_str(), "");
+ memberQualifier.clearLayout();
+ }
+ if (memberQualifier.invariant)
+ error(memberLoc, "cannot use invariant qualifier on structure members", typeList[member].type->getFieldName().c_str(), "");
+ }
+}
+
+//
+// See if this loop satisfies the limitations for ES 2.0 (version 100) for loops in Appendex A:
+//
+// "The loop index has type int or float.
+//
+// "The for statement has the form:
+// for ( init-declaration ; condition ; expression )
+// init-declaration has the form: type-specifier identifier = constant-expression
+// condition has the form: loop-index relational_operator constant-expression
+// where relational_operator is one of: > >= < <= == or !=
+// expression [sic] has one of the following forms:
+// loop-index++
+// loop-index--
+// loop-index += constant-expression
+// loop-index -= constant-expression
+//
+// The body is handled in an AST traversal.
+//
+void TParseContext::inductiveLoopCheck(const TSourceLoc& loc, TIntermNode* init, TIntermLoop* loop)
+{
+ // loop index init must exist and be a declaration, which shows up in the AST as an aggregate of size 1 of the declaration
+ bool badInit = false;
+ if (! init || ! init->getAsAggregate() || init->getAsAggregate()->getSequence().size() != 1)
+ badInit = true;
+ TIntermBinary* binaryInit = 0;
+ if (! badInit) {
+ // get the declaration assignment
+ binaryInit = init->getAsAggregate()->getSequence()[0]->getAsBinaryNode();
+ if (! binaryInit)
+ badInit = true;
+ }
+ if (badInit) {
+ error(loc, "inductive-loop init-declaration requires the form \"type-specifier loop-index = constant-expression\"", "limitations", "");
+ return;
+ }
+
+ // loop index must be type int or float
+ if (! binaryInit->getType().isScalar() || (binaryInit->getBasicType() != EbtInt && binaryInit->getBasicType() != EbtFloat)) {
+ error(loc, "inductive loop requires a scalar 'int' or 'float' loop index", "limitations", "");
+ return;
+ }
+
+ // init is the form "loop-index = constant"
+ if (binaryInit->getOp() != EOpAssign || ! binaryInit->getLeft()->getAsSymbolNode() || ! binaryInit->getRight()->getAsConstantUnion()) {
+ error(loc, "inductive-loop init-declaration requires the form \"type-specifier loop-index = constant-expression\"", "limitations", "");
+ return;
+ }
+
+ // get the unique id of the loop index
+ int loopIndex = binaryInit->getLeft()->getAsSymbolNode()->getId();
+ inductiveLoopIds.insert(loopIndex);
+
+ // condition's form must be "loop-index relational-operator constant-expression"
+ bool badCond = ! loop->getTest();
+ if (! badCond) {
+ TIntermBinary* binaryCond = loop->getTest()->getAsBinaryNode();
+ badCond = ! binaryCond;
+ if (! badCond) {
+ switch (binaryCond->getOp()) {
+ case EOpGreaterThan:
+ case EOpGreaterThanEqual:
+ case EOpLessThan:
+ case EOpLessThanEqual:
+ case EOpEqual:
+ case EOpNotEqual:
+ break;
+ default:
+ badCond = true;
+ }
+ }
+ if (binaryCond && (! binaryCond->getLeft()->getAsSymbolNode() ||
+ binaryCond->getLeft()->getAsSymbolNode()->getId() != loopIndex ||
+ ! binaryCond->getRight()->getAsConstantUnion()))
+ badCond = true;
+ }
+ if (badCond) {
+ error(loc, "inductive-loop condition requires the form \"loop-index <comparison-op> constant-expression\"", "limitations", "");
+ return;
+ }
+
+ // loop-index++
+ // loop-index--
+ // loop-index += constant-expression
+ // loop-index -= constant-expression
+ bool badTerminal = ! loop->getTerminal();
+ if (! badTerminal) {
+ TIntermUnary* unaryTerminal = loop->getTerminal()->getAsUnaryNode();
+ TIntermBinary* binaryTerminal = loop->getTerminal()->getAsBinaryNode();
+ if (unaryTerminal || binaryTerminal) {
+ switch(loop->getTerminal()->getAsOperator()->getOp()) {
+ case EOpPostDecrement:
+ case EOpPostIncrement:
+ case EOpAddAssign:
+ case EOpSubAssign:
+ break;
+ default:
+ badTerminal = true;
+ }
+ } else
+ badTerminal = true;
+ if (binaryTerminal && (! binaryTerminal->getLeft()->getAsSymbolNode() ||
+ binaryTerminal->getLeft()->getAsSymbolNode()->getId() != loopIndex ||
+ ! binaryTerminal->getRight()->getAsConstantUnion()))
+ badTerminal = true;
+ if (unaryTerminal && (! unaryTerminal->getOperand()->getAsSymbolNode() ||
+ unaryTerminal->getOperand()->getAsSymbolNode()->getId() != loopIndex))
+ badTerminal = true;
+ }
+ if (badTerminal) {
+ error(loc, "inductive-loop termination requires the form \"loop-index++, loop-index--, loop-index += constant-expression, or loop-index -= constant-expression\"", "limitations", "");
+ return;
+ }
+
+ // the body
+ inductiveLoopBodyCheck(loop->getBody(), loopIndex, symbolTable);
+}
+
+// Do limit checks for built-in arrays.
+void TParseContext::arrayLimitCheck(const TSourceLoc& loc, const TString& identifier, int size)
+{
+ if (identifier.compare("gl_TexCoord") == 0)
+ limitCheck(loc, size, "gl_MaxTextureCoords", "gl_TexCoord array size");
+ else if (identifier.compare("gl_ClipDistance") == 0)
+ limitCheck(loc, size, "gl_MaxClipDistances", "gl_ClipDistance array size");
+ else if (identifier.compare("gl_CullDistance") == 0)
+ limitCheck(loc, size, "gl_MaxCullDistances", "gl_CullDistance array size");
+#ifdef NV_EXTENSIONS
+ else if (identifier.compare("gl_ClipDistancePerViewNV") == 0)
+ limitCheck(loc, size, "gl_MaxClipDistances", "gl_ClipDistancePerViewNV array size");
+ else if (identifier.compare("gl_CullDistancePerViewNV") == 0)
+ limitCheck(loc, size, "gl_MaxCullDistances", "gl_CullDistancePerViewNV array size");
+#endif
+}
+
+// See if the provided value is less than or equal to the symbol indicated by limit,
+// which should be a constant in the symbol table.
+void TParseContext::limitCheck(const TSourceLoc& loc, int value, const char* limit, const char* feature)
+{
+ TSymbol* symbol = symbolTable.find(limit);
+ assert(symbol->getAsVariable());
+ const TConstUnionArray& constArray = symbol->getAsVariable()->getConstArray();
+ assert(! constArray.empty());
+ if (value > constArray[0].getIConst())
+ error(loc, "must be less than or equal to", feature, "%s (%d)", limit, constArray[0].getIConst());
+}
+
+//
+// Do any additional error checking, etc., once we know the parsing is done.
+//
+void TParseContext::finish()
+{
+ TParseContextBase::finish();
+
+ if (parsingBuiltins)
+ return;
+
+ // Check on array indexes for ES 2.0 (version 100) limitations.
+ for (size_t i = 0; i < needsIndexLimitationChecking.size(); ++i)
+ constantIndexExpressionCheck(needsIndexLimitationChecking[i]);
+
+ // Check for stages that are enabled by extension.
+ // Can't do this at the beginning, it is chicken and egg to add a stage by
+ // extension.
+ // Stage-specific features were correctly tested for already, this is just
+ // about the stage itself.
+ switch (language) {
+ case EShLangGeometry:
+ if (profile == EEsProfile && version == 310)
+ requireExtensions(getCurrentLoc(), Num_AEP_geometry_shader, AEP_geometry_shader, "geometry shaders");
+ break;
+ case EShLangTessControl:
+ case EShLangTessEvaluation:
+ if (profile == EEsProfile && version == 310)
+ requireExtensions(getCurrentLoc(), Num_AEP_tessellation_shader, AEP_tessellation_shader, "tessellation shaders");
+ else if (profile != EEsProfile && version < 400)
+ requireExtensions(getCurrentLoc(), 1, &E_GL_ARB_tessellation_shader, "tessellation shaders");
+ break;
+ case EShLangCompute:
+ if (profile != EEsProfile && version < 430)
+ requireExtensions(getCurrentLoc(), 1, &E_GL_ARB_compute_shader, "compute shaders");
+ break;
+#ifdef NV_EXTENSIONS
+ case EShLangTaskNV:
+ requireExtensions(getCurrentLoc(), 1, &E_GL_NV_mesh_shader, "task shaders");
+ break;
+ case EShLangMeshNV:
+ requireExtensions(getCurrentLoc(), 1, &E_GL_NV_mesh_shader, "mesh shaders");
+ break;
+#endif
+ default:
+ break;
+ }
+
+#ifdef NV_EXTENSIONS
+ // Set default outputs for GL_NV_geometry_shader_passthrough
+ if (language == EShLangGeometry && extensionTurnedOn(E_SPV_NV_geometry_shader_passthrough)) {
+ if (intermediate.getOutputPrimitive() == ElgNone) {
+ switch (intermediate.getInputPrimitive()) {
+ case ElgPoints: intermediate.setOutputPrimitive(ElgPoints); break;
+ case ElgLines: intermediate.setOutputPrimitive(ElgLineStrip); break;
+ case ElgTriangles: intermediate.setOutputPrimitive(ElgTriangleStrip); break;
+ default: break;
+ }
+ }
+ if (intermediate.getVertices() == TQualifier::layoutNotSet) {
+ switch (intermediate.getInputPrimitive()) {
+ case ElgPoints: intermediate.setVertices(1); break;
+ case ElgLines: intermediate.setVertices(2); break;
+ case ElgTriangles: intermediate.setVertices(3); break;
+ default: break;
+ }
+ }
+ }
+#endif
+}
+
+//
+// Layout qualifier stuff.
+//
+
+// Put the id's layout qualification into the public type, for qualifiers not having a number set.
+// This is before we know any type information for error checking.
+void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publicType, TString& id)
+{
+ std::transform(id.begin(), id.end(), id.begin(), ::tolower);
+
+ if (id == TQualifier::getLayoutMatrixString(ElmColumnMajor)) {
+ publicType.qualifier.layoutMatrix = ElmColumnMajor;
+ return;
+ }
+ if (id == TQualifier::getLayoutMatrixString(ElmRowMajor)) {
+ publicType.qualifier.layoutMatrix = ElmRowMajor;
+ return;
+ }
+ if (id == TQualifier::getLayoutPackingString(ElpPacked)) {
+ if (spvVersion.spv != 0)
+ spvRemoved(loc, "packed");
+ publicType.qualifier.layoutPacking = ElpPacked;
+ return;
+ }
+ if (id == TQualifier::getLayoutPackingString(ElpShared)) {
+ if (spvVersion.spv != 0)
+ spvRemoved(loc, "shared");
+ publicType.qualifier.layoutPacking = ElpShared;
+ return;
+ }
+ if (id == TQualifier::getLayoutPackingString(ElpStd140)) {
+ publicType.qualifier.layoutPacking = ElpStd140;
+ return;
+ }
+ if (id == TQualifier::getLayoutPackingString(ElpStd430)) {
+ requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, "std430");
+ profileRequires(loc, ECoreProfile | ECompatibilityProfile, 430, nullptr, "std430");
+ profileRequires(loc, EEsProfile, 310, nullptr, "std430");
+ publicType.qualifier.layoutPacking = ElpStd430;
+ return;
+ }
+ if (id == TQualifier::getLayoutPackingString(ElpScalar)) {
+ requireVulkan(loc, "scalar");
+ requireExtensions(loc, 1, &E_GL_EXT_scalar_block_layout, "scalar block layout");
+ publicType.qualifier.layoutPacking = ElpScalar;
+ return;
+ }
+ // TODO: compile-time performance: may need to stop doing linear searches
+ for (TLayoutFormat format = (TLayoutFormat)(ElfNone + 1); format < ElfCount; format = (TLayoutFormat)(format + 1)) {
+ if (id == TQualifier::getLayoutFormatString(format)) {
+ if ((format > ElfEsFloatGuard && format < ElfFloatGuard) ||
+ (format > ElfEsIntGuard && format < ElfIntGuard) ||
+ (format > ElfEsUintGuard && format < ElfCount))
+ requireProfile(loc, ENoProfile | ECoreProfile | ECompatibilityProfile, "image load-store format");
+ profileRequires(loc, ENoProfile | ECoreProfile | ECompatibilityProfile, 420, E_GL_ARB_shader_image_load_store, "image load store");
+ profileRequires(loc, EEsProfile, 310, E_GL_ARB_shader_image_load_store, "image load store");
+ publicType.qualifier.layoutFormat = format;
+ return;
+ }
+ }
+ if (id == "push_constant") {
+ requireVulkan(loc, "push_constant");
+ publicType.qualifier.layoutPushConstant = true;
+ return;
+ }
+ if (id == "buffer_reference") {
+ requireVulkan(loc, "buffer_reference");
+ requireExtensions(loc, 1, &E_GL_EXT_buffer_reference, "buffer_reference");
+ publicType.qualifier.layoutBufferReference = true;
+ intermediate.setUseStorageBuffer();
+ intermediate.setUsePhysicalStorageBuffer();
+ return;
+ }
+ if (language == EShLangGeometry || language == EShLangTessEvaluation
+#ifdef NV_EXTENSIONS
+ || language == EShLangMeshNV
+#endif
+ ) {
+ if (id == TQualifier::getGeometryString(ElgTriangles)) {
+ publicType.shaderQualifiers.geometry = ElgTriangles;
+ return;
+ }
+ if (language == EShLangGeometry
+#ifdef NV_EXTENSIONS
+ || language == EShLangMeshNV
+#endif
+ ) {
+ if (id == TQualifier::getGeometryString(ElgPoints)) {
+ publicType.shaderQualifiers.geometry = ElgPoints;
+ return;
+ }
+ if (id == TQualifier::getGeometryString(ElgLines)) {
+ publicType.shaderQualifiers.geometry = ElgLines;
+ return;
+ }
+#ifdef NV_EXTENSIONS
+ if (language == EShLangGeometry)
+#endif
+ {
+ if (id == TQualifier::getGeometryString(ElgLineStrip)) {
+ publicType.shaderQualifiers.geometry = ElgLineStrip;
+ return;
+ }
+ if (id == TQualifier::getGeometryString(ElgLinesAdjacency)) {
+ publicType.shaderQualifiers.geometry = ElgLinesAdjacency;
+ return;
+ }
+ if (id == TQualifier::getGeometryString(ElgTrianglesAdjacency)) {
+ publicType.shaderQualifiers.geometry = ElgTrianglesAdjacency;
+ return;
+ }
+ if (id == TQualifier::getGeometryString(ElgTriangleStrip)) {
+ publicType.shaderQualifiers.geometry = ElgTriangleStrip;
+ return;
+ }
+#ifdef NV_EXTENSIONS
+ if (id == "passthrough") {
+ requireExtensions(loc, 1, &E_SPV_NV_geometry_shader_passthrough, "geometry shader passthrough");
+ publicType.qualifier.layoutPassthrough = true;
+ intermediate.setGeoPassthroughEXT();
+ return;
+ }
+#endif
+ }
+ } else {
+ assert(language == EShLangTessEvaluation);
+
+ // input primitive
+ if (id == TQualifier::getGeometryString(ElgTriangles)) {
+ publicType.shaderQualifiers.geometry = ElgTriangles;
+ return;
+ }
+ if (id == TQualifier::getGeometryString(ElgQuads)) {
+ publicType.shaderQualifiers.geometry = ElgQuads;
+ return;
+ }
+ if (id == TQualifier::getGeometryString(ElgIsolines)) {
+ publicType.shaderQualifiers.geometry = ElgIsolines;
+ return;
+ }
+
+ // vertex spacing
+ if (id == TQualifier::getVertexSpacingString(EvsEqual)) {
+ publicType.shaderQualifiers.spacing = EvsEqual;
+ return;
+ }
+ if (id == TQualifier::getVertexSpacingString(EvsFractionalEven)) {
+ publicType.shaderQualifiers.spacing = EvsFractionalEven;
+ return;
+ }
+ if (id == TQualifier::getVertexSpacingString(EvsFractionalOdd)) {
+ publicType.shaderQualifiers.spacing = EvsFractionalOdd;
+ return;
+ }
+
+ // triangle order
+ if (id == TQualifier::getVertexOrderString(EvoCw)) {
+ publicType.shaderQualifiers.order = EvoCw;
+ return;
+ }
+ if (id == TQualifier::getVertexOrderString(EvoCcw)) {
+ publicType.shaderQualifiers.order = EvoCcw;
+ return;
+ }
+
+ // point mode
+ if (id == "point_mode") {
+ publicType.shaderQualifiers.pointMode = true;
+ return;
+ }
+ }
+ }
+ if (language == EShLangFragment) {
+ if (id == "origin_upper_left") {
+ requireProfile(loc, ECoreProfile | ECompatibilityProfile, "origin_upper_left");
+ publicType.shaderQualifiers.originUpperLeft = true;
+ return;
+ }
+ if (id == "pixel_center_integer") {
+ requireProfile(loc, ECoreProfile | ECompatibilityProfile, "pixel_center_integer");
+ publicType.shaderQualifiers.pixelCenterInteger = true;
+ return;
+ }
+ if (id == "early_fragment_tests") {
+ profileRequires(loc, ENoProfile | ECoreProfile | ECompatibilityProfile, 420, E_GL_ARB_shader_image_load_store, "early_fragment_tests");
+ profileRequires(loc, EEsProfile, 310, nullptr, "early_fragment_tests");
+ publicType.shaderQualifiers.earlyFragmentTests = true;
+ return;
+ }
+ if (id == "post_depth_coverage") {
+ requireExtensions(loc, Num_post_depth_coverageEXTs, post_depth_coverageEXTs, "post depth coverage");
+ if (extensionTurnedOn(E_GL_ARB_post_depth_coverage)) {
+ publicType.shaderQualifiers.earlyFragmentTests = true;
+ }
+ publicType.shaderQualifiers.postDepthCoverage = true;
+ return;
+ }
+ for (TLayoutDepth depth = (TLayoutDepth)(EldNone + 1); depth < EldCount; depth = (TLayoutDepth)(depth+1)) {
+ if (id == TQualifier::getLayoutDepthString(depth)) {
+ requireProfile(loc, ECoreProfile | ECompatibilityProfile, "depth layout qualifier");
+ profileRequires(loc, ECoreProfile | ECompatibilityProfile, 420, nullptr, "depth layout qualifier");
+ publicType.shaderQualifiers.layoutDepth = depth;
+ return;
+ }
+ }
+ if (id.compare(0, 13, "blend_support") == 0) {
+ bool found = false;
+ for (TBlendEquationShift be = (TBlendEquationShift)0; be < EBlendCount; be = (TBlendEquationShift)(be + 1)) {
+ if (id == TQualifier::getBlendEquationString(be)) {
+ profileRequires(loc, EEsProfile, 320, E_GL_KHR_blend_equation_advanced, "blend equation");
+ profileRequires(loc, ~EEsProfile, 0, E_GL_KHR_blend_equation_advanced, "blend equation");
+ intermediate.addBlendEquation(be);
+ publicType.shaderQualifiers.blendEquation = true;
+ found = true;
+ break;
+ }
+ }
+ if (! found)
+ error(loc, "unknown blend equation", "blend_support", "");
+ return;
+ }
+#ifdef NV_EXTENSIONS
+ if (id == "override_coverage") {
+ requireExtensions(loc, 1, &E_GL_NV_sample_mask_override_coverage, "sample mask override coverage");
+ publicType.shaderQualifiers.layoutOverrideCoverage = true;
+ return;
+ }
+ }
+ if (language == EShLangVertex ||
+ language == EShLangTessControl ||
+ language == EShLangTessEvaluation ||
+ language == EShLangGeometry ) {
+ if (id == "viewport_relative") {
+ requireExtensions(loc, 1, &E_GL_NV_viewport_array2, "view port array2");
+ publicType.qualifier.layoutViewportRelative = true;
+ return;
+ }
+ } else {
+ if (language == EShLangRayGenNV || language == EShLangIntersectNV ||
+ language == EShLangAnyHitNV || language == EShLangClosestHitNV ||
+ language == EShLangMissNV || language == EShLangCallableNV) {
+ if (id == "shaderrecordnv") {
+ publicType.qualifier.layoutShaderRecordNV = true;
+ return;
+ }
+ }
+ }
+ if (language == EShLangCompute) {
+ if (id.compare(0, 17, "derivative_group_") == 0) {
+ requireExtensions(loc, 1, &E_GL_NV_compute_shader_derivatives, "compute shader derivatives");
+ if (id == "derivative_group_quadsnv") {
+ publicType.shaderQualifiers.layoutDerivativeGroupQuads = true;
+ return;
+ } else if (id == "derivative_group_linearnv") {
+ publicType.shaderQualifiers.layoutDerivativeGroupLinear = true;
+ return;
+ }
+ }
+ }
+#else
+ }
+#endif
+ error(loc, "unrecognized layout identifier, or qualifier requires assignment (e.g., binding = 4)", id.c_str(), "");
+}
+
+// Put the id's layout qualifier value into the public type, for qualifiers having a number set.
+// This is before we know any type information for error checking.
+void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publicType, TString& id, const TIntermTyped* node)
+{
+ const char* feature = "layout-id value";
+ const char* nonLiteralFeature = "non-literal layout-id value";
+
+ integerCheck(node, feature);
+ const TIntermConstantUnion* constUnion = node->getAsConstantUnion();
+ int value;
+ if (constUnion) {
+ value = constUnion->getConstArray()[0].getIConst();
+ if (! constUnion->isLiteral()) {
+ requireProfile(loc, ECoreProfile | ECompatibilityProfile, nonLiteralFeature);
+ profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, nonLiteralFeature);
+ }
+ } else {
+ // grammar should have give out the error message
+ value = 0;
+ }
+
+ if (value < 0) {
+ error(loc, "cannot be negative", feature, "");
+ return;
+ }
+
+ std::transform(id.begin(), id.end(), id.begin(), ::tolower);
+
+ if (id == "offset") {
+ // "offset" can be for either
+ // - uniform offsets
+ // - atomic_uint offsets
+ const char* feature = "offset";
+ if (spvVersion.spv == 0) {
+ requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, feature);
+ const char* exts[2] = { E_GL_ARB_enhanced_layouts, E_GL_ARB_shader_atomic_counters };
+ profileRequires(loc, ECoreProfile | ECompatibilityProfile, 420, 2, exts, feature);
+ profileRequires(loc, EEsProfile, 310, nullptr, feature);
+ }
+ publicType.qualifier.layoutOffset = value;
+ return;
+ } else if (id == "align") {
+ const char* feature = "uniform buffer-member align";
+ if (spvVersion.spv == 0) {
+ requireProfile(loc, ECoreProfile | ECompatibilityProfile, feature);
+ profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, feature);
+ }
+ // "The specified alignment must be a power of 2, or a compile-time error results."
+ if (! IsPow2(value))
+ error(loc, "must be a power of 2", "align", "");
+ else
+ publicType.qualifier.layoutAlign = value;
+ return;
+ } else if (id == "location") {
+ profileRequires(loc, EEsProfile, 300, nullptr, "location");
+ const char* exts[2] = { E_GL_ARB_separate_shader_objects, E_GL_ARB_explicit_attrib_location };
+ profileRequires(loc, ~EEsProfile, 330, 2, exts, "location");
+ if ((unsigned int)value >= TQualifier::layoutLocationEnd)
+ error(loc, "location is too large", id.c_str(), "");
+ else
+ publicType.qualifier.layoutLocation = value;
+ return;
+ } else if (id == "set") {
+ if ((unsigned int)value >= TQualifier::layoutSetEnd)
+ error(loc, "set is too large", id.c_str(), "");
+ else
+ publicType.qualifier.layoutSet = value;
+ if (value != 0)
+ requireVulkan(loc, "descriptor set");
+ return;
+ } else if (id == "binding") {
+ profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, "binding");
+ profileRequires(loc, EEsProfile, 310, nullptr, "binding");
+ if ((unsigned int)value >= TQualifier::layoutBindingEnd)
+ error(loc, "binding is too large", id.c_str(), "");
+ else
+ publicType.qualifier.layoutBinding = value;
+ return;
+ } else if (id == "component") {
+ requireProfile(loc, ECoreProfile | ECompatibilityProfile, "component");
+ profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, "component");
+ if ((unsigned)value >= TQualifier::layoutComponentEnd)
+ error(loc, "component is too large", id.c_str(), "");
+ else
+ publicType.qualifier.layoutComponent = value;
+ return;
+ } else if (id.compare(0, 4, "xfb_") == 0) {
+ // "Any shader making any static use (after preprocessing) of any of these
+ // *xfb_* qualifiers will cause the shader to be in a transform feedback
+ // capturing mode and hence responsible for describing the transform feedback
+ // setup."
+ intermediate.setXfbMode();
+ const char* feature = "transform feedback qualifier";
+ requireStage(loc, (EShLanguageMask)(EShLangVertexMask | EShLangGeometryMask | EShLangTessControlMask | EShLangTessEvaluationMask), feature);
+ requireProfile(loc, ECoreProfile | ECompatibilityProfile, feature);
+ profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, feature);
+ if (id == "xfb_buffer") {
+ // "It is a compile-time error to specify an *xfb_buffer* that is greater than
+ // the implementation-dependent constant gl_MaxTransformFeedbackBuffers."
+ if (value >= resources.maxTransformFeedbackBuffers)
+ error(loc, "buffer is too large:", id.c_str(), "gl_MaxTransformFeedbackBuffers is %d", resources.maxTransformFeedbackBuffers);
+ if (value >= (int)TQualifier::layoutXfbBufferEnd)
+ error(loc, "buffer is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbBufferEnd-1);
+ else
+ publicType.qualifier.layoutXfbBuffer = value;
+ return;
+ } else if (id == "xfb_offset") {
+ if (value >= (int)TQualifier::layoutXfbOffsetEnd)
+ error(loc, "offset is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbOffsetEnd-1);
+ else
+ publicType.qualifier.layoutXfbOffset = value;
+ return;
+ } else if (id == "xfb_stride") {
+ // "The resulting stride (implicit or explicit), when divided by 4, must be less than or equal to the
+ // implementation-dependent constant gl_MaxTransformFeedbackInterleavedComponents."
+ if (value > 4 * resources.maxTransformFeedbackInterleavedComponents) {
+ error(loc, "1/4 stride is too large:", id.c_str(), "gl_MaxTransformFeedbackInterleavedComponents is %d",
+ resources.maxTransformFeedbackInterleavedComponents);
+ }
+ if (value >= (int)TQualifier::layoutXfbStrideEnd)
+ error(loc, "stride is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbStrideEnd-1);
+ else
+ publicType.qualifier.layoutXfbStride = value;
+ return;
+ }
+ }
+
+ if (id == "input_attachment_index") {
+ requireVulkan(loc, "input_attachment_index");
+ if (value >= (int)TQualifier::layoutAttachmentEnd)
+ error(loc, "attachment index is too large", id.c_str(), "");
+ else
+ publicType.qualifier.layoutAttachment = value;
+ return;
+ }
+ if (id == "constant_id") {
+ requireSpv(loc, "constant_id");
+ if (value >= (int)TQualifier::layoutSpecConstantIdEnd) {
+ error(loc, "specialization-constant id is too large", id.c_str(), "");
+ } else {
+ publicType.qualifier.layoutSpecConstantId = value;
+ publicType.qualifier.specConstant = true;
+ if (! intermediate.addUsedConstantId(value))
+ error(loc, "specialization-constant id already used", id.c_str(), "");
+ }
+ return;
+ }
+ if (id == "num_views") {
+ requireExtensions(loc, Num_OVR_multiview_EXTs, OVR_multiview_EXTs, "num_views");
+ publicType.shaderQualifiers.numViews = value;
+ return;
+ }
+
+#if NV_EXTENSIONS
+ if (language == EShLangVertex ||
+ language == EShLangTessControl ||
+ language == EShLangTessEvaluation ||
+ language == EShLangGeometry) {
+ if (id == "secondary_view_offset") {
+ requireExtensions(loc, 1, &E_GL_NV_stereo_view_rendering, "stereo view rendering");
+ publicType.qualifier.layoutSecondaryViewportRelativeOffset = value;
+ return;
+ }
+ }
+#endif
+
+ if (id == "buffer_reference_align") {
+ requireExtensions(loc, 1, &E_GL_EXT_buffer_reference, "buffer_reference_align");
+ if (! IsPow2(value))
+ error(loc, "must be a power of 2", "buffer_reference_align", "");
+ else
+ publicType.qualifier.layoutBufferReferenceAlign = (unsigned int)std::log2(value);
+ return;
+ }
+
+ switch (language) {
+ case EShLangVertex:
+ break;
+
+ case EShLangTessControl:
+ if (id == "vertices") {
+ if (value == 0)
+ error(loc, "must be greater than 0", "vertices", "");
+ else
+ publicType.shaderQualifiers.vertices = value;
+ return;
+ }
+ break;
+
+ case EShLangTessEvaluation:
+ break;
+
+ case EShLangGeometry:
+ if (id == "invocations") {
+ profileRequires(loc, ECompatibilityProfile | ECoreProfile, 400, nullptr, "invocations");
+ if (value == 0)
+ error(loc, "must be at least 1", "invocations", "");
+ else
+ publicType.shaderQualifiers.invocations = value;
+ return;
+ }
+ if (id == "max_vertices") {
+ publicType.shaderQualifiers.vertices = value;
+ if (value > resources.maxGeometryOutputVertices)
+ error(loc, "too large, must be less than gl_MaxGeometryOutputVertices", "max_vertices", "");
+ return;
+ }
+ if (id == "stream") {
+ requireProfile(loc, ~EEsProfile, "selecting output stream");
+ publicType.qualifier.layoutStream = value;
+ if (value > 0)
+ intermediate.setMultiStream();
+ return;
+ }
+ break;
+
+ case EShLangFragment:
+ if (id == "index") {
+ requireProfile(loc, ECompatibilityProfile | ECoreProfile, "index layout qualifier on fragment output");
+ const char* exts[2] = { E_GL_ARB_separate_shader_objects, E_GL_ARB_explicit_attrib_location };
+ profileRequires(loc, ECompatibilityProfile | ECoreProfile, 330, 2, exts, "index layout qualifier on fragment output");
+
+ // "It is also a compile-time error if a fragment shader sets a layout index to less than 0 or greater than 1."
+ if (value < 0 || value > 1) {
+ value = 0;
+ error(loc, "value must be 0 or 1", "index", "");
+ }
+
+ publicType.qualifier.layoutIndex = value;
+ return;
+ }
+ break;
+
+#ifdef NV_EXTENSIONS
+ case EShLangMeshNV:
+ if (id == "max_vertices") {
+ requireExtensions(loc, 1, &E_GL_NV_mesh_shader, "max_vertices");
+ publicType.shaderQualifiers.vertices = value;
+ if (value > resources.maxMeshOutputVerticesNV)
+ error(loc, "too large, must be less than gl_MaxMeshOutputVerticesNV", "max_vertices", "");
+ return;
+ }
+ if (id == "max_primitives") {
+ requireExtensions(loc, 1, &E_GL_NV_mesh_shader, "max_primitives");
+ publicType.shaderQualifiers.primitives = value;
+ if (value > resources.maxMeshOutputPrimitivesNV)
+ error(loc, "too large, must be less than gl_MaxMeshOutputPrimitivesNV", "max_primitives", "");
+ return;
+ }
+ // Fall through
+
+ case EShLangTaskNV:
+ // Fall through
+#endif
+ case EShLangCompute:
+ if (id.compare(0, 11, "local_size_") == 0) {
+#ifdef NV_EXTENSIONS
+ if (language == EShLangMeshNV || language == EShLangTaskNV) {
+ requireExtensions(loc, 1, &E_GL_NV_mesh_shader, "gl_WorkGroupSize");
+ }
+ else
+#endif
+ {
+ profileRequires(loc, EEsProfile, 310, 0, "gl_WorkGroupSize");
+ profileRequires(loc, ~EEsProfile, 430, E_GL_ARB_compute_shader, "gl_WorkGroupSize");
+ }
+ if (id.size() == 12 && value == 0) {
+ error(loc, "must be at least 1", id.c_str(), "");
+ return;
+ }
+ if (id == "local_size_x") {
+ publicType.shaderQualifiers.localSize[0] = value;
+ return;
+ }
+ if (id == "local_size_y") {
+ publicType.shaderQualifiers.localSize[1] = value;
+ return;
+ }
+ if (id == "local_size_z") {
+ publicType.shaderQualifiers.localSize[2] = value;
+ return;
+ }
+ if (spvVersion.spv != 0) {
+ if (id == "local_size_x_id") {
+ publicType.shaderQualifiers.localSizeSpecId[0] = value;
+ return;
+ }
+ if (id == "local_size_y_id") {
+ publicType.shaderQualifiers.localSizeSpecId[1] = value;
+ return;
+ }
+ if (id == "local_size_z_id") {
+ publicType.shaderQualifiers.localSizeSpecId[2] = value;
+ return;
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ error(loc, "there is no such layout identifier for this stage taking an assigned value", id.c_str(), "");
+}
+
+// Merge any layout qualifier information from src into dst, leaving everything else in dst alone
+//
+// "More than one layout qualifier may appear in a single declaration.
+// Additionally, the same layout-qualifier-name can occur multiple times
+// within a layout qualifier or across multiple layout qualifiers in the
+// same declaration. When the same layout-qualifier-name occurs
+// multiple times, in a single declaration, the last occurrence overrides
+// the former occurrence(s). Further, if such a layout-qualifier-name
+// will effect subsequent declarations or other observable behavior, it
+// is only the last occurrence that will have any effect, behaving as if
+// the earlier occurrence(s) within the declaration are not present.
+// This is also true for overriding layout-qualifier-names, where one
+// overrides the other (e.g., row_major vs. column_major); only the last
+// occurrence has any effect."
+void TParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifier& src, bool inheritOnly)
+{
+ if (src.hasMatrix())
+ dst.layoutMatrix = src.layoutMatrix;
+ if (src.hasPacking())
+ dst.layoutPacking = src.layoutPacking;
+
+ if (src.hasStream())
+ dst.layoutStream = src.layoutStream;
+
+ if (src.hasFormat())
+ dst.layoutFormat = src.layoutFormat;
+
+ if (src.hasXfbBuffer())
+ dst.layoutXfbBuffer = src.layoutXfbBuffer;
+
+ if (src.hasAlign())
+ dst.layoutAlign = src.layoutAlign;
+
+ if (src.hasBufferReferenceAlign())
+ dst.layoutBufferReferenceAlign = src.layoutBufferReferenceAlign;
+
+ if (! inheritOnly) {
+ if (src.hasLocation())
+ dst.layoutLocation = src.layoutLocation;
+ if (src.hasComponent())
+ dst.layoutComponent = src.layoutComponent;
+ if (src.hasIndex())
+ dst.layoutIndex = src.layoutIndex;
+
+ if (src.hasOffset())
+ dst.layoutOffset = src.layoutOffset;
+
+ if (src.hasSet())
+ dst.layoutSet = src.layoutSet;
+ if (src.layoutBinding != TQualifier::layoutBindingEnd)
+ dst.layoutBinding = src.layoutBinding;
+
+ if (src.hasXfbStride())
+ dst.layoutXfbStride = src.layoutXfbStride;
+ if (src.hasXfbOffset())
+ dst.layoutXfbOffset = src.layoutXfbOffset;
+ if (src.hasAttachment())
+ dst.layoutAttachment = src.layoutAttachment;
+ if (src.hasSpecConstantId())
+ dst.layoutSpecConstantId = src.layoutSpecConstantId;
+
+ if (src.layoutPushConstant)
+ dst.layoutPushConstant = true;
+
+ if (src.layoutBufferReference)
+ dst.layoutBufferReference = true;
+
+#ifdef NV_EXTENSIONS
+ if (src.layoutPassthrough)
+ dst.layoutPassthrough = true;
+ if (src.layoutViewportRelative)
+ dst.layoutViewportRelative = true;
+ if (src.layoutSecondaryViewportRelativeOffset != -2048)
+ dst.layoutSecondaryViewportRelativeOffset = src.layoutSecondaryViewportRelativeOffset;
+ if (src.layoutShaderRecordNV)
+ dst.layoutShaderRecordNV = true;
+ if (src.pervertexNV)
+ dst.pervertexNV = true;
+#endif
+ }
+}
+
+// Do error layout error checking given a full variable/block declaration.
+void TParseContext::layoutObjectCheck(const TSourceLoc& loc, const TSymbol& symbol)
+{
+ const TType& type = symbol.getType();
+ const TQualifier& qualifier = type.getQualifier();
+
+ // first, cross check WRT to just the type
+ layoutTypeCheck(loc, type);
+
+ // now, any remaining error checking based on the object itself
+
+ if (qualifier.hasAnyLocation()) {
+ switch (qualifier.storage) {
+ case EvqUniform:
+ case EvqBuffer:
+ if (symbol.getAsVariable() == nullptr)
+ error(loc, "can only be used on variable declaration", "location", "");
+ break;
+ default:
+ break;
+ }
+ }
+
+ // user-variable location check, which are required for SPIR-V in/out:
+ // - variables have it directly,
+ // - blocks have it on each member (already enforced), so check first one
+ if (spvVersion.spv > 0 && !parsingBuiltins && qualifier.builtIn == EbvNone &&
+ !qualifier.hasLocation() && !intermediate.getAutoMapLocations()) {
+
+ switch (qualifier.storage) {
+ case EvqVaryingIn:
+ case EvqVaryingOut:
+ if (!type.getQualifier().isTaskMemory() &&
+ (type.getBasicType() != EbtBlock ||
+ (!(*type.getStruct())[0].type->getQualifier().hasLocation() &&
+ (*type.getStruct())[0].type->getQualifier().builtIn == EbvNone)))
+ error(loc, "SPIR-V requires location for user input/output", "location", "");
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Check packing and matrix
+ if (qualifier.hasUniformLayout()) {
+ switch (qualifier.storage) {
+ case EvqUniform:
+ case EvqBuffer:
+ if (type.getBasicType() != EbtBlock) {
+ if (qualifier.hasMatrix())
+ error(loc, "cannot specify matrix layout on a variable declaration", "layout", "");
+ if (qualifier.hasPacking())
+ error(loc, "cannot specify packing on a variable declaration", "layout", "");
+ // "The offset qualifier can only be used on block members of blocks..."
+ if (qualifier.hasOffset() && type.getBasicType() != EbtAtomicUint)
+ error(loc, "cannot specify on a variable declaration", "offset", "");
+ // "The align qualifier can only be used on blocks or block members..."
+ if (qualifier.hasAlign())
+ error(loc, "cannot specify on a variable declaration", "align", "");
+ if (qualifier.layoutPushConstant)
+ error(loc, "can only specify on a uniform block", "push_constant", "");
+#ifdef NV_EXTENSIONS
+ if (qualifier.layoutShaderRecordNV)
+ error(loc, "can only specify on a buffer block", "shaderRecordNV", "");
+#endif
+ }
+ break;
+ default:
+ // these were already filtered by layoutTypeCheck() (or its callees)
+ break;
+ }
+ }
+}
+
+// "For some blocks declared as arrays, the location can only be applied at the block level:
+// When a block is declared as an array where additional locations are needed for each member
+// for each block array element, it is a compile-time error to specify locations on the block
+// members. That is, when locations would be under specified by applying them on block members,
+// they are not allowed on block members. For arrayed interfaces (those generally having an
+// extra level of arrayness due to interface expansion), the outer array is stripped before
+// applying this rule."
+void TParseContext::layoutMemberLocationArrayCheck(const TSourceLoc& loc, bool memberWithLocation,
+ TArraySizes* arraySizes)
+{
+ if (memberWithLocation && arraySizes != nullptr) {
+ if (arraySizes->getNumDims() > (currentBlockQualifier.isArrayedIo(language) ? 1 : 0))
+ error(loc, "cannot use in a block array where new locations are needed for each block element",
+ "location", "");
+ }
+}
+
+// Do layout error checking with respect to a type.
+void TParseContext::layoutTypeCheck(const TSourceLoc& loc, const TType& type)
+{
+ const TQualifier& qualifier = type.getQualifier();
+
+ // first, intra-layout qualifier-only error checking
+ layoutQualifierCheck(loc, qualifier);
+
+ // now, error checking combining type and qualifier
+
+ if (qualifier.hasAnyLocation()) {
+ if (qualifier.hasLocation()) {
+ if (qualifier.storage == EvqVaryingOut && language == EShLangFragment) {
+ if (qualifier.layoutLocation >= (unsigned int)resources.maxDrawBuffers)
+ error(loc, "too large for fragment output", "location", "");
+ }
+ }
+ if (qualifier.hasComponent()) {
+ // "It is a compile-time error if this sequence of components gets larger than 3."
+ if (qualifier.layoutComponent + type.getVectorSize() * (type.getBasicType() == EbtDouble ? 2 : 1) > 4)
+ error(loc, "type overflows the available 4 components", "component", "");
+
+ // "It is a compile-time error to apply the component qualifier to a matrix, a structure, a block, or an array containing any of these."
+ if (type.isMatrix() || type.getBasicType() == EbtBlock || type.getBasicType() == EbtStruct)
+ error(loc, "cannot apply to a matrix, structure, or block", "component", "");
+
+ // " It is a compile-time error to use component 1 or 3 as the beginning of a double or dvec2."
+ if (type.getBasicType() == EbtDouble)
+ if (qualifier.layoutComponent & 1)
+ error(loc, "doubles cannot start on an odd-numbered component", "component", "");
+ }
+
+ switch (qualifier.storage) {
+ case EvqVaryingIn:
+ case EvqVaryingOut:
+ if (type.getBasicType() == EbtBlock)
+ profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, "location qualifier on in/out block");
+#ifdef NV_EXTENSIONS
+ if (type.getQualifier().isTaskMemory())
+ error(loc, "cannot apply to taskNV in/out blocks", "location", "");
+#endif
+ break;
+ case EvqUniform:
+ case EvqBuffer:
+ if (type.getBasicType() == EbtBlock)
+ error(loc, "cannot apply to uniform or buffer block", "location", "");
+ break;
+#ifdef NV_EXTENSIONS
+ case EvqPayloadNV:
+ case EvqPayloadInNV:
+ case EvqHitAttrNV:
+ case EvqCallableDataNV:
+ case EvqCallableDataInNV:
+ break;
+#endif
+ default:
+ error(loc, "can only apply to uniform, buffer, in, or out storage qualifiers", "location", "");
+ break;
+ }
+
+ bool typeCollision;
+ int repeated = intermediate.addUsedLocation(qualifier, type, typeCollision);
+ if (repeated >= 0 && ! typeCollision)
+ error(loc, "overlapping use of location", "location", "%d", repeated);
+ // "fragment-shader outputs ... if two variables are placed within the same
+ // location, they must have the same underlying type (floating-point or integer)"
+ if (typeCollision && language == EShLangFragment && qualifier.isPipeOutput())
+ error(loc, "fragment outputs sharing the same location must be the same basic type", "location", "%d", repeated);
+ }
+
+ if (qualifier.hasXfbOffset() && qualifier.hasXfbBuffer()) {
+ int repeated = intermediate.addXfbBufferOffset(type);
+ if (repeated >= 0)
+ error(loc, "overlapping offsets at", "xfb_offset", "offset %d in buffer %d", repeated, qualifier.layoutXfbBuffer);
+
+ // "The offset must be a multiple of the size of the first component of the first
+ // qualified variable or block member, or a compile-time error results. Further, if applied to an aggregate
+ // containing a double or 64-bit integer, the offset must also be a multiple of 8..."
+ if ((type.containsBasicType(EbtDouble) || type.containsBasicType(EbtInt64) || type.containsBasicType(EbtUint64)) &&
+ ! IsMultipleOfPow2(qualifier.layoutXfbOffset, 8))
+ error(loc, "type contains double or 64-bit integer; xfb_offset must be a multiple of 8", "xfb_offset", "");
+#ifdef AMD_EXTENSIONS
+ else if ((type.containsBasicType(EbtBool) || type.containsBasicType(EbtFloat) ||
+ type.containsBasicType(EbtInt) || type.containsBasicType(EbtUint)) &&
+ ! IsMultipleOfPow2(qualifier.layoutXfbOffset, 4))
+ error(loc, "must be a multiple of size of first component", "xfb_offset", "");
+ // ..., if applied to an aggregate containing a half float or 16-bit integer, the offset must also be a multiple of 2..."
+ else if ((type.containsBasicType(EbtFloat16) || type.containsBasicType(EbtInt16) || type.containsBasicType(EbtUint16)) &&
+ !IsMultipleOfPow2(qualifier.layoutXfbOffset, 2))
+ error(loc, "type contains half float or 16-bit integer; xfb_offset must be a multiple of 2", "xfb_offset", "");
+#else
+ else if (! IsMultipleOfPow2(qualifier.layoutXfbOffset, 4))
+ error(loc, "must be a multiple of size of first component", "xfb_offset", "");
+#endif
+ }
+
+ if (qualifier.hasXfbStride() && qualifier.hasXfbBuffer()) {
+ if (! intermediate.setXfbBufferStride(qualifier.layoutXfbBuffer, qualifier.layoutXfbStride))
+ error(loc, "all stride settings must match for xfb buffer", "xfb_stride", "%d", qualifier.layoutXfbBuffer);
+ }
+
+ if (qualifier.hasBinding()) {
+ // Binding checking, from the spec:
+ //
+ // "If the binding point for any uniform or shader storage block instance is less than zero, or greater than or
+ // equal to the implementation-dependent maximum number of uniform buffer bindings, a compile-time
+ // error will occur. When the binding identifier is used with a uniform or shader storage block instanced as
+ // an array of size N, all elements of the array from binding through binding + N - 1 must be within this
+ // range."
+ //
+ if (! type.isOpaque() && type.getBasicType() != EbtBlock)
+ error(loc, "requires block, or sampler/image, or atomic-counter type", "binding", "");
+ if (type.getBasicType() == EbtSampler) {
+ int lastBinding = qualifier.layoutBinding;
+ if (type.isArray()) {
+ if (spvVersion.vulkan > 0)
+ lastBinding += 1;
+ else {
+ if (type.isSizedArray())
+ lastBinding += type.getCumulativeArraySize();
+ else {
+ lastBinding += 1;
+ if (spvVersion.vulkan == 0)
+ warn(loc, "assuming binding count of one for compile-time checking of binding numbers for unsized array", "[]", "");
+ }
+ }
+ }
+ if (spvVersion.vulkan == 0 && lastBinding >= resources.maxCombinedTextureImageUnits)
+ error(loc, "sampler binding not less than gl_MaxCombinedTextureImageUnits", "binding", type.isArray() ? "(using array)" : "");
+ }
+ if (type.getBasicType() == EbtAtomicUint) {
+ if (qualifier.layoutBinding >= (unsigned int)resources.maxAtomicCounterBindings) {
+ error(loc, "atomic_uint binding is too large; see gl_MaxAtomicCounterBindings", "binding", "");
+ return;
+ }
+ }
+ } else if (!intermediate.getAutoMapBindings()) {
+ // some types require bindings
+
+ // atomic_uint
+ if (type.getBasicType() == EbtAtomicUint)
+ error(loc, "layout(binding=X) is required", "atomic_uint", "");
+
+ // SPIR-V
+ if (spvVersion.spv > 0) {
+ if (qualifier.isUniformOrBuffer()) {
+ if (type.getBasicType() == EbtBlock && !qualifier.layoutPushConstant &&
+#ifdef NV_EXTENSIONS
+ !qualifier.layoutShaderRecordNV &&
+#endif
+ !qualifier.layoutAttachment &&
+ !qualifier.layoutBufferReference)
+ error(loc, "uniform/buffer blocks require layout(binding=X)", "binding", "");
+ else if (spvVersion.vulkan > 0 && type.getBasicType() == EbtSampler)
+ error(loc, "sampler/texture/image requires layout(binding=X)", "binding", "");
+ }
+ }
+ }
+
+ // some things can't have arrays of arrays
+ if (type.isArrayOfArrays()) {
+ if (spvVersion.vulkan > 0) {
+ if (type.isOpaque() || (type.getQualifier().isUniformOrBuffer() && type.getBasicType() == EbtBlock))
+ warn(loc, "Generating SPIR-V array-of-arrays, but Vulkan only supports single array level for this resource", "[][]", "");
+ }
+ }
+
+ // "The offset qualifier can only be used on block members of blocks..."
+ if (qualifier.hasOffset()) {
+ if (type.getBasicType() == EbtBlock)
+ error(loc, "only applies to block members, not blocks", "offset", "");
+ }
+
+ // Image format
+ if (qualifier.hasFormat()) {
+ if (! type.isImage())
+ error(loc, "only apply to images", TQualifier::getLayoutFormatString(qualifier.layoutFormat), "");
+ else {
+ if (type.getSampler().type == EbtFloat && qualifier.layoutFormat > ElfFloatGuard)
+ error(loc, "does not apply to floating point images", TQualifier::getLayoutFormatString(qualifier.layoutFormat), "");
+ if (type.getSampler().type == EbtInt && (qualifier.layoutFormat < ElfFloatGuard || qualifier.layoutFormat > ElfIntGuard))
+ error(loc, "does not apply to signed integer images", TQualifier::getLayoutFormatString(qualifier.layoutFormat), "");
+ if (type.getSampler().type == EbtUint && qualifier.layoutFormat < ElfIntGuard)
+ error(loc, "does not apply to unsigned integer images", TQualifier::getLayoutFormatString(qualifier.layoutFormat), "");
+
+ if (profile == EEsProfile) {
+ // "Except for image variables qualified with the format qualifiers r32f, r32i, and r32ui, image variables must
+ // specify either memory qualifier readonly or the memory qualifier writeonly."
+ if (! (qualifier.layoutFormat == ElfR32f || qualifier.layoutFormat == ElfR32i || qualifier.layoutFormat == ElfR32ui)) {
+ if (! qualifier.readonly && ! qualifier.writeonly)
+ error(loc, "format requires readonly or writeonly memory qualifier", TQualifier::getLayoutFormatString(qualifier.layoutFormat), "");
+ }
+ }
+ }
+ } else if (type.isImage() && ! qualifier.writeonly) {
+ const char *explanation = "image variables not declared 'writeonly' and without a format layout qualifier";
+ requireProfile(loc, ECoreProfile | ECompatibilityProfile, explanation);
+ profileRequires(loc, ECoreProfile | ECompatibilityProfile, 0, E_GL_EXT_shader_image_load_formatted, explanation);
+ }
+
+ if (qualifier.layoutPushConstant && type.getBasicType() != EbtBlock)
+ error(loc, "can only be used with a block", "push_constant", "");
+
+ if (qualifier.layoutBufferReference && type.getBasicType() != EbtBlock)
+ error(loc, "can only be used with a block", "buffer_reference", "");
+
+#ifdef NV_EXTENSIONS
+ if (qualifier.layoutShaderRecordNV && type.getBasicType() != EbtBlock)
+ error(loc, "can only be used with a block", "shaderRecordNV", "");
+#endif
+
+ // input attachment
+ if (type.isSubpass()) {
+ if (! qualifier.hasAttachment())
+ error(loc, "requires an input_attachment_index layout qualifier", "subpass", "");
+ } else {
+ if (qualifier.hasAttachment())
+ error(loc, "can only be used with a subpass", "input_attachment_index", "");
+ }
+
+ // specialization-constant id
+ if (qualifier.hasSpecConstantId()) {
+ if (type.getQualifier().storage != EvqConst)
+ error(loc, "can only be applied to 'const'-qualified scalar", "constant_id", "");
+ if (! type.isScalar())
+ error(loc, "can only be applied to a scalar", "constant_id", "");
+ switch (type.getBasicType())
+ {
+ case EbtInt8:
+ case EbtUint8:
+ case EbtInt16:
+ case EbtUint16:
+ case EbtInt:
+ case EbtUint:
+ case EbtInt64:
+ case EbtUint64:
+ case EbtBool:
+ case EbtFloat:
+ case EbtDouble:
+ case EbtFloat16:
+ break;
+ default:
+ error(loc, "cannot be applied to this type", "constant_id", "");
+ break;
+ }
+ }
+}
+
+// Do layout error checking that can be done within a layout qualifier proper, not needing to know
+// if there are blocks, atomic counters, variables, etc.
+void TParseContext::layoutQualifierCheck(const TSourceLoc& loc, const TQualifier& qualifier)
+{
+ if (qualifier.storage == EvqShared && qualifier.hasLayout())
+ error(loc, "cannot apply layout qualifiers to a shared variable", "shared", "");
+
+ // "It is a compile-time error to use *component* without also specifying the location qualifier (order does not matter)."
+ if (qualifier.hasComponent() && ! qualifier.hasLocation())
+ error(loc, "must specify 'location' to use 'component'", "component", "");
+
+ if (qualifier.hasAnyLocation()) {
+
+ // "As with input layout qualifiers, all shaders except compute shaders
+ // allow *location* layout qualifiers on output variable declarations,
+ // output block declarations, and output block member declarations."
+
+ switch (qualifier.storage) {
+ case EvqVaryingIn:
+ {
+ const char* feature = "location qualifier on input";
+ if (profile == EEsProfile && version < 310)
+ requireStage(loc, EShLangVertex, feature);
+ else
+ requireStage(loc, (EShLanguageMask)~EShLangComputeMask, feature);
+ if (language == EShLangVertex) {
+ const char* exts[2] = { E_GL_ARB_separate_shader_objects, E_GL_ARB_explicit_attrib_location };
+ profileRequires(loc, ~EEsProfile, 330, 2, exts, feature);
+ profileRequires(loc, EEsProfile, 300, nullptr, feature);
+ } else {
+ profileRequires(loc, ~EEsProfile, 410, E_GL_ARB_separate_shader_objects, feature);
+ profileRequires(loc, EEsProfile, 310, nullptr, feature);
+ }
+ break;
+ }
+ case EvqVaryingOut:
+ {
+ const char* feature = "location qualifier on output";
+ if (profile == EEsProfile && version < 310)
+ requireStage(loc, EShLangFragment, feature);
+ else
+ requireStage(loc, (EShLanguageMask)~EShLangComputeMask, feature);
+ if (language == EShLangFragment) {
+ const char* exts[2] = { E_GL_ARB_separate_shader_objects, E_GL_ARB_explicit_attrib_location };
+ profileRequires(loc, ~EEsProfile, 330, 2, exts, feature);
+ profileRequires(loc, EEsProfile, 300, nullptr, feature);
+ } else {
+ profileRequires(loc, ~EEsProfile, 410, E_GL_ARB_separate_shader_objects, feature);
+ profileRequires(loc, EEsProfile, 310, nullptr, feature);
+ }
+ break;
+ }
+ case EvqUniform:
+ case EvqBuffer:
+ {
+ const char* feature = "location qualifier on uniform or buffer";
+ requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, feature);
+ profileRequires(loc, ECoreProfile | ECompatibilityProfile, 430, nullptr, feature);
+ profileRequires(loc, EEsProfile, 310, nullptr, feature);
+ break;
+ }
+ default:
+ break;
+ }
+ if (qualifier.hasIndex()) {
+ if (qualifier.storage != EvqVaryingOut)
+ error(loc, "can only be used on an output", "index", "");
+ if (! qualifier.hasLocation())
+ error(loc, "can only be used with an explicit location", "index", "");
+ }
+ }
+
+ if (qualifier.hasBinding()) {
+ if (! qualifier.isUniformOrBuffer() && !qualifier.isTaskMemory())
+ error(loc, "requires uniform or buffer storage qualifier", "binding", "");
+ }
+ if (qualifier.hasStream()) {
+ if (!qualifier.isPipeOutput())
+ error(loc, "can only be used on an output", "stream", "");
+ }
+ if (qualifier.hasXfb()) {
+ if (!qualifier.isPipeOutput())
+ error(loc, "can only be used on an output", "xfb layout qualifier", "");
+ }
+ if (qualifier.hasUniformLayout()) {
+ if (! qualifier.isUniformOrBuffer() && !qualifier.isTaskMemory()) {
+ if (qualifier.hasMatrix() || qualifier.hasPacking())
+ error(loc, "matrix or packing qualifiers can only be used on a uniform or buffer", "layout", "");
+ if (qualifier.hasOffset() || qualifier.hasAlign())
+ error(loc, "offset/align can only be used on a uniform or buffer", "layout", "");
+ }
+ }
+ if (qualifier.layoutPushConstant) {
+ if (qualifier.storage != EvqUniform)
+ error(loc, "can only be used with a uniform", "push_constant", "");
+ if (qualifier.hasSet())
+ error(loc, "cannot be used with push_constant", "set", "");
+ }
+ if (qualifier.layoutBufferReference) {
+ if (qualifier.storage != EvqBuffer)
+ error(loc, "can only be used with buffer", "buffer_reference", "");
+ }
+#ifdef NV_EXTENSIONS
+ if (qualifier.layoutShaderRecordNV) {
+ if (qualifier.storage != EvqBuffer)
+ error(loc, "can only be used with a buffer", "shaderRecordNV", "");
+ if (qualifier.hasBinding())
+ error(loc, "cannot be used with shaderRecordNV", "binding", "");
+ if (qualifier.hasSet())
+ error(loc, "cannot be used with shaderRecordNV", "set", "");
+
+ }
+ if (qualifier.storage == EvqHitAttrNV && qualifier.hasLayout()) {
+ error(loc, "cannot apply layout qualifiers to hitAttributeNV variable", "hitAttributeNV", "");
+ }
+#endif
+}
+
+// For places that can't have shader-level layout qualifiers
+void TParseContext::checkNoShaderLayouts(const TSourceLoc& loc, const TShaderQualifiers& shaderQualifiers)
+{
+ const char* message = "can only apply to a standalone qualifier";
+
+ if (shaderQualifiers.geometry != ElgNone)
+ error(loc, message, TQualifier::getGeometryString(shaderQualifiers.geometry), "");
+ if (shaderQualifiers.spacing != EvsNone)
+ error(loc, message, TQualifier::getVertexSpacingString(shaderQualifiers.spacing), "");
+ if (shaderQualifiers.order != EvoNone)
+ error(loc, message, TQualifier::getVertexOrderString(shaderQualifiers.order), "");
+ if (shaderQualifiers.pointMode)
+ error(loc, message, "point_mode", "");
+ if (shaderQualifiers.invocations != TQualifier::layoutNotSet)
+ error(loc, message, "invocations", "");
+ if (shaderQualifiers.earlyFragmentTests)
+ error(loc, message, "early_fragment_tests", "");
+ if (shaderQualifiers.postDepthCoverage)
+ error(loc, message, "post_depth_coverage", "");
+ for (int i = 0; i < 3; ++i) {
+ if (shaderQualifiers.localSize[i] > 1)
+ error(loc, message, "local_size", "");
+ if (shaderQualifiers.localSizeSpecId[i] != TQualifier::layoutNotSet)
+ error(loc, message, "local_size id", "");
+ }
+ if (shaderQualifiers.vertices != TQualifier::layoutNotSet) {
+ if (language == EShLangGeometry
+#ifdef NV_EXTENSIONS
+ || language == EShLangMeshNV
+#endif
+ )
+ error(loc, message, "max_vertices", "");
+ else if (language == EShLangTessControl)
+ error(loc, message, "vertices", "");
+ else
+ assert(0);
+ }
+#ifdef NV_EXTENSIONS
+ if (shaderQualifiers.primitives != TQualifier::layoutNotSet) {
+ if (language == EShLangMeshNV)
+ error(loc, message, "max_primitives", "");
+ else
+ assert(0);
+ }
+#endif
+ if (shaderQualifiers.blendEquation)
+ error(loc, message, "blend equation", "");
+ if (shaderQualifiers.numViews != TQualifier::layoutNotSet)
+ error(loc, message, "num_views", "");
+}
+
+// Correct and/or advance an object's offset layout qualifier.
+void TParseContext::fixOffset(const TSourceLoc& loc, TSymbol& symbol)
+{
+ const TQualifier& qualifier = symbol.getType().getQualifier();
+ if (symbol.getType().getBasicType() == EbtAtomicUint) {
+ if (qualifier.hasBinding() && (int)qualifier.layoutBinding < resources.maxAtomicCounterBindings) {
+
+ // Set the offset
+ int offset;
+ if (qualifier.hasOffset())
+ offset = qualifier.layoutOffset;
+ else
+ offset = atomicUintOffsets[qualifier.layoutBinding];
+ symbol.getWritableType().getQualifier().layoutOffset = offset;
+
+ // Check for overlap
+ int numOffsets = 4;
+ if (symbol.getType().isArray()) {
+ if (symbol.getType().isSizedArray() && !symbol.getType().getArraySizes()->isInnerUnsized())
+ numOffsets *= symbol.getType().getCumulativeArraySize();
+ else {
+ // "It is a compile-time error to declare an unsized array of atomic_uint."
+ error(loc, "array must be explicitly sized", "atomic_uint", "");
+ }
+ }
+ int repeated = intermediate.addUsedOffsets(qualifier.layoutBinding, offset, numOffsets);
+ if (repeated >= 0)
+ error(loc, "atomic counters sharing the same offset:", "offset", "%d", repeated);
+
+ // Bump the default offset
+ atomicUintOffsets[qualifier.layoutBinding] = offset + numOffsets;
+ }
+ }
+}
+
+//
+// Look up a function name in the symbol table, and make sure it is a function.
+//
+// Return the function symbol if found, otherwise nullptr.
+//
+const TFunction* TParseContext::findFunction(const TSourceLoc& loc, const TFunction& call, bool& builtIn)
+{
+ const TFunction* function = nullptr;
+
+ if (symbolTable.isFunctionNameVariable(call.getName())) {
+ error(loc, "can't use function syntax on variable", call.getName().c_str(), "");
+ return nullptr;
+ }
+
+ bool explicitTypesEnabled = extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) ||
+ extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int8) ||
+ extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int16) ||
+ extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int32) ||
+ extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int64) ||
+ extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float16) ||
+ extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float32) ||
+ extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float64);
+
+ if (profile == EEsProfile || version < 120)
+ function = findFunctionExact(loc, call, builtIn);
+ else if (version < 400)
+ function = findFunction120(loc, call, builtIn);
+ else if (explicitTypesEnabled)
+ function = findFunctionExplicitTypes(loc, call, builtIn);
+ else
+ function = findFunction400(loc, call, builtIn);
+
+ return function;
+}
+
+// Function finding algorithm for ES and desktop 110.
+const TFunction* TParseContext::findFunctionExact(const TSourceLoc& loc, const TFunction& call, bool& builtIn)
+{
+ TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn);
+ if (symbol == nullptr) {
+ error(loc, "no matching overloaded function found", call.getName().c_str(), "");
+
+ return nullptr;
+ }
+
+ return symbol->getAsFunction();
+}
+
+// Function finding algorithm for desktop versions 120 through 330.
+const TFunction* TParseContext::findFunction120(const TSourceLoc& loc, const TFunction& call, bool& builtIn)
+{
+ // first, look for an exact match
+ TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn);
+ if (symbol)
+ return symbol->getAsFunction();
+
+ // exact match not found, look through a list of overloaded functions of the same name
+
+ // "If no exact match is found, then [implicit conversions] will be applied to find a match. Mismatched types
+ // on input parameters (in or inout or default) must have a conversion from the calling argument type to the
+ // formal parameter type. Mismatched types on output parameters (out or inout) must have a conversion
+ // from the formal parameter type to the calling argument type. When argument conversions are used to find
+ // a match, it is a semantic error if there are multiple ways to apply these conversions to make the call match
+ // more than one function."
+
+ const TFunction* candidate = nullptr;
+ TVector<const TFunction*> candidateList;
+ symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn);
+
+ for (auto it = candidateList.begin(); it != candidateList.end(); ++it) {
+ const TFunction& function = *(*it);
+
+ // to even be a potential match, number of arguments has to match
+ if (call.getParamCount() != function.getParamCount())
+ continue;
+
+ bool possibleMatch = true;
+ for (int i = 0; i < function.getParamCount(); ++i) {
+ // same types is easy
+ if (*function[i].type == *call[i].type)
+ continue;
+
+ // We have a mismatch in type, see if it is implicitly convertible
+
+ if (function[i].type->isArray() || call[i].type->isArray() ||
+ ! function[i].type->sameElementShape(*call[i].type))
+ possibleMatch = false;
+ else {
+ // do direction-specific checks for conversion of basic type
+ if (function[i].type->getQualifier().isParamInput()) {
+ if (! intermediate.canImplicitlyPromote(call[i].type->getBasicType(), function[i].type->getBasicType()))
+ possibleMatch = false;
+ }
+ if (function[i].type->getQualifier().isParamOutput()) {
+ if (! intermediate.canImplicitlyPromote(function[i].type->getBasicType(), call[i].type->getBasicType()))
+ possibleMatch = false;
+ }
+ }
+ if (! possibleMatch)
+ break;
+ }
+ if (possibleMatch) {
+ if (candidate) {
+ // our second match, meaning ambiguity
+ error(loc, "ambiguous function signature match: multiple signatures match under implicit type conversion", call.getName().c_str(), "");
+ } else
+ candidate = &function;
+ }
+ }
+
+ if (candidate == nullptr)
+ error(loc, "no matching overloaded function found", call.getName().c_str(), "");
+
+ return candidate;
+}
+
+// Function finding algorithm for desktop version 400 and above.
+//
+// "When function calls are resolved, an exact type match for all the arguments
+// is sought. If an exact match is found, all other functions are ignored, and
+// the exact match is used. If no exact match is found, then the implicit
+// conversions in section 4.1.10 Implicit Conversions will be applied to find
+// a match. Mismatched types on input parameters (in or inout or default) must
+// have a conversion from the calling argument type to the formal parameter type.
+// Mismatched types on output parameters (out or inout) must have a conversion
+// from the formal parameter type to the calling argument type.
+//
+// "If implicit conversions can be used to find more than one matching function,
+// a single best-matching function is sought. To determine a best match, the
+// conversions between calling argument and formal parameter types are compared
+// for each function argument and pair of matching functions. After these
+// comparisons are performed, each pair of matching functions are compared.
+// A function declaration A is considered a better match than function
+// declaration B if
+//
+// * for at least one function argument, the conversion for that argument in A
+// is better than the corresponding conversion in B; and
+// * there is no function argument for which the conversion in B is better than
+// the corresponding conversion in A.
+//
+// "If a single function declaration is considered a better match than every
+// other matching function declaration, it will be used. Otherwise, a
+// compile-time semantic error for an ambiguous overloaded function call occurs.
+//
+// "To determine whether the conversion for a single argument in one match is
+// better than that for another match, the following rules are applied, in order:
+//
+// 1. An exact match is better than a match involving any implicit conversion.
+// 2. A match involving an implicit conversion from float to double is better
+// than a match involving any other implicit conversion.
+// 3. A match involving an implicit conversion from either int or uint to float
+// is better than a match involving an implicit conversion from either int
+// or uint to double.
+//
+// "If none of the rules above apply to a particular pair of conversions, neither
+// conversion is considered better than the other."
+//
+const TFunction* TParseContext::findFunction400(const TSourceLoc& loc, const TFunction& call, bool& builtIn)
+{
+ // first, look for an exact match
+ TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn);
+ if (symbol)
+ return symbol->getAsFunction();
+
+ // no exact match, use the generic selector, parameterized by the GLSL rules
+
+ // create list of candidates to send
+ TVector<const TFunction*> candidateList;
+ symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn);
+
+ // can 'from' convert to 'to'?
+ const auto convertible = [this,builtIn](const TType& from, const TType& to, TOperator, int) -> bool {
+ if (from == to)
+ return true;
+ if (from.coopMatParameterOK(to))
+ return true;
+ // Allow a sized array to be passed through an unsized array parameter, for coopMatLoad/Store functions
+ if (builtIn && from.isArray() && to.isUnsizedArray()) {
+ TType fromElementType(from, 0);
+ TType toElementType(to, 0);
+ if (fromElementType == toElementType)
+ return true;
+ }
+ if (from.isArray() || to.isArray() || ! from.sameElementShape(to))
+ return false;
+ return intermediate.canImplicitlyPromote(from.getBasicType(), to.getBasicType());
+ };
+
+ // Is 'to2' a better conversion than 'to1'?
+ // Ties should not be considered as better.
+ // Assumes 'convertible' already said true.
+ const auto better = [](const TType& from, const TType& to1, const TType& to2) -> bool {
+ // 1. exact match
+ if (from == to2)
+ return from != to1;
+ if (from == to1)
+ return false;
+
+ // 2. float -> double is better
+ if (from.getBasicType() == EbtFloat) {
+ if (to2.getBasicType() == EbtDouble && to1.getBasicType() != EbtDouble)
+ return true;
+ }
+
+ // 3. -> float is better than -> double
+ return to2.getBasicType() == EbtFloat && to1.getBasicType() == EbtDouble;
+ };
+
+ // for ambiguity reporting
+ bool tie = false;
+
+ // send to the generic selector
+ const TFunction* bestMatch = selectFunction(candidateList, call, convertible, better, tie);
+
+ if (bestMatch == nullptr)
+ error(loc, "no matching overloaded function found", call.getName().c_str(), "");
+ else if (tie)
+ error(loc, "ambiguous best function under implicit type conversion", call.getName().c_str(), "");
+
+ return bestMatch;
+}
+
+// "To determine whether the conversion for a single argument in one match
+// is better than that for another match, the conversion is assigned of the
+// three ranks ordered from best to worst:
+// 1. Exact match: no conversion.
+// 2. Promotion: integral or floating-point promotion.
+// 3. Conversion: integral conversion, floating-point conversion,
+// floating-integral conversion.
+// A conversion C1 is better than a conversion C2 if the rank of C1 is
+// better than the rank of C2."
+const TFunction* TParseContext::findFunctionExplicitTypes(const TSourceLoc& loc, const TFunction& call, bool& builtIn)
+{
+ // first, look for an exact match
+ TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn);
+ if (symbol)
+ return symbol->getAsFunction();
+
+ // no exact match, use the generic selector, parameterized by the GLSL rules
+
+ // create list of candidates to send
+ TVector<const TFunction*> candidateList;
+ symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn);
+
+ // can 'from' convert to 'to'?
+ const auto convertible = [this,builtIn](const TType& from, const TType& to, TOperator, int) -> bool {
+ if (from == to)
+ return true;
+ if (from.coopMatParameterOK(to))
+ return true;
+ // Allow a sized array to be passed through an unsized array parameter, for coopMatLoad/Store functions
+ if (builtIn && from.isArray() && to.isUnsizedArray()) {
+ TType fromElementType(from, 0);
+ TType toElementType(to, 0);
+ if (fromElementType == toElementType)
+ return true;
+ }
+ if (from.isArray() || to.isArray() || ! from.sameElementShape(to))
+ return false;
+ return intermediate.canImplicitlyPromote(from.getBasicType(), to.getBasicType());
+ };
+
+ // Is 'to2' a better conversion than 'to1'?
+ // Ties should not be considered as better.
+ // Assumes 'convertible' already said true.
+ const auto better = [this](const TType& from, const TType& to1, const TType& to2) -> bool {
+ // 1. exact match
+ if (from == to2)
+ return from != to1;
+ if (from == to1)
+ return false;
+
+ // 2. Promotion (integral, floating-point) is better
+ TBasicType from_type = from.getBasicType();
+ TBasicType to1_type = to1.getBasicType();
+ TBasicType to2_type = to2.getBasicType();
+ bool isPromotion1 = (intermediate.isIntegralPromotion(from_type, to1_type) ||
+ intermediate.isFPPromotion(from_type, to1_type));
+ bool isPromotion2 = (intermediate.isIntegralPromotion(from_type, to2_type) ||
+ intermediate.isFPPromotion(from_type, to2_type));
+ if (isPromotion2)
+ return !isPromotion1;
+ if(isPromotion1)
+ return false;
+
+ // 3. Conversion (integral, floating-point , floating-integral)
+ bool isConversion1 = (intermediate.isIntegralConversion(from_type, to1_type) ||
+ intermediate.isFPConversion(from_type, to1_type) ||
+ intermediate.isFPIntegralConversion(from_type, to1_type));
+ bool isConversion2 = (intermediate.isIntegralConversion(from_type, to2_type) ||
+ intermediate.isFPConversion(from_type, to2_type) ||
+ intermediate.isFPIntegralConversion(from_type, to2_type));
+
+ return isConversion2 && !isConversion1;
+ };
+
+ // for ambiguity reporting
+ bool tie = false;
+
+ // send to the generic selector
+ const TFunction* bestMatch = selectFunction(candidateList, call, convertible, better, tie);
+
+ if (bestMatch == nullptr)
+ error(loc, "no matching overloaded function found", call.getName().c_str(), "");
+ else if (tie)
+ error(loc, "ambiguous best function under implicit type conversion", call.getName().c_str(), "");
+
+ return bestMatch;
+}
+
+// When a declaration includes a type, but not a variable name, it can be
+// to establish defaults.
+void TParseContext::declareTypeDefaults(const TSourceLoc& loc, const TPublicType& publicType)
+{
+ if (publicType.basicType == EbtAtomicUint && publicType.qualifier.hasBinding() && publicType.qualifier.hasOffset()) {
+ if (publicType.qualifier.layoutBinding >= (unsigned int)resources.maxAtomicCounterBindings) {
+ error(loc, "atomic_uint binding is too large", "binding", "");
+ return;
+ }
+ atomicUintOffsets[publicType.qualifier.layoutBinding] = publicType.qualifier.layoutOffset;
+ return;
+ }
+
+ if (publicType.qualifier.hasLayout() && !publicType.qualifier.layoutBufferReference)
+ warn(loc, "useless application of layout qualifier", "layout", "");
+}
+
+//
+// Do everything necessary to handle a variable (non-block) declaration.
+// Either redeclaring a variable, or making a new one, updating the symbol
+// table, and all error checking.
+//
+// Returns a subtree node that computes an initializer, if needed.
+// Returns nullptr if there is no code to execute for initialization.
+//
+// 'publicType' is the type part of the declaration (to the left)
+// 'arraySizes' is the arrayness tagged on the identifier (to the right)
+//
+TIntermNode* TParseContext::declareVariable(const TSourceLoc& loc, TString& identifier, const TPublicType& publicType,
+ TArraySizes* arraySizes, TIntermTyped* initializer)
+{
+ // Make a fresh type that combines the characteristics from the individual
+ // identifier syntax and the declaration-type syntax.
+ TType type(publicType);
+ type.transferArraySizes(arraySizes);
+ type.copyArrayInnerSizes(publicType.arraySizes);
+ arrayOfArrayVersionCheck(loc, type.getArraySizes());
+
+ if (type.isCoopMat()) {
+ intermediate.setUseVulkanMemoryModel();
+ intermediate.setUseStorageBuffer();
+
+ if (!publicType.typeParameters || publicType.typeParameters->getNumDims() != 4) {
+ error(loc, "expected four type parameters", identifier.c_str(), "");
+ }
+ if (publicType.typeParameters &&
+ publicType.typeParameters->getDimSize(0) != 16 &&
+ publicType.typeParameters->getDimSize(0) != 32 &&
+ publicType.typeParameters->getDimSize(0) != 64) {
+ error(loc, "expected 16, 32, or 64 bits for first type parameter", identifier.c_str(), "");
+ }
+ } else {
+ if (publicType.typeParameters && publicType.typeParameters->getNumDims() != 0) {
+ error(loc, "unexpected type parameters", identifier.c_str(), "");
+ }
+ }
+
+ if (voidErrorCheck(loc, identifier, type.getBasicType()))
+ return nullptr;
+
+ if (initializer)
+ rValueErrorCheck(loc, "initializer", initializer);
+ else
+ nonInitConstCheck(loc, identifier, type);
+
+ samplerCheck(loc, type, identifier, initializer);
+ atomicUintCheck(loc, type, identifier);
+ transparentOpaqueCheck(loc, type, identifier);
+#ifdef NV_EXTENSIONS
+ accStructNVCheck(loc, type, identifier);
+#endif
+ if (type.getQualifier().storage == EvqConst && type.containsBasicType(EbtReference)) {
+ error(loc, "variables with reference type can't have qualifier 'const'", "qualifier", "");
+ }
+
+ if (type.getQualifier().storage != EvqUniform && type.getQualifier().storage != EvqBuffer) {
+ if (type.containsBasicType(EbtFloat16))
+ requireFloat16Arithmetic(loc, "qualifier", "float16 types can only be in uniform block or buffer storage");
+ if (type.contains16BitInt())
+ requireInt16Arithmetic(loc, "qualifier", "(u)int16 types can only be in uniform block or buffer storage");
+ if (type.contains8BitInt())
+ requireInt8Arithmetic(loc, "qualifier", "(u)int8 types can only be in uniform block or buffer storage");
+ }
+
+ if (type.getQualifier().storage == EvqShared &&
+ type.containsCoopMat())
+ error(loc, "qualifier", "Cooperative matrix types must not be used in shared memory", "");
+
+ if (identifier != "gl_FragCoord" && (publicType.shaderQualifiers.originUpperLeft || publicType.shaderQualifiers.pixelCenterInteger))
+ error(loc, "can only apply origin_upper_left and pixel_center_origin to gl_FragCoord", "layout qualifier", "");
+ if (identifier != "gl_FragDepth" && publicType.shaderQualifiers.layoutDepth != EldNone)
+ error(loc, "can only apply depth layout to gl_FragDepth", "layout qualifier", "");
+
+ // Check for redeclaration of built-ins and/or attempting to declare a reserved name
+ TSymbol* symbol = redeclareBuiltinVariable(loc, identifier, type.getQualifier(), publicType.shaderQualifiers);
+ if (symbol == nullptr)
+ reservedErrorCheck(loc, identifier);
+
+ inheritGlobalDefaults(type.getQualifier());
+
+ // Declare the variable
+ if (type.isArray()) {
+ // Check that implicit sizing is only where allowed.
+ arraySizesCheck(loc, type.getQualifier(), type.getArraySizes(), initializer, false);
+
+ if (! arrayQualifierError(loc, type.getQualifier()) && ! arrayError(loc, type))
+ declareArray(loc, identifier, type, symbol);
+
+ if (initializer) {
+ profileRequires(loc, ENoProfile, 120, E_GL_3DL_array_objects, "initializer");
+ profileRequires(loc, EEsProfile, 300, nullptr, "initializer");
+ }
+ } else {
+ // non-array case
+ if (symbol == nullptr)
+ symbol = declareNonArray(loc, identifier, type);
+ else if (type != symbol->getType())
+ error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str());
+ }
+
+ if (symbol == nullptr)
+ return nullptr;
+
+ // Deal with initializer
+ TIntermNode* initNode = nullptr;
+ if (symbol != nullptr && initializer) {
+ TVariable* variable = symbol->getAsVariable();
+ if (! variable) {
+ error(loc, "initializer requires a variable, not a member", identifier.c_str(), "");
+ return nullptr;
+ }
+ initNode = executeInitializer(loc, initializer, variable);
+ }
+
+ // look for errors in layout qualifier use
+ layoutObjectCheck(loc, *symbol);
+
+ // fix up
+ fixOffset(loc, *symbol);
+
+ return initNode;
+}
+
+// Pick up global defaults from the provide global defaults into dst.
+void TParseContext::inheritGlobalDefaults(TQualifier& dst) const
+{
+ if (dst.storage == EvqVaryingOut) {
+ if (! dst.hasStream() && language == EShLangGeometry)
+ dst.layoutStream = globalOutputDefaults.layoutStream;
+ if (! dst.hasXfbBuffer())
+ dst.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer;
+ }
+}
+
+//
+// Make an internal-only variable whose name is for debug purposes only
+// and won't be searched for. Callers will only use the return value to use
+// the variable, not the name to look it up. It is okay if the name
+// is the same as other names; there won't be any conflict.
+//
+TVariable* TParseContext::makeInternalVariable(const char* name, const TType& type) const
+{
+ TString* nameString = NewPoolTString(name);
+ TVariable* variable = new TVariable(nameString, type);
+ symbolTable.makeInternalVariable(*variable);
+
+ return variable;
+}
+
+//
+// Declare a non-array variable, the main point being there is no redeclaration
+// for resizing allowed.
+//
+// Return the successfully declared variable.
+//
+TVariable* TParseContext::declareNonArray(const TSourceLoc& loc, const TString& identifier, const TType& type)
+{
+ // make a new variable
+ TVariable* variable = new TVariable(&identifier, type);
+
+ ioArrayCheck(loc, type, identifier);
+
+ // add variable to symbol table
+ if (symbolTable.insert(*variable)) {
+ if (symbolTable.atGlobalLevel())
+ trackLinkage(*variable);
+ return variable;
+ }
+
+ error(loc, "redefinition", variable->getName().c_str(), "");
+ return nullptr;
+}
+
+//
+// Handle all types of initializers from the grammar.
+//
+// Returning nullptr just means there is no code to execute to handle the
+// initializer, which will, for example, be the case for constant initializers.
+//
+TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyped* initializer, TVariable* variable)
+{
+ //
+ // Identifier must be of type constant, a global, or a temporary, and
+ // starting at version 120, desktop allows uniforms to have initializers.
+ //
+ TStorageQualifier qualifier = variable->getType().getQualifier().storage;
+ if (! (qualifier == EvqTemporary || qualifier == EvqGlobal || qualifier == EvqConst ||
+ (qualifier == EvqUniform && profile != EEsProfile && version >= 120))) {
+ error(loc, " cannot initialize this type of qualifier ", variable->getType().getStorageQualifierString(), "");
+ return nullptr;
+ }
+ arrayObjectCheck(loc, variable->getType(), "array initializer");
+
+ //
+ // If the initializer was from braces { ... }, we convert the whole subtree to a
+ // constructor-style subtree, allowing the rest of the code to operate
+ // identically for both kinds of initializers.
+ //
+ // Type can't be deduced from the initializer list, so a skeletal type to
+ // follow has to be passed in. Constness and specialization-constness
+ // should be deduced bottom up, not dictated by the skeletal type.
+ //
+ TType skeletalType;
+ skeletalType.shallowCopy(variable->getType());
+ skeletalType.getQualifier().makeTemporary();
+ initializer = convertInitializerList(loc, skeletalType, initializer);
+ if (! initializer) {
+ // error recovery; don't leave const without constant values
+ if (qualifier == EvqConst)
+ variable->getWritableType().getQualifier().makeTemporary();
+ return nullptr;
+ }
+
+ // Fix outer arrayness if variable is unsized, getting size from the initializer
+ if (initializer->getType().isSizedArray() && variable->getType().isUnsizedArray())
+ variable->getWritableType().changeOuterArraySize(initializer->getType().getOuterArraySize());
+
+ // Inner arrayness can also get set by an initializer
+ if (initializer->getType().isArrayOfArrays() && variable->getType().isArrayOfArrays() &&
+ initializer->getType().getArraySizes()->getNumDims() ==
+ variable->getType().getArraySizes()->getNumDims()) {
+ // adopt unsized sizes from the initializer's sizes
+ for (int d = 1; d < variable->getType().getArraySizes()->getNumDims(); ++d) {
+ if (variable->getType().getArraySizes()->getDimSize(d) == UnsizedArraySize) {
+ variable->getWritableType().getArraySizes()->setDimSize(d,
+ initializer->getType().getArraySizes()->getDimSize(d));
+ }
+ }
+ }
+
+ // Uniforms require a compile-time constant initializer
+ if (qualifier == EvqUniform && ! initializer->getType().getQualifier().isFrontEndConstant()) {
+ error(loc, "uniform initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str());
+ variable->getWritableType().getQualifier().makeTemporary();
+ return nullptr;
+ }
+ // Global consts require a constant initializer (specialization constant is okay)
+ if (qualifier == EvqConst && symbolTable.atGlobalLevel() && ! initializer->getType().getQualifier().isConstant()) {
+ error(loc, "global const initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str());
+ variable->getWritableType().getQualifier().makeTemporary();
+ return nullptr;
+ }
+
+ // Const variables require a constant initializer, depending on version
+ if (qualifier == EvqConst) {
+ if (! initializer->getType().getQualifier().isConstant()) {
+ const char* initFeature = "non-constant initializer";
+ requireProfile(loc, ~EEsProfile, initFeature);
+ profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature);
+ variable->getWritableType().getQualifier().storage = EvqConstReadOnly;
+ qualifier = EvqConstReadOnly;
+ }
+ } else {
+ // Non-const global variables in ES need a const initializer.
+ //
+ // "In declarations of global variables with no storage qualifier or with a const
+ // qualifier any initializer must be a constant expression."
+ if (symbolTable.atGlobalLevel() && ! initializer->getType().getQualifier().isConstant()) {
+ const char* initFeature = "non-constant global initializer (needs GL_EXT_shader_non_constant_global_initializers)";
+ if (profile == EEsProfile) {
+ if (relaxedErrors() && ! extensionTurnedOn(E_GL_EXT_shader_non_constant_global_initializers))
+ warn(loc, "not allowed in this version", initFeature, "");
+ else
+ profileRequires(loc, EEsProfile, 0, E_GL_EXT_shader_non_constant_global_initializers, initFeature);
+ }
+ }
+ }
+
+ if (qualifier == EvqConst || qualifier == EvqUniform) {
+ // Compile-time tagging of the variable with its constant value...
+
+ initializer = intermediate.addConversion(EOpAssign, variable->getType(), initializer);
+ if (! initializer || ! initializer->getType().getQualifier().isConstant() || variable->getType() != initializer->getType()) {
+ error(loc, "non-matching or non-convertible constant type for const initializer",
+ variable->getType().getStorageQualifierString(), "");
+ variable->getWritableType().getQualifier().makeTemporary();
+ return nullptr;
+ }
+
+ // We either have a folded constant in getAsConstantUnion, or we have to use
+ // the initializer's subtree in the AST to represent the computation of a
+ // specialization constant.
+ assert(initializer->getAsConstantUnion() || initializer->getType().getQualifier().isSpecConstant());
+ if (initializer->getAsConstantUnion())
+ variable->setConstArray(initializer->getAsConstantUnion()->getConstArray());
+ else {
+ // It's a specialization constant.
+ variable->getWritableType().getQualifier().makeSpecConstant();
+
+ // Keep the subtree that computes the specialization constant with the variable.
+ // Later, a symbol node will adopt the subtree from the variable.
+ variable->setConstSubtree(initializer);
+ }
+ } else {
+ // normal assigning of a value to a variable...
+ specializationCheck(loc, initializer->getType(), "initializer");
+ TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc);
+ TIntermTyped* initNode = intermediate.addAssign(EOpAssign, intermSymbol, initializer, loc);
+ if (! initNode)
+ assignError(loc, "=", intermSymbol->getCompleteString(), initializer->getCompleteString());
+
+ return initNode;
+ }
+
+ return nullptr;
+}
+
+//
+// Reprocess any initializer-list (the "{ ... }" syntax) parts of the
+// initializer.
+//
+// Need to hierarchically assign correct types and implicit
+// conversions. Will do this mimicking the same process used for
+// creating a constructor-style initializer, ensuring we get the
+// same form. However, it has to in parallel walk the 'type'
+// passed in, as type cannot be deduced from an initializer list.
+//
+TIntermTyped* TParseContext::convertInitializerList(const TSourceLoc& loc, const TType& type, TIntermTyped* initializer)
+{
+ // Will operate recursively. Once a subtree is found that is constructor style,
+ // everything below it is already good: Only the "top part" of the initializer
+ // can be an initializer list, where "top part" can extend for several (or all) levels.
+
+ // see if we have bottomed out in the tree within the initializer-list part
+ TIntermAggregate* initList = initializer->getAsAggregate();
+ if (! initList || initList->getOp() != EOpNull)
+ return initializer;
+
+ // Of the initializer-list set of nodes, need to process bottom up,
+ // so recurse deep, then process on the way up.
+
+ // Go down the tree here...
+ if (type.isArray()) {
+ // The type's array might be unsized, which could be okay, so base sizes on the size of the aggregate.
+ // Later on, initializer execution code will deal with array size logic.
+ TType arrayType;
+ arrayType.shallowCopy(type); // sharing struct stuff is fine
+ arrayType.copyArraySizes(*type.getArraySizes()); // but get a fresh copy of the array information, to edit below
+
+ // edit array sizes to fill in unsized dimensions
+ arrayType.changeOuterArraySize((int)initList->getSequence().size());
+ TIntermTyped* firstInit = initList->getSequence()[0]->getAsTyped();
+ if (arrayType.isArrayOfArrays() && firstInit->getType().isArray() &&
+ arrayType.getArraySizes()->getNumDims() == firstInit->getType().getArraySizes()->getNumDims() + 1) {
+ for (int d = 1; d < arrayType.getArraySizes()->getNumDims(); ++d) {
+ if (arrayType.getArraySizes()->getDimSize(d) == UnsizedArraySize)
+ arrayType.getArraySizes()->setDimSize(d, firstInit->getType().getArraySizes()->getDimSize(d - 1));
+ }
+ }
+
+ TType elementType(arrayType, 0); // dereferenced type
+ for (size_t i = 0; i < initList->getSequence().size(); ++i) {
+ initList->getSequence()[i] = convertInitializerList(loc, elementType, initList->getSequence()[i]->getAsTyped());
+ if (initList->getSequence()[i] == nullptr)
+ return nullptr;
+ }
+
+ return addConstructor(loc, initList, arrayType);
+ } else if (type.isStruct()) {
+ if (type.getStruct()->size() != initList->getSequence().size()) {
+ error(loc, "wrong number of structure members", "initializer list", "");
+ return nullptr;
+ }
+ for (size_t i = 0; i < type.getStruct()->size(); ++i) {
+ initList->getSequence()[i] = convertInitializerList(loc, *(*type.getStruct())[i].type, initList->getSequence()[i]->getAsTyped());
+ if (initList->getSequence()[i] == nullptr)
+ return nullptr;
+ }
+ } else if (type.isMatrix()) {
+ if (type.getMatrixCols() != (int)initList->getSequence().size()) {
+ error(loc, "wrong number of matrix columns:", "initializer list", type.getCompleteString().c_str());
+ return nullptr;
+ }
+ TType vectorType(type, 0); // dereferenced type
+ for (int i = 0; i < type.getMatrixCols(); ++i) {
+ initList->getSequence()[i] = convertInitializerList(loc, vectorType, initList->getSequence()[i]->getAsTyped());
+ if (initList->getSequence()[i] == nullptr)
+ return nullptr;
+ }
+ } else if (type.isVector()) {
+ if (type.getVectorSize() != (int)initList->getSequence().size()) {
+ error(loc, "wrong vector size (or rows in a matrix column):", "initializer list", type.getCompleteString().c_str());
+ return nullptr;
+ }
+ } else {
+ error(loc, "unexpected initializer-list type:", "initializer list", type.getCompleteString().c_str());
+ return nullptr;
+ }
+
+ // Now that the subtree is processed, process this node as if the
+ // initializer list is a set of arguments to a constructor.
+ TIntermNode* emulatedConstructorArguments;
+ if (initList->getSequence().size() == 1)
+ emulatedConstructorArguments = initList->getSequence()[0];
+ else
+ emulatedConstructorArguments = initList;
+ return addConstructor(loc, emulatedConstructorArguments, type);
+}
+
+//
+// Test for the correctness of the parameters passed to various constructor functions
+// and also convert them to the right data type, if allowed and required.
+//
+// 'node' is what to construct from.
+// 'type' is what type to construct.
+//
+// Returns nullptr for an error or the constructed node (aggregate or typed) for no error.
+//
+TIntermTyped* TParseContext::addConstructor(const TSourceLoc& loc, TIntermNode* node, const TType& type)
+{
+ if (node == nullptr || node->getAsTyped() == nullptr)
+ return nullptr;
+ rValueErrorCheck(loc, "constructor", node->getAsTyped());
+
+ TIntermAggregate* aggrNode = node->getAsAggregate();
+ TOperator op = intermediate.mapTypeToConstructorOp(type);
+
+ // Combined texture-sampler constructors are completely semantic checked
+ // in constructorTextureSamplerError()
+ if (op == EOpConstructTextureSampler) {
+ if (aggrNode->getSequence()[1]->getAsTyped()->getType().getSampler().shadow) {
+ // Transfer depth into the texture (SPIR-V image) type, as a hint
+ // for tools to know this texture/image is a depth image.
+ aggrNode->getSequence()[0]->getAsTyped()->getWritableType().getSampler().shadow = true;
+ }
+ return intermediate.setAggregateOperator(aggrNode, op, type, loc);
+ }
+
+ TTypeList::const_iterator memberTypes;
+ if (op == EOpConstructStruct)
+ memberTypes = type.getStruct()->begin();
+
+ TType elementType;
+ if (type.isArray()) {
+ TType dereferenced(type, 0);
+ elementType.shallowCopy(dereferenced);
+ } else
+ elementType.shallowCopy(type);
+
+ bool singleArg;
+ if (aggrNode) {
+ if (aggrNode->getOp() != EOpNull)
+ singleArg = true;
+ else
+ singleArg = false;
+ } else
+ singleArg = true;
+
+ TIntermTyped *newNode;
+ if (singleArg) {
+ // If structure constructor or array constructor is being called
+ // for only one parameter inside the structure, we need to call constructAggregate function once.
+ if (type.isArray())
+ newNode = constructAggregate(node, elementType, 1, node->getLoc());
+ else if (op == EOpConstructStruct)
+ newNode = constructAggregate(node, *(*memberTypes).type, 1, node->getLoc());
+ else
+ newNode = constructBuiltIn(type, op, node->getAsTyped(), node->getLoc(), false);
+
+ if (newNode && (type.isArray() || op == EOpConstructStruct))
+ newNode = intermediate.setAggregateOperator(newNode, EOpConstructStruct, type, loc);
+
+ return newNode;
+ }
+
+ //
+ // Handle list of arguments.
+ //
+ TIntermSequence &sequenceVector = aggrNode->getSequence(); // Stores the information about the parameter to the constructor
+ // if the structure constructor contains more than one parameter, then construct
+ // each parameter
+
+ int paramCount = 0; // keeps track of the constructor parameter number being checked
+
+ // for each parameter to the constructor call, check to see if the right type is passed or convert them
+ // to the right type if possible (and allowed).
+ // for structure constructors, just check if the right type is passed, no conversion is allowed.
+ for (TIntermSequence::iterator p = sequenceVector.begin();
+ p != sequenceVector.end(); p++, paramCount++) {
+ if (type.isArray())
+ newNode = constructAggregate(*p, elementType, paramCount+1, node->getLoc());
+ else if (op == EOpConstructStruct)
+ newNode = constructAggregate(*p, *(memberTypes[paramCount]).type, paramCount+1, node->getLoc());
+ else
+ newNode = constructBuiltIn(type, op, (*p)->getAsTyped(), node->getLoc(), true);
+
+ if (newNode)
+ *p = newNode;
+ else
+ return nullptr;
+ }
+
+ return intermediate.setAggregateOperator(aggrNode, op, type, loc);
+}
+
+// Function for constructor implementation. Calls addUnaryMath with appropriate EOp value
+// for the parameter to the constructor (passed to this function). Essentially, it converts
+// the parameter types correctly. If a constructor expects an int (like ivec2) and is passed a
+// float, then float is converted to int.
+//
+// Returns nullptr for an error or the constructed node.
+//
+TIntermTyped* TParseContext::constructBuiltIn(const TType& type, TOperator op, TIntermTyped* node, const TSourceLoc& loc,
+ bool subset)
+{
+ // If we are changing a matrix in both domain of basic type and to a non matrix,
+ // do the shape change first (by default, below, basic type is changed before shape).
+ // This avoids requesting a matrix of a new type that is going to be discarded anyway.
+ // TODO: This could be generalized to more type combinations, but that would require
+ // more extensive testing and full algorithm rework. For now, the need to do two changes makes
+ // the recursive call work, and avoids the most aggregious case of creating integer matrices.
+ if (node->getType().isMatrix() && (type.isScalar() || type.isVector()) &&
+ type.isFloatingDomain() != node->getType().isFloatingDomain()) {
+ TType transitionType(node->getBasicType(), glslang::EvqTemporary, type.getVectorSize(), 0, 0, node->isVector());
+ TOperator transitionOp = intermediate.mapTypeToConstructorOp(transitionType);
+ node = constructBuiltIn(transitionType, transitionOp, node, loc, false);
+ }
+
+ TIntermTyped* newNode;
+ TOperator basicOp;
+
+ //
+ // First, convert types as needed.
+ //
+ switch (op) {
+ case EOpConstructVec2:
+ case EOpConstructVec3:
+ case EOpConstructVec4:
+ case EOpConstructMat2x2:
+ case EOpConstructMat2x3:
+ case EOpConstructMat2x4:
+ case EOpConstructMat3x2:
+ case EOpConstructMat3x3:
+ case EOpConstructMat3x4:
+ case EOpConstructMat4x2:
+ case EOpConstructMat4x3:
+ case EOpConstructMat4x4:
+ case EOpConstructFloat:
+ basicOp = EOpConstructFloat;
+ break;
+
+ case EOpConstructDVec2:
+ case EOpConstructDVec3:
+ case EOpConstructDVec4:
+ case EOpConstructDMat2x2:
+ case EOpConstructDMat2x3:
+ case EOpConstructDMat2x4:
+ case EOpConstructDMat3x2:
+ case EOpConstructDMat3x3:
+ case EOpConstructDMat3x4:
+ case EOpConstructDMat4x2:
+ case EOpConstructDMat4x3:
+ case EOpConstructDMat4x4:
+ case EOpConstructDouble:
+ basicOp = EOpConstructDouble;
+ break;
+
+ case EOpConstructF16Vec2:
+ case EOpConstructF16Vec3:
+ case EOpConstructF16Vec4:
+ case EOpConstructF16Mat2x2:
+ case EOpConstructF16Mat2x3:
+ case EOpConstructF16Mat2x4:
+ case EOpConstructF16Mat3x2:
+ case EOpConstructF16Mat3x3:
+ case EOpConstructF16Mat3x4:
+ case EOpConstructF16Mat4x2:
+ case EOpConstructF16Mat4x3:
+ case EOpConstructF16Mat4x4:
+ case EOpConstructFloat16:
+ basicOp = EOpConstructFloat16;
+ break;
+
+ case EOpConstructI8Vec2:
+ case EOpConstructI8Vec3:
+ case EOpConstructI8Vec4:
+ case EOpConstructInt8:
+ basicOp = EOpConstructInt8;
+ break;
+
+ case EOpConstructU8Vec2:
+ case EOpConstructU8Vec3:
+ case EOpConstructU8Vec4:
+ case EOpConstructUint8:
+ basicOp = EOpConstructUint8;
+ break;
+
+ case EOpConstructI16Vec2:
+ case EOpConstructI16Vec3:
+ case EOpConstructI16Vec4:
+ case EOpConstructInt16:
+ basicOp = EOpConstructInt16;
+ break;
+
+ case EOpConstructU16Vec2:
+ case EOpConstructU16Vec3:
+ case EOpConstructU16Vec4:
+ case EOpConstructUint16:
+ basicOp = EOpConstructUint16;
+ break;
+
+ case EOpConstructIVec2:
+ case EOpConstructIVec3:
+ case EOpConstructIVec4:
+ case EOpConstructInt:
+ basicOp = EOpConstructInt;
+ break;
+
+ case EOpConstructUVec2:
+ case EOpConstructUVec3:
+ case EOpConstructUVec4:
+ case EOpConstructUint:
+ basicOp = EOpConstructUint;
+ break;
+
+ case EOpConstructI64Vec2:
+ case EOpConstructI64Vec3:
+ case EOpConstructI64Vec4:
+ case EOpConstructInt64:
+ basicOp = EOpConstructInt64;
+ break;
+
+ case EOpConstructUint64:
+ if (type.isScalar() && node->getType().getBasicType() == EbtReference) {
+ TIntermTyped* newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConvPtrToUint64, true, node, type);
+ return newNode;
+ }
+ // fall through
+ case EOpConstructU64Vec2:
+ case EOpConstructU64Vec3:
+ case EOpConstructU64Vec4:
+ basicOp = EOpConstructUint64;
+ break;
+
+ case EOpConstructBVec2:
+ case EOpConstructBVec3:
+ case EOpConstructBVec4:
+ case EOpConstructBool:
+ basicOp = EOpConstructBool;
+ break;
+
+ case EOpConstructNonuniform:
+ node->getWritableType().getQualifier().nonUniform = true;
+ return node;
+ break;
+
+ case EOpConstructReference:
+ // construct reference from reference
+ if (node->getType().getBasicType() == EbtReference) {
+ newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConstructReference, true, node, type);
+ return newNode;
+ // construct reference from uint64
+ } else if (node->getType().isScalar() && node->getType().getBasicType() == EbtUint64) {
+ TIntermTyped* newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConvUint64ToPtr, true, node, type);
+ return newNode;
+ } else {
+ return nullptr;
+ }
+
+ case EOpConstructCooperativeMatrix:
+ if (!node->getType().isCoopMat()) {
+ if (type.getBasicType() != node->getType().getBasicType()) {
+ node = intermediate.addConversion(type.getBasicType(), node);
+ }
+ node = intermediate.setAggregateOperator(node, EOpConstructCooperativeMatrix, type, node->getLoc());
+ } else {
+ switch (type.getBasicType()) {
+ default:
+ assert(0);
+ break;
+ case EbtFloat:
+ assert(node->getType().getBasicType() == EbtFloat16);
+ node = intermediate.addUnaryNode(EOpConvFloat16ToFloat, node, node->getLoc(), type);
+ break;
+ case EbtFloat16:
+ assert(node->getType().getBasicType() == EbtFloat);
+ node = intermediate.addUnaryNode(EOpConvFloatToFloat16, node, node->getLoc(), type);
+ break;
+ }
+ // If it's a (non-specialization) constant, it must be folded.
+ if (node->getAsUnaryNode()->getOperand()->getAsConstantUnion())
+ return node->getAsUnaryNode()->getOperand()->getAsConstantUnion()->fold(op, node->getType());
+ }
+
+ return node;
+
+ default:
+ error(loc, "unsupported construction", "", "");
+
+ return nullptr;
+ }
+ newNode = intermediate.addUnaryMath(basicOp, node, node->getLoc());
+ if (newNode == nullptr) {
+ error(loc, "can't convert", "constructor", "");
+ return nullptr;
+ }
+
+ //
+ // Now, if there still isn't an operation to do the construction, and we need one, add one.
+ //
+
+ // Otherwise, skip out early.
+ if (subset || (newNode != node && newNode->getType() == type))
+ return newNode;
+
+ // setAggregateOperator will insert a new node for the constructor, as needed.
+ return intermediate.setAggregateOperator(newNode, op, type, loc);
+}
+
+// This function tests for the type of the parameters to the structure or array constructor. Raises
+// an error message if the expected type does not match the parameter passed to the constructor.
+//
+// Returns nullptr for an error or the input node itself if the expected and the given parameter types match.
+//
+TIntermTyped* TParseContext::constructAggregate(TIntermNode* node, const TType& type, int paramCount, const TSourceLoc& loc)
+{
+ TIntermTyped* converted = intermediate.addConversion(EOpConstructStruct, type, node->getAsTyped());
+ if (! converted || converted->getType() != type) {
+ error(loc, "", "constructor", "cannot convert parameter %d from '%s' to '%s'", paramCount,
+ node->getAsTyped()->getType().getCompleteString().c_str(), type.getCompleteString().c_str());
+
+ return nullptr;
+ }
+
+ return converted;
+}
+
+//
+// Do everything needed to add an interface block.
+//
+void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, const TString* instanceName,
+ TArraySizes* arraySizes)
+{
+ blockStageIoCheck(loc, currentBlockQualifier);
+ blockQualifierCheck(loc, currentBlockQualifier, instanceName != nullptr);
+ if (arraySizes != nullptr) {
+ arraySizesCheck(loc, currentBlockQualifier, arraySizes, nullptr, false);
+ arrayOfArrayVersionCheck(loc, arraySizes);
+ if (arraySizes->getNumDims() > 1)
+ requireProfile(loc, ~EEsProfile, "array-of-array of block");
+ }
+
+ // fix and check for member storage qualifiers and types that don't belong within a block
+ for (unsigned int member = 0; member < typeList.size(); ++member) {
+ TType& memberType = *typeList[member].type;
+ TQualifier& memberQualifier = memberType.getQualifier();
+ const TSourceLoc& memberLoc = typeList[member].loc;
+ globalQualifierFixCheck(memberLoc, memberQualifier);
+ if (memberQualifier.storage != EvqTemporary && memberQualifier.storage != EvqGlobal && memberQualifier.storage != currentBlockQualifier.storage)
+ error(memberLoc, "member storage qualifier cannot contradict block storage qualifier", memberType.getFieldName().c_str(), "");
+ memberQualifier.storage = currentBlockQualifier.storage;
+#ifdef NV_EXTENSIONS
+ if (currentBlockQualifier.perPrimitiveNV)
+ memberQualifier.perPrimitiveNV = currentBlockQualifier.perPrimitiveNV;
+ if (currentBlockQualifier.perViewNV)
+ memberQualifier.perViewNV = currentBlockQualifier.perViewNV;
+ if (currentBlockQualifier.perTaskNV)
+ memberQualifier.perTaskNV = currentBlockQualifier.perTaskNV;
+#endif
+ if ((currentBlockQualifier.storage == EvqUniform || currentBlockQualifier.storage == EvqBuffer) && (memberQualifier.isInterpolation() || memberQualifier.isAuxiliary()))
+ error(memberLoc, "member of uniform or buffer block cannot have an auxiliary or interpolation qualifier", memberType.getFieldName().c_str(), "");
+ if (memberType.isArray())
+ arraySizesCheck(memberLoc, currentBlockQualifier, memberType.getArraySizes(), nullptr, member == typeList.size() - 1);
+ if (memberQualifier.hasOffset()) {
+ if (spvVersion.spv == 0) {
+ requireProfile(memberLoc, ~EEsProfile, "offset on block member");
+ profileRequires(memberLoc, ~EEsProfile, 440, E_GL_ARB_enhanced_layouts, "offset on block member");
+ }
+ }
+
+ if (memberType.containsOpaque())
+ error(memberLoc, "member of block cannot be or contain a sampler, image, or atomic_uint type", typeList[member].type->getFieldName().c_str(), "");
+
+ if (memberType.containsCoopMat())
+ error(memberLoc, "member of block cannot be or contain a cooperative matrix type", typeList[member].type->getFieldName().c_str(), "");
+ }
+
+ // This might be a redeclaration of a built-in block. If so, redeclareBuiltinBlock() will
+ // do all the rest.
+ if (! symbolTable.atBuiltInLevel() && builtInName(*blockName)) {
+ redeclareBuiltinBlock(loc, typeList, *blockName, instanceName, arraySizes);
+ return;
+ }
+
+ // Not a redeclaration of a built-in; check that all names are user names.
+ reservedErrorCheck(loc, *blockName);
+ if (instanceName)
+ reservedErrorCheck(loc, *instanceName);
+ for (unsigned int member = 0; member < typeList.size(); ++member)
+ reservedErrorCheck(typeList[member].loc, typeList[member].type->getFieldName());
+
+ // Make default block qualification, and adjust the member qualifications
+
+ TQualifier defaultQualification;
+ switch (currentBlockQualifier.storage) {
+ case EvqUniform: defaultQualification = globalUniformDefaults; break;
+ case EvqBuffer: defaultQualification = globalBufferDefaults; break;
+ case EvqVaryingIn: defaultQualification = globalInputDefaults; break;
+ case EvqVaryingOut: defaultQualification = globalOutputDefaults; break;
+ default: defaultQualification.clear(); break;
+ }
+
+ // Special case for "push_constant uniform", which has a default of std430,
+ // contrary to normal uniform defaults, and can't have a default tracked for it.
+ if ((currentBlockQualifier.layoutPushConstant && !currentBlockQualifier.hasPacking())
+#ifdef NV_EXTENSIONS
+ || (currentBlockQualifier.layoutShaderRecordNV && !currentBlockQualifier.hasPacking())
+#endif
+ )
+ currentBlockQualifier.layoutPacking = ElpStd430;
+
+#ifdef NV_EXTENSIONS
+ // Special case for "taskNV in/out", which has a default of std430,
+ if (currentBlockQualifier.perTaskNV && !currentBlockQualifier.hasPacking())
+ currentBlockQualifier.layoutPacking = ElpStd430;
+#endif
+
+ // fix and check for member layout qualifiers
+
+ mergeObjectLayoutQualifiers(defaultQualification, currentBlockQualifier, true);
+
+ // "The align qualifier can only be used on blocks or block members, and only for blocks declared with std140 or std430 layouts."
+ if (currentBlockQualifier.hasAlign()) {
+ if (defaultQualification.layoutPacking != ElpStd140 &&
+ defaultQualification.layoutPacking != ElpStd430 &&
+ defaultQualification.layoutPacking != ElpScalar) {
+ error(loc, "can only be used with std140, std430, or scalar layout packing", "align", "");
+ defaultQualification.layoutAlign = -1;
+ }
+ }
+
+ bool memberWithLocation = false;
+ bool memberWithoutLocation = false;
+#ifdef NV_EXTENSIONS
+ bool memberWithPerViewQualifier = false;
+#endif
+ for (unsigned int member = 0; member < typeList.size(); ++member) {
+ TQualifier& memberQualifier = typeList[member].type->getQualifier();
+ const TSourceLoc& memberLoc = typeList[member].loc;
+ if (memberQualifier.hasStream()) {
+ if (defaultQualification.layoutStream != memberQualifier.layoutStream)
+ error(memberLoc, "member cannot contradict block", "stream", "");
+ }
+
+ // "This includes a block's inheritance of the
+ // current global default buffer, a block member's inheritance of the block's
+ // buffer, and the requirement that any *xfb_buffer* declared on a block
+ // member must match the buffer inherited from the block."
+ if (memberQualifier.hasXfbBuffer()) {
+ if (defaultQualification.layoutXfbBuffer != memberQualifier.layoutXfbBuffer)
+ error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_buffer", "");
+ }
+
+ if (memberQualifier.hasPacking())
+ error(memberLoc, "member of block cannot have a packing layout qualifier", typeList[member].type->getFieldName().c_str(), "");
+ if (memberQualifier.hasLocation()) {
+ const char* feature = "location on block member";
+ switch (currentBlockQualifier.storage) {
+ case EvqVaryingIn:
+ case EvqVaryingOut:
+ requireProfile(memberLoc, ECoreProfile | ECompatibilityProfile | EEsProfile, feature);
+ profileRequires(memberLoc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, feature);
+ profileRequires(memberLoc, EEsProfile, 320, Num_AEP_shader_io_blocks, AEP_shader_io_blocks, feature);
+ memberWithLocation = true;
+ break;
+ default:
+ error(memberLoc, "can only use in an in/out block", feature, "");
+ break;
+ }
+ } else
+ memberWithoutLocation = true;
+
+ // "The offset qualifier can only be used on block members of blocks declared with std140 or std430 layouts."
+ // "The align qualifier can only be used on blocks or block members, and only for blocks declared with std140 or std430 layouts."
+ if (memberQualifier.hasAlign() || memberQualifier.hasOffset()) {
+ if (defaultQualification.layoutPacking != ElpStd140 &&
+ defaultQualification.layoutPacking != ElpStd430 &&
+ defaultQualification.layoutPacking != ElpScalar)
+ error(memberLoc, "can only be used with std140, std430, or scalar layout packing", "offset/align", "");
+ }
+
+#ifdef NV_EXTENSIONS
+ if (memberQualifier.isPerView()) {
+ memberWithPerViewQualifier = true;
+ }
+#endif
+
+ TQualifier newMemberQualification = defaultQualification;
+ mergeQualifiers(memberLoc, newMemberQualification, memberQualifier, false);
+ memberQualifier = newMemberQualification;
+ }
+
+ layoutMemberLocationArrayCheck(loc, memberWithLocation, arraySizes);
+
+ // Ensure that the block has an XfbBuffer assigned. This is needed
+ // because if the block has a XfbOffset assigned, then it is
+ // assumed that it has implicitly assigned the current global
+ // XfbBuffer, and because it's members need to be assigned a
+ // XfbOffset if they lack it.
+ if (currentBlockQualifier.storage == EvqVaryingOut && globalOutputDefaults.hasXfbBuffer()) {
+ if (!currentBlockQualifier.hasXfbBuffer() && currentBlockQualifier.hasXfbOffset())
+ currentBlockQualifier.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer;
+ }
+
+ // Process the members
+ fixBlockLocations(loc, currentBlockQualifier, typeList, memberWithLocation, memberWithoutLocation);
+ fixXfbOffsets(currentBlockQualifier, typeList);
+ fixBlockUniformOffsets(currentBlockQualifier, typeList);
+ for (unsigned int member = 0; member < typeList.size(); ++member)
+ layoutTypeCheck(typeList[member].loc, *typeList[member].type);
+
+#ifdef NV_EXTENSIONS
+ if (memberWithPerViewQualifier) {
+ for (unsigned int member = 0; member < typeList.size(); ++member) {
+ resizeMeshViewDimension(typeList[member].loc, *typeList[member].type);
+ }
+ }
+#endif
+
+ // reverse merge, so that currentBlockQualifier now has all layout information
+ // (can't use defaultQualification directly, it's missing other non-layout-default-class qualifiers)
+ mergeObjectLayoutQualifiers(currentBlockQualifier, defaultQualification, true);
+
+ //
+ // Build and add the interface block as a new type named 'blockName'
+ //
+
+ TType blockType(&typeList, *blockName, currentBlockQualifier);
+ if (arraySizes != nullptr)
+ blockType.transferArraySizes(arraySizes);
+ else
+ ioArrayCheck(loc, blockType, instanceName ? *instanceName : *blockName);
+
+ if (currentBlockQualifier.layoutBufferReference) {
+
+ if (currentBlockQualifier.storage != EvqBuffer)
+ error(loc, "can only be used with buffer", "buffer_reference", "");
+
+ // Create the block reference type. If it was forward-declared, detect that
+ // as a referent struct type with no members. Replace the referent type with
+ // blockType.
+ TType blockNameType(EbtReference, blockType, *blockName);
+ TVariable* blockNameVar = new TVariable(blockName, blockNameType, true);
+ if (! symbolTable.insert(*blockNameVar)) {
+ TSymbol* existingName = symbolTable.find(*blockName);
+ if (existingName->getType().getBasicType() == EbtReference &&
+ existingName->getType().getReferentType()->getStruct() &&
+ existingName->getType().getReferentType()->getStruct()->size() == 0 &&
+ existingName->getType().getQualifier().storage == blockType.getQualifier().storage) {
+ existingName->getType().getReferentType()->deepCopy(blockType);
+ } else {
+ error(loc, "block name cannot be redefined", blockName->c_str(), "");
+ }
+ }
+ if (!instanceName) {
+ return;
+ }
+ } else {
+ //
+ // Don't make a user-defined type out of block name; that will cause an error
+ // if the same block name gets reused in a different interface.
+ //
+ // "Block names have no other use within a shader
+ // beyond interface matching; it is a compile-time error to use a block name at global scope for anything
+ // other than as a block name (e.g., use of a block name for a global variable name or function name is
+ // currently reserved)."
+ //
+ // Use the symbol table to prevent normal reuse of the block's name, as a variable entry,
+ // whose type is EbtBlock, but without all the structure; that will come from the type
+ // the instances point to.
+ //
+ TType blockNameType(EbtBlock, blockType.getQualifier().storage);
+ TVariable* blockNameVar = new TVariable(blockName, blockNameType);
+ if (! symbolTable.insert(*blockNameVar)) {
+ TSymbol* existingName = symbolTable.find(*blockName);
+ if (existingName->getType().getBasicType() == EbtBlock) {
+ if (existingName->getType().getQualifier().storage == blockType.getQualifier().storage) {
+ error(loc, "Cannot reuse block name within the same interface:", blockName->c_str(), blockType.getStorageQualifierString());
+ return;
+ }
+ } else {
+ error(loc, "block name cannot redefine a non-block name", blockName->c_str(), "");
+ return;
+ }
+ }
+ }
+
+ // Add the variable, as anonymous or named instanceName.
+ // Make an anonymous variable if no name was provided.
+ if (! instanceName)
+ instanceName = NewPoolTString("");
+
+ TVariable& variable = *new TVariable(instanceName, blockType);
+ if (! symbolTable.insert(variable)) {
+ if (*instanceName == "")
+ error(loc, "nameless block contains a member that already has a name at global scope", blockName->c_str(), "");
+ else
+ error(loc, "block instance name redefinition", variable.getName().c_str(), "");
+
+ return;
+ }
+
+ // Check for general layout qualifier errors
+ layoutObjectCheck(loc, variable);
+
+ // fix up
+ if (isIoResizeArray(blockType)) {
+ ioArraySymbolResizeList.push_back(&variable);
+ checkIoArraysConsistency(loc, true);
+ } else
+ fixIoArraySize(loc, variable.getWritableType());
+
+ // Save it in the AST for linker use.
+ trackLinkage(variable);
+}
+
+// Do all block-declaration checking regarding the combination of in/out/uniform/buffer
+// with a particular stage.
+void TParseContext::blockStageIoCheck(const TSourceLoc& loc, const TQualifier& qualifier)
+{
+ switch (qualifier.storage) {
+ case EvqUniform:
+ profileRequires(loc, EEsProfile, 300, nullptr, "uniform block");
+ profileRequires(loc, ENoProfile, 140, nullptr, "uniform block");
+ if (currentBlockQualifier.layoutPacking == ElpStd430 && ! currentBlockQualifier.layoutPushConstant)
+ requireExtensions(loc, 1, &E_GL_EXT_scalar_block_layout, "std430 requires the buffer storage qualifier");
+ break;
+ case EvqBuffer:
+ requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, "buffer block");
+ profileRequires(loc, ECoreProfile | ECompatibilityProfile, 430, nullptr, "buffer block");
+ profileRequires(loc, EEsProfile, 310, nullptr, "buffer block");
+ break;
+ case EvqVaryingIn:
+ profileRequires(loc, ~EEsProfile, 150, E_GL_ARB_separate_shader_objects, "input block");
+ // It is a compile-time error to have an input block in a vertex shader or an output block in a fragment shader
+ // "Compute shaders do not permit user-defined input variables..."
+ requireStage(loc, (EShLanguageMask)(EShLangTessControlMask|EShLangTessEvaluationMask|EShLangGeometryMask|EShLangFragmentMask
+#ifdef NV_EXTENSIONS
+ |EShLangMeshNVMask
+#endif
+ ), "input block");
+ if (language == EShLangFragment) {
+ profileRequires(loc, EEsProfile, 320, Num_AEP_shader_io_blocks, AEP_shader_io_blocks, "fragment input block");
+ }
+#ifdef NV_EXTENSIONS
+ else if (language == EShLangMeshNV && ! qualifier.isTaskMemory()) {
+ error(loc, "input blocks cannot be used in a mesh shader", "out", "");
+ }
+#endif
+ break;
+ case EvqVaryingOut:
+ profileRequires(loc, ~EEsProfile, 150, E_GL_ARB_separate_shader_objects, "output block");
+ requireStage(loc, (EShLanguageMask)(EShLangVertexMask|EShLangTessControlMask|EShLangTessEvaluationMask|EShLangGeometryMask
+#ifdef NV_EXTENSIONS
+ |EShLangMeshNVMask|EShLangTaskNVMask
+#endif
+ ), "output block");
+ // ES 310 can have a block before shader_io is turned on, so skip this test for built-ins
+ if (language == EShLangVertex && ! parsingBuiltins) {
+ profileRequires(loc, EEsProfile, 320, Num_AEP_shader_io_blocks, AEP_shader_io_blocks, "vertex output block");
+ }
+#ifdef NV_EXTENSIONS
+ else if (language == EShLangMeshNV && qualifier.isTaskMemory()) {
+ error(loc, "can only use on input blocks in mesh shader", "taskNV", "");
+ }
+ else if (language == EShLangTaskNV && ! qualifier.isTaskMemory()) {
+ error(loc, "output blocks cannot be used in a task shader", "out", "");
+ }
+#endif
+ break;
+#ifdef NV_EXTENSIONS
+ case EvqPayloadNV:
+ profileRequires(loc, ~EEsProfile, 460, E_GL_NV_ray_tracing, "rayPayloadNV block");
+ requireStage(loc, (EShLanguageMask)(EShLangRayGenNVMask | EShLangAnyHitNVMask | EShLangClosestHitNVMask | EShLangMissNVMask),
+ "rayPayloadNV block");
+ break;
+ case EvqPayloadInNV:
+ profileRequires(loc, ~EEsProfile, 460, E_GL_NV_ray_tracing, "rayPayloadInNV block");
+ requireStage(loc, (EShLanguageMask)(EShLangAnyHitNVMask | EShLangClosestHitNVMask | EShLangMissNVMask),
+ "rayPayloadInNV block");
+ break;
+ case EvqHitAttrNV:
+ profileRequires(loc, ~EEsProfile, 460, E_GL_NV_ray_tracing, "hitAttributeNV block");
+ requireStage(loc, (EShLanguageMask)(EShLangIntersectNVMask | EShLangAnyHitNVMask | EShLangClosestHitNVMask), "hitAttributeNV block");
+ break;
+ case EvqCallableDataNV:
+ profileRequires(loc, ~EEsProfile, 460, E_GL_NV_ray_tracing, "callableDataNV block");
+ requireStage(loc, (EShLanguageMask)(EShLangRayGenNVMask | EShLangClosestHitNVMask | EShLangMissNVMask | EShLangCallableNVMask),
+ "callableDataNV block");
+ break;
+ case EvqCallableDataInNV:
+ profileRequires(loc, ~EEsProfile, 460, E_GL_NV_ray_tracing, "callableDataInNV block");
+ requireStage(loc, (EShLanguageMask)(EShLangCallableNVMask), "callableDataInNV block");
+ break;
+#endif
+ default:
+ error(loc, "only uniform, buffer, in, or out blocks are supported", blockName->c_str(), "");
+ break;
+ }
+}
+
+// Do all block-declaration checking regarding its qualifiers.
+void TParseContext::blockQualifierCheck(const TSourceLoc& loc, const TQualifier& qualifier, bool /*instanceName*/)
+{
+ // The 4.5 specification says:
+ //
+ // interface-block :
+ // layout-qualifieropt interface-qualifier block-name { member-list } instance-nameopt ;
+ //
+ // interface-qualifier :
+ // in
+ // out
+ // patch in
+ // patch out
+ // uniform
+ // buffer
+ //
+ // Note however memory qualifiers aren't included, yet the specification also says
+ //
+ // "...memory qualifiers may also be used in the declaration of shader storage blocks..."
+
+ if (qualifier.isInterpolation())
+ error(loc, "cannot use interpolation qualifiers on an interface block", "flat/smooth/noperspective", "");
+ if (qualifier.centroid)
+ error(loc, "cannot use centroid qualifier on an interface block", "centroid", "");
+ if (qualifier.sample)
+ error(loc, "cannot use sample qualifier on an interface block", "sample", "");
+ if (qualifier.invariant)
+ error(loc, "cannot use invariant qualifier on an interface block", "invariant", "");
+ if (qualifier.layoutPushConstant)
+ intermediate.addPushConstantCount();
+#ifdef NV_EXTENSIONS
+ if (qualifier.layoutShaderRecordNV)
+ intermediate.addShaderRecordNVCount();
+ if (qualifier.perTaskNV)
+ intermediate.addTaskNVCount();
+#endif
+}
+
+//
+// "For a block, this process applies to the entire block, or until the first member
+// is reached that has a location layout qualifier. When a block member is declared with a location
+// qualifier, its location comes from that qualifier: The member's location qualifier overrides the block-level
+// declaration. Subsequent members are again assigned consecutive locations, based on the newest location,
+// until the next member declared with a location qualifier. The values used for locations do not have to be
+// declared in increasing order."
+void TParseContext::fixBlockLocations(const TSourceLoc& loc, TQualifier& qualifier, TTypeList& typeList, bool memberWithLocation, bool memberWithoutLocation)
+{
+ // "If a block has no block-level location layout qualifier, it is required that either all or none of its members
+ // have a location layout qualifier, or a compile-time error results."
+ if (! qualifier.hasLocation() && memberWithLocation && memberWithoutLocation)
+ error(loc, "either the block needs a location, or all members need a location, or no members have a location", "location", "");
+ else {
+ if (memberWithLocation) {
+ // remove any block-level location and make it per *every* member
+ int nextLocation = 0; // by the rule above, initial value is not relevant
+ if (qualifier.hasAnyLocation()) {
+ nextLocation = qualifier.layoutLocation;
+ qualifier.layoutLocation = TQualifier::layoutLocationEnd;
+ if (qualifier.hasComponent()) {
+ // "It is a compile-time error to apply the *component* qualifier to a ... block"
+ error(loc, "cannot apply to a block", "component", "");
+ }
+ if (qualifier.hasIndex()) {
+ error(loc, "cannot apply to a block", "index", "");
+ }
+ }
+ for (unsigned int member = 0; member < typeList.size(); ++member) {
+ TQualifier& memberQualifier = typeList[member].type->getQualifier();
+ const TSourceLoc& memberLoc = typeList[member].loc;
+ if (! memberQualifier.hasLocation()) {
+ if (nextLocation >= (int)TQualifier::layoutLocationEnd)
+ error(memberLoc, "location is too large", "location", "");
+ memberQualifier.layoutLocation = nextLocation;
+ memberQualifier.layoutComponent = TQualifier::layoutComponentEnd;
+ }
+ nextLocation = memberQualifier.layoutLocation + intermediate.computeTypeLocationSize(
+ *typeList[member].type, language);
+ }
+ }
+ }
+}
+
+void TParseContext::fixXfbOffsets(TQualifier& qualifier, TTypeList& typeList)
+{
+ // "If a block is qualified with xfb_offset, all its
+ // members are assigned transform feedback buffer offsets. If a block is not qualified with xfb_offset, any
+ // members of that block not qualified with an xfb_offset will not be assigned transform feedback buffer
+ // offsets."
+
+ if (! qualifier.hasXfbBuffer() || ! qualifier.hasXfbOffset())
+ return;
+
+ int nextOffset = qualifier.layoutXfbOffset;
+ for (unsigned int member = 0; member < typeList.size(); ++member) {
+ TQualifier& memberQualifier = typeList[member].type->getQualifier();
+ bool contains64BitType = false;
+#ifdef AMD_EXTENSIONS
+ bool contains32BitType = false;
+ bool contains16BitType = false;
+ int memberSize = intermediate.computeTypeXfbSize(*typeList[member].type, contains64BitType, contains32BitType, contains16BitType);
+#else
+ int memberSize = intermediate.computeTypeXfbSize(*typeList[member].type, contains64BitType);
+#endif
+ // see if we need to auto-assign an offset to this member
+ if (! memberQualifier.hasXfbOffset()) {
+ // "if applied to an aggregate containing a double or 64-bit integer, the offset must also be a multiple of 8"
+ if (contains64BitType)
+ RoundToPow2(nextOffset, 8);
+#ifdef AMD_EXTENSIONS
+ else if (contains32BitType)
+ RoundToPow2(nextOffset, 4);
+ else if (contains16BitType)
+ RoundToPow2(nextOffset, 2);
+#endif
+ memberQualifier.layoutXfbOffset = nextOffset;
+ } else
+ nextOffset = memberQualifier.layoutXfbOffset;
+ nextOffset += memberSize;
+ }
+
+ // The above gave all block members an offset, so we can take it off the block now,
+ // which will avoid double counting the offset usage.
+ qualifier.layoutXfbOffset = TQualifier::layoutXfbOffsetEnd;
+}
+
+// Calculate and save the offset of each block member, using the recursively
+// defined block offset rules and the user-provided offset and align.
+//
+// Also, compute and save the total size of the block. For the block's size, arrayness
+// is not taken into account, as each element is backed by a separate buffer.
+//
+void TParseContext::fixBlockUniformOffsets(TQualifier& qualifier, TTypeList& typeList)
+{
+ if (!qualifier.isUniformOrBuffer() && !qualifier.isTaskMemory())
+ return;
+ if (qualifier.layoutPacking != ElpStd140 && qualifier.layoutPacking != ElpStd430 && qualifier.layoutPacking != ElpScalar)
+ return;
+
+ int offset = 0;
+ int memberSize;
+ for (unsigned int member = 0; member < typeList.size(); ++member) {
+ TQualifier& memberQualifier = typeList[member].type->getQualifier();
+ const TSourceLoc& memberLoc = typeList[member].loc;
+
+ // "When align is applied to an array, it effects only the start of the array, not the array's internal stride."
+
+ // modify just the children's view of matrix layout, if there is one for this member
+ TLayoutMatrix subMatrixLayout = typeList[member].type->getQualifier().layoutMatrix;
+ int dummyStride;
+ int memberAlignment = intermediate.getMemberAlignment(*typeList[member].type, memberSize, dummyStride, qualifier.layoutPacking,
+ subMatrixLayout != ElmNone ? subMatrixLayout == ElmRowMajor : qualifier.layoutMatrix == ElmRowMajor);
+ if (memberQualifier.hasOffset()) {
+ // "The specified offset must be a multiple
+ // of the base alignment of the type of the block member it qualifies, or a compile-time error results."
+ if (! IsMultipleOfPow2(memberQualifier.layoutOffset, memberAlignment))
+ error(memberLoc, "must be a multiple of the member's alignment", "offset", "");
+
+ // GLSL: "It is a compile-time error to specify an offset that is smaller than the offset of the previous
+ // member in the block or that lies within the previous member of the block"
+ if (spvVersion.spv == 0) {
+ if (memberQualifier.layoutOffset < offset)
+ error(memberLoc, "cannot lie in previous members", "offset", "");
+
+ // "The offset qualifier forces the qualified member to start at or after the specified
+ // integral-constant expression, which will be its byte offset from the beginning of the buffer.
+ // "The actual offset of a member is computed as
+ // follows: If offset was declared, start with that offset, otherwise start with the next available offset."
+ offset = std::max(offset, memberQualifier.layoutOffset);
+ } else {
+ // TODO: Vulkan: "It is a compile-time error to have any offset, explicit or assigned,
+ // that lies within another member of the block."
+
+ offset = memberQualifier.layoutOffset;
+ }
+ }
+
+ // "The actual alignment of a member will be the greater of the specified align alignment and the standard
+ // (e.g., std140) base alignment for the member's type."
+ if (memberQualifier.hasAlign())
+ memberAlignment = std::max(memberAlignment, memberQualifier.layoutAlign);
+
+ // "If the resulting offset is not a multiple of the actual alignment,
+ // increase it to the first offset that is a multiple of
+ // the actual alignment."
+ RoundToPow2(offset, memberAlignment);
+ typeList[member].type->getQualifier().layoutOffset = offset;
+ offset += memberSize;
+ }
+}
+
+// For an identifier that is already declared, add more qualification to it.
+void TParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, const TString& identifier)
+{
+ TSymbol* symbol = symbolTable.find(identifier);
+
+ // A forward declaration of a block reference looks to the grammar like adding
+ // a qualifier to an existing symbol. Detect this and create the block reference
+ // type with an empty type list, which will be filled in later in
+ // TParseContext::declareBlock.
+ if (!symbol && qualifier.layoutBufferReference) {
+ TTypeList typeList;
+ TType blockType(&typeList, identifier, qualifier);;
+ TType blockNameType(EbtReference, blockType, identifier);
+ TVariable* blockNameVar = new TVariable(&identifier, blockNameType, true);
+ if (! symbolTable.insert(*blockNameVar)) {
+ error(loc, "block name cannot redefine a non-block name", blockName->c_str(), "");
+ }
+ return;
+ }
+
+ if (! symbol) {
+ error(loc, "identifier not previously declared", identifier.c_str(), "");
+ return;
+ }
+ if (symbol->getAsFunction()) {
+ error(loc, "cannot re-qualify a function name", identifier.c_str(), "");
+ return;
+ }
+
+ if (qualifier.isAuxiliary() ||
+ qualifier.isMemory() ||
+ qualifier.isInterpolation() ||
+ qualifier.hasLayout() ||
+ qualifier.storage != EvqTemporary ||
+ qualifier.precision != EpqNone) {
+ error(loc, "cannot add storage, auxiliary, memory, interpolation, layout, or precision qualifier to an existing variable", identifier.c_str(), "");
+ return;
+ }
+
+ // For read-only built-ins, add a new symbol for holding the modified qualifier.
+ // This will bring up an entire block, if a block type has to be modified (e.g., gl_Position inside a block)
+ if (symbol->isReadOnly())
+ symbol = symbolTable.copyUp(symbol);
+
+ if (qualifier.invariant) {
+ if (intermediate.inIoAccessed(identifier))
+ error(loc, "cannot change qualification after use", "invariant", "");
+ symbol->getWritableType().getQualifier().invariant = true;
+ invariantCheck(loc, symbol->getType().getQualifier());
+ } else if (qualifier.noContraction) {
+ if (intermediate.inIoAccessed(identifier))
+ error(loc, "cannot change qualification after use", "precise", "");
+ symbol->getWritableType().getQualifier().noContraction = true;
+ } else if (qualifier.specConstant) {
+ symbol->getWritableType().getQualifier().makeSpecConstant();
+ if (qualifier.hasSpecConstantId())
+ symbol->getWritableType().getQualifier().layoutSpecConstantId = qualifier.layoutSpecConstantId;
+ } else
+ warn(loc, "unknown requalification", "", "");
+}
+
+void TParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, TIdentifierList& identifiers)
+{
+ for (unsigned int i = 0; i < identifiers.size(); ++i)
+ addQualifierToExisting(loc, qualifier, *identifiers[i]);
+}
+
+// Make sure 'invariant' isn't being applied to a non-allowed object.
+void TParseContext::invariantCheck(const TSourceLoc& loc, const TQualifier& qualifier)
+{
+ if (! qualifier.invariant)
+ return;
+
+ bool pipeOut = qualifier.isPipeOutput();
+ bool pipeIn = qualifier.isPipeInput();
+ if (version >= 300 || (profile != EEsProfile && version >= 420)) {
+ if (! pipeOut)
+ error(loc, "can only apply to an output", "invariant", "");
+ } else {
+ if ((language == EShLangVertex && pipeIn) || (! pipeOut && ! pipeIn))
+ error(loc, "can only apply to an output, or to an input in a non-vertex stage\n", "invariant", "");
+ }
+}
+
+//
+// Updating default qualifier for the case of a declaration with just a qualifier,
+// no type, block, or identifier.
+//
+void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, const TPublicType& publicType)
+{
+ if (publicType.shaderQualifiers.vertices != TQualifier::layoutNotSet) {
+#ifdef NV_EXTENSIONS
+ assert(language == EShLangTessControl || language == EShLangGeometry || language == EShLangMeshNV);
+#else
+ assert(language == EShLangTessControl || language == EShLangGeometry);
+#endif
+ const char* id = (language == EShLangTessControl) ? "vertices" : "max_vertices";
+
+ if (publicType.qualifier.storage != EvqVaryingOut)
+ error(loc, "can only apply to 'out'", id, "");
+ if (! intermediate.setVertices(publicType.shaderQualifiers.vertices))
+ error(loc, "cannot change previously set layout value", id, "");
+
+ if (language == EShLangTessControl)
+ checkIoArraysConsistency(loc);
+ }
+#ifdef NV_EXTENSIONS
+ if (publicType.shaderQualifiers.primitives != TQualifier::layoutNotSet) {
+ assert(language == EShLangMeshNV);
+ const char* id = "max_primitives";
+
+ if (publicType.qualifier.storage != EvqVaryingOut)
+ error(loc, "can only apply to 'out'", id, "");
+ if (! intermediate.setPrimitives(publicType.shaderQualifiers.primitives))
+ error(loc, "cannot change previously set layout value", id, "");
+ }
+#endif
+ if (publicType.shaderQualifiers.invocations != TQualifier::layoutNotSet) {
+ if (publicType.qualifier.storage != EvqVaryingIn)
+ error(loc, "can only apply to 'in'", "invocations", "");
+ if (! intermediate.setInvocations(publicType.shaderQualifiers.invocations))
+ error(loc, "cannot change previously set layout value", "invocations", "");
+ }
+ if (publicType.shaderQualifiers.geometry != ElgNone) {
+ if (publicType.qualifier.storage == EvqVaryingIn) {
+ switch (publicType.shaderQualifiers.geometry) {
+ case ElgPoints:
+ case ElgLines:
+ case ElgLinesAdjacency:
+ case ElgTriangles:
+ case ElgTrianglesAdjacency:
+ case ElgQuads:
+ case ElgIsolines:
+#ifdef NV_EXTENSIONS
+ if (language == EShLangMeshNV) {
+ error(loc, "cannot apply to input", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), "");
+ break;
+ }
+#endif
+ if (intermediate.setInputPrimitive(publicType.shaderQualifiers.geometry)) {
+ if (language == EShLangGeometry)
+ checkIoArraysConsistency(loc);
+ } else
+ error(loc, "cannot change previously set input primitive", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), "");
+ break;
+ default:
+ error(loc, "cannot apply to input", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), "");
+ }
+ } else if (publicType.qualifier.storage == EvqVaryingOut) {
+ switch (publicType.shaderQualifiers.geometry) {
+#ifdef NV_EXTENSIONS
+ case ElgLines:
+ case ElgTriangles:
+ if (language != EShLangMeshNV) {
+ error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), "");
+ break;
+ }
+#endif
+ // Fall through
+ case ElgPoints:
+ case ElgLineStrip:
+ case ElgTriangleStrip:
+ if (! intermediate.setOutputPrimitive(publicType.shaderQualifiers.geometry))
+ error(loc, "cannot change previously set output primitive", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), "");
+ break;
+ default:
+ error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), "");
+ }
+ } else
+ error(loc, "cannot apply to:", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), GetStorageQualifierString(publicType.qualifier.storage));
+ }
+ if (publicType.shaderQualifiers.spacing != EvsNone) {
+ if (publicType.qualifier.storage == EvqVaryingIn) {
+ if (! intermediate.setVertexSpacing(publicType.shaderQualifiers.spacing))
+ error(loc, "cannot change previously set vertex spacing", TQualifier::getVertexSpacingString(publicType.shaderQualifiers.spacing), "");
+ } else
+ error(loc, "can only apply to 'in'", TQualifier::getVertexSpacingString(publicType.shaderQualifiers.spacing), "");
+ }
+ if (publicType.shaderQualifiers.order != EvoNone) {
+ if (publicType.qualifier.storage == EvqVaryingIn) {
+ if (! intermediate.setVertexOrder(publicType.shaderQualifiers.order))
+ error(loc, "cannot change previously set vertex order", TQualifier::getVertexOrderString(publicType.shaderQualifiers.order), "");
+ } else
+ error(loc, "can only apply to 'in'", TQualifier::getVertexOrderString(publicType.shaderQualifiers.order), "");
+ }
+ if (publicType.shaderQualifiers.pointMode) {
+ if (publicType.qualifier.storage == EvqVaryingIn)
+ intermediate.setPointMode();
+ else
+ error(loc, "can only apply to 'in'", "point_mode", "");
+ }
+ for (int i = 0; i < 3; ++i) {
+ if (publicType.shaderQualifiers.localSize[i] > 1) {
+ if (publicType.qualifier.storage == EvqVaryingIn) {
+ if (! intermediate.setLocalSize(i, publicType.shaderQualifiers.localSize[i]))
+ error(loc, "cannot change previously set size", "local_size", "");
+ else {
+ int max = 0;
+ if (language == EShLangCompute) {
+ switch (i) {
+ case 0: max = resources.maxComputeWorkGroupSizeX; break;
+ case 1: max = resources.maxComputeWorkGroupSizeY; break;
+ case 2: max = resources.maxComputeWorkGroupSizeZ; break;
+ default: break;
+ }
+ if (intermediate.getLocalSize(i) > (unsigned int)max)
+ error(loc, "too large; see gl_MaxComputeWorkGroupSize", "local_size", "");
+ }
+#ifdef NV_EXTENSIONS
+ else if (language == EShLangMeshNV) {
+ switch (i) {
+ case 0: max = resources.maxMeshWorkGroupSizeX_NV; break;
+ case 1: max = resources.maxMeshWorkGroupSizeY_NV; break;
+ case 2: max = resources.maxMeshWorkGroupSizeZ_NV; break;
+ default: break;
+ }
+ if (intermediate.getLocalSize(i) > (unsigned int)max)
+ error(loc, "too large; see gl_MaxMeshWorkGroupSizeNV", "local_size", "");
+ }
+ else if (language == EShLangTaskNV) {
+ switch (i) {
+ case 0: max = resources.maxTaskWorkGroupSizeX_NV; break;
+ case 1: max = resources.maxTaskWorkGroupSizeY_NV; break;
+ case 2: max = resources.maxTaskWorkGroupSizeZ_NV; break;
+ default: break;
+ }
+ if (intermediate.getLocalSize(i) > (unsigned int)max)
+ error(loc, "too large; see gl_MaxTaskWorkGroupSizeNV", "local_size", "");
+ }
+#endif
+ else {
+ assert(0);
+ }
+
+ // Fix the existing constant gl_WorkGroupSize with this new information.
+ TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize");
+ if (workGroupSize != nullptr)
+ workGroupSize->getWritableConstArray()[i].setUConst(intermediate.getLocalSize(i));
+ }
+ } else
+ error(loc, "can only apply to 'in'", "local_size", "");
+ }
+ if (publicType.shaderQualifiers.localSizeSpecId[i] != TQualifier::layoutNotSet) {
+ if (publicType.qualifier.storage == EvqVaryingIn) {
+ if (! intermediate.setLocalSizeSpecId(i, publicType.shaderQualifiers.localSizeSpecId[i]))
+ error(loc, "cannot change previously set size", "local_size", "");
+ } else
+ error(loc, "can only apply to 'in'", "local_size id", "");
+ // Set the workgroup built-in variable as a specialization constant
+ TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize");
+ if (workGroupSize != nullptr)
+ workGroupSize->getWritableType().getQualifier().specConstant = true;
+ }
+ }
+ if (publicType.shaderQualifiers.earlyFragmentTests) {
+ if (publicType.qualifier.storage == EvqVaryingIn)
+ intermediate.setEarlyFragmentTests();
+ else
+ error(loc, "can only apply to 'in'", "early_fragment_tests", "");
+ }
+ if (publicType.shaderQualifiers.postDepthCoverage) {
+ if (publicType.qualifier.storage == EvqVaryingIn)
+ intermediate.setPostDepthCoverage();
+ else
+ error(loc, "can only apply to 'in'", "post_coverage_coverage", "");
+ }
+ if (publicType.shaderQualifiers.blendEquation) {
+ if (publicType.qualifier.storage != EvqVaryingOut)
+ error(loc, "can only apply to 'out'", "blend equation", "");
+ }
+
+#ifdef NV_EXTENSIONS
+ if (publicType.shaderQualifiers.layoutDerivativeGroupQuads &&
+ publicType.shaderQualifiers.layoutDerivativeGroupLinear) {
+ error(loc, "cannot be both specified", "derivative_group_quadsNV and derivative_group_linearNV", "");
+ }
+
+ if (publicType.shaderQualifiers.layoutDerivativeGroupQuads) {
+ if (publicType.qualifier.storage == EvqVaryingIn) {
+ if ((intermediate.getLocalSize(0) & 1) ||
+ (intermediate.getLocalSize(1) & 1))
+ error(loc, "requires local_size_x and local_size_y to be multiple of two", "derivative_group_quadsNV", "");
+ else
+ intermediate.setLayoutDerivativeMode(LayoutDerivativeGroupQuads);
+ }
+ else
+ error(loc, "can only apply to 'in'", "derivative_group_quadsNV", "");
+ }
+ if (publicType.shaderQualifiers.layoutDerivativeGroupLinear) {
+ if (publicType.qualifier.storage == EvqVaryingIn) {
+ if((intermediate.getLocalSize(0) *
+ intermediate.getLocalSize(1) *
+ intermediate.getLocalSize(2)) % 4 != 0)
+ error(loc, "requires total group size to be multiple of four", "derivative_group_linearNV", "");
+ else
+ intermediate.setLayoutDerivativeMode(LayoutDerivativeGroupLinear);
+ }
+ else
+ error(loc, "can only apply to 'in'", "derivative_group_linearNV", "");
+ }
+ // Check mesh out array sizes, once all the necessary out qualifiers are defined.
+ if ((language == EShLangMeshNV) &&
+ (intermediate.getVertices() != TQualifier::layoutNotSet) &&
+ (intermediate.getPrimitives() != TQualifier::layoutNotSet) &&
+ (intermediate.getOutputPrimitive() != ElgNone))
+ {
+ checkIoArraysConsistency(loc);
+ }
+#endif
+ const TQualifier& qualifier = publicType.qualifier;
+
+ if (qualifier.isAuxiliary() ||
+ qualifier.isMemory() ||
+ qualifier.isInterpolation() ||
+ qualifier.precision != EpqNone)
+ error(loc, "cannot use auxiliary, memory, interpolation, or precision qualifier in a default qualifier declaration (declaration with no type)", "qualifier", "");
+ // "The offset qualifier can only be used on block members of blocks..."
+ // "The align qualifier can only be used on blocks or block members..."
+ if (qualifier.hasOffset() ||
+ qualifier.hasAlign())
+ error(loc, "cannot use offset or align qualifiers in a default qualifier declaration (declaration with no type)", "layout qualifier", "");
+
+ layoutQualifierCheck(loc, qualifier);
+
+ switch (qualifier.storage) {
+ case EvqUniform:
+ if (qualifier.hasMatrix())
+ globalUniformDefaults.layoutMatrix = qualifier.layoutMatrix;
+ if (qualifier.hasPacking())
+ globalUniformDefaults.layoutPacking = qualifier.layoutPacking;
+ break;
+ case EvqBuffer:
+ if (qualifier.hasMatrix())
+ globalBufferDefaults.layoutMatrix = qualifier.layoutMatrix;
+ if (qualifier.hasPacking())
+ globalBufferDefaults.layoutPacking = qualifier.layoutPacking;
+ break;
+ case EvqVaryingIn:
+ break;
+ case EvqVaryingOut:
+ if (qualifier.hasStream())
+ globalOutputDefaults.layoutStream = qualifier.layoutStream;
+ if (qualifier.hasXfbBuffer())
+ globalOutputDefaults.layoutXfbBuffer = qualifier.layoutXfbBuffer;
+ if (globalOutputDefaults.hasXfbBuffer() && qualifier.hasXfbStride()) {
+ if (! intermediate.setXfbBufferStride(globalOutputDefaults.layoutXfbBuffer, qualifier.layoutXfbStride))
+ error(loc, "all stride settings must match for xfb buffer", "xfb_stride", "%d", qualifier.layoutXfbBuffer);
+ }
+ break;
+ default:
+ error(loc, "default qualifier requires 'uniform', 'buffer', 'in', or 'out' storage qualification", "", "");
+ return;
+ }
+
+ if (qualifier.hasBinding())
+ error(loc, "cannot declare a default, include a type or full declaration", "binding", "");
+ if (qualifier.hasAnyLocation())
+ error(loc, "cannot declare a default, use a full declaration", "location/component/index", "");
+ if (qualifier.hasXfbOffset())
+ error(loc, "cannot declare a default, use a full declaration", "xfb_offset", "");
+ if (qualifier.layoutPushConstant)
+ error(loc, "cannot declare a default, can only be used on a block", "push_constant", "");
+ if (qualifier.layoutBufferReference)
+ error(loc, "cannot declare a default, can only be used on a block", "buffer_reference", "");
+ if (qualifier.hasSpecConstantId())
+ error(loc, "cannot declare a default, can only be used on a scalar", "constant_id", "");
+#ifdef NV_EXTENSIONS
+ if (qualifier.layoutShaderRecordNV)
+ error(loc, "cannot declare a default, can only be used on a block", "shaderRecordNV", "");
+#endif
+}
+
+//
+// Take the sequence of statements that has been built up since the last case/default,
+// put it on the list of top-level nodes for the current (inner-most) switch statement,
+// and follow that by the case/default we are on now. (See switch topology comment on
+// TIntermSwitch.)
+//
+void TParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode)
+{
+ TIntermSequence* switchSequence = switchSequenceStack.back();
+
+ if (statements) {
+ if (switchSequence->size() == 0)
+ error(statements->getLoc(), "cannot have statements before first case/default label", "switch", "");
+ statements->setOperator(EOpSequence);
+ switchSequence->push_back(statements);
+ }
+ if (branchNode) {
+ // check all previous cases for the same label (or both are 'default')
+ for (unsigned int s = 0; s < switchSequence->size(); ++s) {
+ TIntermBranch* prevBranch = (*switchSequence)[s]->getAsBranchNode();
+ if (prevBranch) {
+ TIntermTyped* prevExpression = prevBranch->getExpression();
+ TIntermTyped* newExpression = branchNode->getAsBranchNode()->getExpression();
+ if (prevExpression == nullptr && newExpression == nullptr)
+ error(branchNode->getLoc(), "duplicate label", "default", "");
+ else if (prevExpression != nullptr &&
+ newExpression != nullptr &&
+ prevExpression->getAsConstantUnion() &&
+ newExpression->getAsConstantUnion() &&
+ prevExpression->getAsConstantUnion()->getConstArray()[0].getIConst() ==
+ newExpression->getAsConstantUnion()->getConstArray()[0].getIConst())
+ error(branchNode->getLoc(), "duplicated value", "case", "");
+ }
+ }
+ switchSequence->push_back(branchNode);
+ }
+}
+
+//
+// Turn the top-level node sequence built up of wrapupSwitchSubsequence9)
+// into a switch node.
+//
+TIntermNode* TParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* expression, TIntermAggregate* lastStatements)
+{
+ profileRequires(loc, EEsProfile, 300, nullptr, "switch statements");
+ profileRequires(loc, ENoProfile, 130, nullptr, "switch statements");
+
+ wrapupSwitchSubsequence(lastStatements, nullptr);
+
+ if (expression == nullptr ||
+ (expression->getBasicType() != EbtInt && expression->getBasicType() != EbtUint) ||
+ expression->getType().isArray() || expression->getType().isMatrix() || expression->getType().isVector())
+ error(loc, "condition must be a scalar integer expression", "switch", "");
+
+ // If there is nothing to do, drop the switch but still execute the expression
+ TIntermSequence* switchSequence = switchSequenceStack.back();
+ if (switchSequence->size() == 0)
+ return expression;
+
+ if (lastStatements == nullptr) {
+ // This was originally an ERRROR, because early versions of the specification said
+ // "it is an error to have no statement between a label and the end of the switch statement."
+ // The specifications were updated to remove this (being ill-defined what a "statement" was),
+ // so, this became a warning. However, 3.0 tests still check for the error.
+ if (profile == EEsProfile && version <= 300 && ! relaxedErrors())
+ error(loc, "last case/default label not followed by statements", "switch", "");
+ else
+ warn(loc, "last case/default label not followed by statements", "switch", "");
+
+ // emulate a break for error recovery
+ lastStatements = intermediate.makeAggregate(intermediate.addBranch(EOpBreak, loc));
+ lastStatements->setOperator(EOpSequence);
+ switchSequence->push_back(lastStatements);
+ }
+
+ TIntermAggregate* body = new TIntermAggregate(EOpSequence);
+ body->getSequence() = *switchSequenceStack.back();
+ body->setLoc(loc);
+
+ TIntermSwitch* switchNode = new TIntermSwitch(expression, body);
+ switchNode->setLoc(loc);
+
+ return switchNode;
+}
+
+} // end namespace glslang
+
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/ParseHelper.h b/src/3rdparty/glslang/glslang/MachineIndependent/ParseHelper.h
new file mode 100644
index 0000000..a1ffe64
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/ParseHelper.h
@@ -0,0 +1,510 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2012-2013 LunarG, Inc.
+// Copyright (C) 2015-2018 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+//
+// This header defines a two-level parse-helper hierarchy, derived from
+// TParseVersions:
+// - TParseContextBase: sharable across multiple parsers
+// - TParseContext: GLSL specific helper
+//
+
+#ifndef _PARSER_HELPER_INCLUDED_
+#define _PARSER_HELPER_INCLUDED_
+
+#include <cstdarg>
+#include <functional>
+
+#include "parseVersions.h"
+#include "../Include/ShHandle.h"
+#include "SymbolTable.h"
+#include "localintermediate.h"
+#include "Scan.h"
+#include "attribute.h"
+
+namespace glslang {
+
+struct TPragma {
+ TPragma(bool o, bool d) : optimize(o), debug(d) { }
+ bool optimize;
+ bool debug;
+ TPragmaTable pragmaTable;
+};
+
+class TScanContext;
+class TPpContext;
+
+typedef std::set<int> TIdSetType;
+
+//
+// Sharable code (as well as what's in TParseVersions) across
+// parse helpers.
+//
+class TParseContextBase : public TParseVersions {
+public:
+ TParseContextBase(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins, int version,
+ EProfile profile, const SpvVersion& spvVersion, EShLanguage language,
+ TInfoSink& infoSink, bool forwardCompatible, EShMessages messages,
+ const TString* entryPoint = nullptr)
+ : TParseVersions(interm, version, profile, spvVersion, language, infoSink, forwardCompatible, messages),
+ scopeMangler("::"),
+ symbolTable(symbolTable),
+ statementNestingLevel(0), loopNestingLevel(0), structNestingLevel(0), controlFlowNestingLevel(0),
+ postEntryPointReturn(false),
+ contextPragma(true, false),
+ parsingBuiltins(parsingBuiltins), scanContext(nullptr), ppContext(nullptr),
+ limits(resources.limits),
+ globalUniformBlock(nullptr),
+ globalUniformBinding(TQualifier::layoutBindingEnd),
+ globalUniformSet(TQualifier::layoutSetEnd)
+ {
+ if (entryPoint != nullptr)
+ sourceEntryPointName = *entryPoint;
+ }
+ virtual ~TParseContextBase() { }
+
+ virtual void C_DECL error(const TSourceLoc&, const char* szReason, const char* szToken,
+ const char* szExtraInfoFormat, ...);
+ virtual void C_DECL warn(const TSourceLoc&, const char* szReason, const char* szToken,
+ const char* szExtraInfoFormat, ...);
+ virtual void C_DECL ppError(const TSourceLoc&, const char* szReason, const char* szToken,
+ const char* szExtraInfoFormat, ...);
+ virtual void C_DECL ppWarn(const TSourceLoc&, const char* szReason, const char* szToken,
+ const char* szExtraInfoFormat, ...);
+
+ virtual void setLimits(const TBuiltInResource&) = 0;
+
+ void checkIndex(const TSourceLoc&, const TType&, int& index);
+
+ EShLanguage getLanguage() const { return language; }
+ void setScanContext(TScanContext* c) { scanContext = c; }
+ TScanContext* getScanContext() const { return scanContext; }
+ void setPpContext(TPpContext* c) { ppContext = c; }
+ TPpContext* getPpContext() const { return ppContext; }
+
+ virtual void setLineCallback(const std::function<void(int, int, bool, int, const char*)>& func) { lineCallback = func; }
+ virtual void setExtensionCallback(const std::function<void(int, const char*, const char*)>& func) { extensionCallback = func; }
+ virtual void setVersionCallback(const std::function<void(int, int, const char*)>& func) { versionCallback = func; }
+ virtual void setPragmaCallback(const std::function<void(int, const TVector<TString>&)>& func) { pragmaCallback = func; }
+ virtual void setErrorCallback(const std::function<void(int, const char*)>& func) { errorCallback = func; }
+
+ virtual void reservedPpErrorCheck(const TSourceLoc&, const char* name, const char* op) = 0;
+ virtual bool lineContinuationCheck(const TSourceLoc&, bool endOfComment) = 0;
+ virtual bool lineDirectiveShouldSetNextLine() const = 0;
+ virtual void handlePragma(const TSourceLoc&, const TVector<TString>&) = 0;
+
+ virtual bool parseShaderStrings(TPpContext&, TInputScanner& input, bool versionWillBeError = false) = 0;
+
+ virtual void notifyVersion(int line, int version, const char* type_string)
+ {
+ if (versionCallback)
+ versionCallback(line, version, type_string);
+ }
+ virtual void notifyErrorDirective(int line, const char* error_message)
+ {
+ if (errorCallback)
+ errorCallback(line, error_message);
+ }
+ virtual void notifyLineDirective(int curLineNo, int newLineNo, bool hasSource, int sourceNum, const char* sourceName)
+ {
+ if (lineCallback)
+ lineCallback(curLineNo, newLineNo, hasSource, sourceNum, sourceName);
+ }
+ virtual void notifyExtensionDirective(int line, const char* extension, const char* behavior)
+ {
+ if (extensionCallback)
+ extensionCallback(line, extension, behavior);
+ }
+
+ // Manage the global uniform block (default uniforms in GLSL, $Global in HLSL)
+ virtual void growGlobalUniformBlock(const TSourceLoc&, TType&, const TString& memberName, TTypeList* typeList = nullptr);
+
+ // Potentially rename shader entry point function
+ void renameShaderFunction(TString*& name) const
+ {
+ // Replace the entry point name given in the shader with the real entry point name,
+ // if there is a substitution.
+ if (name != nullptr && *name == sourceEntryPointName && intermediate.getEntryPointName().size() > 0)
+ name = NewPoolTString(intermediate.getEntryPointName().c_str());
+ }
+
+ virtual bool lValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*);
+ virtual void rValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*);
+
+ const char* const scopeMangler;
+
+ // Basic parsing state, easily accessible to the grammar
+
+ TSymbolTable& symbolTable; // symbol table that goes with the current language, version, and profile
+ int statementNestingLevel; // 0 if outside all flow control or compound statements
+ int loopNestingLevel; // 0 if outside all loops
+ int structNestingLevel; // 0 if outside blocks and structures
+ int controlFlowNestingLevel; // 0 if outside all flow control
+ const TType* currentFunctionType; // the return type of the function that's currently being parsed
+ bool functionReturnsValue; // true if a non-void function has a return
+ // if inside a function, true if the function is the entry point and this is after a return statement
+ bool postEntryPointReturn;
+ // case, node, case, case, node, ...; ensure only one node between cases; stack of them for nesting
+ TList<TIntermSequence*> switchSequenceStack;
+ // the statementNestingLevel the current switch statement is at, which must match the level of its case statements
+ TList<int> switchLevel;
+ struct TPragma contextPragma;
+
+protected:
+ TParseContextBase(TParseContextBase&);
+ TParseContextBase& operator=(TParseContextBase&);
+
+ const bool parsingBuiltins; // true if parsing built-in symbols/functions
+ TVector<TSymbol*> linkageSymbols; // will be transferred to 'linkage', after all editing is done, order preserving
+ TScanContext* scanContext;
+ TPpContext* ppContext;
+ TBuiltInResource resources;
+ TLimits& limits;
+ TString sourceEntryPointName;
+
+ // These, if set, will be called when a line, pragma ... is preprocessed.
+ // They will be called with any parameters to the original directive.
+ std::function<void(int, int, bool, int, const char*)> lineCallback;
+ std::function<void(int, const TVector<TString>&)> pragmaCallback;
+ std::function<void(int, int, const char*)> versionCallback;
+ std::function<void(int, const char*, const char*)> extensionCallback;
+ std::function<void(int, const char*)> errorCallback;
+
+ // see implementation for detail
+ const TFunction* selectFunction(const TVector<const TFunction*>, const TFunction&,
+ std::function<bool(const TType&, const TType&, TOperator, int arg)>,
+ std::function<bool(const TType&, const TType&, const TType&)>,
+ /* output */ bool& tie);
+
+ virtual void parseSwizzleSelector(const TSourceLoc&, const TString&, int size,
+ TSwizzleSelectors<TVectorSelector>&);
+
+ // Manage the global uniform block (default uniforms in GLSL, $Global in HLSL)
+ TVariable* globalUniformBlock; // the actual block, inserted into the symbol table
+ unsigned int globalUniformBinding; // the block's binding number
+ unsigned int globalUniformSet; // the block's set number
+ int firstNewMember; // the index of the first member not yet inserted into the symbol table
+ // override this to set the language-specific name
+ virtual const char* getGlobalUniformBlockName() const { return ""; }
+ virtual void setUniformBlockDefaults(TType&) const { }
+ virtual void finalizeGlobalUniformBlockLayout(TVariable&) { }
+ virtual void outputMessage(const TSourceLoc&, const char* szReason, const char* szToken,
+ const char* szExtraInfoFormat, TPrefixType prefix,
+ va_list args);
+ virtual void trackLinkage(TSymbol& symbol);
+ virtual void makeEditable(TSymbol*&);
+ virtual TVariable* getEditableVariable(const char* name);
+ virtual void finish();
+};
+
+//
+// Manage the state for when to respect precision qualifiers and when to warn about
+// the defaults being different than might be expected.
+//
+class TPrecisionManager {
+public:
+ TPrecisionManager() : obey(false), warn(false), explicitIntDefault(false), explicitFloatDefault(false){ }
+ virtual ~TPrecisionManager() {}
+
+ void respectPrecisionQualifiers() { obey = true; }
+ bool respectingPrecisionQualifiers() const { return obey; }
+ bool shouldWarnAboutDefaults() const { return warn; }
+ void defaultWarningGiven() { warn = false; }
+ void warnAboutDefaults() { warn = true; }
+ void explicitIntDefaultSeen()
+ {
+ explicitIntDefault = true;
+ if (explicitFloatDefault)
+ warn = false;
+ }
+ void explicitFloatDefaultSeen()
+ {
+ explicitFloatDefault = true;
+ if (explicitIntDefault)
+ warn = false;
+ }
+
+protected:
+ bool obey; // respect precision qualifiers
+ bool warn; // need to give a warning about the defaults
+ bool explicitIntDefault; // user set the default for int/uint
+ bool explicitFloatDefault; // user set the default for float
+};
+
+//
+// GLSL-specific parse helper. Should have GLSL in the name, but that's
+// too big of a change for comparing branches at the moment, and perhaps
+// impacts downstream consumers as well.
+//
+class TParseContext : public TParseContextBase {
+public:
+ TParseContext(TSymbolTable&, TIntermediate&, bool parsingBuiltins, int version, EProfile, const SpvVersion& spvVersion, EShLanguage, TInfoSink&,
+ bool forwardCompatible = false, EShMessages messages = EShMsgDefault,
+ const TString* entryPoint = nullptr);
+ virtual ~TParseContext();
+
+ bool obeyPrecisionQualifiers() const { return precisionManager.respectingPrecisionQualifiers(); };
+ void setPrecisionDefaults();
+
+ void setLimits(const TBuiltInResource&) override;
+ bool parseShaderStrings(TPpContext&, TInputScanner& input, bool versionWillBeError = false) override;
+ void parserError(const char* s); // for bison's yyerror
+
+ void reservedErrorCheck(const TSourceLoc&, const TString&);
+ void reservedPpErrorCheck(const TSourceLoc&, const char* name, const char* op) override;
+ bool lineContinuationCheck(const TSourceLoc&, bool endOfComment) override;
+ bool lineDirectiveShouldSetNextLine() const override;
+ bool builtInName(const TString&);
+
+ void handlePragma(const TSourceLoc&, const TVector<TString>&) override;
+ TIntermTyped* handleVariable(const TSourceLoc&, TSymbol* symbol, const TString* string);
+ TIntermTyped* handleBracketDereference(const TSourceLoc&, TIntermTyped* base, TIntermTyped* index);
+ void handleIndexLimits(const TSourceLoc&, TIntermTyped* base, TIntermTyped* index);
+
+ void makeEditable(TSymbol*&) override;
+ bool isIoResizeArray(const TType&) const;
+ void fixIoArraySize(const TSourceLoc&, TType&);
+ void ioArrayCheck(const TSourceLoc&, const TType&, const TString& identifier);
+ void handleIoResizeArrayAccess(const TSourceLoc&, TIntermTyped* base);
+ void checkIoArraysConsistency(const TSourceLoc&, bool tailOnly = false);
+ int getIoArrayImplicitSize(const TQualifier&, TString* featureString = nullptr) const;
+ void checkIoArrayConsistency(const TSourceLoc&, int requiredSize, const char* feature, TType&, const TString&);
+
+ TIntermTyped* handleBinaryMath(const TSourceLoc&, const char* str, TOperator op, TIntermTyped* left, TIntermTyped* right);
+ TIntermTyped* handleUnaryMath(const TSourceLoc&, const char* str, TOperator op, TIntermTyped* childNode);
+ TIntermTyped* handleDotDereference(const TSourceLoc&, TIntermTyped* base, const TString& field);
+ void blockMemberExtensionCheck(const TSourceLoc&, const TIntermTyped* base, int member, const TString& memberName);
+ TFunction* handleFunctionDeclarator(const TSourceLoc&, TFunction& function, bool prototype);
+ TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&);
+ TIntermTyped* handleFunctionCall(const TSourceLoc&, TFunction*, TIntermNode*);
+ TIntermTyped* handleBuiltInFunctionCall(TSourceLoc, TIntermNode* arguments, const TFunction& function);
+ void computeBuiltinPrecisions(TIntermTyped&, const TFunction&);
+ TIntermNode* handleReturnValue(const TSourceLoc&, TIntermTyped*);
+ void checkLocation(const TSourceLoc&, TOperator);
+ TIntermTyped* handleLengthMethod(const TSourceLoc&, TFunction*, TIntermNode*);
+ void addInputArgumentConversions(const TFunction&, TIntermNode*&) const;
+ TIntermTyped* addOutputArgumentConversions(const TFunction&, TIntermAggregate&) const;
+ void builtInOpCheck(const TSourceLoc&, const TFunction&, TIntermOperator&);
+ void nonOpBuiltInCheck(const TSourceLoc&, const TFunction&, TIntermAggregate&);
+ void userFunctionCallCheck(const TSourceLoc&, TIntermAggregate&);
+ void samplerConstructorLocationCheck(const TSourceLoc&, const char* token, TIntermNode*);
+ TFunction* handleConstructorCall(const TSourceLoc&, const TPublicType&);
+ void handlePrecisionQualifier(const TSourceLoc&, TQualifier&, TPrecisionQualifier);
+ void checkPrecisionQualifier(const TSourceLoc&, TPrecisionQualifier);
+ void memorySemanticsCheck(const TSourceLoc&, const TFunction&, const TIntermOperator& callNode);
+
+ void assignError(const TSourceLoc&, const char* op, TString left, TString right);
+ void unaryOpError(const TSourceLoc&, const char* op, TString operand);
+ void binaryOpError(const TSourceLoc&, const char* op, TString left, TString right);
+ void variableCheck(TIntermTyped*& nodePtr);
+ bool lValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*) override;
+ void rValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*) override;
+ void constantValueCheck(TIntermTyped* node, const char* token);
+ void integerCheck(const TIntermTyped* node, const char* token);
+ void globalCheck(const TSourceLoc&, const char* token);
+ bool constructorError(const TSourceLoc&, TIntermNode*, TFunction&, TOperator, TType&);
+ bool constructorTextureSamplerError(const TSourceLoc&, const TFunction&);
+ void arraySizeCheck(const TSourceLoc&, TIntermTyped* expr, TArraySize&, const char *sizeType);
+ bool arrayQualifierError(const TSourceLoc&, const TQualifier&);
+ bool arrayError(const TSourceLoc&, const TType&);
+ void arraySizeRequiredCheck(const TSourceLoc&, const TArraySizes&);
+ void structArrayCheck(const TSourceLoc&, const TType& structure);
+ void arraySizesCheck(const TSourceLoc&, const TQualifier&, TArraySizes*, const TIntermTyped* initializer, bool lastMember);
+ void arrayOfArrayVersionCheck(const TSourceLoc&, const TArraySizes*);
+ bool voidErrorCheck(const TSourceLoc&, const TString&, TBasicType);
+ void boolCheck(const TSourceLoc&, const TIntermTyped*);
+ void boolCheck(const TSourceLoc&, const TPublicType&);
+ void samplerCheck(const TSourceLoc&, const TType&, const TString& identifier, TIntermTyped* initializer);
+ void atomicUintCheck(const TSourceLoc&, const TType&, const TString& identifier);
+ void accStructNVCheck(const TSourceLoc & loc, const TType & type, const TString & identifier);
+ void transparentOpaqueCheck(const TSourceLoc&, const TType&, const TString& identifier);
+ void memberQualifierCheck(glslang::TPublicType&);
+ void globalQualifierFixCheck(const TSourceLoc&, TQualifier&);
+ void globalQualifierTypeCheck(const TSourceLoc&, const TQualifier&, const TPublicType&);
+ bool structQualifierErrorCheck(const TSourceLoc&, const TPublicType& pType);
+ void mergeQualifiers(const TSourceLoc&, TQualifier& dst, const TQualifier& src, bool force);
+ void setDefaultPrecision(const TSourceLoc&, TPublicType&, TPrecisionQualifier);
+ int computeSamplerTypeIndex(TSampler&);
+ TPrecisionQualifier getDefaultPrecision(TPublicType&);
+ void precisionQualifierCheck(const TSourceLoc&, TBasicType, TQualifier&);
+ void parameterTypeCheck(const TSourceLoc&, TStorageQualifier qualifier, const TType& type);
+ bool containsFieldWithBasicType(const TType& type ,TBasicType basicType);
+ TSymbol* redeclareBuiltinVariable(const TSourceLoc&, const TString&, const TQualifier&, const TShaderQualifiers&);
+ void redeclareBuiltinBlock(const TSourceLoc&, TTypeList& typeList, const TString& blockName, const TString* instanceName, TArraySizes* arraySizes);
+ void paramCheckFixStorage(const TSourceLoc&, const TStorageQualifier&, TType& type);
+ void paramCheckFix(const TSourceLoc&, const TQualifier&, TType& type);
+ void nestedBlockCheck(const TSourceLoc&);
+ void nestedStructCheck(const TSourceLoc&);
+ void arrayObjectCheck(const TSourceLoc&, const TType&, const char* op);
+ void opaqueCheck(const TSourceLoc&, const TType&, const char* op);
+ void referenceCheck(const TSourceLoc&, const TType&, const char* op);
+ void storage16BitAssignmentCheck(const TSourceLoc&, const TType&, const char* op);
+ void specializationCheck(const TSourceLoc&, const TType&, const char* op);
+ void structTypeCheck(const TSourceLoc&, TPublicType&);
+ void inductiveLoopCheck(const TSourceLoc&, TIntermNode* init, TIntermLoop* loop);
+ void arrayLimitCheck(const TSourceLoc&, const TString&, int size);
+ void limitCheck(const TSourceLoc&, int value, const char* limit, const char* feature);
+
+ void inductiveLoopBodyCheck(TIntermNode*, int loopIndexId, TSymbolTable&);
+ void constantIndexExpressionCheck(TIntermNode*);
+
+ void setLayoutQualifier(const TSourceLoc&, TPublicType&, TString&);
+ void setLayoutQualifier(const TSourceLoc&, TPublicType&, TString&, const TIntermTyped*);
+ void mergeObjectLayoutQualifiers(TQualifier& dest, const TQualifier& src, bool inheritOnly);
+ void layoutObjectCheck(const TSourceLoc&, const TSymbol&);
+ void layoutMemberLocationArrayCheck(const TSourceLoc&, bool memberWithLocation, TArraySizes* arraySizes);
+ void layoutTypeCheck(const TSourceLoc&, const TType&);
+ void layoutQualifierCheck(const TSourceLoc&, const TQualifier&);
+ void checkNoShaderLayouts(const TSourceLoc&, const TShaderQualifiers&);
+ void fixOffset(const TSourceLoc&, TSymbol&);
+
+ const TFunction* findFunction(const TSourceLoc& loc, const TFunction& call, bool& builtIn);
+ const TFunction* findFunctionExact(const TSourceLoc& loc, const TFunction& call, bool& builtIn);
+ const TFunction* findFunction120(const TSourceLoc& loc, const TFunction& call, bool& builtIn);
+ const TFunction* findFunction400(const TSourceLoc& loc, const TFunction& call, bool& builtIn);
+ const TFunction* findFunctionExplicitTypes(const TSourceLoc& loc, const TFunction& call, bool& builtIn);
+ void declareTypeDefaults(const TSourceLoc&, const TPublicType&);
+ TIntermNode* declareVariable(const TSourceLoc&, TString& identifier, const TPublicType&, TArraySizes* typeArray = 0, TIntermTyped* initializer = 0);
+ TIntermTyped* addConstructor(const TSourceLoc&, TIntermNode*, const TType&);
+ TIntermTyped* constructAggregate(TIntermNode*, const TType&, int, const TSourceLoc&);
+ TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermTyped*, const TSourceLoc&, bool subset);
+ void declareBlock(const TSourceLoc&, TTypeList& typeList, const TString* instanceName = 0, TArraySizes* arraySizes = 0);
+ void blockStageIoCheck(const TSourceLoc&, const TQualifier&);
+ void blockQualifierCheck(const TSourceLoc&, const TQualifier&, bool instanceName);
+ void fixBlockLocations(const TSourceLoc&, TQualifier&, TTypeList&, bool memberWithLocation, bool memberWithoutLocation);
+ void fixXfbOffsets(TQualifier&, TTypeList&);
+ void fixBlockUniformOffsets(TQualifier&, TTypeList&);
+ void addQualifierToExisting(const TSourceLoc&, TQualifier, const TString& identifier);
+ void addQualifierToExisting(const TSourceLoc&, TQualifier, TIdentifierList&);
+ void invariantCheck(const TSourceLoc&, const TQualifier&);
+ void updateStandaloneQualifierDefaults(const TSourceLoc&, const TPublicType&);
+ void wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode);
+ TIntermNode* addSwitch(const TSourceLoc&, TIntermTyped* expression, TIntermAggregate* body);
+
+ TAttributeType attributeFromName(const TString& name) const;
+ TAttributes* makeAttributes(const TString& identifier) const;
+ TAttributes* makeAttributes(const TString& identifier, TIntermNode* node) const;
+ TAttributes* mergeAttributes(TAttributes*, TAttributes*) const;
+
+ // Determine selection control from attributes
+ void handleSelectionAttributes(const TAttributes& attributes, TIntermNode*);
+ void handleSwitchAttributes(const TAttributes& attributes, TIntermNode*);
+
+ // Determine loop control from attributes
+ void handleLoopAttributes(const TAttributes& attributes, TIntermNode*);
+
+ void resizeMeshViewDimension(const TSourceLoc&, TType&);
+
+protected:
+ void nonInitConstCheck(const TSourceLoc&, TString& identifier, TType& type);
+ void inheritGlobalDefaults(TQualifier& dst) const;
+ TVariable* makeInternalVariable(const char* name, const TType&) const;
+ TVariable* declareNonArray(const TSourceLoc&, const TString& identifier, const TType&);
+ void declareArray(const TSourceLoc&, const TString& identifier, const TType&, TSymbol*&);
+ void checkRuntimeSizable(const TSourceLoc&, const TIntermTyped&);
+ bool isRuntimeLength(const TIntermTyped&) const;
+ TIntermNode* executeInitializer(const TSourceLoc&, TIntermTyped* initializer, TVariable* variable);
+ TIntermTyped* convertInitializerList(const TSourceLoc&, const TType&, TIntermTyped* initializer);
+ void finish() override;
+
+public:
+ //
+ // Generally, bison productions, the scanner, and the PP need read/write access to these; just give them direct access
+ //
+
+ // Current state of parsing
+ bool inMain; // if inside a function, true if the function is main
+ const TString* blockName;
+ TQualifier currentBlockQualifier;
+ TPrecisionQualifier defaultPrecision[EbtNumTypes];
+ TBuiltInResource resources;
+ TLimits& limits;
+
+protected:
+ TParseContext(TParseContext&);
+ TParseContext& operator=(TParseContext&);
+
+ static const int maxSamplerIndex = EsdNumDims * (EbtNumTypes * (2 * 2 * 2 * 2 * 2)); // see computeSamplerTypeIndex()
+ TPrecisionQualifier defaultSamplerPrecision[maxSamplerIndex];
+ TPrecisionManager precisionManager;
+ TQualifier globalBufferDefaults;
+ TQualifier globalUniformDefaults;
+ TQualifier globalInputDefaults;
+ TQualifier globalOutputDefaults;
+ int* atomicUintOffsets; // to become an array of the right size to hold an offset per binding point
+ TString currentCaller; // name of last function body entered (not valid when at global scope)
+ TIdSetType inductiveLoopIds;
+ bool anyIndexLimits;
+ TVector<TIntermTyped*> needsIndexLimitationChecking;
+
+ //
+ // Geometry shader input arrays:
+ // - array sizing is based on input primitive and/or explicit size
+ //
+ // Tessellation control output arrays:
+ // - array sizing is based on output layout(vertices=...) and/or explicit size
+ //
+ // Both:
+ // - array sizing is retroactive
+ // - built-in block redeclarations interact with this
+ //
+ // Design:
+ // - use a per-context "resize-list", a list of symbols whose array sizes
+ // can be fixed
+ //
+ // - the resize-list starts empty at beginning of user-shader compilation, it does
+ // not have built-ins in it
+ //
+ // - on built-in array use: copyUp() symbol and add it to the resize-list
+ //
+ // - on user array declaration: add it to the resize-list
+ //
+ // - on block redeclaration: copyUp() symbol and add it to the resize-list
+ // * note, that appropriately gives an error if redeclaring a block that
+ // was already used and hence already copied-up
+ //
+ // - on seeing a layout declaration that sizes the array, fix everything in the
+ // resize-list, giving errors for mismatch
+ //
+ // - on seeing an array size declaration, give errors on mismatch between it and previous
+ // array-sizing declarations
+ //
+ TVector<TSymbol*> ioArraySymbolResizeList;
+};
+
+} // end namespace glslang
+
+#endif // _PARSER_HELPER_INCLUDED_
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/PoolAlloc.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/PoolAlloc.cpp
new file mode 100644
index 0000000..84c40f4
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/PoolAlloc.cpp
@@ -0,0 +1,315 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "../Include/Common.h"
+#include "../Include/PoolAlloc.h"
+
+#include "../Include/InitializeGlobals.h"
+#include "../OSDependent/osinclude.h"
+
+namespace glslang {
+
+// Process-wide TLS index
+OS_TLSIndex PoolIndex;
+
+// Return the thread-specific current pool.
+TPoolAllocator& GetThreadPoolAllocator()
+{
+ return *static_cast<TPoolAllocator*>(OS_GetTLSValue(PoolIndex));
+}
+
+// Set the thread-specific current pool.
+void SetThreadPoolAllocator(TPoolAllocator* poolAllocator)
+{
+ OS_SetTLSValue(PoolIndex, poolAllocator);
+}
+
+// Process-wide set up of the TLS pool storage.
+bool InitializePoolIndex()
+{
+ // Allocate a TLS index.
+ if ((PoolIndex = OS_AllocTLSIndex()) == OS_INVALID_TLS_INDEX)
+ return false;
+
+ return true;
+}
+
+//
+// Implement the functionality of the TPoolAllocator class, which
+// is documented in PoolAlloc.h.
+//
+TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) :
+ pageSize(growthIncrement),
+ alignment(allocationAlignment),
+ freeList(nullptr),
+ inUseList(nullptr),
+ numCalls(0)
+{
+ //
+ // Don't allow page sizes we know are smaller than all common
+ // OS page sizes.
+ //
+ if (pageSize < 4*1024)
+ pageSize = 4*1024;
+
+ //
+ // A large currentPageOffset indicates a new page needs to
+ // be obtained to allocate memory.
+ //
+ currentPageOffset = pageSize;
+
+ //
+ // Adjust alignment to be at least pointer aligned and
+ // power of 2.
+ //
+ size_t minAlign = sizeof(void*);
+ alignment &= ~(minAlign - 1);
+ if (alignment < minAlign)
+ alignment = minAlign;
+ size_t a = 1;
+ while (a < alignment)
+ a <<= 1;
+ alignment = a;
+ alignmentMask = a - 1;
+
+ //
+ // Align header skip
+ //
+ headerSkip = minAlign;
+ if (headerSkip < sizeof(tHeader)) {
+ headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask;
+ }
+
+ push();
+}
+
+TPoolAllocator::~TPoolAllocator()
+{
+ while (inUseList) {
+ tHeader* next = inUseList->nextPage;
+ inUseList->~tHeader();
+ delete [] reinterpret_cast<char*>(inUseList);
+ inUseList = next;
+ }
+
+ //
+ // Always delete the free list memory - it can't be being
+ // (correctly) referenced, whether the pool allocator was
+ // global or not. We should not check the guard blocks
+ // here, because we did it already when the block was
+ // placed into the free list.
+ //
+ while (freeList) {
+ tHeader* next = freeList->nextPage;
+ delete [] reinterpret_cast<char*>(freeList);
+ freeList = next;
+ }
+}
+
+const unsigned char TAllocation::guardBlockBeginVal = 0xfb;
+const unsigned char TAllocation::guardBlockEndVal = 0xfe;
+const unsigned char TAllocation::userDataFill = 0xcd;
+
+# ifdef GUARD_BLOCKS
+ const size_t TAllocation::guardBlockSize = 16;
+# else
+ const size_t TAllocation::guardBlockSize = 0;
+# endif
+
+//
+// Check a single guard block for damage
+//
+#ifdef GUARD_BLOCKS
+void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const
+#else
+void TAllocation::checkGuardBlock(unsigned char*, unsigned char, const char*) const
+#endif
+{
+#ifdef GUARD_BLOCKS
+ for (size_t x = 0; x < guardBlockSize; x++) {
+ if (blockMem[x] != val) {
+ const int maxSize = 80;
+ char assertMsg[maxSize];
+
+ // We don't print the assert message. It's here just to be helpful.
+ snprintf(assertMsg, maxSize, "PoolAlloc: Damage %s %zu byte allocation at 0x%p\n",
+ locText, size, data());
+ assert(0 && "PoolAlloc: Damage in guard block");
+ }
+ }
+#else
+ assert(guardBlockSize == 0);
+#endif
+}
+
+void TPoolAllocator::push()
+{
+ tAllocState state = { currentPageOffset, inUseList };
+
+ stack.push_back(state);
+
+ //
+ // Indicate there is no current page to allocate from.
+ //
+ currentPageOffset = pageSize;
+}
+
+//
+// Do a mass-deallocation of all the individual allocations
+// that have occurred since the last push(), or since the
+// last pop(), or since the object's creation.
+//
+// The deallocated pages are saved for future allocations.
+//
+void TPoolAllocator::pop()
+{
+ if (stack.size() < 1)
+ return;
+
+ tHeader* page = stack.back().page;
+ currentPageOffset = stack.back().offset;
+
+ while (inUseList != page) {
+ tHeader* nextInUse = inUseList->nextPage;
+ size_t pageCount = inUseList->pageCount;
+
+ // This technically ends the lifetime of the header as C++ object,
+ // but we will still control the memory and reuse it.
+ inUseList->~tHeader(); // currently, just a debug allocation checker
+
+ if (pageCount > 1) {
+ delete [] reinterpret_cast<char*>(inUseList);
+ } else {
+ inUseList->nextPage = freeList;
+ freeList = inUseList;
+ }
+ inUseList = nextInUse;
+ }
+
+ stack.pop_back();
+}
+
+//
+// Do a mass-deallocation of all the individual allocations
+// that have occurred.
+//
+void TPoolAllocator::popAll()
+{
+ while (stack.size() > 0)
+ pop();
+}
+
+void* TPoolAllocator::allocate(size_t numBytes)
+{
+ // If we are using guard blocks, all allocations are bracketed by
+ // them: [guardblock][allocation][guardblock]. numBytes is how
+ // much memory the caller asked for. allocationSize is the total
+ // size including guard blocks. In release build,
+ // guardBlockSize=0 and this all gets optimized away.
+ size_t allocationSize = TAllocation::allocationSize(numBytes);
+
+ //
+ // Just keep some interesting statistics.
+ //
+ ++numCalls;
+ totalBytes += numBytes;
+
+ //
+ // Do the allocation, most likely case first, for efficiency.
+ // This step could be moved to be inline sometime.
+ //
+ if (currentPageOffset + allocationSize <= pageSize) {
+ //
+ // Safe to allocate from currentPageOffset.
+ //
+ unsigned char* memory = reinterpret_cast<unsigned char*>(inUseList) + currentPageOffset;
+ currentPageOffset += allocationSize;
+ currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask;
+
+ return initializeAllocation(inUseList, memory, numBytes);
+ }
+
+ if (allocationSize + headerSkip > pageSize) {
+ //
+ // Do a multi-page allocation. Don't mix these with the others.
+ // The OS is efficient and allocating and free-ing multiple pages.
+ //
+ size_t numBytesToAlloc = allocationSize + headerSkip;
+ tHeader* memory = reinterpret_cast<tHeader*>(::new char[numBytesToAlloc]);
+ if (memory == 0)
+ return 0;
+
+ // Use placement-new to initialize header
+ new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize);
+ inUseList = memory;
+
+ currentPageOffset = pageSize; // make next allocation come from a new page
+
+ // No guard blocks for multi-page allocations (yet)
+ return reinterpret_cast<void*>(reinterpret_cast<UINT_PTR>(memory) + headerSkip);
+ }
+
+ //
+ // Need a simple page to allocate from.
+ //
+ tHeader* memory;
+ if (freeList) {
+ memory = freeList;
+ freeList = freeList->nextPage;
+ } else {
+ memory = reinterpret_cast<tHeader*>(::new char[pageSize]);
+ if (memory == 0)
+ return 0;
+ }
+
+ // Use placement-new to initialize header
+ new(memory) tHeader(inUseList, 1);
+ inUseList = memory;
+
+ unsigned char* ret = reinterpret_cast<unsigned char*>(inUseList) + headerSkip;
+ currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask;
+
+ return initializeAllocation(inUseList, ret, numBytes);
+}
+
+//
+// Check all allocations in a list for damage by calling check on each.
+//
+void TAllocation::checkAllocList() const
+{
+ for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc)
+ alloc->check();
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/RemoveTree.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/RemoveTree.cpp
new file mode 100644
index 0000000..1d33bfd
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/RemoveTree.cpp
@@ -0,0 +1,118 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2013 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "../Include/intermediate.h"
+#include "RemoveTree.h"
+
+namespace glslang {
+
+//
+// Code to recursively delete the intermediate tree.
+//
+struct TRemoveTraverser : TIntermTraverser {
+ TRemoveTraverser() : TIntermTraverser(false, false, true, false) {}
+
+ virtual void visitSymbol(TIntermSymbol* node)
+ {
+ delete node;
+ }
+
+ virtual bool visitBinary(TVisit /* visit*/ , TIntermBinary* node)
+ {
+ delete node;
+
+ return true;
+ }
+
+ virtual bool visitUnary(TVisit /* visit */, TIntermUnary* node)
+ {
+ delete node;
+
+ return true;
+ }
+
+ virtual bool visitAggregate(TVisit /* visit*/ , TIntermAggregate* node)
+ {
+ delete node;
+
+ return true;
+ }
+
+ virtual bool visitSelection(TVisit /* visit*/ , TIntermSelection* node)
+ {
+ delete node;
+
+ return true;
+ }
+
+ virtual bool visitSwitch(TVisit /* visit*/ , TIntermSwitch* node)
+ {
+ delete node;
+
+ return true;
+ }
+
+ virtual void visitConstantUnion(TIntermConstantUnion* node)
+ {
+ delete node;
+ }
+
+ virtual bool visitLoop(TVisit /* visit*/ , TIntermLoop* node)
+ {
+ delete node;
+
+ return true;
+ }
+
+ virtual bool visitBranch(TVisit /* visit*/ , TIntermBranch* node)
+ {
+ delete node;
+
+ return true;
+ }
+};
+
+//
+// Entry point.
+//
+void RemoveAllTreeNodes(TIntermNode* root)
+{
+ TRemoveTraverser it;
+
+ root->traverse(&it);
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/RemoveTree.h b/src/3rdparty/glslang/glslang/MachineIndependent/RemoveTree.h
new file mode 100644
index 0000000..1ed0156
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/RemoveTree.h
@@ -0,0 +1,41 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#pragma once
+
+namespace glslang {
+
+void RemoveAllTreeNodes(TIntermNode*);
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/Scan.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/Scan.cpp
new file mode 100644
index 0000000..482f6ba
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/Scan.cpp
@@ -0,0 +1,1793 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2013 LunarG, Inc.
+// Copyright (C) 2017 ARM Limited.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+//
+// GLSL scanning, leveraging the scanning done by the preprocessor.
+//
+
+#include <cstring>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "../Include/Types.h"
+#include "SymbolTable.h"
+#include "ParseHelper.h"
+#include "attribute.h"
+#include "glslang_tab.cpp.h"
+#include "ScanContext.h"
+#include "Scan.h"
+
+// preprocessor includes
+#include "preprocessor/PpContext.h"
+#include "preprocessor/PpTokens.h"
+
+// Required to avoid missing prototype warnings for some compilers
+int yylex(YYSTYPE*, glslang::TParseContext&);
+
+namespace glslang {
+
+// read past any white space
+void TInputScanner::consumeWhiteSpace(bool& foundNonSpaceTab)
+{
+ int c = peek(); // don't accidentally consume anything other than whitespace
+ while (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
+ if (c == '\r' || c == '\n')
+ foundNonSpaceTab = true;
+ get();
+ c = peek();
+ }
+}
+
+// return true if a comment was actually consumed
+bool TInputScanner::consumeComment()
+{
+ if (peek() != '/')
+ return false;
+
+ get(); // consume the '/'
+ int c = peek();
+ if (c == '/') {
+
+ // a '//' style comment
+ get(); // consume the second '/'
+ c = get();
+ do {
+ while (c != EndOfInput && c != '\\' && c != '\r' && c != '\n')
+ c = get();
+
+ if (c == EndOfInput || c == '\r' || c == '\n') {
+ while (c == '\r' || c == '\n')
+ c = get();
+
+ // we reached the end of the comment
+ break;
+ } else {
+ // it's a '\', so we need to keep going, after skipping what's escaped
+
+ // read the skipped character
+ c = get();
+
+ // if it's a two-character newline, skip both characters
+ if (c == '\r' && peek() == '\n')
+ get();
+ c = get();
+ }
+ } while (true);
+
+ // put back the last non-comment character
+ if (c != EndOfInput)
+ unget();
+
+ return true;
+ } else if (c == '*') {
+
+ // a '/*' style comment
+ get(); // consume the '*'
+ c = get();
+ do {
+ while (c != EndOfInput && c != '*')
+ c = get();
+ if (c == '*') {
+ c = get();
+ if (c == '/')
+ break; // end of comment
+ // not end of comment
+ } else // end of input
+ break;
+ } while (true);
+
+ return true;
+ } else {
+ // it's not a comment, put the '/' back
+ unget();
+
+ return false;
+ }
+}
+
+// skip whitespace, then skip a comment, rinse, repeat
+void TInputScanner::consumeWhitespaceComment(bool& foundNonSpaceTab)
+{
+ do {
+ consumeWhiteSpace(foundNonSpaceTab);
+
+ // if not starting a comment now, then done
+ int c = peek();
+ if (c != '/' || c == EndOfInput)
+ return;
+
+ // skip potential comment
+ foundNonSpaceTab = true;
+ if (! consumeComment())
+ return;
+
+ } while (true);
+}
+
+// Returns true if there was non-white space (e.g., a comment, newline) before the #version
+// or no #version was found; otherwise, returns false. There is no error case, it always
+// succeeds, but will leave version == 0 if no #version was found.
+//
+// Sets notFirstToken based on whether tokens (beyond white space and comments)
+// appeared before the #version.
+//
+// N.B. does not attempt to leave input in any particular known state. The assumption
+// is that scanning will start anew, following the rules for the chosen version/profile,
+// and with a corresponding parsing context.
+//
+bool TInputScanner::scanVersion(int& version, EProfile& profile, bool& notFirstToken)
+{
+ // This function doesn't have to get all the semantics correct,
+ // just find the #version if there is a correct one present.
+ // The preprocessor will have the responsibility of getting all the semantics right.
+
+ bool versionNotFirst = false; // means not first WRT comments and white space, nothing more
+ notFirstToken = false; // means not first WRT to real tokens
+ version = 0; // means not found
+ profile = ENoProfile;
+
+ bool foundNonSpaceTab = false;
+ bool lookingInMiddle = false;
+ int c;
+ do {
+ if (lookingInMiddle) {
+ notFirstToken = true;
+ // make forward progress by finishing off the current line plus extra new lines
+ if (peek() == '\n' || peek() == '\r') {
+ while (peek() == '\n' || peek() == '\r')
+ get();
+ } else
+ do {
+ c = get();
+ } while (c != EndOfInput && c != '\n' && c != '\r');
+ while (peek() == '\n' || peek() == '\r')
+ get();
+ if (peek() == EndOfInput)
+ return true;
+ }
+ lookingInMiddle = true;
+
+ // Nominal start, skipping the desktop allowed comments and white space, but tracking if
+ // something else was found for ES:
+ consumeWhitespaceComment(foundNonSpaceTab);
+ if (foundNonSpaceTab)
+ versionNotFirst = true;
+
+ // "#"
+ if (get() != '#') {
+ versionNotFirst = true;
+ continue;
+ }
+
+ // whitespace
+ do {
+ c = get();
+ } while (c == ' ' || c == '\t');
+
+ // "version"
+ if ( c != 'v' ||
+ get() != 'e' ||
+ get() != 'r' ||
+ get() != 's' ||
+ get() != 'i' ||
+ get() != 'o' ||
+ get() != 'n') {
+ versionNotFirst = true;
+ continue;
+ }
+
+ // whitespace
+ do {
+ c = get();
+ } while (c == ' ' || c == '\t');
+
+ // version number
+ while (c >= '0' && c <= '9') {
+ version = 10 * version + (c - '0');
+ c = get();
+ }
+ if (version == 0) {
+ versionNotFirst = true;
+ continue;
+ }
+
+ // whitespace
+ while (c == ' ' || c == '\t')
+ c = get();
+
+ // profile
+ const int maxProfileLength = 13; // not including any 0
+ char profileString[maxProfileLength];
+ int profileLength;
+ for (profileLength = 0; profileLength < maxProfileLength; ++profileLength) {
+ if (c == EndOfInput || c == ' ' || c == '\t' || c == '\n' || c == '\r')
+ break;
+ profileString[profileLength] = (char)c;
+ c = get();
+ }
+ if (c != EndOfInput && c != ' ' && c != '\t' && c != '\n' && c != '\r') {
+ versionNotFirst = true;
+ continue;
+ }
+
+ if (profileLength == 2 && strncmp(profileString, "es", profileLength) == 0)
+ profile = EEsProfile;
+ else if (profileLength == 4 && strncmp(profileString, "core", profileLength) == 0)
+ profile = ECoreProfile;
+ else if (profileLength == 13 && strncmp(profileString, "compatibility", profileLength) == 0)
+ profile = ECompatibilityProfile;
+
+ return versionNotFirst;
+ } while (true);
+}
+
+// Fill this in when doing glslang-level scanning, to hand back to the parser.
+class TParserToken {
+public:
+ explicit TParserToken(YYSTYPE& b) : sType(b) { }
+
+ YYSTYPE& sType;
+protected:
+ TParserToken(TParserToken&);
+ TParserToken& operator=(TParserToken&);
+};
+
+} // end namespace glslang
+
+// This is the function the glslang parser (i.e., bison) calls to get its next token
+int yylex(YYSTYPE* glslangTokenDesc, glslang::TParseContext& parseContext)
+{
+ glslang::TParserToken token(*glslangTokenDesc);
+
+ return parseContext.getScanContext()->tokenize(parseContext.getPpContext(), token);
+}
+
+namespace {
+
+struct str_eq
+{
+ bool operator()(const char* lhs, const char* rhs) const
+ {
+ return strcmp(lhs, rhs) == 0;
+ }
+};
+
+struct str_hash
+{
+ size_t operator()(const char* str) const
+ {
+ // djb2
+ unsigned long hash = 5381;
+ int c;
+
+ while ((c = *str++) != 0)
+ hash = ((hash << 5) + hash) + c;
+
+ return hash;
+ }
+};
+
+// A single global usable by all threads, by all versions, by all languages.
+// After a single process-level initialization, this is read only and thread safe
+std::unordered_map<const char*, int, str_hash, str_eq>* KeywordMap = nullptr;
+std::unordered_set<const char*, str_hash, str_eq>* ReservedSet = nullptr;
+
+};
+
+namespace glslang {
+
+void TScanContext::fillInKeywordMap()
+{
+ if (KeywordMap != nullptr) {
+ // this is really an error, as this should called only once per process
+ // but, the only risk is if two threads called simultaneously
+ return;
+ }
+ KeywordMap = new std::unordered_map<const char*, int, str_hash, str_eq>;
+
+ (*KeywordMap)["const"] = CONST;
+ (*KeywordMap)["uniform"] = UNIFORM;
+ (*KeywordMap)["nonuniformEXT"] = NONUNIFORM;
+ (*KeywordMap)["in"] = IN;
+ (*KeywordMap)["out"] = OUT;
+ (*KeywordMap)["inout"] = INOUT;
+ (*KeywordMap)["struct"] = STRUCT;
+ (*KeywordMap)["break"] = BREAK;
+ (*KeywordMap)["continue"] = CONTINUE;
+ (*KeywordMap)["do"] = DO;
+ (*KeywordMap)["for"] = FOR;
+ (*KeywordMap)["while"] = WHILE;
+ (*KeywordMap)["switch"] = SWITCH;
+ (*KeywordMap)["case"] = CASE;
+ (*KeywordMap)["default"] = DEFAULT;
+ (*KeywordMap)["if"] = IF;
+ (*KeywordMap)["else"] = ELSE;
+ (*KeywordMap)["discard"] = DISCARD;
+ (*KeywordMap)["return"] = RETURN;
+ (*KeywordMap)["void"] = VOID;
+ (*KeywordMap)["bool"] = BOOL;
+ (*KeywordMap)["float"] = FLOAT;
+ (*KeywordMap)["int"] = INT;
+ (*KeywordMap)["bvec2"] = BVEC2;
+ (*KeywordMap)["bvec3"] = BVEC3;
+ (*KeywordMap)["bvec4"] = BVEC4;
+ (*KeywordMap)["vec2"] = VEC2;
+ (*KeywordMap)["vec3"] = VEC3;
+ (*KeywordMap)["vec4"] = VEC4;
+ (*KeywordMap)["ivec2"] = IVEC2;
+ (*KeywordMap)["ivec3"] = IVEC3;
+ (*KeywordMap)["ivec4"] = IVEC4;
+ (*KeywordMap)["mat2"] = MAT2;
+ (*KeywordMap)["mat3"] = MAT3;
+ (*KeywordMap)["mat4"] = MAT4;
+ (*KeywordMap)["true"] = BOOLCONSTANT;
+ (*KeywordMap)["false"] = BOOLCONSTANT;
+ (*KeywordMap)["attribute"] = ATTRIBUTE;
+ (*KeywordMap)["varying"] = VARYING;
+ (*KeywordMap)["buffer"] = BUFFER;
+ (*KeywordMap)["coherent"] = COHERENT;
+ (*KeywordMap)["devicecoherent"] = DEVICECOHERENT;
+ (*KeywordMap)["queuefamilycoherent"] = QUEUEFAMILYCOHERENT;
+ (*KeywordMap)["workgroupcoherent"] = WORKGROUPCOHERENT;
+ (*KeywordMap)["subgroupcoherent"] = SUBGROUPCOHERENT;
+ (*KeywordMap)["nonprivate"] = NONPRIVATE;
+ (*KeywordMap)["restrict"] = RESTRICT;
+ (*KeywordMap)["readonly"] = READONLY;
+ (*KeywordMap)["writeonly"] = WRITEONLY;
+ (*KeywordMap)["atomic_uint"] = ATOMIC_UINT;
+ (*KeywordMap)["volatile"] = VOLATILE;
+ (*KeywordMap)["layout"] = LAYOUT;
+ (*KeywordMap)["shared"] = SHARED;
+ (*KeywordMap)["patch"] = PATCH;
+ (*KeywordMap)["sample"] = SAMPLE;
+ (*KeywordMap)["subroutine"] = SUBROUTINE;
+ (*KeywordMap)["highp"] = HIGH_PRECISION;
+ (*KeywordMap)["mediump"] = MEDIUM_PRECISION;
+ (*KeywordMap)["lowp"] = LOW_PRECISION;
+ (*KeywordMap)["precision"] = PRECISION;
+ (*KeywordMap)["mat2x2"] = MAT2X2;
+ (*KeywordMap)["mat2x3"] = MAT2X3;
+ (*KeywordMap)["mat2x4"] = MAT2X4;
+ (*KeywordMap)["mat3x2"] = MAT3X2;
+ (*KeywordMap)["mat3x3"] = MAT3X3;
+ (*KeywordMap)["mat3x4"] = MAT3X4;
+ (*KeywordMap)["mat4x2"] = MAT4X2;
+ (*KeywordMap)["mat4x3"] = MAT4X3;
+ (*KeywordMap)["mat4x4"] = MAT4X4;
+ (*KeywordMap)["dmat2"] = DMAT2;
+ (*KeywordMap)["dmat3"] = DMAT3;
+ (*KeywordMap)["dmat4"] = DMAT4;
+ (*KeywordMap)["dmat2x2"] = DMAT2X2;
+ (*KeywordMap)["dmat2x3"] = DMAT2X3;
+ (*KeywordMap)["dmat2x4"] = DMAT2X4;
+ (*KeywordMap)["dmat3x2"] = DMAT3X2;
+ (*KeywordMap)["dmat3x3"] = DMAT3X3;
+ (*KeywordMap)["dmat3x4"] = DMAT3X4;
+ (*KeywordMap)["dmat4x2"] = DMAT4X2;
+ (*KeywordMap)["dmat4x3"] = DMAT4X3;
+ (*KeywordMap)["dmat4x4"] = DMAT4X4;
+ (*KeywordMap)["image1D"] = IMAGE1D;
+ (*KeywordMap)["iimage1D"] = IIMAGE1D;
+ (*KeywordMap)["uimage1D"] = UIMAGE1D;
+ (*KeywordMap)["image2D"] = IMAGE2D;
+ (*KeywordMap)["iimage2D"] = IIMAGE2D;
+ (*KeywordMap)["uimage2D"] = UIMAGE2D;
+ (*KeywordMap)["image3D"] = IMAGE3D;
+ (*KeywordMap)["iimage3D"] = IIMAGE3D;
+ (*KeywordMap)["uimage3D"] = UIMAGE3D;
+ (*KeywordMap)["image2DRect"] = IMAGE2DRECT;
+ (*KeywordMap)["iimage2DRect"] = IIMAGE2DRECT;
+ (*KeywordMap)["uimage2DRect"] = UIMAGE2DRECT;
+ (*KeywordMap)["imageCube"] = IMAGECUBE;
+ (*KeywordMap)["iimageCube"] = IIMAGECUBE;
+ (*KeywordMap)["uimageCube"] = UIMAGECUBE;
+ (*KeywordMap)["imageBuffer"] = IMAGEBUFFER;
+ (*KeywordMap)["iimageBuffer"] = IIMAGEBUFFER;
+ (*KeywordMap)["uimageBuffer"] = UIMAGEBUFFER;
+ (*KeywordMap)["image1DArray"] = IMAGE1DARRAY;
+ (*KeywordMap)["iimage1DArray"] = IIMAGE1DARRAY;
+ (*KeywordMap)["uimage1DArray"] = UIMAGE1DARRAY;
+ (*KeywordMap)["image2DArray"] = IMAGE2DARRAY;
+ (*KeywordMap)["iimage2DArray"] = IIMAGE2DARRAY;
+ (*KeywordMap)["uimage2DArray"] = UIMAGE2DARRAY;
+ (*KeywordMap)["imageCubeArray"] = IMAGECUBEARRAY;
+ (*KeywordMap)["iimageCubeArray"] = IIMAGECUBEARRAY;
+ (*KeywordMap)["uimageCubeArray"] = UIMAGECUBEARRAY;
+ (*KeywordMap)["image2DMS"] = IMAGE2DMS;
+ (*KeywordMap)["iimage2DMS"] = IIMAGE2DMS;
+ (*KeywordMap)["uimage2DMS"] = UIMAGE2DMS;
+ (*KeywordMap)["image2DMSArray"] = IMAGE2DMSARRAY;
+ (*KeywordMap)["iimage2DMSArray"] = IIMAGE2DMSARRAY;
+ (*KeywordMap)["uimage2DMSArray"] = UIMAGE2DMSARRAY;
+ (*KeywordMap)["double"] = DOUBLE;
+ (*KeywordMap)["dvec2"] = DVEC2;
+ (*KeywordMap)["dvec3"] = DVEC3;
+ (*KeywordMap)["dvec4"] = DVEC4;
+ (*KeywordMap)["uint"] = UINT;
+ (*KeywordMap)["uvec2"] = UVEC2;
+ (*KeywordMap)["uvec3"] = UVEC3;
+ (*KeywordMap)["uvec4"] = UVEC4;
+
+ (*KeywordMap)["int64_t"] = INT64_T;
+ (*KeywordMap)["uint64_t"] = UINT64_T;
+ (*KeywordMap)["i64vec2"] = I64VEC2;
+ (*KeywordMap)["i64vec3"] = I64VEC3;
+ (*KeywordMap)["i64vec4"] = I64VEC4;
+ (*KeywordMap)["u64vec2"] = U64VEC2;
+ (*KeywordMap)["u64vec3"] = U64VEC3;
+ (*KeywordMap)["u64vec4"] = U64VEC4;
+
+ // GL_EXT_shader_explicit_arithmetic_types
+ (*KeywordMap)["int8_t"] = INT8_T;
+ (*KeywordMap)["i8vec2"] = I8VEC2;
+ (*KeywordMap)["i8vec3"] = I8VEC3;
+ (*KeywordMap)["i8vec4"] = I8VEC4;
+ (*KeywordMap)["uint8_t"] = UINT8_T;
+ (*KeywordMap)["u8vec2"] = U8VEC2;
+ (*KeywordMap)["u8vec3"] = U8VEC3;
+ (*KeywordMap)["u8vec4"] = U8VEC4;
+
+ (*KeywordMap)["int16_t"] = INT16_T;
+ (*KeywordMap)["i16vec2"] = I16VEC2;
+ (*KeywordMap)["i16vec3"] = I16VEC3;
+ (*KeywordMap)["i16vec4"] = I16VEC4;
+ (*KeywordMap)["uint16_t"] = UINT16_T;
+ (*KeywordMap)["u16vec2"] = U16VEC2;
+ (*KeywordMap)["u16vec3"] = U16VEC3;
+ (*KeywordMap)["u16vec4"] = U16VEC4;
+
+ (*KeywordMap)["int32_t"] = INT32_T;
+ (*KeywordMap)["i32vec2"] = I32VEC2;
+ (*KeywordMap)["i32vec3"] = I32VEC3;
+ (*KeywordMap)["i32vec4"] = I32VEC4;
+ (*KeywordMap)["uint32_t"] = UINT32_T;
+ (*KeywordMap)["u32vec2"] = U32VEC2;
+ (*KeywordMap)["u32vec3"] = U32VEC3;
+ (*KeywordMap)["u32vec4"] = U32VEC4;
+
+ (*KeywordMap)["float16_t"] = FLOAT16_T;
+ (*KeywordMap)["f16vec2"] = F16VEC2;
+ (*KeywordMap)["f16vec3"] = F16VEC3;
+ (*KeywordMap)["f16vec4"] = F16VEC4;
+ (*KeywordMap)["f16mat2"] = F16MAT2;
+ (*KeywordMap)["f16mat3"] = F16MAT3;
+ (*KeywordMap)["f16mat4"] = F16MAT4;
+ (*KeywordMap)["f16mat2x2"] = F16MAT2X2;
+ (*KeywordMap)["f16mat2x3"] = F16MAT2X3;
+ (*KeywordMap)["f16mat2x4"] = F16MAT2X4;
+ (*KeywordMap)["f16mat3x2"] = F16MAT3X2;
+ (*KeywordMap)["f16mat3x3"] = F16MAT3X3;
+ (*KeywordMap)["f16mat3x4"] = F16MAT3X4;
+ (*KeywordMap)["f16mat4x2"] = F16MAT4X2;
+ (*KeywordMap)["f16mat4x3"] = F16MAT4X3;
+ (*KeywordMap)["f16mat4x4"] = F16MAT4X4;
+
+ (*KeywordMap)["float32_t"] = FLOAT32_T;
+ (*KeywordMap)["f32vec2"] = F32VEC2;
+ (*KeywordMap)["f32vec3"] = F32VEC3;
+ (*KeywordMap)["f32vec4"] = F32VEC4;
+ (*KeywordMap)["f32mat2"] = F32MAT2;
+ (*KeywordMap)["f32mat3"] = F32MAT3;
+ (*KeywordMap)["f32mat4"] = F32MAT4;
+ (*KeywordMap)["f32mat2x2"] = F32MAT2X2;
+ (*KeywordMap)["f32mat2x3"] = F32MAT2X3;
+ (*KeywordMap)["f32mat2x4"] = F32MAT2X4;
+ (*KeywordMap)["f32mat3x2"] = F32MAT3X2;
+ (*KeywordMap)["f32mat3x3"] = F32MAT3X3;
+ (*KeywordMap)["f32mat3x4"] = F32MAT3X4;
+ (*KeywordMap)["f32mat4x2"] = F32MAT4X2;
+ (*KeywordMap)["f32mat4x3"] = F32MAT4X3;
+ (*KeywordMap)["f32mat4x4"] = F32MAT4X4;
+ (*KeywordMap)["float64_t"] = FLOAT64_T;
+ (*KeywordMap)["f64vec2"] = F64VEC2;
+ (*KeywordMap)["f64vec3"] = F64VEC3;
+ (*KeywordMap)["f64vec4"] = F64VEC4;
+ (*KeywordMap)["f64mat2"] = F64MAT2;
+ (*KeywordMap)["f64mat3"] = F64MAT3;
+ (*KeywordMap)["f64mat4"] = F64MAT4;
+ (*KeywordMap)["f64mat2x2"] = F64MAT2X2;
+ (*KeywordMap)["f64mat2x3"] = F64MAT2X3;
+ (*KeywordMap)["f64mat2x4"] = F64MAT2X4;
+ (*KeywordMap)["f64mat3x2"] = F64MAT3X2;
+ (*KeywordMap)["f64mat3x3"] = F64MAT3X3;
+ (*KeywordMap)["f64mat3x4"] = F64MAT3X4;
+ (*KeywordMap)["f64mat4x2"] = F64MAT4X2;
+ (*KeywordMap)["f64mat4x3"] = F64MAT4X3;
+ (*KeywordMap)["f64mat4x4"] = F64MAT4X4;
+
+ (*KeywordMap)["sampler2D"] = SAMPLER2D;
+ (*KeywordMap)["samplerCube"] = SAMPLERCUBE;
+ (*KeywordMap)["samplerCubeArray"] = SAMPLERCUBEARRAY;
+ (*KeywordMap)["samplerCubeArrayShadow"] = SAMPLERCUBEARRAYSHADOW;
+ (*KeywordMap)["isamplerCubeArray"] = ISAMPLERCUBEARRAY;
+ (*KeywordMap)["usamplerCubeArray"] = USAMPLERCUBEARRAY;
+ (*KeywordMap)["sampler1DArrayShadow"] = SAMPLER1DARRAYSHADOW;
+ (*KeywordMap)["isampler1DArray"] = ISAMPLER1DARRAY;
+ (*KeywordMap)["usampler1D"] = USAMPLER1D;
+ (*KeywordMap)["isampler1D"] = ISAMPLER1D;
+ (*KeywordMap)["usampler1DArray"] = USAMPLER1DARRAY;
+ (*KeywordMap)["samplerBuffer"] = SAMPLERBUFFER;
+ (*KeywordMap)["samplerCubeShadow"] = SAMPLERCUBESHADOW;
+ (*KeywordMap)["sampler2DArray"] = SAMPLER2DARRAY;
+ (*KeywordMap)["sampler2DArrayShadow"] = SAMPLER2DARRAYSHADOW;
+ (*KeywordMap)["isampler2D"] = ISAMPLER2D;
+ (*KeywordMap)["isampler3D"] = ISAMPLER3D;
+ (*KeywordMap)["isamplerCube"] = ISAMPLERCUBE;
+ (*KeywordMap)["isampler2DArray"] = ISAMPLER2DARRAY;
+ (*KeywordMap)["usampler2D"] = USAMPLER2D;
+ (*KeywordMap)["usampler3D"] = USAMPLER3D;
+ (*KeywordMap)["usamplerCube"] = USAMPLERCUBE;
+ (*KeywordMap)["usampler2DArray"] = USAMPLER2DARRAY;
+ (*KeywordMap)["isampler2DRect"] = ISAMPLER2DRECT;
+ (*KeywordMap)["usampler2DRect"] = USAMPLER2DRECT;
+ (*KeywordMap)["isamplerBuffer"] = ISAMPLERBUFFER;
+ (*KeywordMap)["usamplerBuffer"] = USAMPLERBUFFER;
+ (*KeywordMap)["sampler2DMS"] = SAMPLER2DMS;
+ (*KeywordMap)["isampler2DMS"] = ISAMPLER2DMS;
+ (*KeywordMap)["usampler2DMS"] = USAMPLER2DMS;
+ (*KeywordMap)["sampler2DMSArray"] = SAMPLER2DMSARRAY;
+ (*KeywordMap)["isampler2DMSArray"] = ISAMPLER2DMSARRAY;
+ (*KeywordMap)["usampler2DMSArray"] = USAMPLER2DMSARRAY;
+ (*KeywordMap)["sampler1D"] = SAMPLER1D;
+ (*KeywordMap)["sampler1DShadow"] = SAMPLER1DSHADOW;
+ (*KeywordMap)["sampler3D"] = SAMPLER3D;
+ (*KeywordMap)["sampler2DShadow"] = SAMPLER2DSHADOW;
+ (*KeywordMap)["sampler2DRect"] = SAMPLER2DRECT;
+ (*KeywordMap)["sampler2DRectShadow"] = SAMPLER2DRECTSHADOW;
+ (*KeywordMap)["sampler1DArray"] = SAMPLER1DARRAY;
+
+ (*KeywordMap)["samplerExternalOES"] = SAMPLEREXTERNALOES; // GL_OES_EGL_image_external
+
+ (*KeywordMap)["__samplerExternal2DY2YEXT"] = SAMPLEREXTERNAL2DY2YEXT; // GL_EXT_YUV_target
+
+ (*KeywordMap)["sampler"] = SAMPLER;
+ (*KeywordMap)["samplerShadow"] = SAMPLERSHADOW;
+
+ (*KeywordMap)["texture2D"] = TEXTURE2D;
+ (*KeywordMap)["textureCube"] = TEXTURECUBE;
+ (*KeywordMap)["textureCubeArray"] = TEXTURECUBEARRAY;
+ (*KeywordMap)["itextureCubeArray"] = ITEXTURECUBEARRAY;
+ (*KeywordMap)["utextureCubeArray"] = UTEXTURECUBEARRAY;
+ (*KeywordMap)["itexture1DArray"] = ITEXTURE1DARRAY;
+ (*KeywordMap)["utexture1D"] = UTEXTURE1D;
+ (*KeywordMap)["itexture1D"] = ITEXTURE1D;
+ (*KeywordMap)["utexture1DArray"] = UTEXTURE1DARRAY;
+ (*KeywordMap)["textureBuffer"] = TEXTUREBUFFER;
+ (*KeywordMap)["texture2DArray"] = TEXTURE2DARRAY;
+ (*KeywordMap)["itexture2D"] = ITEXTURE2D;
+ (*KeywordMap)["itexture3D"] = ITEXTURE3D;
+ (*KeywordMap)["itextureCube"] = ITEXTURECUBE;
+ (*KeywordMap)["itexture2DArray"] = ITEXTURE2DARRAY;
+ (*KeywordMap)["utexture2D"] = UTEXTURE2D;
+ (*KeywordMap)["utexture3D"] = UTEXTURE3D;
+ (*KeywordMap)["utextureCube"] = UTEXTURECUBE;
+ (*KeywordMap)["utexture2DArray"] = UTEXTURE2DARRAY;
+ (*KeywordMap)["itexture2DRect"] = ITEXTURE2DRECT;
+ (*KeywordMap)["utexture2DRect"] = UTEXTURE2DRECT;
+ (*KeywordMap)["itextureBuffer"] = ITEXTUREBUFFER;
+ (*KeywordMap)["utextureBuffer"] = UTEXTUREBUFFER;
+ (*KeywordMap)["texture2DMS"] = TEXTURE2DMS;
+ (*KeywordMap)["itexture2DMS"] = ITEXTURE2DMS;
+ (*KeywordMap)["utexture2DMS"] = UTEXTURE2DMS;
+ (*KeywordMap)["texture2DMSArray"] = TEXTURE2DMSARRAY;
+ (*KeywordMap)["itexture2DMSArray"] = ITEXTURE2DMSARRAY;
+ (*KeywordMap)["utexture2DMSArray"] = UTEXTURE2DMSARRAY;
+ (*KeywordMap)["texture1D"] = TEXTURE1D;
+ (*KeywordMap)["texture3D"] = TEXTURE3D;
+ (*KeywordMap)["texture2DRect"] = TEXTURE2DRECT;
+ (*KeywordMap)["texture1DArray"] = TEXTURE1DARRAY;
+
+ (*KeywordMap)["subpassInput"] = SUBPASSINPUT;
+ (*KeywordMap)["subpassInputMS"] = SUBPASSINPUTMS;
+ (*KeywordMap)["isubpassInput"] = ISUBPASSINPUT;
+ (*KeywordMap)["isubpassInputMS"] = ISUBPASSINPUTMS;
+ (*KeywordMap)["usubpassInput"] = USUBPASSINPUT;
+ (*KeywordMap)["usubpassInputMS"] = USUBPASSINPUTMS;
+
+#ifdef AMD_EXTENSIONS
+ (*KeywordMap)["f16sampler1D"] = F16SAMPLER1D;
+ (*KeywordMap)["f16sampler2D"] = F16SAMPLER2D;
+ (*KeywordMap)["f16sampler3D"] = F16SAMPLER3D;
+ (*KeywordMap)["f16sampler2DRect"] = F16SAMPLER2DRECT;
+ (*KeywordMap)["f16samplerCube"] = F16SAMPLERCUBE;
+ (*KeywordMap)["f16sampler1DArray"] = F16SAMPLER1DARRAY;
+ (*KeywordMap)["f16sampler2DArray"] = F16SAMPLER2DARRAY;
+ (*KeywordMap)["f16samplerCubeArray"] = F16SAMPLERCUBEARRAY;
+ (*KeywordMap)["f16samplerBuffer"] = F16SAMPLERBUFFER;
+ (*KeywordMap)["f16sampler2DMS"] = F16SAMPLER2DMS;
+ (*KeywordMap)["f16sampler2DMSArray"] = F16SAMPLER2DMSARRAY;
+ (*KeywordMap)["f16sampler1DShadow"] = F16SAMPLER1DSHADOW;
+ (*KeywordMap)["f16sampler2DShadow"] = F16SAMPLER2DSHADOW;
+ (*KeywordMap)["f16sampler2DRectShadow"] = F16SAMPLER2DRECTSHADOW;
+ (*KeywordMap)["f16samplerCubeShadow"] = F16SAMPLERCUBESHADOW;
+ (*KeywordMap)["f16sampler1DArrayShadow"] = F16SAMPLER1DARRAYSHADOW;
+ (*KeywordMap)["f16sampler2DArrayShadow"] = F16SAMPLER2DARRAYSHADOW;
+ (*KeywordMap)["f16samplerCubeArrayShadow"] = F16SAMPLERCUBEARRAYSHADOW;
+
+ (*KeywordMap)["f16image1D"] = F16IMAGE1D;
+ (*KeywordMap)["f16image2D"] = F16IMAGE2D;
+ (*KeywordMap)["f16image3D"] = F16IMAGE3D;
+ (*KeywordMap)["f16image2DRect"] = F16IMAGE2DRECT;
+ (*KeywordMap)["f16imageCube"] = F16IMAGECUBE;
+ (*KeywordMap)["f16image1DArray"] = F16IMAGE1DARRAY;
+ (*KeywordMap)["f16image2DArray"] = F16IMAGE2DARRAY;
+ (*KeywordMap)["f16imageCubeArray"] = F16IMAGECUBEARRAY;
+ (*KeywordMap)["f16imageBuffer"] = F16IMAGEBUFFER;
+ (*KeywordMap)["f16image2DMS"] = F16IMAGE2DMS;
+ (*KeywordMap)["f16image2DMSArray"] = F16IMAGE2DMSARRAY;
+
+ (*KeywordMap)["f16texture1D"] = F16TEXTURE1D;
+ (*KeywordMap)["f16texture2D"] = F16TEXTURE2D;
+ (*KeywordMap)["f16texture3D"] = F16TEXTURE3D;
+ (*KeywordMap)["f16texture2DRect"] = F16TEXTURE2DRECT;
+ (*KeywordMap)["f16textureCube"] = F16TEXTURECUBE;
+ (*KeywordMap)["f16texture1DArray"] = F16TEXTURE1DARRAY;
+ (*KeywordMap)["f16texture2DArray"] = F16TEXTURE2DARRAY;
+ (*KeywordMap)["f16textureCubeArray"] = F16TEXTURECUBEARRAY;
+ (*KeywordMap)["f16textureBuffer"] = F16TEXTUREBUFFER;
+ (*KeywordMap)["f16texture2DMS"] = F16TEXTURE2DMS;
+ (*KeywordMap)["f16texture2DMSArray"] = F16TEXTURE2DMSARRAY;
+
+ (*KeywordMap)["f16subpassInput"] = F16SUBPASSINPUT;
+ (*KeywordMap)["f16subpassInputMS"] = F16SUBPASSINPUTMS;
+#endif
+
+ (*KeywordMap)["noperspective"] = NOPERSPECTIVE;
+ (*KeywordMap)["smooth"] = SMOOTH;
+ (*KeywordMap)["flat"] = FLAT;
+#ifdef AMD_EXTENSIONS
+ (*KeywordMap)["__explicitInterpAMD"] = EXPLICITINTERPAMD;
+#endif
+ (*KeywordMap)["centroid"] = CENTROID;
+#ifdef NV_EXTENSIONS
+ (*KeywordMap)["pervertexNV"] = PERVERTEXNV;
+#endif
+ (*KeywordMap)["precise"] = PRECISE;
+ (*KeywordMap)["invariant"] = INVARIANT;
+ (*KeywordMap)["packed"] = PACKED;
+ (*KeywordMap)["resource"] = RESOURCE;
+ (*KeywordMap)["superp"] = SUPERP;
+
+#ifdef NV_EXTENSIONS
+ (*KeywordMap)["rayPayloadNV"] = PAYLOADNV;
+ (*KeywordMap)["rayPayloadInNV"] = PAYLOADINNV;
+ (*KeywordMap)["hitAttributeNV"] = HITATTRNV;
+ (*KeywordMap)["callableDataNV"] = CALLDATANV;
+ (*KeywordMap)["callableDataInNV"] = CALLDATAINNV;
+ (*KeywordMap)["accelerationStructureNV"] = ACCSTRUCTNV;
+ (*KeywordMap)["perprimitiveNV"] = PERPRIMITIVENV;
+ (*KeywordMap)["perviewNV"] = PERVIEWNV;
+ (*KeywordMap)["taskNV"] = PERTASKNV;
+#endif
+
+ (*KeywordMap)["fcoopmatNV"] = FCOOPMATNV;
+
+ ReservedSet = new std::unordered_set<const char*, str_hash, str_eq>;
+
+ ReservedSet->insert("common");
+ ReservedSet->insert("partition");
+ ReservedSet->insert("active");
+ ReservedSet->insert("asm");
+ ReservedSet->insert("class");
+ ReservedSet->insert("union");
+ ReservedSet->insert("enum");
+ ReservedSet->insert("typedef");
+ ReservedSet->insert("template");
+ ReservedSet->insert("this");
+ ReservedSet->insert("goto");
+ ReservedSet->insert("inline");
+ ReservedSet->insert("noinline");
+ ReservedSet->insert("public");
+ ReservedSet->insert("static");
+ ReservedSet->insert("extern");
+ ReservedSet->insert("external");
+ ReservedSet->insert("interface");
+ ReservedSet->insert("long");
+ ReservedSet->insert("short");
+ ReservedSet->insert("half");
+ ReservedSet->insert("fixed");
+ ReservedSet->insert("unsigned");
+ ReservedSet->insert("input");
+ ReservedSet->insert("output");
+ ReservedSet->insert("hvec2");
+ ReservedSet->insert("hvec3");
+ ReservedSet->insert("hvec4");
+ ReservedSet->insert("fvec2");
+ ReservedSet->insert("fvec3");
+ ReservedSet->insert("fvec4");
+ ReservedSet->insert("sampler3DRect");
+ ReservedSet->insert("filter");
+ ReservedSet->insert("sizeof");
+ ReservedSet->insert("cast");
+ ReservedSet->insert("namespace");
+ ReservedSet->insert("using");
+}
+
+void TScanContext::deleteKeywordMap()
+{
+ delete KeywordMap;
+ KeywordMap = nullptr;
+ delete ReservedSet;
+ ReservedSet = nullptr;
+}
+
+// Called by yylex to get the next token.
+// Returning 0 implies end of input.
+int TScanContext::tokenize(TPpContext* pp, TParserToken& token)
+{
+ do {
+ parserToken = &token;
+ TPpToken ppToken;
+ int token = pp->tokenize(ppToken);
+ if (token == EndOfInput)
+ return 0;
+
+ tokenText = ppToken.name;
+ loc = ppToken.loc;
+ parserToken->sType.lex.loc = loc;
+ switch (token) {
+ case ';': afterType = false; afterBuffer = false; return SEMICOLON;
+ case ',': afterType = false; return COMMA;
+ case ':': return COLON;
+ case '=': afterType = false; return EQUAL;
+ case '(': afterType = false; return LEFT_PAREN;
+ case ')': afterType = false; return RIGHT_PAREN;
+ case '.': field = true; return DOT;
+ case '!': return BANG;
+ case '-': return DASH;
+ case '~': return TILDE;
+ case '+': return PLUS;
+ case '*': return STAR;
+ case '/': return SLASH;
+ case '%': return PERCENT;
+ case '<': return LEFT_ANGLE;
+ case '>': return RIGHT_ANGLE;
+ case '|': return VERTICAL_BAR;
+ case '^': return CARET;
+ case '&': return AMPERSAND;
+ case '?': return QUESTION;
+ case '[': return LEFT_BRACKET;
+ case ']': return RIGHT_BRACKET;
+ case '{': afterStruct = false; afterBuffer = false; return LEFT_BRACE;
+ case '}': return RIGHT_BRACE;
+ case '\\':
+ parseContext.error(loc, "illegal use of escape character", "\\", "");
+ break;
+
+ case PPAtomAddAssign: return ADD_ASSIGN;
+ case PPAtomSubAssign: return SUB_ASSIGN;
+ case PPAtomMulAssign: return MUL_ASSIGN;
+ case PPAtomDivAssign: return DIV_ASSIGN;
+ case PPAtomModAssign: return MOD_ASSIGN;
+
+ case PpAtomRight: return RIGHT_OP;
+ case PpAtomLeft: return LEFT_OP;
+
+ case PpAtomRightAssign: return RIGHT_ASSIGN;
+ case PpAtomLeftAssign: return LEFT_ASSIGN;
+ case PpAtomAndAssign: return AND_ASSIGN;
+ case PpAtomOrAssign: return OR_ASSIGN;
+ case PpAtomXorAssign: return XOR_ASSIGN;
+
+ case PpAtomAnd: return AND_OP;
+ case PpAtomOr: return OR_OP;
+ case PpAtomXor: return XOR_OP;
+
+ case PpAtomEQ: return EQ_OP;
+ case PpAtomGE: return GE_OP;
+ case PpAtomNE: return NE_OP;
+ case PpAtomLE: return LE_OP;
+
+ case PpAtomDecrement: return DEC_OP;
+ case PpAtomIncrement: return INC_OP;
+
+ case PpAtomColonColon:
+ parseContext.error(loc, "not supported", "::", "");
+ break;
+
+ case PpAtomConstInt: parserToken->sType.lex.i = ppToken.ival; return INTCONSTANT;
+ case PpAtomConstUint: parserToken->sType.lex.i = ppToken.ival; return UINTCONSTANT;
+ case PpAtomConstInt16: parserToken->sType.lex.i = ppToken.ival; return INT16CONSTANT;
+ case PpAtomConstUint16: parserToken->sType.lex.i = ppToken.ival; return UINT16CONSTANT;
+ case PpAtomConstInt64: parserToken->sType.lex.i64 = ppToken.i64val; return INT64CONSTANT;
+ case PpAtomConstUint64: parserToken->sType.lex.i64 = ppToken.i64val; return UINT64CONSTANT;
+ case PpAtomConstFloat: parserToken->sType.lex.d = ppToken.dval; return FLOATCONSTANT;
+ case PpAtomConstDouble: parserToken->sType.lex.d = ppToken.dval; return DOUBLECONSTANT;
+ case PpAtomConstFloat16: parserToken->sType.lex.d = ppToken.dval; return FLOAT16CONSTANT;
+ case PpAtomIdentifier:
+ {
+ int token = tokenizeIdentifier();
+ field = false;
+ return token;
+ }
+
+ case EndOfInput: return 0;
+
+ default:
+ char buf[2];
+ buf[0] = (char)token;
+ buf[1] = 0;
+ parseContext.error(loc, "unexpected token", buf, "");
+ break;
+ }
+ } while (true);
+}
+
+int TScanContext::tokenizeIdentifier()
+{
+ if (ReservedSet->find(tokenText) != ReservedSet->end())
+ return reservedWord();
+
+ auto it = KeywordMap->find(tokenText);
+ if (it == KeywordMap->end()) {
+ // Should have an identifier of some sort
+ return identifierOrType();
+ }
+ keyword = it->second;
+
+ switch (keyword) {
+ case CONST:
+ case UNIFORM:
+ case IN:
+ case OUT:
+ case INOUT:
+ case BREAK:
+ case CONTINUE:
+ case DO:
+ case FOR:
+ case WHILE:
+ case IF:
+ case ELSE:
+ case DISCARD:
+ case RETURN:
+ case CASE:
+ return keyword;
+
+ case STRUCT:
+ afterStruct = true;
+ return keyword;
+
+ case NONUNIFORM:
+ if (parseContext.extensionTurnedOn(E_GL_EXT_nonuniform_qualifier))
+ return keyword;
+ else
+ return identifierOrType();
+
+ case SWITCH:
+ case DEFAULT:
+ if ((parseContext.profile == EEsProfile && parseContext.version < 300) ||
+ (parseContext.profile != EEsProfile && parseContext.version < 130))
+ reservedWord();
+ return keyword;
+
+ case VOID:
+ case BOOL:
+ case FLOAT:
+ case INT:
+ case BVEC2:
+ case BVEC3:
+ case BVEC4:
+ case VEC2:
+ case VEC3:
+ case VEC4:
+ case IVEC2:
+ case IVEC3:
+ case IVEC4:
+ case MAT2:
+ case MAT3:
+ case MAT4:
+ case SAMPLER2D:
+ case SAMPLERCUBE:
+ afterType = true;
+ return keyword;
+
+ case BOOLCONSTANT:
+ if (strcmp("true", tokenText) == 0)
+ parserToken->sType.lex.b = true;
+ else
+ parserToken->sType.lex.b = false;
+ return keyword;
+
+ case ATTRIBUTE:
+ case VARYING:
+ if (parseContext.profile == EEsProfile && parseContext.version >= 300)
+ reservedWord();
+ return keyword;
+
+ case BUFFER:
+ afterBuffer = true;
+ if ((parseContext.profile == EEsProfile && parseContext.version < 310) ||
+ (parseContext.profile != EEsProfile && parseContext.version < 430))
+ return identifierOrType();
+ return keyword;
+
+#ifdef NV_EXTENSIONS
+ case PAYLOADNV:
+ case PAYLOADINNV:
+ case HITATTRNV:
+ case CALLDATANV:
+ case CALLDATAINNV:
+ case ACCSTRUCTNV:
+ if (parseContext.symbolTable.atBuiltInLevel() ||
+ (parseContext.profile != EEsProfile && parseContext.version >= 460
+ && parseContext.extensionTurnedOn(E_GL_NV_ray_tracing)))
+ return keyword;
+ return identifierOrType();
+#endif
+
+ case ATOMIC_UINT:
+ if ((parseContext.profile == EEsProfile && parseContext.version >= 310) ||
+ parseContext.extensionTurnedOn(E_GL_ARB_shader_atomic_counters))
+ return keyword;
+ return es30ReservedFromGLSL(420);
+
+ case COHERENT:
+ case DEVICECOHERENT:
+ case QUEUEFAMILYCOHERENT:
+ case WORKGROUPCOHERENT:
+ case SUBGROUPCOHERENT:
+ case NONPRIVATE:
+ case RESTRICT:
+ case READONLY:
+ case WRITEONLY:
+ if (parseContext.profile == EEsProfile && parseContext.version >= 310)
+ return keyword;
+ return es30ReservedFromGLSL(parseContext.extensionTurnedOn(E_GL_ARB_shader_image_load_store) ? 130 : 420);
+
+ case VOLATILE:
+ if (parseContext.profile == EEsProfile && parseContext.version >= 310)
+ return keyword;
+ if (! parseContext.symbolTable.atBuiltInLevel() && (parseContext.profile == EEsProfile ||
+ (parseContext.version < 420 && ! parseContext.extensionTurnedOn(E_GL_ARB_shader_image_load_store))))
+ reservedWord();
+ return keyword;
+
+ case LAYOUT:
+ {
+ const int numLayoutExts = 2;
+ const char* layoutExts[numLayoutExts] = { E_GL_ARB_shading_language_420pack,
+ E_GL_ARB_explicit_attrib_location };
+ if ((parseContext.profile == EEsProfile && parseContext.version < 300) ||
+ (parseContext.profile != EEsProfile && parseContext.version < 140 &&
+ ! parseContext.extensionsTurnedOn(numLayoutExts, layoutExts)))
+ return identifierOrType();
+ return keyword;
+ }
+ case SHARED:
+ if ((parseContext.profile == EEsProfile && parseContext.version < 300) ||
+ (parseContext.profile != EEsProfile && parseContext.version < 140))
+ return identifierOrType();
+ return keyword;
+
+ case PATCH:
+ if (parseContext.symbolTable.atBuiltInLevel() ||
+ (parseContext.profile == EEsProfile &&
+ (parseContext.version >= 320 ||
+ parseContext.extensionsTurnedOn(Num_AEP_tessellation_shader, AEP_tessellation_shader))) ||
+ (parseContext.profile != EEsProfile && parseContext.extensionTurnedOn(E_GL_ARB_tessellation_shader)))
+ return keyword;
+
+ return es30ReservedFromGLSL(400);
+
+ case SAMPLE:
+ if ((parseContext.profile == EEsProfile && parseContext.version >= 320) ||
+ parseContext.extensionsTurnedOn(1, &E_GL_OES_shader_multisample_interpolation))
+ return keyword;
+ return es30ReservedFromGLSL(400);
+
+ case SUBROUTINE:
+ return es30ReservedFromGLSL(400);
+
+ case HIGH_PRECISION:
+ case MEDIUM_PRECISION:
+ case LOW_PRECISION:
+ case PRECISION:
+ return precisionKeyword();
+
+ case MAT2X2:
+ case MAT2X3:
+ case MAT2X4:
+ case MAT3X2:
+ case MAT3X3:
+ case MAT3X4:
+ case MAT4X2:
+ case MAT4X3:
+ case MAT4X4:
+ return matNxM();
+
+ case DMAT2:
+ case DMAT3:
+ case DMAT4:
+ case DMAT2X2:
+ case DMAT2X3:
+ case DMAT2X4:
+ case DMAT3X2:
+ case DMAT3X3:
+ case DMAT3X4:
+ case DMAT4X2:
+ case DMAT4X3:
+ case DMAT4X4:
+ return dMat();
+
+ case IMAGE1D:
+ case IIMAGE1D:
+ case UIMAGE1D:
+ case IMAGE1DARRAY:
+ case IIMAGE1DARRAY:
+ case UIMAGE1DARRAY:
+ case IMAGE2DRECT:
+ case IIMAGE2DRECT:
+ case UIMAGE2DRECT:
+ afterType = true;
+ return firstGenerationImage(false);
+
+ case IMAGEBUFFER:
+ case IIMAGEBUFFER:
+ case UIMAGEBUFFER:
+ afterType = true;
+ if ((parseContext.profile == EEsProfile && parseContext.version >= 320) ||
+ parseContext.extensionsTurnedOn(Num_AEP_texture_buffer, AEP_texture_buffer))
+ return keyword;
+ return firstGenerationImage(false);
+
+ case IMAGE2D:
+ case IIMAGE2D:
+ case UIMAGE2D:
+ case IMAGE3D:
+ case IIMAGE3D:
+ case UIMAGE3D:
+ case IMAGECUBE:
+ case IIMAGECUBE:
+ case UIMAGECUBE:
+ case IMAGE2DARRAY:
+ case IIMAGE2DARRAY:
+ case UIMAGE2DARRAY:
+ afterType = true;
+ return firstGenerationImage(true);
+
+ case IMAGECUBEARRAY:
+ case IIMAGECUBEARRAY:
+ case UIMAGECUBEARRAY:
+ afterType = true;
+ if ((parseContext.profile == EEsProfile && parseContext.version >= 320) ||
+ parseContext.extensionsTurnedOn(Num_AEP_texture_cube_map_array, AEP_texture_cube_map_array))
+ return keyword;
+ return secondGenerationImage();
+
+ case IMAGE2DMS:
+ case IIMAGE2DMS:
+ case UIMAGE2DMS:
+ case IMAGE2DMSARRAY:
+ case IIMAGE2DMSARRAY:
+ case UIMAGE2DMSARRAY:
+ afterType = true;
+ return secondGenerationImage();
+
+ case DOUBLE:
+ case DVEC2:
+ case DVEC3:
+ case DVEC4:
+ afterType = true;
+ if (parseContext.profile == EEsProfile || parseContext.version < 400)
+ reservedWord();
+ return keyword;
+
+ case INT64_T:
+ case UINT64_T:
+ case I64VEC2:
+ case I64VEC3:
+ case I64VEC4:
+ case U64VEC2:
+ case U64VEC3:
+ case U64VEC4:
+ afterType = true;
+ if (parseContext.symbolTable.atBuiltInLevel() ||
+ (parseContext.profile != EEsProfile && parseContext.version >= 450 &&
+ (parseContext.extensionTurnedOn(E_GL_ARB_gpu_shader_int64) ||
+ parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) ||
+ parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int64))))
+ return keyword;
+ return identifierOrType();
+
+ case INT8_T:
+ case UINT8_T:
+ case I8VEC2:
+ case I8VEC3:
+ case I8VEC4:
+ case U8VEC2:
+ case U8VEC3:
+ case U8VEC4:
+ afterType = true;
+ if (parseContext.symbolTable.atBuiltInLevel() ||
+ ((parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) ||
+ parseContext.extensionTurnedOn(E_GL_EXT_shader_8bit_storage) ||
+ parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int8)) &&
+ parseContext.profile != EEsProfile && parseContext.version >= 450))
+ return keyword;
+ return identifierOrType();
+
+ case INT16_T:
+ case UINT16_T:
+ case I16VEC2:
+ case I16VEC3:
+ case I16VEC4:
+ case U16VEC2:
+ case U16VEC3:
+ case U16VEC4:
+ afterType = true;
+ if (parseContext.symbolTable.atBuiltInLevel() ||
+ (parseContext.profile != EEsProfile && parseContext.version >= 450 &&
+ (
+#ifdef AMD_EXTENSIONS
+ parseContext.extensionTurnedOn(E_GL_AMD_gpu_shader_int16) ||
+#endif
+ parseContext.extensionTurnedOn(E_GL_EXT_shader_16bit_storage) ||
+ parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) ||
+ parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int16))))
+ return keyword;
+ return identifierOrType();
+ case INT32_T:
+ case UINT32_T:
+ case I32VEC2:
+ case I32VEC3:
+ case I32VEC4:
+ case U32VEC2:
+ case U32VEC3:
+ case U32VEC4:
+ afterType = true;
+ if (parseContext.symbolTable.atBuiltInLevel() ||
+ ((parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) ||
+ parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int32)) &&
+ parseContext.profile != EEsProfile && parseContext.version >= 450))
+ return keyword;
+ return identifierOrType();
+ case FLOAT32_T:
+ case F32VEC2:
+ case F32VEC3:
+ case F32VEC4:
+ case F32MAT2:
+ case F32MAT3:
+ case F32MAT4:
+ case F32MAT2X2:
+ case F32MAT2X3:
+ case F32MAT2X4:
+ case F32MAT3X2:
+ case F32MAT3X3:
+ case F32MAT3X4:
+ case F32MAT4X2:
+ case F32MAT4X3:
+ case F32MAT4X4:
+ afterType = true;
+ if (parseContext.symbolTable.atBuiltInLevel() ||
+ ((parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) ||
+ parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float32)) &&
+ parseContext.profile != EEsProfile && parseContext.version >= 450))
+ return keyword;
+ return identifierOrType();
+
+ case FLOAT64_T:
+ case F64VEC2:
+ case F64VEC3:
+ case F64VEC4:
+ case F64MAT2:
+ case F64MAT3:
+ case F64MAT4:
+ case F64MAT2X2:
+ case F64MAT2X3:
+ case F64MAT2X4:
+ case F64MAT3X2:
+ case F64MAT3X3:
+ case F64MAT3X4:
+ case F64MAT4X2:
+ case F64MAT4X3:
+ case F64MAT4X4:
+ afterType = true;
+ if (parseContext.symbolTable.atBuiltInLevel() ||
+ ((parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) ||
+ parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float64)) &&
+ parseContext.profile != EEsProfile && parseContext.version >= 450))
+ return keyword;
+ return identifierOrType();
+
+ case FLOAT16_T:
+ case F16VEC2:
+ case F16VEC3:
+ case F16VEC4:
+ afterType = true;
+ if (parseContext.symbolTable.atBuiltInLevel() ||
+ (parseContext.profile != EEsProfile && parseContext.version >= 450 &&
+ (
+#ifdef AMD_EXTENSIONS
+ parseContext.extensionTurnedOn(E_GL_AMD_gpu_shader_half_float) ||
+#endif
+ parseContext.extensionTurnedOn(E_GL_EXT_shader_16bit_storage) ||
+ parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) ||
+ parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float16))))
+ return keyword;
+
+ return identifierOrType();
+
+ case F16MAT2:
+ case F16MAT3:
+ case F16MAT4:
+ case F16MAT2X2:
+ case F16MAT2X3:
+ case F16MAT2X4:
+ case F16MAT3X2:
+ case F16MAT3X3:
+ case F16MAT3X4:
+ case F16MAT4X2:
+ case F16MAT4X3:
+ case F16MAT4X4:
+ afterType = true;
+ if (parseContext.symbolTable.atBuiltInLevel() ||
+ (parseContext.profile != EEsProfile && parseContext.version >= 450 &&
+ (
+#ifdef AMD_EXTENSIONS
+ parseContext.extensionTurnedOn(E_GL_AMD_gpu_shader_half_float) ||
+#endif
+ parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) ||
+ parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float16))))
+ return keyword;
+
+ return identifierOrType();
+
+ case SAMPLERCUBEARRAY:
+ case SAMPLERCUBEARRAYSHADOW:
+ case ISAMPLERCUBEARRAY:
+ case USAMPLERCUBEARRAY:
+ afterType = true;
+ if ((parseContext.profile == EEsProfile && parseContext.version >= 320) ||
+ parseContext.extensionsTurnedOn(Num_AEP_texture_cube_map_array, AEP_texture_cube_map_array))
+ return keyword;
+ if (parseContext.profile == EEsProfile || (parseContext.version < 400 && ! parseContext.extensionTurnedOn(E_GL_ARB_texture_cube_map_array)))
+ reservedWord();
+ return keyword;
+
+ case ISAMPLER1D:
+ case ISAMPLER1DARRAY:
+ case SAMPLER1DARRAYSHADOW:
+ case USAMPLER1D:
+ case USAMPLER1DARRAY:
+ afterType = true;
+ return es30ReservedFromGLSL(130);
+
+ case UINT:
+ case UVEC2:
+ case UVEC3:
+ case UVEC4:
+ case SAMPLERCUBESHADOW:
+ case SAMPLER2DARRAY:
+ case SAMPLER2DARRAYSHADOW:
+ case ISAMPLER2D:
+ case ISAMPLER3D:
+ case ISAMPLERCUBE:
+ case ISAMPLER2DARRAY:
+ case USAMPLER2D:
+ case USAMPLER3D:
+ case USAMPLERCUBE:
+ case USAMPLER2DARRAY:
+ afterType = true;
+ return nonreservedKeyword(300, 130);
+
+ case ISAMPLER2DRECT:
+ case USAMPLER2DRECT:
+ afterType = true;
+ return es30ReservedFromGLSL(140);
+
+ case SAMPLERBUFFER:
+ afterType = true;
+ if ((parseContext.profile == EEsProfile && parseContext.version >= 320) ||
+ parseContext.extensionsTurnedOn(Num_AEP_texture_buffer, AEP_texture_buffer))
+ return keyword;
+ return es30ReservedFromGLSL(130);
+
+ case ISAMPLERBUFFER:
+ case USAMPLERBUFFER:
+ afterType = true;
+ if ((parseContext.profile == EEsProfile && parseContext.version >= 320) ||
+ parseContext.extensionsTurnedOn(Num_AEP_texture_buffer, AEP_texture_buffer))
+ return keyword;
+ return es30ReservedFromGLSL(140);
+
+ case SAMPLER2DMS:
+ case ISAMPLER2DMS:
+ case USAMPLER2DMS:
+ afterType = true;
+ if (parseContext.profile == EEsProfile && parseContext.version >= 310)
+ return keyword;
+ return es30ReservedFromGLSL(150);
+
+ case SAMPLER2DMSARRAY:
+ case ISAMPLER2DMSARRAY:
+ case USAMPLER2DMSARRAY:
+ afterType = true;
+ if ((parseContext.profile == EEsProfile && parseContext.version >= 320) ||
+ parseContext.extensionsTurnedOn(1, &E_GL_OES_texture_storage_multisample_2d_array))
+ return keyword;
+ return es30ReservedFromGLSL(150);
+
+ case SAMPLER1D:
+ case SAMPLER1DSHADOW:
+ afterType = true;
+ if (parseContext.profile == EEsProfile)
+ reservedWord();
+ return keyword;
+
+ case SAMPLER3D:
+ afterType = true;
+ if (parseContext.profile == EEsProfile && parseContext.version < 300) {
+ if (!parseContext.extensionTurnedOn(E_GL_OES_texture_3D))
+ reservedWord();
+ }
+ return keyword;
+
+ case SAMPLER2DSHADOW:
+ afterType = true;
+ if (parseContext.profile == EEsProfile && parseContext.version < 300) {
+ if (!parseContext.extensionTurnedOn(E_GL_EXT_shadow_samplers))
+ reservedWord();
+ }
+ return keyword;
+
+ case SAMPLER2DRECT:
+ case SAMPLER2DRECTSHADOW:
+ afterType = true;
+ if (parseContext.profile == EEsProfile)
+ reservedWord();
+ else if (parseContext.version < 140 && ! parseContext.symbolTable.atBuiltInLevel() && ! parseContext.extensionTurnedOn(E_GL_ARB_texture_rectangle)) {
+ if (parseContext.relaxedErrors())
+ parseContext.requireExtensions(loc, 1, &E_GL_ARB_texture_rectangle, "texture-rectangle sampler keyword");
+ else
+ reservedWord();
+ }
+ return keyword;
+
+ case SAMPLER1DARRAY:
+ afterType = true;
+ if (parseContext.profile == EEsProfile && parseContext.version == 300)
+ reservedWord();
+ else if ((parseContext.profile == EEsProfile && parseContext.version < 300) ||
+ (parseContext.profile != EEsProfile && parseContext.version < 130))
+ return identifierOrType();
+ return keyword;
+
+ case SAMPLEREXTERNALOES:
+ afterType = true;
+ if (parseContext.symbolTable.atBuiltInLevel() ||
+ parseContext.extensionTurnedOn(E_GL_OES_EGL_image_external) ||
+ parseContext.extensionTurnedOn(E_GL_OES_EGL_image_external_essl3))
+ return keyword;
+ return identifierOrType();
+
+ case SAMPLEREXTERNAL2DY2YEXT:
+ afterType = true;
+ if (parseContext.symbolTable.atBuiltInLevel() ||
+ parseContext.extensionTurnedOn(E_GL_EXT_YUV_target))
+ return keyword;
+ return identifierOrType();
+
+ case TEXTURE2D:
+ case TEXTURECUBE:
+ case TEXTURECUBEARRAY:
+ case ITEXTURECUBEARRAY:
+ case UTEXTURECUBEARRAY:
+ case ITEXTURE1DARRAY:
+ case UTEXTURE1D:
+ case ITEXTURE1D:
+ case UTEXTURE1DARRAY:
+ case TEXTUREBUFFER:
+ case TEXTURE2DARRAY:
+ case ITEXTURE2D:
+ case ITEXTURE3D:
+ case ITEXTURECUBE:
+ case ITEXTURE2DARRAY:
+ case UTEXTURE2D:
+ case UTEXTURE3D:
+ case UTEXTURECUBE:
+ case UTEXTURE2DARRAY:
+ case ITEXTURE2DRECT:
+ case UTEXTURE2DRECT:
+ case ITEXTUREBUFFER:
+ case UTEXTUREBUFFER:
+ case TEXTURE2DMS:
+ case ITEXTURE2DMS:
+ case UTEXTURE2DMS:
+ case TEXTURE2DMSARRAY:
+ case ITEXTURE2DMSARRAY:
+ case UTEXTURE2DMSARRAY:
+ case TEXTURE1D:
+ case TEXTURE3D:
+ case TEXTURE2DRECT:
+ case TEXTURE1DARRAY:
+ case SAMPLER:
+ case SAMPLERSHADOW:
+ if (parseContext.spvVersion.vulkan > 0)
+ return keyword;
+ else
+ return identifierOrType();
+
+ case SUBPASSINPUT:
+ case SUBPASSINPUTMS:
+ case ISUBPASSINPUT:
+ case ISUBPASSINPUTMS:
+ case USUBPASSINPUT:
+ case USUBPASSINPUTMS:
+ if (parseContext.spvVersion.vulkan > 0)
+ return keyword;
+ else
+ return identifierOrType();
+
+#ifdef AMD_EXTENSIONS
+ case F16SAMPLER1D:
+ case F16SAMPLER2D:
+ case F16SAMPLER3D:
+ case F16SAMPLER2DRECT:
+ case F16SAMPLERCUBE:
+ case F16SAMPLER1DARRAY:
+ case F16SAMPLER2DARRAY:
+ case F16SAMPLERCUBEARRAY:
+ case F16SAMPLERBUFFER:
+ case F16SAMPLER2DMS:
+ case F16SAMPLER2DMSARRAY:
+ case F16SAMPLER1DSHADOW:
+ case F16SAMPLER2DSHADOW:
+ case F16SAMPLER1DARRAYSHADOW:
+ case F16SAMPLER2DARRAYSHADOW:
+ case F16SAMPLER2DRECTSHADOW:
+ case F16SAMPLERCUBESHADOW:
+ case F16SAMPLERCUBEARRAYSHADOW:
+
+ case F16IMAGE1D:
+ case F16IMAGE2D:
+ case F16IMAGE3D:
+ case F16IMAGE2DRECT:
+ case F16IMAGECUBE:
+ case F16IMAGE1DARRAY:
+ case F16IMAGE2DARRAY:
+ case F16IMAGECUBEARRAY:
+ case F16IMAGEBUFFER:
+ case F16IMAGE2DMS:
+ case F16IMAGE2DMSARRAY:
+
+ case F16TEXTURE1D:
+ case F16TEXTURE2D:
+ case F16TEXTURE3D:
+ case F16TEXTURE2DRECT:
+ case F16TEXTURECUBE:
+ case F16TEXTURE1DARRAY:
+ case F16TEXTURE2DARRAY:
+ case F16TEXTURECUBEARRAY:
+ case F16TEXTUREBUFFER:
+ case F16TEXTURE2DMS:
+ case F16TEXTURE2DMSARRAY:
+
+ case F16SUBPASSINPUT:
+ case F16SUBPASSINPUTMS:
+ afterType = true;
+ if (parseContext.symbolTable.atBuiltInLevel() ||
+ (parseContext.extensionTurnedOn(E_GL_AMD_gpu_shader_half_float_fetch) &&
+ parseContext.profile != EEsProfile && parseContext.version >= 450))
+ return keyword;
+ return identifierOrType();
+#endif
+
+ case NOPERSPECTIVE:
+#ifdef NV_EXTENSIONS
+ if (parseContext.profile == EEsProfile && parseContext.version >= 300 &&
+ parseContext.extensionTurnedOn(E_GL_NV_shader_noperspective_interpolation))
+ return keyword;
+#endif
+ return es30ReservedFromGLSL(130);
+
+ case SMOOTH:
+ if ((parseContext.profile == EEsProfile && parseContext.version < 300) ||
+ (parseContext.profile != EEsProfile && parseContext.version < 130))
+ return identifierOrType();
+ return keyword;
+
+#ifdef AMD_EXTENSIONS
+ case EXPLICITINTERPAMD:
+ if (parseContext.profile != EEsProfile && parseContext.version >= 450 &&
+ parseContext.extensionTurnedOn(E_GL_AMD_shader_explicit_vertex_parameter))
+ return keyword;
+ return identifierOrType();
+#endif
+
+#ifdef NV_EXTENSIONS
+ case PERVERTEXNV:
+ if (((parseContext.profile != EEsProfile && parseContext.version >= 450) ||
+ (parseContext.profile == EEsProfile && parseContext.version >= 320)) &&
+ parseContext.extensionTurnedOn(E_GL_NV_fragment_shader_barycentric))
+ return keyword;
+ return identifierOrType();
+#endif
+
+ case FLAT:
+ if (parseContext.profile == EEsProfile && parseContext.version < 300)
+ reservedWord();
+ else if (parseContext.profile != EEsProfile && parseContext.version < 130)
+ return identifierOrType();
+ return keyword;
+
+ case CENTROID:
+ if (parseContext.version < 120)
+ return identifierOrType();
+ return keyword;
+
+ case PRECISE:
+ if ((parseContext.profile == EEsProfile &&
+ (parseContext.version >= 320 || parseContext.extensionsTurnedOn(Num_AEP_gpu_shader5, AEP_gpu_shader5))) ||
+ (parseContext.profile != EEsProfile && parseContext.version >= 400))
+ return keyword;
+ if (parseContext.profile == EEsProfile && parseContext.version == 310) {
+ reservedWord();
+ return keyword;
+ }
+ return identifierOrType();
+
+ case INVARIANT:
+ if (parseContext.profile != EEsProfile && parseContext.version < 120)
+ return identifierOrType();
+ return keyword;
+
+ case PACKED:
+ if ((parseContext.profile == EEsProfile && parseContext.version < 300) ||
+ (parseContext.profile != EEsProfile && parseContext.version < 330))
+ return reservedWord();
+ return identifierOrType();
+
+ case RESOURCE:
+ {
+ bool reserved = (parseContext.profile == EEsProfile && parseContext.version >= 300) ||
+ (parseContext.profile != EEsProfile && parseContext.version >= 420);
+ return identifierOrReserved(reserved);
+ }
+ case SUPERP:
+ {
+ bool reserved = parseContext.profile == EEsProfile || parseContext.version >= 130;
+ return identifierOrReserved(reserved);
+ }
+
+#ifdef NV_EXTENSIONS
+ case PERPRIMITIVENV:
+ case PERVIEWNV:
+ case PERTASKNV:
+ if ((parseContext.profile != EEsProfile && parseContext.version >= 450) ||
+ (parseContext.profile == EEsProfile && parseContext.version >= 320) ||
+ parseContext.extensionTurnedOn(E_GL_NV_mesh_shader))
+ return keyword;
+ return identifierOrType();
+#endif
+
+ case FCOOPMATNV:
+ afterType = true;
+ if (parseContext.symbolTable.atBuiltInLevel() ||
+ parseContext.extensionTurnedOn(E_GL_NV_cooperative_matrix))
+ return keyword;
+ return identifierOrType();
+
+ default:
+ parseContext.infoSink.info.message(EPrefixInternalError, "Unknown glslang keyword", loc);
+ return 0;
+ }
+}
+
+int TScanContext::identifierOrType()
+{
+ parserToken->sType.lex.string = NewPoolTString(tokenText);
+ if (field)
+ return IDENTIFIER;
+
+ parserToken->sType.lex.symbol = parseContext.symbolTable.find(*parserToken->sType.lex.string);
+ if ((afterType == false && afterStruct == false) && parserToken->sType.lex.symbol != nullptr) {
+ if (const TVariable* variable = parserToken->sType.lex.symbol->getAsVariable()) {
+ if (variable->isUserType() &&
+ // treat redeclaration of forward-declared buffer/uniform reference as an identifier
+ !(variable->getType().getBasicType() == EbtReference && afterBuffer)) {
+ afterType = true;
+
+ return TYPE_NAME;
+ }
+ }
+ }
+
+ return IDENTIFIER;
+}
+
+// Give an error for use of a reserved symbol.
+// However, allow built-in declarations to use reserved words, to allow
+// extension support before the extension is enabled.
+int TScanContext::reservedWord()
+{
+ if (! parseContext.symbolTable.atBuiltInLevel())
+ parseContext.error(loc, "Reserved word.", tokenText, "", "");
+
+ return 0;
+}
+
+int TScanContext::identifierOrReserved(bool reserved)
+{
+ if (reserved) {
+ reservedWord();
+
+ return 0;
+ }
+
+ if (parseContext.forwardCompatible)
+ parseContext.warn(loc, "using future reserved keyword", tokenText, "");
+
+ return identifierOrType();
+}
+
+// For keywords that suddenly showed up on non-ES (not previously reserved)
+// but then got reserved by ES 3.0.
+int TScanContext::es30ReservedFromGLSL(int version)
+{
+ if (parseContext.symbolTable.atBuiltInLevel())
+ return keyword;
+
+ if ((parseContext.profile == EEsProfile && parseContext.version < 300) ||
+ (parseContext.profile != EEsProfile && parseContext.version < version)) {
+ if (parseContext.forwardCompatible)
+ parseContext.warn(loc, "future reserved word in ES 300 and keyword in GLSL", tokenText, "");
+
+ return identifierOrType();
+ } else if (parseContext.profile == EEsProfile && parseContext.version >= 300)
+ reservedWord();
+
+ return keyword;
+}
+
+// For a keyword that was never reserved, until it suddenly
+// showed up, both in an es version and a non-ES version.
+int TScanContext::nonreservedKeyword(int esVersion, int nonEsVersion)
+{
+ if ((parseContext.profile == EEsProfile && parseContext.version < esVersion) ||
+ (parseContext.profile != EEsProfile && parseContext.version < nonEsVersion)) {
+ if (parseContext.forwardCompatible)
+ parseContext.warn(loc, "using future keyword", tokenText, "");
+
+ return identifierOrType();
+ }
+
+ return keyword;
+}
+
+int TScanContext::precisionKeyword()
+{
+ if (parseContext.profile == EEsProfile || parseContext.version >= 130)
+ return keyword;
+
+ if (parseContext.forwardCompatible)
+ parseContext.warn(loc, "using ES precision qualifier keyword", tokenText, "");
+
+ return identifierOrType();
+}
+
+int TScanContext::matNxM()
+{
+ afterType = true;
+
+ if (parseContext.version > 110)
+ return keyword;
+
+ if (parseContext.forwardCompatible)
+ parseContext.warn(loc, "using future non-square matrix type keyword", tokenText, "");
+
+ return identifierOrType();
+}
+
+int TScanContext::dMat()
+{
+ afterType = true;
+
+ if (parseContext.profile == EEsProfile && parseContext.version >= 300) {
+ reservedWord();
+
+ return keyword;
+ }
+
+ if (parseContext.profile != EEsProfile && parseContext.version >= 400)
+ return keyword;
+
+ if (parseContext.forwardCompatible)
+ parseContext.warn(loc, "using future type keyword", tokenText, "");
+
+ return identifierOrType();
+}
+
+int TScanContext::firstGenerationImage(bool inEs310)
+{
+ if (parseContext.symbolTable.atBuiltInLevel() ||
+ (parseContext.profile != EEsProfile && (parseContext.version >= 420 ||
+ parseContext.extensionTurnedOn(E_GL_ARB_shader_image_load_store))) ||
+ (inEs310 && parseContext.profile == EEsProfile && parseContext.version >= 310))
+ return keyword;
+
+ if ((parseContext.profile == EEsProfile && parseContext.version >= 300) ||
+ (parseContext.profile != EEsProfile && parseContext.version >= 130)) {
+ reservedWord();
+
+ return keyword;
+ }
+
+ if (parseContext.forwardCompatible)
+ parseContext.warn(loc, "using future type keyword", tokenText, "");
+
+ return identifierOrType();
+}
+
+int TScanContext::secondGenerationImage()
+{
+ if (parseContext.profile == EEsProfile && parseContext.version >= 310) {
+ reservedWord();
+ return keyword;
+ }
+
+ if (parseContext.symbolTable.atBuiltInLevel() ||
+ (parseContext.profile != EEsProfile &&
+ (parseContext.version >= 420 || parseContext.extensionTurnedOn(E_GL_ARB_shader_image_load_store))))
+ return keyword;
+
+ if (parseContext.forwardCompatible)
+ parseContext.warn(loc, "using future type keyword", tokenText, "");
+
+ return identifierOrType();
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/Scan.h b/src/3rdparty/glslang/glslang/MachineIndependent/Scan.h
new file mode 100644
index 0000000..24b75cf
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/Scan.h
@@ -0,0 +1,276 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2013 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+#ifndef _GLSLANG_SCAN_INCLUDED_
+#define _GLSLANG_SCAN_INCLUDED_
+
+#include "Versions.h"
+
+namespace glslang {
+
+// Use a global end-of-input character, so no translation is needed across
+// layers of encapsulation. Characters are all 8 bit, and positive, so there is
+// no aliasing of character 255 onto -1, for example.
+const int EndOfInput = -1;
+
+//
+// A character scanner that seamlessly, on read-only strings, reads across an
+// array of strings without assuming null termination.
+//
+class TInputScanner {
+public:
+ TInputScanner(int n, const char* const s[], size_t L[], const char* const* names = nullptr,
+ int b = 0, int f = 0, bool single = false) :
+ numSources(n),
+ // up to this point, common usage is "char*", but now we need positive 8-bit characters
+ sources(reinterpret_cast<const unsigned char* const *>(s)),
+ lengths(L), currentSource(0), currentChar(0), stringBias(b), finale(f), singleLogical(single),
+ endOfFileReached(false)
+ {
+ loc = new TSourceLoc[numSources];
+ for (int i = 0; i < numSources; ++i) {
+ loc[i].init(i - stringBias);
+ }
+ if (names != nullptr) {
+ for (int i = 0; i < numSources; ++i)
+ loc[i].name = names[i] != nullptr ? NewPoolTString(names[i]) : nullptr;
+ }
+ loc[currentSource].line = 1;
+ logicalSourceLoc.init(1);
+ logicalSourceLoc.name = loc[0].name;
+ }
+
+ virtual ~TInputScanner()
+ {
+ delete [] loc;
+ }
+
+ // retrieve the next character and advance one character
+ int get()
+ {
+ int ret = peek();
+ if (ret == EndOfInput)
+ return ret;
+ ++loc[currentSource].column;
+ ++logicalSourceLoc.column;
+ if (ret == '\n') {
+ ++loc[currentSource].line;
+ ++logicalSourceLoc.line;
+ logicalSourceLoc.column = 0;
+ loc[currentSource].column = 0;
+ }
+ advance();
+
+ return ret;
+ }
+
+ // retrieve the next character, no advance
+ int peek()
+ {
+ if (currentSource >= numSources) {
+ endOfFileReached = true;
+ return EndOfInput;
+ }
+ // Make sure we do not read off the end of a string.
+ // N.B. Sources can have a length of 0.
+ int sourceToRead = currentSource;
+ size_t charToRead = currentChar;
+ while(charToRead >= lengths[sourceToRead]) {
+ charToRead = 0;
+ sourceToRead += 1;
+ if (sourceToRead >= numSources) {
+ return EndOfInput;
+ }
+ }
+
+ // Here, we care about making negative valued characters positive
+ return sources[sourceToRead][charToRead];
+ }
+
+ // go back one character
+ void unget()
+ {
+ // Do not roll back once we've reached the end of the file.
+ if (endOfFileReached)
+ return;
+
+ if (currentChar > 0) {
+ --currentChar;
+ --loc[currentSource].column;
+ --logicalSourceLoc.column;
+ if (loc[currentSource].column < 0) {
+ // We've moved back past a new line. Find the
+ // previous newline (or start of the file) to compute
+ // the column count on the now current line.
+ size_t chIndex = currentChar;
+ while (chIndex > 0) {
+ if (sources[currentSource][chIndex] == '\n') {
+ break;
+ }
+ --chIndex;
+ }
+ logicalSourceLoc.column = (int)(currentChar - chIndex);
+ loc[currentSource].column = (int)(currentChar - chIndex);
+ }
+ } else {
+ do {
+ --currentSource;
+ } while (currentSource > 0 && lengths[currentSource] == 0);
+ if (lengths[currentSource] == 0) {
+ // set to 0 if we've backed up to the start of an empty string
+ currentChar = 0;
+ } else
+ currentChar = lengths[currentSource] - 1;
+ }
+ if (peek() == '\n') {
+ --loc[currentSource].line;
+ --logicalSourceLoc.line;
+ }
+ }
+
+ // for #line override
+ void setLine(int newLine)
+ {
+ logicalSourceLoc.line = newLine;
+ loc[getLastValidSourceIndex()].line = newLine;
+ }
+
+ // for #line override in filename based parsing
+ void setFile(const char* filename)
+ {
+ TString* fn_tstr = NewPoolTString(filename);
+ logicalSourceLoc.name = fn_tstr;
+ loc[getLastValidSourceIndex()].name = fn_tstr;
+ }
+
+ void setFile(const char* filename, int i)
+ {
+ TString* fn_tstr = NewPoolTString(filename);
+ if (i == getLastValidSourceIndex()) {
+ logicalSourceLoc.name = fn_tstr;
+ }
+ loc[i].name = fn_tstr;
+ }
+
+ void setString(int newString)
+ {
+ logicalSourceLoc.string = newString;
+ loc[getLastValidSourceIndex()].string = newString;
+ logicalSourceLoc.name = nullptr;
+ loc[getLastValidSourceIndex()].name = nullptr;
+ }
+
+ // for #include content indentation
+ void setColumn(int col)
+ {
+ logicalSourceLoc.column = col;
+ loc[getLastValidSourceIndex()].column = col;
+ }
+
+ void setEndOfInput()
+ {
+ endOfFileReached = true;
+ currentSource = numSources;
+ }
+
+ bool atEndOfInput() const { return endOfFileReached; }
+
+ const TSourceLoc& getSourceLoc() const
+ {
+ if (singleLogical) {
+ return logicalSourceLoc;
+ } else {
+ return loc[std::max(0, std::min(currentSource, numSources - finale - 1))];
+ }
+ }
+ // Returns the index (starting from 0) of the most recent valid source string we are reading from.
+ int getLastValidSourceIndex() const { return std::min(currentSource, numSources - 1); }
+
+ void consumeWhiteSpace(bool& foundNonSpaceTab);
+ bool consumeComment();
+ void consumeWhitespaceComment(bool& foundNonSpaceTab);
+ bool scanVersion(int& version, EProfile& profile, bool& notFirstToken);
+
+protected:
+
+ // advance one character
+ void advance()
+ {
+ ++currentChar;
+ if (currentChar >= lengths[currentSource]) {
+ ++currentSource;
+ if (currentSource < numSources) {
+ loc[currentSource].string = loc[currentSource - 1].string + 1;
+ loc[currentSource].line = 1;
+ loc[currentSource].column = 0;
+ }
+ while (currentSource < numSources && lengths[currentSource] == 0) {
+ ++currentSource;
+ if (currentSource < numSources) {
+ loc[currentSource].string = loc[currentSource - 1].string + 1;
+ loc[currentSource].line = 1;
+ loc[currentSource].column = 0;
+ }
+ }
+ currentChar = 0;
+ }
+ }
+
+ int numSources; // number of strings in source
+ const unsigned char* const *sources; // array of strings; must be converted to positive values on use, to avoid aliasing with -1 as EndOfInput
+ const size_t *lengths; // length of each string
+ int currentSource;
+ size_t currentChar;
+
+ // This is for reporting what string/line an error occurred on, and can be overridden by #line.
+ // It remembers the last state of each source string as it is left for the next one, so unget()
+ // can restore that state.
+ TSourceLoc* loc; // an array
+
+ int stringBias; // the first string that is the user's string number 0
+ int finale; // number of internal strings after user's last string
+
+ TSourceLoc logicalSourceLoc;
+ bool singleLogical; // treats the strings as a single logical string.
+ // locations will be reported from the first string.
+
+ // Set to true once peek() returns EndOfFile, so that we won't roll back
+ // once we've reached EndOfFile.
+ bool endOfFileReached;
+};
+
+} // end namespace glslang
+
+#endif // _GLSLANG_SCAN_INCLUDED_
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/ScanContext.h b/src/3rdparty/glslang/glslang/MachineIndependent/ScanContext.h
new file mode 100644
index 0000000..74b2b3c
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/ScanContext.h
@@ -0,0 +1,93 @@
+//
+// Copyright (C) 2013 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+//
+// This holds context specific to the GLSL scanner, which
+// sits between the preprocessor scanner and parser.
+//
+
+#pragma once
+
+#include "ParseHelper.h"
+
+namespace glslang {
+
+class TPpContext;
+class TPpToken;
+class TParserToken;
+
+class TScanContext {
+public:
+ explicit TScanContext(TParseContextBase& pc) :
+ parseContext(pc),
+ afterType(false), afterStruct(false),
+ field(false), afterBuffer(false) { }
+ virtual ~TScanContext() { }
+
+ static void fillInKeywordMap();
+ static void deleteKeywordMap();
+
+ int tokenize(TPpContext*, TParserToken&);
+
+protected:
+ TScanContext(TScanContext&);
+ TScanContext& operator=(TScanContext&);
+
+ int tokenizeIdentifier();
+ int identifierOrType();
+ int reservedWord();
+ int identifierOrReserved(bool reserved);
+ int es30ReservedFromGLSL(int version);
+ int nonreservedKeyword(int esVersion, int nonEsVersion);
+ int precisionKeyword();
+ int matNxM();
+ int dMat();
+ int firstGenerationImage(bool inEs310);
+ int secondGenerationImage();
+
+ TParseContextBase& parseContext;
+ bool afterType; // true if we've recognized a type, so can only be looking for an identifier
+ bool afterStruct; // true if we've recognized the STRUCT keyword, so can only be looking for an identifier
+ bool field; // true if we're on a field, right after a '.'
+ bool afterBuffer; // true if we've recognized the BUFFER keyword
+ TSourceLoc loc;
+ TParserToken* parserToken;
+ TPpToken* ppToken;
+
+ const char* tokenText;
+ int keyword;
+};
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/ShaderLang.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/ShaderLang.cpp
new file mode 100644
index 0000000..adfe534
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/ShaderLang.cpp
@@ -0,0 +1,2041 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2013-2016 LunarG, Inc.
+// Copyright (C) 2015-2018 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+//
+// Implement the top-level of interface to the compiler/linker,
+// as defined in ShaderLang.h
+// This is the platform independent interface between an OGL driver
+// and the shading language compiler/linker.
+//
+#include <cstring>
+#include <iostream>
+#include <sstream>
+#include <memory>
+#include "SymbolTable.h"
+#include "ParseHelper.h"
+#include "Scan.h"
+#include "ScanContext.h"
+
+#ifdef ENABLE_HLSL
+#include "../../hlsl/hlslParseHelper.h"
+#include "../../hlsl/hlslParseables.h"
+#include "../../hlsl/hlslScanContext.h"
+#endif
+
+#include "../Include/ShHandle.h"
+#include "../../OGLCompilersDLL/InitializeDll.h"
+
+#include "preprocessor/PpContext.h"
+
+#define SH_EXPORTING
+#include "../Public/ShaderLang.h"
+#include "reflection.h"
+#include "iomapper.h"
+#include "Initialize.h"
+
+// TODO: this really shouldn't be here, it is only because of the trial addition
+// of printing pre-processed tokens, which requires knowing the string literal
+// token to print ", but none of that seems appropriate for this file.
+#include "preprocessor/PpTokens.h"
+
+namespace { // anonymous namespace for file-local functions and symbols
+
+// Total number of successful initializers of glslang: a refcount
+// Shared global; access should be protected by a global mutex/critical section.
+int NumberOfClients = 0;
+
+using namespace glslang;
+
+// Create a language specific version of parseables.
+TBuiltInParseables* CreateBuiltInParseables(TInfoSink& infoSink, EShSource source)
+{
+ switch (source) {
+ case EShSourceGlsl: return new TBuiltIns(); // GLSL builtIns
+#ifdef ENABLE_HLSL
+ case EShSourceHlsl: return new TBuiltInParseablesHlsl(); // HLSL intrinsics
+#endif
+
+ default:
+ infoSink.info.message(EPrefixInternalError, "Unable to determine source language");
+ return nullptr;
+ }
+}
+
+// Create a language specific version of a parse context.
+TParseContextBase* CreateParseContext(TSymbolTable& symbolTable, TIntermediate& intermediate,
+ int version, EProfile profile, EShSource source,
+ EShLanguage language, TInfoSink& infoSink,
+ SpvVersion spvVersion, bool forwardCompatible, EShMessages messages,
+ bool parsingBuiltIns, std::string sourceEntryPointName = "")
+{
+ switch (source) {
+ case EShSourceGlsl: {
+ if (sourceEntryPointName.size() == 0)
+ intermediate.setEntryPointName("main");
+ TString entryPoint = sourceEntryPointName.c_str();
+ return new TParseContext(symbolTable, intermediate, parsingBuiltIns, version, profile, spvVersion,
+ language, infoSink, forwardCompatible, messages, &entryPoint);
+ }
+#ifdef ENABLE_HLSL
+ case EShSourceHlsl:
+ return new HlslParseContext(symbolTable, intermediate, parsingBuiltIns, version, profile, spvVersion,
+ language, infoSink, sourceEntryPointName.c_str(), forwardCompatible, messages);
+#endif
+ default:
+ infoSink.info.message(EPrefixInternalError, "Unable to determine source language");
+ return nullptr;
+ }
+}
+
+// Local mapping functions for making arrays of symbol tables....
+
+const int VersionCount = 17; // index range in MapVersionToIndex
+
+int MapVersionToIndex(int version)
+{
+ int index = 0;
+
+ switch (version) {
+ case 100: index = 0; break;
+ case 110: index = 1; break;
+ case 120: index = 2; break;
+ case 130: index = 3; break;
+ case 140: index = 4; break;
+ case 150: index = 5; break;
+ case 300: index = 6; break;
+ case 330: index = 7; break;
+ case 400: index = 8; break;
+ case 410: index = 9; break;
+ case 420: index = 10; break;
+ case 430: index = 11; break;
+ case 440: index = 12; break;
+ case 310: index = 13; break;
+ case 450: index = 14; break;
+ case 500: index = 0; break; // HLSL
+ case 320: index = 15; break;
+ case 460: index = 16; break;
+ default: assert(0); break;
+ }
+
+ assert(index < VersionCount);
+
+ return index;
+}
+
+const int SpvVersionCount = 3; // index range in MapSpvVersionToIndex
+
+int MapSpvVersionToIndex(const SpvVersion& spvVersion)
+{
+ int index = 0;
+
+ if (spvVersion.openGl > 0)
+ index = 1;
+ else if (spvVersion.vulkan > 0)
+ index = 2;
+
+ assert(index < SpvVersionCount);
+
+ return index;
+}
+
+const int ProfileCount = 4; // index range in MapProfileToIndex
+
+int MapProfileToIndex(EProfile profile)
+{
+ int index = 0;
+
+ switch (profile) {
+ case ENoProfile: index = 0; break;
+ case ECoreProfile: index = 1; break;
+ case ECompatibilityProfile: index = 2; break;
+ case EEsProfile: index = 3; break;
+ default: break;
+ }
+
+ assert(index < ProfileCount);
+
+ return index;
+}
+
+const int SourceCount = 2;
+
+int MapSourceToIndex(EShSource source)
+{
+ int index = 0;
+
+ switch (source) {
+ case EShSourceGlsl: index = 0; break;
+ case EShSourceHlsl: index = 1; break;
+ default: break;
+ }
+
+ assert(index < SourceCount);
+
+ return index;
+}
+
+// only one of these needed for non-ES; ES needs 2 for different precision defaults of built-ins
+enum EPrecisionClass {
+ EPcGeneral,
+ EPcFragment,
+ EPcCount
+};
+
+// A process-global symbol table per version per profile for built-ins common
+// to multiple stages (languages), and a process-global symbol table per version
+// per profile per stage for built-ins unique to each stage. They will be sparsely
+// populated, so they will only be generated as needed.
+//
+// Each has a different set of built-ins, and we want to preserve that from
+// compile to compile.
+//
+TSymbolTable* CommonSymbolTable[VersionCount][SpvVersionCount][ProfileCount][SourceCount][EPcCount] = {};
+TSymbolTable* SharedSymbolTables[VersionCount][SpvVersionCount][ProfileCount][SourceCount][EShLangCount] = {};
+
+TPoolAllocator* PerProcessGPA = nullptr;
+
+//
+// Parse and add to the given symbol table the content of the given shader string.
+//
+bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language,
+ EShSource source, TInfoSink& infoSink, TSymbolTable& symbolTable)
+{
+ TIntermediate intermediate(language, version, profile);
+
+ intermediate.setSource(source);
+
+ std::unique_ptr<TParseContextBase> parseContext(CreateParseContext(symbolTable, intermediate, version, profile, source,
+ language, infoSink, spvVersion, true, EShMsgDefault,
+ true));
+
+ TShader::ForbidIncluder includer;
+ TPpContext ppContext(*parseContext, "", includer);
+ TScanContext scanContext(*parseContext);
+ parseContext->setScanContext(&scanContext);
+ parseContext->setPpContext(&ppContext);
+
+ //
+ // Push the symbol table to give it an initial scope. This
+ // push should not have a corresponding pop, so that built-ins
+ // are preserved, and the test for an empty table fails.
+ //
+
+ symbolTable.push();
+
+ const char* builtInShaders[2];
+ size_t builtInLengths[2];
+ builtInShaders[0] = builtIns.c_str();
+ builtInLengths[0] = builtIns.size();
+
+ if (builtInLengths[0] == 0)
+ return true;
+
+ TInputScanner input(1, builtInShaders, builtInLengths);
+ if (! parseContext->parseShaderStrings(ppContext, input) != 0) {
+ infoSink.info.message(EPrefixInternalError, "Unable to parse built-ins");
+ printf("Unable to parse built-ins\n%s\n", infoSink.info.c_str());
+ printf("%s\n", builtInShaders[0]);
+
+ return false;
+ }
+
+ return true;
+}
+
+int CommonIndex(EProfile profile, EShLanguage language)
+{
+ return (profile == EEsProfile && language == EShLangFragment) ? EPcFragment : EPcGeneral;
+}
+
+//
+// To initialize per-stage shared tables, with the common table already complete.
+//
+void InitializeStageSymbolTable(TBuiltInParseables& builtInParseables, int version, EProfile profile, const SpvVersion& spvVersion,
+ EShLanguage language, EShSource source, TInfoSink& infoSink, TSymbolTable** commonTable,
+ TSymbolTable** symbolTables)
+{
+ (*symbolTables[language]).adoptLevels(*commonTable[CommonIndex(profile, language)]);
+ InitializeSymbolTable(builtInParseables.getStageString(language), version, profile, spvVersion, language, source,
+ infoSink, *symbolTables[language]);
+ builtInParseables.identifyBuiltIns(version, profile, spvVersion, language, *symbolTables[language]);
+ if (profile == EEsProfile && version >= 300)
+ (*symbolTables[language]).setNoBuiltInRedeclarations();
+ if (version == 110)
+ (*symbolTables[language]).setSeparateNameSpaces();
+}
+
+//
+// Initialize the full set of shareable symbol tables;
+// The common (cross-stage) and those shareable per-stage.
+//
+bool InitializeSymbolTables(TInfoSink& infoSink, TSymbolTable** commonTable, TSymbolTable** symbolTables, int version, EProfile profile, const SpvVersion& spvVersion, EShSource source)
+{
+ std::unique_ptr<TBuiltInParseables> builtInParseables(CreateBuiltInParseables(infoSink, source));
+
+ if (builtInParseables == nullptr)
+ return false;
+
+ builtInParseables->initialize(version, profile, spvVersion);
+
+ // do the common tables
+ InitializeSymbolTable(builtInParseables->getCommonString(), version, profile, spvVersion, EShLangVertex, source,
+ infoSink, *commonTable[EPcGeneral]);
+ if (profile == EEsProfile)
+ InitializeSymbolTable(builtInParseables->getCommonString(), version, profile, spvVersion, EShLangFragment, source,
+ infoSink, *commonTable[EPcFragment]);
+
+ // do the per-stage tables
+
+ // always have vertex and fragment
+ InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangVertex, source,
+ infoSink, commonTable, symbolTables);
+ InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangFragment, source,
+ infoSink, commonTable, symbolTables);
+
+ // check for tessellation
+ if ((profile != EEsProfile && version >= 150) ||
+ (profile == EEsProfile && version >= 310)) {
+ InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangTessControl, source,
+ infoSink, commonTable, symbolTables);
+ InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangTessEvaluation, source,
+ infoSink, commonTable, symbolTables);
+ }
+
+ // check for geometry
+ if ((profile != EEsProfile && version >= 150) ||
+ (profile == EEsProfile && version >= 310))
+ InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangGeometry, source,
+ infoSink, commonTable, symbolTables);
+
+ // check for compute
+ if ((profile != EEsProfile && version >= 420) ||
+ (profile == EEsProfile && version >= 310))
+ InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangCompute, source,
+ infoSink, commonTable, symbolTables);
+
+#ifdef NV_EXTENSIONS
+ // check for ray tracing stages
+ if (profile != EEsProfile && version >= 450) {
+ InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangRayGenNV, source,
+ infoSink, commonTable, symbolTables);
+ InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangIntersectNV, source,
+ infoSink, commonTable, symbolTables);
+ InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangAnyHitNV, source,
+ infoSink, commonTable, symbolTables);
+ InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangClosestHitNV, source,
+ infoSink, commonTable, symbolTables);
+ InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangMissNV, source,
+ infoSink, commonTable, symbolTables);
+ InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangCallableNV, source,
+ infoSink, commonTable, symbolTables);
+ }
+
+ // check for mesh
+ if ((profile != EEsProfile && version >= 450) ||
+ (profile == EEsProfile && version >= 320))
+ InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangMeshNV, source,
+ infoSink, commonTable, symbolTables);
+
+ // check for task
+ if ((profile != EEsProfile && version >= 450) ||
+ (profile == EEsProfile && version >= 320))
+ InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangTaskNV, source,
+ infoSink, commonTable, symbolTables);
+#endif
+
+ return true;
+}
+
+bool AddContextSpecificSymbols(const TBuiltInResource* resources, TInfoSink& infoSink, TSymbolTable& symbolTable, int version,
+ EProfile profile, const SpvVersion& spvVersion, EShLanguage language, EShSource source)
+{
+ std::unique_ptr<TBuiltInParseables> builtInParseables(CreateBuiltInParseables(infoSink, source));
+
+ if (builtInParseables == nullptr)
+ return false;
+
+ builtInParseables->initialize(*resources, version, profile, spvVersion, language);
+ InitializeSymbolTable(builtInParseables->getCommonString(), version, profile, spvVersion, language, source, infoSink, symbolTable);
+ builtInParseables->identifyBuiltIns(version, profile, spvVersion, language, symbolTable, *resources);
+
+ return true;
+}
+
+//
+// To do this on the fly, we want to leave the current state of our thread's
+// pool allocator intact, so:
+// - Switch to a new pool for parsing the built-ins
+// - Do the parsing, which builds the symbol table, using the new pool
+// - Switch to the process-global pool to save a copy of the resulting symbol table
+// - Free up the new pool used to parse the built-ins
+// - Switch back to the original thread's pool
+//
+// This only gets done the first time any thread needs a particular symbol table
+// (lazy evaluation).
+//
+void SetupBuiltinSymbolTable(int version, EProfile profile, const SpvVersion& spvVersion, EShSource source)
+{
+ TInfoSink infoSink;
+
+ // Make sure only one thread tries to do this at a time
+ glslang::GetGlobalLock();
+
+ // See if it's already been done for this version/profile combination
+ int versionIndex = MapVersionToIndex(version);
+ int spvVersionIndex = MapSpvVersionToIndex(spvVersion);
+ int profileIndex = MapProfileToIndex(profile);
+ int sourceIndex = MapSourceToIndex(source);
+ if (CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][EPcGeneral]) {
+ glslang::ReleaseGlobalLock();
+
+ return;
+ }
+
+ // Switch to a new pool
+ TPoolAllocator& previousAllocator = GetThreadPoolAllocator();
+ TPoolAllocator* builtInPoolAllocator = new TPoolAllocator;
+ SetThreadPoolAllocator(builtInPoolAllocator);
+
+ // Dynamically allocate the local symbol tables so we can control when they are deallocated WRT when the pool is popped.
+ TSymbolTable* commonTable[EPcCount];
+ TSymbolTable* stageTables[EShLangCount];
+ for (int precClass = 0; precClass < EPcCount; ++precClass)
+ commonTable[precClass] = new TSymbolTable;
+ for (int stage = 0; stage < EShLangCount; ++stage)
+ stageTables[stage] = new TSymbolTable;
+
+ // Generate the local symbol tables using the new pool
+ InitializeSymbolTables(infoSink, commonTable, stageTables, version, profile, spvVersion, source);
+
+ // Switch to the process-global pool
+ SetThreadPoolAllocator(PerProcessGPA);
+
+ // Copy the local symbol tables from the new pool to the global tables using the process-global pool
+ for (int precClass = 0; precClass < EPcCount; ++precClass) {
+ if (! commonTable[precClass]->isEmpty()) {
+ CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][precClass] = new TSymbolTable;
+ CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][precClass]->copyTable(*commonTable[precClass]);
+ CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][precClass]->readOnly();
+ }
+ }
+ for (int stage = 0; stage < EShLangCount; ++stage) {
+ if (! stageTables[stage]->isEmpty()) {
+ SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage] = new TSymbolTable;
+ SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage]->adoptLevels(*CommonSymbolTable
+ [versionIndex][spvVersionIndex][profileIndex][sourceIndex][CommonIndex(profile, (EShLanguage)stage)]);
+ SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage]->copyTable(*stageTables[stage]);
+ SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage]->readOnly();
+ }
+ }
+
+ // Clean up the local tables before deleting the pool they used.
+ for (int precClass = 0; precClass < EPcCount; ++precClass)
+ delete commonTable[precClass];
+ for (int stage = 0; stage < EShLangCount; ++stage)
+ delete stageTables[stage];
+
+ delete builtInPoolAllocator;
+ SetThreadPoolAllocator(&previousAllocator);
+
+ glslang::ReleaseGlobalLock();
+}
+
+// Return true if the shader was correctly specified for version/profile/stage.
+bool DeduceVersionProfile(TInfoSink& infoSink, EShLanguage stage, bool versionNotFirst, int defaultVersion,
+ EShSource source, int& version, EProfile& profile, const SpvVersion& spvVersion)
+{
+ const int FirstProfileVersion = 150;
+ bool correct = true;
+
+ if (source == EShSourceHlsl) {
+ version = 500; // shader model; currently a characteristic of glslang, not the input
+ profile = ECoreProfile; // allow doubles in prototype parsing
+ return correct;
+ }
+
+ // Get a version...
+ if (version == 0) {
+ version = defaultVersion;
+ // infoSink.info.message(EPrefixWarning, "#version: statement missing; use #version on first line of shader");
+ }
+
+ // Get a good profile...
+ if (profile == ENoProfile) {
+ if (version == 300 || version == 310 || version == 320) {
+ correct = false;
+ infoSink.info.message(EPrefixError, "#version: versions 300, 310, and 320 require specifying the 'es' profile");
+ profile = EEsProfile;
+ } else if (version == 100)
+ profile = EEsProfile;
+ else if (version >= FirstProfileVersion)
+ profile = ECoreProfile;
+ else
+ profile = ENoProfile;
+ } else {
+ // a profile was provided...
+ if (version < 150) {
+ correct = false;
+ infoSink.info.message(EPrefixError, "#version: versions before 150 do not allow a profile token");
+ if (version == 100)
+ profile = EEsProfile;
+ else
+ profile = ENoProfile;
+ } else if (version == 300 || version == 310 || version == 320) {
+ if (profile != EEsProfile) {
+ correct = false;
+ infoSink.info.message(EPrefixError, "#version: versions 300, 310, and 320 support only the es profile");
+ }
+ profile = EEsProfile;
+ } else {
+ if (profile == EEsProfile) {
+ correct = false;
+ infoSink.info.message(EPrefixError, "#version: only version 300, 310, and 320 support the es profile");
+ if (version >= FirstProfileVersion)
+ profile = ECoreProfile;
+ else
+ profile = ENoProfile;
+ }
+ // else: typical desktop case... e.g., "#version 410 core"
+ }
+ }
+
+ // Fix version...
+ switch (version) {
+ // ES versions
+ case 100: break;
+ case 300: break;
+ case 310: break;
+ case 320: break;
+
+ // desktop versions
+ case 110: break;
+ case 120: break;
+ case 130: break;
+ case 140: break;
+ case 150: break;
+ case 330: break;
+ case 400: break;
+ case 410: break;
+ case 420: break;
+ case 430: break;
+ case 440: break;
+ case 450: break;
+ case 460: break;
+
+ // unknown version
+ default:
+ correct = false;
+ infoSink.info.message(EPrefixError, "version not supported");
+ if (profile == EEsProfile)
+ version = 310;
+ else {
+ version = 450;
+ profile = ECoreProfile;
+ }
+ break;
+ }
+
+ // Correct for stage type...
+ switch (stage) {
+ case EShLangGeometry:
+ if ((profile == EEsProfile && version < 310) ||
+ (profile != EEsProfile && version < 150)) {
+ correct = false;
+ infoSink.info.message(EPrefixError, "#version: geometry shaders require es profile with version 310 or non-es profile with version 150 or above");
+ version = (profile == EEsProfile) ? 310 : 150;
+ if (profile == EEsProfile || profile == ENoProfile)
+ profile = ECoreProfile;
+ }
+ break;
+ case EShLangTessControl:
+ case EShLangTessEvaluation:
+ if ((profile == EEsProfile && version < 310) ||
+ (profile != EEsProfile && version < 150)) {
+ correct = false;
+ infoSink.info.message(EPrefixError, "#version: tessellation shaders require es profile with version 310 or non-es profile with version 150 or above");
+ version = (profile == EEsProfile) ? 310 : 400; // 150 supports the extension, correction is to 400 which does not
+ if (profile == EEsProfile || profile == ENoProfile)
+ profile = ECoreProfile;
+ }
+ break;
+ case EShLangCompute:
+ if ((profile == EEsProfile && version < 310) ||
+ (profile != EEsProfile && version < 420)) {
+ correct = false;
+ infoSink.info.message(EPrefixError, "#version: compute shaders require es profile with version 310 or above, or non-es profile with version 420 or above");
+ version = profile == EEsProfile ? 310 : 420;
+ }
+ break;
+#ifdef NV_EXTENSIONS
+ case EShLangRayGenNV:
+ case EShLangIntersectNV:
+ case EShLangAnyHitNV:
+ case EShLangClosestHitNV:
+ case EShLangMissNV:
+ case EShLangCallableNV:
+ if (profile == EEsProfile || version < 460) {
+ correct = false;
+ infoSink.info.message(EPrefixError, "#version: ray tracing shaders require non-es profile with version 460 or above");
+ version = 460;
+ }
+ break;
+ case EShLangMeshNV:
+ case EShLangTaskNV:
+ if ((profile == EEsProfile && version < 320) ||
+ (profile != EEsProfile && version < 450)) {
+ correct = false;
+ infoSink.info.message(EPrefixError, "#version: mesh/task shaders require es profile with version 320 or above, or non-es profile with version 450 or above");
+ version = profile == EEsProfile ? 320 : 450;
+ }
+#endif
+ default:
+ break;
+ }
+
+ if (profile == EEsProfile && version >= 300 && versionNotFirst) {
+ correct = false;
+ infoSink.info.message(EPrefixError, "#version: statement must appear first in es-profile shader; before comments or newlines");
+ }
+
+ // Check for SPIR-V compatibility
+ if (spvVersion.spv != 0) {
+ switch (profile) {
+ case EEsProfile:
+ if (spvVersion.vulkan > 0 && version < 310) {
+ correct = false;
+ infoSink.info.message(EPrefixError, "#version: ES shaders for Vulkan SPIR-V require version 310 or higher");
+ version = 310;
+ }
+ if (spvVersion.openGl >= 100) {
+ correct = false;
+ infoSink.info.message(EPrefixError, "#version: ES shaders for OpenGL SPIR-V are not supported");
+ version = 310;
+ }
+ break;
+ case ECompatibilityProfile:
+ infoSink.info.message(EPrefixError, "#version: compilation for SPIR-V does not support the compatibility profile");
+ break;
+ default:
+ if (spvVersion.vulkan > 0 && version < 140) {
+ correct = false;
+ infoSink.info.message(EPrefixError, "#version: Desktop shaders for Vulkan SPIR-V require version 140 or higher");
+ version = 140;
+ }
+ if (spvVersion.openGl >= 100 && version < 330) {
+ correct = false;
+ infoSink.info.message(EPrefixError, "#version: Desktop shaders for OpenGL SPIR-V require version 330 or higher");
+ version = 330;
+ }
+ break;
+ }
+ }
+
+ return correct;
+}
+
+// There are multiple paths in for setting environment stuff.
+// TEnvironment takes precedence, for what it sets, so sort all this out.
+// Ideally, the internal code could be made to use TEnvironment, but for
+// now, translate it to the historically used parameters.
+void TranslateEnvironment(const TEnvironment* environment, EShMessages& messages, EShSource& source,
+ EShLanguage& stage, SpvVersion& spvVersion)
+{
+ // Set up environmental defaults, first ignoring 'environment'.
+ if (messages & EShMsgSpvRules)
+ spvVersion.spv = EShTargetSpv_1_0;
+ if (messages & EShMsgVulkanRules) {
+ spvVersion.vulkan = EShTargetVulkan_1_0;
+ spvVersion.vulkanGlsl = 100;
+ } else if (spvVersion.spv != 0)
+ spvVersion.openGl = 100;
+
+ // Now, override, based on any content set in 'environment'.
+ // 'environment' must be cleared to ESh*None settings when items
+ // are not being set.
+ if (environment != nullptr) {
+ // input language
+ if (environment->input.languageFamily != EShSourceNone) {
+ stage = environment->input.stage;
+ switch (environment->input.dialect) {
+ case EShClientNone:
+ break;
+ case EShClientVulkan:
+ spvVersion.vulkanGlsl = environment->input.dialectVersion;
+ break;
+ case EShClientOpenGL:
+ spvVersion.openGl = environment->input.dialectVersion;
+ break;
+ }
+ switch (environment->input.languageFamily) {
+ case EShSourceNone:
+ break;
+ case EShSourceGlsl:
+ source = EShSourceGlsl;
+ messages = static_cast<EShMessages>(messages & ~EShMsgReadHlsl);
+ break;
+ case EShSourceHlsl:
+ source = EShSourceHlsl;
+ messages = static_cast<EShMessages>(messages | EShMsgReadHlsl);
+ break;
+ }
+ }
+
+ // client
+ switch (environment->client.client) {
+ case EShClientVulkan:
+ spvVersion.vulkan = environment->client.version;
+ break;
+ default:
+ break;
+ }
+
+ // generated code
+ switch (environment->target.language) {
+ case EshTargetSpv:
+ spvVersion.spv = environment->target.version;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+// Most processes are recorded when set in the intermediate representation,
+// These are the few that are not.
+void RecordProcesses(TIntermediate& intermediate, EShMessages messages, const std::string& sourceEntryPointName)
+{
+ if ((messages & EShMsgRelaxedErrors) != 0)
+ intermediate.addProcess("relaxed-errors");
+ if ((messages & EShMsgSuppressWarnings) != 0)
+ intermediate.addProcess("suppress-warnings");
+ if ((messages & EShMsgKeepUncalled) != 0)
+ intermediate.addProcess("keep-uncalled");
+ if (sourceEntryPointName.size() > 0) {
+ intermediate.addProcess("source-entrypoint");
+ intermediate.addProcessArgument(sourceEntryPointName);
+ }
+}
+
+// This is the common setup and cleanup code for PreprocessDeferred and
+// CompileDeferred.
+// It takes any callable with a signature of
+// bool (TParseContextBase& parseContext, TPpContext& ppContext,
+// TInputScanner& input, bool versionWillBeError,
+// TSymbolTable& , TIntermediate& ,
+// EShOptimizationLevel , EShMessages );
+// Which returns false if a failure was detected and true otherwise.
+//
+template<typename ProcessingContext>
+bool ProcessDeferred(
+ TCompiler* compiler,
+ const char* const shaderStrings[],
+ const int numStrings,
+ const int* inputLengths,
+ const char* const stringNames[],
+ const char* customPreamble,
+ const EShOptimizationLevel optLevel,
+ const TBuiltInResource* resources,
+ int defaultVersion, // use 100 for ES environment, 110 for desktop; this is the GLSL version, not SPIR-V or Vulkan
+ EProfile defaultProfile,
+ // set version/profile to defaultVersion/defaultProfile regardless of the #version
+ // directive in the source code
+ bool forceDefaultVersionAndProfile,
+ bool forwardCompatible, // give errors for use of deprecated features
+ EShMessages messages, // warnings/errors/AST; things to print out
+ TIntermediate& intermediate, // returned tree, etc.
+ ProcessingContext& processingContext,
+ bool requireNonempty,
+ TShader::Includer& includer,
+ const std::string sourceEntryPointName = "",
+ const TEnvironment* environment = nullptr) // optional way of fully setting all versions, overriding the above
+{
+ // This must be undone (.pop()) by the caller, after it finishes consuming the created tree.
+ GetThreadPoolAllocator().push();
+
+ if (numStrings == 0)
+ return true;
+
+ // Move to length-based strings, rather than null-terminated strings.
+ // Also, add strings to include the preamble and to ensure the shader is not null,
+ // which lets the grammar accept what was a null (post preprocessing) shader.
+ //
+ // Shader will look like
+ // string 0: system preamble
+ // string 1: custom preamble
+ // string 2...numStrings+1: user's shader
+ // string numStrings+2: "int;"
+ const int numPre = 2;
+ const int numPost = requireNonempty? 1 : 0;
+ const int numTotal = numPre + numStrings + numPost;
+ std::unique_ptr<size_t[]> lengths(new size_t[numTotal]);
+ std::unique_ptr<const char*[]> strings(new const char*[numTotal]);
+ std::unique_ptr<const char*[]> names(new const char*[numTotal]);
+ for (int s = 0; s < numStrings; ++s) {
+ strings[s + numPre] = shaderStrings[s];
+ if (inputLengths == nullptr || inputLengths[s] < 0)
+ lengths[s + numPre] = strlen(shaderStrings[s]);
+ else
+ lengths[s + numPre] = inputLengths[s];
+ }
+ if (stringNames != nullptr) {
+ for (int s = 0; s < numStrings; ++s)
+ names[s + numPre] = stringNames[s];
+ } else {
+ for (int s = 0; s < numStrings; ++s)
+ names[s + numPre] = nullptr;
+ }
+
+ // Get all the stages, languages, clients, and other environment
+ // stuff sorted out.
+ EShSource source = (messages & EShMsgReadHlsl) != 0 ? EShSourceHlsl : EShSourceGlsl;
+ SpvVersion spvVersion;
+ EShLanguage stage = compiler->getLanguage();
+ TranslateEnvironment(environment, messages, source, stage, spvVersion);
+ if (environment != nullptr && environment->target.hlslFunctionality1)
+ intermediate.setHlslFunctionality1();
+
+ // First, without using the preprocessor or parser, find the #version, so we know what
+ // symbol tables, processing rules, etc. to set up. This does not need the extra strings
+ // outlined above, just the user shader, after the system and user preambles.
+ glslang::TInputScanner userInput(numStrings, &strings[numPre], &lengths[numPre]);
+ int version = 0;
+ EProfile profile = ENoProfile;
+ bool versionNotFirstToken = false;
+ bool versionNotFirst = (source == EShSourceHlsl)
+ ? true
+ : userInput.scanVersion(version, profile, versionNotFirstToken);
+ bool versionNotFound = version == 0;
+ if (forceDefaultVersionAndProfile && source == EShSourceGlsl) {
+ if (! (messages & EShMsgSuppressWarnings) && ! versionNotFound &&
+ (version != defaultVersion || profile != defaultProfile)) {
+ compiler->infoSink.info << "Warning, (version, profile) forced to be ("
+ << defaultVersion << ", " << ProfileName(defaultProfile)
+ << "), while in source code it is ("
+ << version << ", " << ProfileName(profile) << ")\n";
+ }
+
+ if (versionNotFound) {
+ versionNotFirstToken = false;
+ versionNotFirst = false;
+ versionNotFound = false;
+ }
+ version = defaultVersion;
+ profile = defaultProfile;
+ }
+
+ bool goodVersion = DeduceVersionProfile(compiler->infoSink, stage,
+ versionNotFirst, defaultVersion, source, version, profile, spvVersion);
+ bool versionWillBeError = (versionNotFound || (profile == EEsProfile && version >= 300 && versionNotFirst));
+ bool warnVersionNotFirst = false;
+ if (! versionWillBeError && versionNotFirstToken) {
+ if (messages & EShMsgRelaxedErrors)
+ warnVersionNotFirst = true;
+ else
+ versionWillBeError = true;
+ }
+
+ intermediate.setSource(source);
+ intermediate.setVersion(version);
+ intermediate.setProfile(profile);
+ intermediate.setSpv(spvVersion);
+ RecordProcesses(intermediate, messages, sourceEntryPointName);
+ if (spvVersion.vulkan > 0)
+ intermediate.setOriginUpperLeft();
+ if ((messages & EShMsgHlslOffsets) || source == EShSourceHlsl)
+ intermediate.setHlslOffsets();
+ if (messages & EShMsgDebugInfo) {
+ intermediate.setSourceFile(names[numPre]);
+ for (int s = 0; s < numStrings; ++s) {
+ // The string may not be null-terminated, so make sure we provide
+ // the length along with the string.
+ intermediate.addSourceText(strings[numPre + s], lengths[numPre + s]);
+ }
+ }
+ SetupBuiltinSymbolTable(version, profile, spvVersion, source);
+
+ TSymbolTable* cachedTable = SharedSymbolTables[MapVersionToIndex(version)]
+ [MapSpvVersionToIndex(spvVersion)]
+ [MapProfileToIndex(profile)]
+ [MapSourceToIndex(source)]
+ [stage];
+
+ // Dynamically allocate the symbol table so we can control when it is deallocated WRT the pool.
+ std::unique_ptr<TSymbolTable> symbolTable(new TSymbolTable);
+ if (cachedTable)
+ symbolTable->adoptLevels(*cachedTable);
+
+ // Add built-in symbols that are potentially context dependent;
+ // they get popped again further down.
+ if (! AddContextSpecificSymbols(resources, compiler->infoSink, *symbolTable, version, profile, spvVersion,
+ stage, source)) {
+ return false;
+ }
+
+ //
+ // Now we can process the full shader under proper symbols and rules.
+ //
+
+ std::unique_ptr<TParseContextBase> parseContext(CreateParseContext(*symbolTable, intermediate, version, profile, source,
+ stage, compiler->infoSink,
+ spvVersion, forwardCompatible, messages, false, sourceEntryPointName));
+ TPpContext ppContext(*parseContext, names[numPre] ? names[numPre] : "", includer);
+
+ // only GLSL (bison triggered, really) needs an externally set scan context
+ glslang::TScanContext scanContext(*parseContext);
+ if (source == EShSourceGlsl)
+ parseContext->setScanContext(&scanContext);
+
+ parseContext->setPpContext(&ppContext);
+ parseContext->setLimits(*resources);
+ if (! goodVersion)
+ parseContext->addError();
+ if (warnVersionNotFirst) {
+ TSourceLoc loc;
+ loc.init();
+ parseContext->warn(loc, "Illegal to have non-comment, non-whitespace tokens before #version", "#version", "");
+ }
+
+ parseContext->initializeExtensionBehavior();
+
+ // Fill in the strings as outlined above.
+ std::string preamble;
+ parseContext->getPreamble(preamble);
+ strings[0] = preamble.c_str();
+ lengths[0] = strlen(strings[0]);
+ names[0] = nullptr;
+ strings[1] = customPreamble;
+ lengths[1] = strlen(strings[1]);
+ names[1] = nullptr;
+ assert(2 == numPre);
+ if (requireNonempty) {
+ const int postIndex = numStrings + numPre;
+ strings[postIndex] = "\n int;";
+ lengths[postIndex] = strlen(strings[numStrings + numPre]);
+ names[postIndex] = nullptr;
+ }
+ TInputScanner fullInput(numStrings + numPre + numPost, strings.get(), lengths.get(), names.get(), numPre, numPost);
+
+ // Push a new symbol allocation scope that will get used for the shader's globals.
+ symbolTable->push();
+
+ bool success = processingContext(*parseContext, ppContext, fullInput,
+ versionWillBeError, *symbolTable,
+ intermediate, optLevel, messages);
+ return success;
+}
+
+// Responsible for keeping track of the most recent source string and line in
+// the preprocessor and outputting newlines appropriately if the source string
+// or line changes.
+class SourceLineSynchronizer {
+public:
+ SourceLineSynchronizer(const std::function<int()>& lastSourceIndex,
+ std::string* output)
+ : getLastSourceIndex(lastSourceIndex), output(output), lastSource(-1), lastLine(0) {}
+// SourceLineSynchronizer(const SourceLineSynchronizer&) = delete;
+// SourceLineSynchronizer& operator=(const SourceLineSynchronizer&) = delete;
+
+ // Sets the internally tracked source string index to that of the most
+ // recently read token. If we switched to a new source string, returns
+ // true and inserts a newline. Otherwise, returns false and outputs nothing.
+ bool syncToMostRecentString() {
+ if (getLastSourceIndex() != lastSource) {
+ // After switching to a new source string, we need to reset lastLine
+ // because line number resets every time a new source string is
+ // used. We also need to output a newline to separate the output
+ // from the previous source string (if there is one).
+ if (lastSource != -1 || lastLine != 0)
+ *output += '\n';
+ lastSource = getLastSourceIndex();
+ lastLine = -1;
+ return true;
+ }
+ return false;
+ }
+
+ // Calls syncToMostRecentString() and then sets the internally tracked line
+ // number to tokenLine. If we switched to a new line, returns true and inserts
+ // newlines appropriately. Otherwise, returns false and outputs nothing.
+ bool syncToLine(int tokenLine) {
+ syncToMostRecentString();
+ const bool newLineStarted = lastLine < tokenLine;
+ for (; lastLine < tokenLine; ++lastLine) {
+ if (lastLine > 0) *output += '\n';
+ }
+ return newLineStarted;
+ }
+
+ // Sets the internally tracked line number to newLineNum.
+ void setLineNum(int newLineNum) { lastLine = newLineNum; }
+
+private:
+ SourceLineSynchronizer& operator=(const SourceLineSynchronizer&);
+
+ // A function for getting the index of the last valid source string we've
+ // read tokens from.
+ const std::function<int()> getLastSourceIndex;
+ // output string for newlines.
+ std::string* output;
+ // lastSource is the source string index (starting from 0) of the last token
+ // processed. It is tracked in order for newlines to be inserted when a new
+ // source string starts. -1 means we haven't started processing any source
+ // string.
+ int lastSource;
+ // lastLine is the line number (starting from 1) of the last token processed.
+ // It is tracked in order for newlines to be inserted when a token appears
+ // on a new line. 0 means we haven't started processing any line in the
+ // current source string.
+ int lastLine;
+};
+
+// DoPreprocessing is a valid ProcessingContext template argument,
+// which only performs the preprocessing step of compilation.
+// It places the result in the "string" argument to its constructor.
+//
+// This is not an officially supported or fully working path.
+struct DoPreprocessing {
+ explicit DoPreprocessing(std::string* string): outputString(string) {}
+ bool operator()(TParseContextBase& parseContext, TPpContext& ppContext,
+ TInputScanner& input, bool versionWillBeError,
+ TSymbolTable&, TIntermediate&,
+ EShOptimizationLevel, EShMessages)
+ {
+ // This is a list of tokens that do not require a space before or after.
+ static const std::string unNeededSpaceTokens = ";()[]";
+ static const std::string noSpaceBeforeTokens = ",";
+ glslang::TPpToken ppToken;
+
+ parseContext.setScanner(&input);
+ ppContext.setInput(input, versionWillBeError);
+
+ std::string outputBuffer;
+ SourceLineSynchronizer lineSync(
+ std::bind(&TInputScanner::getLastValidSourceIndex, &input), &outputBuffer);
+
+ parseContext.setExtensionCallback([&lineSync, &outputBuffer](
+ int line, const char* extension, const char* behavior) {
+ lineSync.syncToLine(line);
+ outputBuffer += "#extension ";
+ outputBuffer += extension;
+ outputBuffer += " : ";
+ outputBuffer += behavior;
+ });
+
+ parseContext.setLineCallback([&lineSync, &outputBuffer, &parseContext](
+ int curLineNum, int newLineNum, bool hasSource, int sourceNum, const char* sourceName) {
+ // SourceNum is the number of the source-string that is being parsed.
+ lineSync.syncToLine(curLineNum);
+ outputBuffer += "#line ";
+ outputBuffer += std::to_string(newLineNum);
+ if (hasSource) {
+ outputBuffer += ' ';
+ if (sourceName != nullptr) {
+ outputBuffer += '\"';
+ outputBuffer += sourceName;
+ outputBuffer += '\"';
+ } else {
+ outputBuffer += std::to_string(sourceNum);
+ }
+ }
+ if (parseContext.lineDirectiveShouldSetNextLine()) {
+ // newLineNum is the new line number for the line following the #line
+ // directive. So the new line number for the current line is
+ newLineNum -= 1;
+ }
+ outputBuffer += '\n';
+ // And we are at the next line of the #line directive now.
+ lineSync.setLineNum(newLineNum + 1);
+ });
+
+ parseContext.setVersionCallback(
+ [&lineSync, &outputBuffer](int line, int version, const char* str) {
+ lineSync.syncToLine(line);
+ outputBuffer += "#version ";
+ outputBuffer += std::to_string(version);
+ if (str) {
+ outputBuffer += ' ';
+ outputBuffer += str;
+ }
+ });
+
+ parseContext.setPragmaCallback([&lineSync, &outputBuffer](
+ int line, const glslang::TVector<glslang::TString>& ops) {
+ lineSync.syncToLine(line);
+ outputBuffer += "#pragma ";
+ for(size_t i = 0; i < ops.size(); ++i) {
+ outputBuffer += ops[i].c_str();
+ }
+ });
+
+ parseContext.setErrorCallback([&lineSync, &outputBuffer](
+ int line, const char* errorMessage) {
+ lineSync.syncToLine(line);
+ outputBuffer += "#error ";
+ outputBuffer += errorMessage;
+ });
+
+ int lastToken = EndOfInput; // lastToken records the last token processed.
+ do {
+ int token = ppContext.tokenize(ppToken);
+ if (token == EndOfInput)
+ break;
+
+ bool isNewString = lineSync.syncToMostRecentString();
+ bool isNewLine = lineSync.syncToLine(ppToken.loc.line);
+
+ if (isNewLine) {
+ // Don't emit whitespace onto empty lines.
+ // Copy any whitespace characters at the start of a line
+ // from the input to the output.
+ outputBuffer += std::string(ppToken.loc.column - 1, ' ');
+ }
+
+ // Output a space in between tokens, but not at the start of a line,
+ // and also not around special tokens. This helps with readability
+ // and consistency.
+ if (!isNewString && !isNewLine && lastToken != EndOfInput &&
+ (unNeededSpaceTokens.find((char)token) == std::string::npos) &&
+ (unNeededSpaceTokens.find((char)lastToken) == std::string::npos) &&
+ (noSpaceBeforeTokens.find((char)token) == std::string::npos)) {
+ outputBuffer += ' ';
+ }
+ lastToken = token;
+ if (token == PpAtomConstString)
+ outputBuffer += "\"";
+ outputBuffer += ppToken.name;
+ if (token == PpAtomConstString)
+ outputBuffer += "\"";
+ } while (true);
+ outputBuffer += '\n';
+ *outputString = std::move(outputBuffer);
+
+ bool success = true;
+ if (parseContext.getNumErrors() > 0) {
+ success = false;
+ parseContext.infoSink.info.prefix(EPrefixError);
+ parseContext.infoSink.info << parseContext.getNumErrors() << " compilation errors. No code generated.\n\n";
+ }
+ return success;
+ }
+ std::string* outputString;
+};
+
+// DoFullParse is a valid ProcessingConext template argument for fully
+// parsing the shader. It populates the "intermediate" with the AST.
+struct DoFullParse{
+ bool operator()(TParseContextBase& parseContext, TPpContext& ppContext,
+ TInputScanner& fullInput, bool versionWillBeError,
+ TSymbolTable&, TIntermediate& intermediate,
+ EShOptimizationLevel optLevel, EShMessages messages)
+ {
+ bool success = true;
+ // Parse the full shader.
+ if (! parseContext.parseShaderStrings(ppContext, fullInput, versionWillBeError))
+ success = false;
+
+ if (success && intermediate.getTreeRoot()) {
+ if (optLevel == EShOptNoGeneration)
+ parseContext.infoSink.info.message(EPrefixNone, "No errors. No code generation or linking was requested.");
+ else
+ success = intermediate.postProcess(intermediate.getTreeRoot(), parseContext.getLanguage());
+ } else if (! success) {
+ parseContext.infoSink.info.prefix(EPrefixError);
+ parseContext.infoSink.info << parseContext.getNumErrors() << " compilation errors. No code generated.\n\n";
+ }
+
+ if (messages & EShMsgAST)
+ intermediate.output(parseContext.infoSink, true);
+
+ return success;
+ }
+};
+
+// Take a single compilation unit, and run the preprocessor on it.
+// Return: True if there were no issues found in preprocessing,
+// False if during preprocessing any unknown version, pragmas or
+// extensions were found.
+//
+// NOTE: Doing just preprocessing to obtain a correct preprocessed shader string
+// is not an officially supported or fully working path.
+bool PreprocessDeferred(
+ TCompiler* compiler,
+ const char* const shaderStrings[],
+ const int numStrings,
+ const int* inputLengths,
+ const char* const stringNames[],
+ const char* preamble,
+ const EShOptimizationLevel optLevel,
+ const TBuiltInResource* resources,
+ int defaultVersion, // use 100 for ES environment, 110 for desktop
+ EProfile defaultProfile,
+ bool forceDefaultVersionAndProfile,
+ bool forwardCompatible, // give errors for use of deprecated features
+ EShMessages messages, // warnings/errors/AST; things to print out
+ TShader::Includer& includer,
+ TIntermediate& intermediate, // returned tree, etc.
+ std::string* outputString)
+{
+ DoPreprocessing parser(outputString);
+ return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames,
+ preamble, optLevel, resources, defaultVersion,
+ defaultProfile, forceDefaultVersionAndProfile,
+ forwardCompatible, messages, intermediate, parser,
+ false, includer);
+}
+
+//
+// do a partial compile on the given strings for a single compilation unit
+// for a potential deferred link into a single stage (and deferred full compile of that
+// stage through machine-dependent compilation).
+//
+// all preprocessing, parsing, semantic checks, etc. for a single compilation unit
+// are done here.
+//
+// return: the tree and other information is filled into the intermediate argument,
+// and true is returned by the function for success.
+//
+bool CompileDeferred(
+ TCompiler* compiler,
+ const char* const shaderStrings[],
+ const int numStrings,
+ const int* inputLengths,
+ const char* const stringNames[],
+ const char* preamble,
+ const EShOptimizationLevel optLevel,
+ const TBuiltInResource* resources,
+ int defaultVersion, // use 100 for ES environment, 110 for desktop
+ EProfile defaultProfile,
+ bool forceDefaultVersionAndProfile,
+ bool forwardCompatible, // give errors for use of deprecated features
+ EShMessages messages, // warnings/errors/AST; things to print out
+ TIntermediate& intermediate,// returned tree, etc.
+ TShader::Includer& includer,
+ const std::string sourceEntryPointName = "",
+ TEnvironment* environment = nullptr)
+{
+ DoFullParse parser;
+ return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames,
+ preamble, optLevel, resources, defaultVersion,
+ defaultProfile, forceDefaultVersionAndProfile,
+ forwardCompatible, messages, intermediate, parser,
+ true, includer, sourceEntryPointName, environment);
+}
+
+} // end anonymous namespace for local functions
+
+//
+// ShInitialize() should be called exactly once per process, not per thread.
+//
+int ShInitialize()
+{
+ glslang::InitGlobalLock();
+
+ if (! InitProcess())
+ return 0;
+
+ glslang::GetGlobalLock();
+ ++NumberOfClients;
+ glslang::ReleaseGlobalLock();
+
+ if (PerProcessGPA == nullptr)
+ PerProcessGPA = new TPoolAllocator();
+
+ glslang::TScanContext::fillInKeywordMap();
+#ifdef ENABLE_HLSL
+ glslang::HlslScanContext::fillInKeywordMap();
+#endif
+
+ return 1;
+}
+
+//
+// Driver calls these to create and destroy compiler/linker
+// objects.
+//
+
+ShHandle ShConstructCompiler(const EShLanguage language, int debugOptions)
+{
+ if (!InitThread())
+ return 0;
+
+ TShHandleBase* base = static_cast<TShHandleBase*>(ConstructCompiler(language, debugOptions));
+
+ return reinterpret_cast<void*>(base);
+}
+
+ShHandle ShConstructLinker(const EShExecutable executable, int debugOptions)
+{
+ if (!InitThread())
+ return 0;
+
+ TShHandleBase* base = static_cast<TShHandleBase*>(ConstructLinker(executable, debugOptions));
+
+ return reinterpret_cast<void*>(base);
+}
+
+ShHandle ShConstructUniformMap()
+{
+ if (!InitThread())
+ return 0;
+
+ TShHandleBase* base = static_cast<TShHandleBase*>(ConstructUniformMap());
+
+ return reinterpret_cast<void*>(base);
+}
+
+void ShDestruct(ShHandle handle)
+{
+ if (handle == 0)
+ return;
+
+ TShHandleBase* base = static_cast<TShHandleBase*>(handle);
+
+ if (base->getAsCompiler())
+ DeleteCompiler(base->getAsCompiler());
+ else if (base->getAsLinker())
+ DeleteLinker(base->getAsLinker());
+ else if (base->getAsUniformMap())
+ DeleteUniformMap(base->getAsUniformMap());
+}
+
+//
+// Cleanup symbol tables
+//
+int ShFinalize()
+{
+ glslang::GetGlobalLock();
+ --NumberOfClients;
+ assert(NumberOfClients >= 0);
+ bool finalize = NumberOfClients == 0;
+ glslang::ReleaseGlobalLock();
+ if (! finalize)
+ return 1;
+
+ for (int version = 0; version < VersionCount; ++version) {
+ for (int spvVersion = 0; spvVersion < SpvVersionCount; ++spvVersion) {
+ for (int p = 0; p < ProfileCount; ++p) {
+ for (int source = 0; source < SourceCount; ++source) {
+ for (int stage = 0; stage < EShLangCount; ++stage) {
+ delete SharedSymbolTables[version][spvVersion][p][source][stage];
+ SharedSymbolTables[version][spvVersion][p][source][stage] = 0;
+ }
+ }
+ }
+ }
+ }
+
+ for (int version = 0; version < VersionCount; ++version) {
+ for (int spvVersion = 0; spvVersion < SpvVersionCount; ++spvVersion) {
+ for (int p = 0; p < ProfileCount; ++p) {
+ for (int source = 0; source < SourceCount; ++source) {
+ for (int pc = 0; pc < EPcCount; ++pc) {
+ delete CommonSymbolTable[version][spvVersion][p][source][pc];
+ CommonSymbolTable[version][spvVersion][p][source][pc] = 0;
+ }
+ }
+ }
+ }
+ }
+
+ if (PerProcessGPA != nullptr) {
+ delete PerProcessGPA;
+ PerProcessGPA = nullptr;
+ }
+
+ glslang::TScanContext::deleteKeywordMap();
+#ifdef ENABLE_HLSL
+ glslang::HlslScanContext::deleteKeywordMap();
+#endif
+
+ return 1;
+}
+
+//
+// Do a full compile on the given strings for a single compilation unit
+// forming a complete stage. The result of the machine dependent compilation
+// is left in the provided compile object.
+//
+// Return: The return value is really boolean, indicating
+// success (1) or failure (0).
+//
+int ShCompile(
+ const ShHandle handle,
+ const char* const shaderStrings[],
+ const int numStrings,
+ const int* inputLengths,
+ const EShOptimizationLevel optLevel,
+ const TBuiltInResource* resources,
+ int /*debugOptions*/,
+ int defaultVersion, // use 100 for ES environment, 110 for desktop
+ bool forwardCompatible, // give errors for use of deprecated features
+ EShMessages messages // warnings/errors/AST; things to print out
+ )
+{
+ // Map the generic handle to the C++ object
+ if (handle == 0)
+ return 0;
+
+ TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
+ TCompiler* compiler = base->getAsCompiler();
+ if (compiler == 0)
+ return 0;
+
+ SetThreadPoolAllocator(compiler->getPool());
+
+ compiler->infoSink.info.erase();
+ compiler->infoSink.debug.erase();
+
+ TIntermediate intermediate(compiler->getLanguage());
+ TShader::ForbidIncluder includer;
+ bool success = CompileDeferred(compiler, shaderStrings, numStrings, inputLengths, nullptr,
+ "", optLevel, resources, defaultVersion, ENoProfile, false,
+ forwardCompatible, messages, intermediate, includer);
+
+ //
+ // Call the machine dependent compiler
+ //
+ if (success && intermediate.getTreeRoot() && optLevel != EShOptNoGeneration)
+ success = compiler->compile(intermediate.getTreeRoot(), intermediate.getVersion(), intermediate.getProfile());
+
+ intermediate.removeTree();
+
+ // Throw away all the temporary memory used by the compilation process.
+ // The push was done in the CompileDeferred() call above.
+ GetThreadPoolAllocator().pop();
+
+ return success ? 1 : 0;
+}
+
+//
+// Link the given compile objects.
+//
+// Return: The return value of is really boolean, indicating
+// success or failure.
+//
+int ShLinkExt(
+ const ShHandle linkHandle,
+ const ShHandle compHandles[],
+ const int numHandles)
+{
+ if (linkHandle == 0 || numHandles == 0)
+ return 0;
+
+ THandleList cObjects;
+
+ for (int i = 0; i < numHandles; ++i) {
+ if (compHandles[i] == 0)
+ return 0;
+ TShHandleBase* base = reinterpret_cast<TShHandleBase*>(compHandles[i]);
+ if (base->getAsLinker()) {
+ cObjects.push_back(base->getAsLinker());
+ }
+ if (base->getAsCompiler())
+ cObjects.push_back(base->getAsCompiler());
+
+ if (cObjects[i] == 0)
+ return 0;
+ }
+
+ TShHandleBase* base = reinterpret_cast<TShHandleBase*>(linkHandle);
+ TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
+
+ SetThreadPoolAllocator(linker->getPool());
+
+ if (linker == 0)
+ return 0;
+
+ linker->infoSink.info.erase();
+
+ for (int i = 0; i < numHandles; ++i) {
+ if (cObjects[i]->getAsCompiler()) {
+ if (! cObjects[i]->getAsCompiler()->linkable()) {
+ linker->infoSink.info.message(EPrefixError, "Not all shaders have valid object code.");
+ return 0;
+ }
+ }
+ }
+
+ bool ret = linker->link(cObjects);
+
+ return ret ? 1 : 0;
+}
+
+//
+// ShSetEncrpytionMethod is a place-holder for specifying
+// how source code is encrypted.
+//
+void ShSetEncryptionMethod(ShHandle handle)
+{
+ if (handle == 0)
+ return;
+}
+
+//
+// Return any compiler/linker/uniformmap log of messages for the application.
+//
+const char* ShGetInfoLog(const ShHandle handle)
+{
+ if (handle == 0)
+ return 0;
+
+ TShHandleBase* base = static_cast<TShHandleBase*>(handle);
+ TInfoSink* infoSink;
+
+ if (base->getAsCompiler())
+ infoSink = &(base->getAsCompiler()->getInfoSink());
+ else if (base->getAsLinker())
+ infoSink = &(base->getAsLinker()->getInfoSink());
+ else
+ return 0;
+
+ infoSink->info << infoSink->debug.c_str();
+ return infoSink->info.c_str();
+}
+
+//
+// Return the resulting binary code from the link process. Structure
+// is machine dependent.
+//
+const void* ShGetExecutable(const ShHandle handle)
+{
+ if (handle == 0)
+ return 0;
+
+ TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
+
+ TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
+ if (linker == 0)
+ return 0;
+
+ return linker->getObjectCode();
+}
+
+//
+// Let the linker know where the application said it's attributes are bound.
+// The linker does not use these values, they are remapped by the ICD or
+// hardware. It just needs them to know what's aliased.
+//
+// Return: The return value of is really boolean, indicating
+// success or failure.
+//
+int ShSetVirtualAttributeBindings(const ShHandle handle, const ShBindingTable* table)
+{
+ if (handle == 0)
+ return 0;
+
+ TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
+ TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
+
+ if (linker == 0)
+ return 0;
+
+ linker->setAppAttributeBindings(table);
+
+ return 1;
+}
+
+//
+// Let the linker know where the predefined attributes have to live.
+//
+int ShSetFixedAttributeBindings(const ShHandle handle, const ShBindingTable* table)
+{
+ if (handle == 0)
+ return 0;
+
+ TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
+ TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
+
+ if (linker == 0)
+ return 0;
+
+ linker->setFixedAttributeBindings(table);
+ return 1;
+}
+
+//
+// Some attribute locations are off-limits to the linker...
+//
+int ShExcludeAttributes(const ShHandle handle, int *attributes, int count)
+{
+ if (handle == 0)
+ return 0;
+
+ TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
+ TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
+ if (linker == 0)
+ return 0;
+
+ linker->setExcludedAttributes(attributes, count);
+
+ return 1;
+}
+
+//
+// Return the index for OpenGL to use for knowing where a uniform lives.
+//
+// Return: The return value of is really boolean, indicating
+// success or failure.
+//
+int ShGetUniformLocation(const ShHandle handle, const char* name)
+{
+ if (handle == 0)
+ return -1;
+
+ TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
+ TUniformMap* uniformMap= base->getAsUniformMap();
+ if (uniformMap == 0)
+ return -1;
+
+ return uniformMap->getLocation(name);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Deferred-Lowering C++ Interface
+// -----------------------------------
+//
+// Below is a new alternate C++ interface that might potentially replace the above
+// opaque handle-based interface.
+//
+// See more detailed comment in ShaderLang.h
+//
+
+namespace glslang {
+
+#include "../Include/revision.h"
+
+#define QUOTE(s) #s
+#define STR(n) QUOTE(n)
+
+const char* GetEsslVersionString()
+{
+ return "OpenGL ES GLSL 3.20 glslang Khronos. " STR(GLSLANG_MINOR_VERSION) "." STR(GLSLANG_PATCH_LEVEL);
+}
+
+const char* GetGlslVersionString()
+{
+ return "4.60 glslang Khronos. " STR(GLSLANG_MINOR_VERSION) "." STR(GLSLANG_PATCH_LEVEL);
+}
+
+int GetKhronosToolId()
+{
+ return 8;
+}
+
+bool InitializeProcess()
+{
+ return ShInitialize() != 0;
+}
+
+void FinalizeProcess()
+{
+ ShFinalize();
+}
+
+class TDeferredCompiler : public TCompiler {
+public:
+ TDeferredCompiler(EShLanguage s, TInfoSink& i) : TCompiler(s, i) { }
+ virtual bool compile(TIntermNode*, int = 0, EProfile = ENoProfile) { return true; }
+};
+
+TShader::TShader(EShLanguage s)
+ : stage(s), lengths(nullptr), stringNames(nullptr), preamble("")
+{
+ pool = new TPoolAllocator;
+ infoSink = new TInfoSink;
+ compiler = new TDeferredCompiler(stage, *infoSink);
+ intermediate = new TIntermediate(s);
+
+ // clear environment (avoid constructors in them for use in a C interface)
+ environment.input.languageFamily = EShSourceNone;
+ environment.input.dialect = EShClientNone;
+ environment.client.client = EShClientNone;
+ environment.target.language = EShTargetNone;
+ environment.target.hlslFunctionality1 = false;
+}
+
+TShader::~TShader()
+{
+ delete infoSink;
+ delete compiler;
+ delete intermediate;
+ delete pool;
+}
+
+void TShader::setStrings(const char* const* s, int n)
+{
+ strings = s;
+ numStrings = n;
+ lengths = nullptr;
+}
+
+void TShader::setStringsWithLengths(const char* const* s, const int* l, int n)
+{
+ strings = s;
+ numStrings = n;
+ lengths = l;
+}
+
+void TShader::setStringsWithLengthsAndNames(
+ const char* const* s, const int* l, const char* const* names, int n)
+{
+ strings = s;
+ numStrings = n;
+ lengths = l;
+ stringNames = names;
+}
+
+void TShader::setEntryPoint(const char* entryPoint)
+{
+ intermediate->setEntryPointName(entryPoint);
+}
+
+void TShader::setSourceEntryPoint(const char* name)
+{
+ sourceEntryPointName = name;
+}
+
+void TShader::addProcesses(const std::vector<std::string>& p)
+{
+ intermediate->addProcesses(p);
+}
+
+// Set binding base for given resource type
+void TShader::setShiftBinding(TResourceType res, unsigned int base) {
+ intermediate->setShiftBinding(res, base);
+}
+
+// Set binding base for given resource type for a given binding set.
+void TShader::setShiftBindingForSet(TResourceType res, unsigned int base, unsigned int set) {
+ intermediate->setShiftBindingForSet(res, base, set);
+}
+
+// Set binding base for sampler types
+void TShader::setShiftSamplerBinding(unsigned int base) { setShiftBinding(EResSampler, base); }
+// Set binding base for texture types (SRV)
+void TShader::setShiftTextureBinding(unsigned int base) { setShiftBinding(EResTexture, base); }
+// Set binding base for image types
+void TShader::setShiftImageBinding(unsigned int base) { setShiftBinding(EResImage, base); }
+// Set binding base for uniform buffer objects (CBV)
+void TShader::setShiftUboBinding(unsigned int base) { setShiftBinding(EResUbo, base); }
+// Synonym for setShiftUboBinding, to match HLSL language.
+void TShader::setShiftCbufferBinding(unsigned int base) { setShiftBinding(EResUbo, base); }
+// Set binding base for UAV (unordered access view)
+void TShader::setShiftUavBinding(unsigned int base) { setShiftBinding(EResUav, base); }
+// Set binding base for SSBOs
+void TShader::setShiftSsboBinding(unsigned int base) { setShiftBinding(EResSsbo, base); }
+// Enables binding automapping using TIoMapper
+void TShader::setAutoMapBindings(bool map) { intermediate->setAutoMapBindings(map); }
+// Enables position.Y output negation in vertex shader
+void TShader::setInvertY(bool invert) { intermediate->setInvertY(invert); }
+// Fragile: currently within one stage: simple auto-assignment of location
+void TShader::setAutoMapLocations(bool map) { intermediate->setAutoMapLocations(map); }
+void TShader::addUniformLocationOverride(const char* name, int loc)
+{
+ intermediate->addUniformLocationOverride(name, loc);
+}
+void TShader::setUniformLocationBase(int base)
+{
+ intermediate->setUniformLocationBase(base);
+}
+// See comment above TDefaultHlslIoMapper in iomapper.cpp:
+void TShader::setHlslIoMapping(bool hlslIoMap) { intermediate->setHlslIoMapping(hlslIoMap); }
+void TShader::setFlattenUniformArrays(bool flatten) { intermediate->setFlattenUniformArrays(flatten); }
+void TShader::setNoStorageFormat(bool useUnknownFormat) { intermediate->setNoStorageFormat(useUnknownFormat); }
+void TShader::setResourceSetBinding(const std::vector<std::string>& base) { intermediate->setResourceSetBinding(base); }
+void TShader::setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode) { intermediate->setTextureSamplerTransformMode(mode); }
+
+//
+// Turn the shader strings into a parse tree in the TIntermediate.
+//
+// Returns true for success.
+//
+bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile,
+ bool forwardCompatible, EShMessages messages, Includer& includer)
+{
+ if (! InitThread())
+ return false;
+ SetThreadPoolAllocator(pool);
+
+ if (! preamble)
+ preamble = "";
+
+ return CompileDeferred(compiler, strings, numStrings, lengths, stringNames,
+ preamble, EShOptNone, builtInResources, defaultVersion,
+ defaultProfile, forceDefaultVersionAndProfile,
+ forwardCompatible, messages, *intermediate, includer, sourceEntryPointName,
+ &environment);
+}
+
+// Fill in a string with the result of preprocessing ShaderStrings
+// Returns true if all extensions, pragmas and version strings were valid.
+//
+// NOTE: Doing just preprocessing to obtain a correct preprocessed shader string
+// is not an officially supported or fully working path.
+bool TShader::preprocess(const TBuiltInResource* builtInResources,
+ int defaultVersion, EProfile defaultProfile,
+ bool forceDefaultVersionAndProfile,
+ bool forwardCompatible, EShMessages message,
+ std::string* output_string,
+ Includer& includer)
+{
+ if (! InitThread())
+ return false;
+ SetThreadPoolAllocator(pool);
+
+ if (! preamble)
+ preamble = "";
+
+ return PreprocessDeferred(compiler, strings, numStrings, lengths, stringNames, preamble,
+ EShOptNone, builtInResources, defaultVersion,
+ defaultProfile, forceDefaultVersionAndProfile,
+ forwardCompatible, message, includer, *intermediate, output_string);
+}
+
+const char* TShader::getInfoLog()
+{
+ return infoSink->info.c_str();
+}
+
+const char* TShader::getInfoDebugLog()
+{
+ return infoSink->debug.c_str();
+}
+
+TProgram::TProgram() : reflection(0), ioMapper(nullptr), linked(false)
+{
+ pool = new TPoolAllocator;
+ infoSink = new TInfoSink;
+ for (int s = 0; s < EShLangCount; ++s) {
+ intermediate[s] = 0;
+ newedIntermediate[s] = false;
+ }
+}
+
+TProgram::~TProgram()
+{
+ delete ioMapper;
+ delete infoSink;
+ delete reflection;
+
+ for (int s = 0; s < EShLangCount; ++s)
+ if (newedIntermediate[s])
+ delete intermediate[s];
+
+ delete pool;
+}
+
+//
+// Merge the compilation units within each stage into a single TIntermediate.
+// All starting compilation units need to be the result of calling TShader::parse().
+//
+// Return true for success.
+//
+bool TProgram::link(EShMessages messages)
+{
+ if (linked)
+ return false;
+ linked = true;
+
+ bool error = false;
+
+ SetThreadPoolAllocator(pool);
+
+ for (int s = 0; s < EShLangCount; ++s) {
+ if (! linkStage((EShLanguage)s, messages))
+ error = true;
+ }
+
+ // TODO: Link: cross-stage error checking
+
+ return ! error;
+}
+
+//
+// Merge the compilation units within the given stage into a single TIntermediate.
+//
+// Return true for success.
+//
+bool TProgram::linkStage(EShLanguage stage, EShMessages messages)
+{
+ if (stages[stage].size() == 0)
+ return true;
+
+ int numEsShaders = 0, numNonEsShaders = 0;
+ for (auto it = stages[stage].begin(); it != stages[stage].end(); ++it) {
+ if ((*it)->intermediate->getProfile() == EEsProfile) {
+ numEsShaders++;
+ } else {
+ numNonEsShaders++;
+ }
+ }
+
+ if (numEsShaders > 0 && numNonEsShaders > 0) {
+ infoSink->info.message(EPrefixError, "Cannot mix ES profile with non-ES profile shaders");
+ return false;
+ } else if (numEsShaders > 1) {
+ infoSink->info.message(EPrefixError, "Cannot attach multiple ES shaders of the same type to a single program");
+ return false;
+ }
+
+ //
+ // Be efficient for the common single compilation unit per stage case,
+ // reusing it's TIntermediate instead of merging into a new one.
+ //
+ TIntermediate *firstIntermediate = stages[stage].front()->intermediate;
+ if (stages[stage].size() == 1)
+ intermediate[stage] = firstIntermediate;
+ else {
+ intermediate[stage] = new TIntermediate(stage,
+ firstIntermediate->getVersion(),
+ firstIntermediate->getProfile());
+
+
+ // The new TIntermediate must use the same origin as the original TIntermediates.
+ // Otherwise linking will fail due to different coordinate systems.
+ if (firstIntermediate->getOriginUpperLeft()) {
+ intermediate[stage]->setOriginUpperLeft();
+ }
+ intermediate[stage]->setSpv(firstIntermediate->getSpv());
+
+ newedIntermediate[stage] = true;
+ }
+
+ if (messages & EShMsgAST)
+ infoSink->info << "\nLinked " << StageName(stage) << " stage:\n\n";
+
+ if (stages[stage].size() > 1) {
+ std::list<TShader*>::const_iterator it;
+ for (it = stages[stage].begin(); it != stages[stage].end(); ++it)
+ intermediate[stage]->merge(*infoSink, *(*it)->intermediate);
+ }
+
+ intermediate[stage]->finalCheck(*infoSink, (messages & EShMsgKeepUncalled) != 0);
+
+ if (messages & EShMsgAST)
+ intermediate[stage]->output(*infoSink, true);
+
+ return intermediate[stage]->getNumErrors() == 0;
+}
+
+const char* TProgram::getInfoLog()
+{
+ return infoSink->info.c_str();
+}
+
+const char* TProgram::getInfoDebugLog()
+{
+ return infoSink->debug.c_str();
+}
+
+//
+// Reflection implementation.
+//
+
+bool TProgram::buildReflection(int opts)
+{
+ if (! linked || reflection)
+ return false;
+
+ int firstStage = EShLangVertex, lastStage = EShLangFragment;
+
+ if (opts & EShReflectionIntermediateIO) {
+ // if we're reflecting intermediate I/O, determine the first and last stage linked and use those as the
+ // boundaries for which stages generate pipeline inputs/outputs
+ firstStage = EShLangCount;
+ lastStage = 0;
+ for (int s = 0; s < EShLangCount; ++s) {
+ if (intermediate[s]) {
+ firstStage = std::min(firstStage, s);
+ lastStage = std::max(lastStage, s);
+ }
+ }
+ }
+
+ reflection = new TReflection((EShReflectionOptions)opts, (EShLanguage)firstStage, (EShLanguage)lastStage);
+
+ for (int s = 0; s < EShLangCount; ++s) {
+ if (intermediate[s]) {
+ if (! reflection->addStage((EShLanguage)s, *intermediate[s]))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+unsigned TProgram::getLocalSize(int dim) const { return reflection->getLocalSize(dim); }
+int TProgram::getReflectionIndex(const char* name) const { return reflection->getIndex(name); }
+
+int TProgram::getNumUniformVariables() const { return reflection->getNumUniforms(); }
+const TObjectReflection& TProgram::getUniform(int index) const { return reflection->getUniform(index); }
+int TProgram::getNumUniformBlocks() const { return reflection->getNumUniformBlocks(); }
+const TObjectReflection& TProgram::getUniformBlock(int index) const { return reflection->getUniformBlock(index); }
+int TProgram::getNumPipeInputs() const { return reflection->getNumPipeInputs(); }
+const TObjectReflection& TProgram::getPipeInput(int index) const { return reflection->getPipeInput(index); }
+int TProgram::getNumPipeOutputs() const { return reflection->getNumPipeOutputs(); }
+const TObjectReflection& TProgram::getPipeOutput(int index) const { return reflection->getPipeOutput(index); }
+int TProgram::getNumBufferVariables() const { return reflection->getNumBufferVariables(); }
+const TObjectReflection& TProgram::getBufferVariable(int index) const { return reflection->getBufferVariable(index); }
+int TProgram::getNumBufferBlocks() const { return reflection->getNumStorageBuffers(); }
+const TObjectReflection& TProgram::getBufferBlock(int index) const { return reflection->getStorageBufferBlock(index); }
+int TProgram::getNumAtomicCounters() const { return reflection->getNumAtomicCounters(); }
+const TObjectReflection& TProgram::getAtomicCounter(int index) const { return reflection->getAtomicCounter(index); }
+
+void TProgram::dumpReflection() { reflection->dump(); }
+
+//
+// I/O mapping implementation.
+//
+bool TProgram::mapIO(TIoMapResolver* resolver)
+{
+ if (! linked || ioMapper)
+ return false;
+
+ ioMapper = new TIoMapper;
+
+ for (int s = 0; s < EShLangCount; ++s) {
+ if (intermediate[s]) {
+ if (! ioMapper->addStage((EShLanguage)s, *intermediate[s], *infoSink, resolver))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/SymbolTable.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/SymbolTable.cpp
new file mode 100644
index 0000000..d8d6846
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/SymbolTable.cpp
@@ -0,0 +1,396 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2012-2013 LunarG, Inc.
+// Copyright (C) 2017 ARM Limited.
+// Copyright (C) 2015-2018 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+//
+// Symbol table for parsing. Most functionality and main ideas
+// are documented in the header file.
+//
+
+#include "SymbolTable.h"
+
+namespace glslang {
+
+//
+// TType helper function needs a place to live.
+//
+
+//
+// Recursively generate mangled names.
+//
+void TType::buildMangledName(TString& mangledName) const
+{
+ if (isMatrix())
+ mangledName += 'm';
+ else if (isVector())
+ mangledName += 'v';
+
+ switch (basicType) {
+ case EbtFloat: mangledName += 'f'; break;
+ case EbtDouble: mangledName += 'd'; break;
+ case EbtFloat16: mangledName += "f16"; break;
+ case EbtInt: mangledName += 'i'; break;
+ case EbtUint: mangledName += 'u'; break;
+ case EbtInt8: mangledName += "i8"; break;
+ case EbtUint8: mangledName += "u8"; break;
+ case EbtInt16: mangledName += "i16"; break;
+ case EbtUint16: mangledName += "u16"; break;
+ case EbtInt64: mangledName += "i64"; break;
+ case EbtUint64: mangledName += "u64"; break;
+ case EbtBool: mangledName += 'b'; break;
+ case EbtAtomicUint: mangledName += "au"; break;
+#ifdef NV_EXTENSIONS
+ case EbtAccStructNV: mangledName += "asnv"; break;
+#endif
+ case EbtSampler:
+ switch (sampler.type) {
+#ifdef AMD_EXTENSIONS
+ case EbtFloat16: mangledName += "f16"; break;
+#endif
+ case EbtInt: mangledName += "i"; break;
+ case EbtUint: mangledName += "u"; break;
+ default: break; // some compilers want this
+ }
+ if (sampler.image)
+ mangledName += "I"; // a normal image
+ else if (sampler.sampler)
+ mangledName += "p"; // a "pure" sampler
+ else if (!sampler.combined)
+ mangledName += "t"; // a "pure" texture
+ else
+ mangledName += "s"; // traditional combined sampler
+ if (sampler.arrayed)
+ mangledName += "A";
+ if (sampler.shadow)
+ mangledName += "S";
+ if (sampler.external)
+ mangledName += "E";
+ if (sampler.yuv)
+ mangledName += "Y";
+ switch (sampler.dim) {
+ case Esd1D: mangledName += "1"; break;
+ case Esd2D: mangledName += "2"; break;
+ case Esd3D: mangledName += "3"; break;
+ case EsdCube: mangledName += "C"; break;
+ case EsdRect: mangledName += "R2"; break;
+ case EsdBuffer: mangledName += "B"; break;
+ case EsdSubpass: mangledName += "P"; break;
+ default: break; // some compilers want this
+ }
+
+ if (sampler.hasReturnStruct()) {
+ // Name mangle for sampler return struct uses struct table index.
+ mangledName += "-tx-struct";
+
+ char text[16]; // plenty enough space for the small integers.
+ snprintf(text, sizeof(text), "%d-", sampler.structReturnIndex);
+ mangledName += text;
+ } else {
+ switch (sampler.getVectorSize()) {
+ case 1: mangledName += "1"; break;
+ case 2: mangledName += "2"; break;
+ case 3: mangledName += "3"; break;
+ case 4: break; // default to prior name mangle behavior
+ }
+ }
+
+ if (sampler.ms)
+ mangledName += "M";
+ break;
+ case EbtStruct:
+ case EbtBlock:
+ if (basicType == EbtStruct)
+ mangledName += "struct-";
+ else
+ mangledName += "block-";
+ if (typeName)
+ mangledName += *typeName;
+ for (unsigned int i = 0; i < structure->size(); ++i) {
+ mangledName += '-';
+ (*structure)[i].type->buildMangledName(mangledName);
+ }
+ default:
+ break;
+ }
+
+ if (getVectorSize() > 0)
+ mangledName += static_cast<char>('0' + getVectorSize());
+ else {
+ mangledName += static_cast<char>('0' + getMatrixCols());
+ mangledName += static_cast<char>('0' + getMatrixRows());
+ }
+
+ if (arraySizes) {
+ const int maxSize = 11;
+ char buf[maxSize];
+ for (int i = 0; i < arraySizes->getNumDims(); ++i) {
+ if (arraySizes->getDimNode(i)) {
+ if (arraySizes->getDimNode(i)->getAsSymbolNode())
+ snprintf(buf, maxSize, "s%d", arraySizes->getDimNode(i)->getAsSymbolNode()->getId());
+ else
+ snprintf(buf, maxSize, "s%p", arraySizes->getDimNode(i));
+ } else
+ snprintf(buf, maxSize, "%d", arraySizes->getDimSize(i));
+ mangledName += '[';
+ mangledName += buf;
+ mangledName += ']';
+ }
+ }
+}
+
+//
+// Dump functions.
+//
+
+void TVariable::dump(TInfoSink& infoSink) const
+{
+ infoSink.debug << getName().c_str() << ": " << type.getStorageQualifierString() << " " << type.getBasicTypeString();
+ if (type.isArray()) {
+ infoSink.debug << "[0]";
+ }
+ infoSink.debug << "\n";
+}
+
+void TFunction::dump(TInfoSink& infoSink) const
+{
+ infoSink.debug << getName().c_str() << ": " << returnType.getBasicTypeString() << " " << getMangledName().c_str() << "\n";
+}
+
+void TAnonMember::dump(TInfoSink& TInfoSink) const
+{
+ TInfoSink.debug << "anonymous member " << getMemberNumber() << " of " << getAnonContainer().getName().c_str() << "\n";
+}
+
+void TSymbolTableLevel::dump(TInfoSink &infoSink) const
+{
+ tLevel::const_iterator it;
+ for (it = level.begin(); it != level.end(); ++it)
+ (*it).second->dump(infoSink);
+}
+
+void TSymbolTable::dump(TInfoSink &infoSink) const
+{
+ for (int level = currentLevel(); level >= 0; --level) {
+ infoSink.debug << "LEVEL " << level << "\n";
+ table[level]->dump(infoSink);
+ }
+}
+
+//
+// Functions have buried pointers to delete.
+//
+TFunction::~TFunction()
+{
+ for (TParamList::iterator i = parameters.begin(); i != parameters.end(); ++i)
+ delete (*i).type;
+}
+
+//
+// Symbol table levels are a map of pointers to symbols that have to be deleted.
+//
+TSymbolTableLevel::~TSymbolTableLevel()
+{
+ for (tLevel::iterator it = level.begin(); it != level.end(); ++it)
+ delete (*it).second;
+
+ delete [] defaultPrecision;
+}
+
+//
+// Change all function entries in the table with the non-mangled name
+// to be related to the provided built-in operation.
+//
+void TSymbolTableLevel::relateToOperator(const char* name, TOperator op)
+{
+ tLevel::const_iterator candidate = level.lower_bound(name);
+ while (candidate != level.end()) {
+ const TString& candidateName = (*candidate).first;
+ TString::size_type parenAt = candidateName.find_first_of('(');
+ if (parenAt != candidateName.npos && candidateName.compare(0, parenAt, name) == 0) {
+ TFunction* function = (*candidate).second->getAsFunction();
+ function->relateToOperator(op);
+ } else
+ break;
+ ++candidate;
+ }
+}
+
+// Make all function overloads of the given name require an extension(s).
+// Should only be used for a version/profile that actually needs the extension(s).
+void TSymbolTableLevel::setFunctionExtensions(const char* name, int num, const char* const extensions[])
+{
+ tLevel::const_iterator candidate = level.lower_bound(name);
+ while (candidate != level.end()) {
+ const TString& candidateName = (*candidate).first;
+ TString::size_type parenAt = candidateName.find_first_of('(');
+ if (parenAt != candidateName.npos && candidateName.compare(0, parenAt, name) == 0) {
+ TSymbol* symbol = candidate->second;
+ symbol->setExtensions(num, extensions);
+ } else
+ break;
+ ++candidate;
+ }
+}
+
+//
+// Make all symbols in this table level read only.
+//
+void TSymbolTableLevel::readOnly()
+{
+ for (tLevel::iterator it = level.begin(); it != level.end(); ++it)
+ (*it).second->makeReadOnly();
+}
+
+//
+// Copy a symbol, but the copy is writable; call readOnly() afterward if that's not desired.
+//
+TSymbol::TSymbol(const TSymbol& copyOf)
+{
+ name = NewPoolTString(copyOf.name->c_str());
+ uniqueId = copyOf.uniqueId;
+ writable = true;
+}
+
+TVariable::TVariable(const TVariable& copyOf) : TSymbol(copyOf)
+{
+ type.deepCopy(copyOf.type);
+ userType = copyOf.userType;
+
+ // we don't support specialization-constant subtrees in cloned tables, only extensions
+ constSubtree = nullptr;
+ extensions = nullptr;
+ memberExtensions = nullptr;
+ if (copyOf.getNumExtensions() > 0)
+ setExtensions(copyOf.getNumExtensions(), copyOf.getExtensions());
+ if (copyOf.hasMemberExtensions()) {
+ for (int m = 0; m < (int)copyOf.type.getStruct()->size(); ++m) {
+ if (copyOf.getNumMemberExtensions(m) > 0)
+ setMemberExtensions(m, copyOf.getNumMemberExtensions(m), copyOf.getMemberExtensions(m));
+ }
+ }
+
+ if (! copyOf.constArray.empty()) {
+ assert(! copyOf.type.isStruct());
+ TConstUnionArray newArray(copyOf.constArray, 0, copyOf.constArray.size());
+ constArray = newArray;
+ }
+}
+
+TVariable* TVariable::clone() const
+{
+ TVariable *variable = new TVariable(*this);
+
+ return variable;
+}
+
+TFunction::TFunction(const TFunction& copyOf) : TSymbol(copyOf)
+{
+ for (unsigned int i = 0; i < copyOf.parameters.size(); ++i) {
+ TParameter param;
+ parameters.push_back(param);
+ parameters.back().copyParam(copyOf.parameters[i]);
+ }
+
+ extensions = nullptr;
+ if (copyOf.getNumExtensions() > 0)
+ setExtensions(copyOf.getNumExtensions(), copyOf.getExtensions());
+ returnType.deepCopy(copyOf.returnType);
+ mangledName = copyOf.mangledName;
+ op = copyOf.op;
+ defined = copyOf.defined;
+ prototyped = copyOf.prototyped;
+ implicitThis = copyOf.implicitThis;
+ illegalImplicitThis = copyOf.illegalImplicitThis;
+ defaultParamCount = copyOf.defaultParamCount;
+}
+
+TFunction* TFunction::clone() const
+{
+ TFunction *function = new TFunction(*this);
+
+ return function;
+}
+
+TAnonMember* TAnonMember::clone() const
+{
+ // Anonymous members of a given block should be cloned at a higher level,
+ // where they can all be assured to still end up pointing to a single
+ // copy of the original container.
+ assert(0);
+
+ return 0;
+}
+
+TSymbolTableLevel* TSymbolTableLevel::clone() const
+{
+ TSymbolTableLevel *symTableLevel = new TSymbolTableLevel();
+ symTableLevel->anonId = anonId;
+ symTableLevel->thisLevel = thisLevel;
+ std::vector<bool> containerCopied(anonId, false);
+ tLevel::const_iterator iter;
+ for (iter = level.begin(); iter != level.end(); ++iter) {
+ const TAnonMember* anon = iter->second->getAsAnonMember();
+ if (anon) {
+ // Insert all the anonymous members of this same container at once,
+ // avoid inserting the remaining members in the future, once this has been done,
+ // allowing them to all be part of the same new container.
+ if (! containerCopied[anon->getAnonId()]) {
+ TVariable* container = anon->getAnonContainer().clone();
+ container->changeName(NewPoolTString(""));
+ // insert the container and all its members
+ symTableLevel->insert(*container, false);
+ containerCopied[anon->getAnonId()] = true;
+ }
+ } else
+ symTableLevel->insert(*iter->second->clone(), false);
+ }
+
+ return symTableLevel;
+}
+
+void TSymbolTable::copyTable(const TSymbolTable& copyOf)
+{
+ assert(adoptedLevels == copyOf.adoptedLevels);
+
+ uniqueId = copyOf.uniqueId;
+ noBuiltInRedeclarations = copyOf.noBuiltInRedeclarations;
+ separateNameSpaces = copyOf.separateNameSpaces;
+ for (unsigned int i = copyOf.adoptedLevels; i < copyOf.table.size(); ++i)
+ table.push_back(copyOf.table[i]->clone());
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/SymbolTable.h b/src/3rdparty/glslang/glslang/MachineIndependent/SymbolTable.h
new file mode 100644
index 0000000..f9c1903
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/SymbolTable.h
@@ -0,0 +1,871 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2013 LunarG, Inc.
+// Copyright (C) 2015-2018 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef _SYMBOL_TABLE_INCLUDED_
+#define _SYMBOL_TABLE_INCLUDED_
+
+//
+// Symbol table for parsing. Has these design characteristics:
+//
+// * Same symbol table can be used to compile many shaders, to preserve
+// effort of creating and loading with the large numbers of built-in
+// symbols.
+//
+// --> This requires a copy mechanism, so initial pools used to create
+// the shared information can be popped. Done through "clone"
+// methods.
+//
+// * Name mangling will be used to give each function a unique name
+// so that symbol table lookups are never ambiguous. This allows
+// a simpler symbol table structure.
+//
+// * Pushing and popping of scope, so symbol table will really be a stack
+// of symbol tables. Searched from the top, with new inserts going into
+// the top.
+//
+// * Constants: Compile time constant symbols will keep their values
+// in the symbol table. The parser can substitute constants at parse
+// time, including doing constant folding and constant propagation.
+//
+// * No temporaries: Temporaries made from operations (+, --, .xy, etc.)
+// are tracked in the intermediate representation, not the symbol table.
+//
+
+#include "../Include/Common.h"
+#include "../Include/intermediate.h"
+#include "../Include/InfoSink.h"
+
+namespace glslang {
+
+//
+// Symbol base class. (Can build functions or variables out of these...)
+//
+
+class TVariable;
+class TFunction;
+class TAnonMember;
+
+typedef TVector<const char*> TExtensionList;
+
+class TSymbol {
+public:
+ POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator())
+ explicit TSymbol(const TString *n) : name(n), extensions(0), writable(true) { }
+ virtual TSymbol* clone() const = 0;
+ virtual ~TSymbol() { } // rely on all symbol owned memory coming from the pool
+
+ virtual const TString& getName() const { return *name; }
+ virtual void changeName(const TString* newName) { name = newName; }
+ virtual void addPrefix(const char* prefix)
+ {
+ TString newName(prefix);
+ newName.append(*name);
+ changeName(NewPoolTString(newName.c_str()));
+ }
+ virtual const TString& getMangledName() const { return getName(); }
+ virtual TFunction* getAsFunction() { return 0; }
+ virtual const TFunction* getAsFunction() const { return 0; }
+ virtual TVariable* getAsVariable() { return 0; }
+ virtual const TVariable* getAsVariable() const { return 0; }
+ virtual const TAnonMember* getAsAnonMember() const { return 0; }
+ virtual const TType& getType() const = 0;
+ virtual TType& getWritableType() = 0;
+ virtual void setUniqueId(int id) { uniqueId = id; }
+ virtual int getUniqueId() const { return uniqueId; }
+ virtual void setExtensions(int numExts, const char* const exts[])
+ {
+ assert(extensions == 0);
+ assert(numExts > 0);
+ extensions = NewPoolObject(extensions);
+ for (int e = 0; e < numExts; ++e)
+ extensions->push_back(exts[e]);
+ }
+ virtual int getNumExtensions() const { return extensions == nullptr ? 0 : (int)extensions->size(); }
+ virtual const char** getExtensions() const { return extensions->data(); }
+ virtual void dump(TInfoSink &infoSink) const = 0;
+
+ virtual bool isReadOnly() const { return ! writable; }
+ virtual void makeReadOnly() { writable = false; }
+
+protected:
+ explicit TSymbol(const TSymbol&);
+ TSymbol& operator=(const TSymbol&);
+
+ const TString *name;
+ unsigned int uniqueId; // For cross-scope comparing during code generation
+
+ // For tracking what extensions must be present
+ // (don't use if correct version/profile is present).
+ TExtensionList* extensions; // an array of pointers to existing constant char strings
+
+ //
+ // N.B.: Non-const functions that will be generally used should assert on this,
+ // to avoid overwriting shared symbol-table information.
+ //
+ bool writable;
+};
+
+//
+// Variable class, meaning a symbol that's not a function.
+//
+// There could be a separate class hierarchy for Constant variables;
+// Only one of int, bool, or float, (or none) is correct for
+// any particular use, but it's easy to do this way, and doesn't
+// seem worth having separate classes, and "getConst" can't simply return
+// different values for different types polymorphically, so this is
+// just simple and pragmatic.
+//
+class TVariable : public TSymbol {
+public:
+ TVariable(const TString *name, const TType& t, bool uT = false )
+ : TSymbol(name),
+ userType(uT),
+ constSubtree(nullptr),
+ memberExtensions(nullptr),
+ anonId(-1)
+ { type.shallowCopy(t); }
+ virtual TVariable* clone() const;
+ virtual ~TVariable() { }
+
+ virtual TVariable* getAsVariable() { return this; }
+ virtual const TVariable* getAsVariable() const { return this; }
+ virtual const TType& getType() const { return type; }
+ virtual TType& getWritableType() { assert(writable); return type; }
+ virtual bool isUserType() const { return userType; }
+ virtual const TConstUnionArray& getConstArray() const { return constArray; }
+ virtual TConstUnionArray& getWritableConstArray() { assert(writable); return constArray; }
+ virtual void setConstArray(const TConstUnionArray& array) { constArray = array; }
+ virtual void setConstSubtree(TIntermTyped* subtree) { constSubtree = subtree; }
+ virtual TIntermTyped* getConstSubtree() const { return constSubtree; }
+ virtual void setAnonId(int i) { anonId = i; }
+ virtual int getAnonId() const { return anonId; }
+
+ virtual void setMemberExtensions(int member, int numExts, const char* const exts[])
+ {
+ assert(type.isStruct());
+ assert(numExts > 0);
+ if (memberExtensions == nullptr) {
+ memberExtensions = NewPoolObject(memberExtensions);
+ memberExtensions->resize(type.getStruct()->size());
+ }
+ for (int e = 0; e < numExts; ++e)
+ (*memberExtensions)[member].push_back(exts[e]);
+ }
+ virtual bool hasMemberExtensions() const { return memberExtensions != nullptr; }
+ virtual int getNumMemberExtensions(int member) const
+ {
+ return memberExtensions == nullptr ? 0 : (int)(*memberExtensions)[member].size();
+ }
+ virtual const char** getMemberExtensions(int member) const { return (*memberExtensions)[member].data(); }
+
+ virtual void dump(TInfoSink &infoSink) const;
+
+protected:
+ explicit TVariable(const TVariable&);
+ TVariable& operator=(const TVariable&);
+
+ TType type;
+ bool userType;
+
+ // we are assuming that Pool Allocator will free the memory allocated to unionArray
+ // when this object is destroyed
+
+ TConstUnionArray constArray; // for compile-time constant value
+ TIntermTyped* constSubtree; // for specialization constant computation
+ TVector<TExtensionList>* memberExtensions; // per-member extension list, allocated only when needed
+ int anonId; // the ID used for anonymous blocks: TODO: see if uniqueId could serve a dual purpose
+};
+
+//
+// The function sub-class of symbols and the parser will need to
+// share this definition of a function parameter.
+//
+struct TParameter {
+ TString *name;
+ TType* type;
+ TIntermTyped* defaultValue;
+ void copyParam(const TParameter& param)
+ {
+ if (param.name)
+ name = NewPoolTString(param.name->c_str());
+ else
+ name = 0;
+ type = param.type->clone();
+ defaultValue = param.defaultValue;
+ }
+ TBuiltInVariable getDeclaredBuiltIn() const { return type->getQualifier().declaredBuiltIn; }
+};
+
+//
+// The function sub-class of a symbol.
+//
+class TFunction : public TSymbol {
+public:
+ explicit TFunction(TOperator o) :
+ TSymbol(0),
+ op(o),
+ defined(false), prototyped(false), implicitThis(false), illegalImplicitThis(false), defaultParamCount(0) { }
+ TFunction(const TString *name, const TType& retType, TOperator tOp = EOpNull) :
+ TSymbol(name),
+ mangledName(*name + '('),
+ op(tOp),
+ defined(false), prototyped(false), implicitThis(false), illegalImplicitThis(false), defaultParamCount(0)
+ {
+ returnType.shallowCopy(retType);
+ declaredBuiltIn = retType.getQualifier().builtIn;
+ }
+ virtual TFunction* clone() const override;
+ virtual ~TFunction();
+
+ virtual TFunction* getAsFunction() override { return this; }
+ virtual const TFunction* getAsFunction() const override { return this; }
+
+ // Install 'p' as the (non-'this') last parameter.
+ // Non-'this' parameters are reflected in both the list of parameters and the
+ // mangled name.
+ virtual void addParameter(TParameter& p)
+ {
+ assert(writable);
+ parameters.push_back(p);
+ p.type->appendMangledName(mangledName);
+
+ if (p.defaultValue != nullptr)
+ defaultParamCount++;
+ }
+
+ // Install 'this' as the first parameter.
+ // 'this' is reflected in the list of parameters, but not the mangled name.
+ virtual void addThisParameter(TType& type, const char* name)
+ {
+ TParameter p = { NewPoolTString(name), new TType, nullptr };
+ p.type->shallowCopy(type);
+ parameters.insert(parameters.begin(), p);
+ }
+
+ virtual void addPrefix(const char* prefix) override
+ {
+ TSymbol::addPrefix(prefix);
+ mangledName.insert(0, prefix);
+ }
+
+ virtual void removePrefix(const TString& prefix)
+ {
+ assert(mangledName.compare(0, prefix.size(), prefix) == 0);
+ mangledName.erase(0, prefix.size());
+ }
+
+ virtual const TString& getMangledName() const override { return mangledName; }
+ virtual const TType& getType() const override { return returnType; }
+ virtual TBuiltInVariable getDeclaredBuiltInType() const { return declaredBuiltIn; }
+ virtual TType& getWritableType() override { return returnType; }
+ virtual void relateToOperator(TOperator o) { assert(writable); op = o; }
+ virtual TOperator getBuiltInOp() const { return op; }
+ virtual void setDefined() { assert(writable); defined = true; }
+ virtual bool isDefined() const { return defined; }
+ virtual void setPrototyped() { assert(writable); prototyped = true; }
+ virtual bool isPrototyped() const { return prototyped; }
+ virtual void setImplicitThis() { assert(writable); implicitThis = true; }
+ virtual bool hasImplicitThis() const { return implicitThis; }
+ virtual void setIllegalImplicitThis() { assert(writable); illegalImplicitThis = true; }
+ virtual bool hasIllegalImplicitThis() const { return illegalImplicitThis; }
+
+ // Return total number of parameters
+ virtual int getParamCount() const { return static_cast<int>(parameters.size()); }
+ // Return number of parameters with default values.
+ virtual int getDefaultParamCount() const { return defaultParamCount; }
+ // Return number of fixed parameters (without default values)
+ virtual int getFixedParamCount() const { return getParamCount() - getDefaultParamCount(); }
+
+ virtual TParameter& operator[](int i) { assert(writable); return parameters[i]; }
+ virtual const TParameter& operator[](int i) const { return parameters[i]; }
+
+ virtual void dump(TInfoSink &infoSink) const override;
+
+protected:
+ explicit TFunction(const TFunction&);
+ TFunction& operator=(const TFunction&);
+
+ typedef TVector<TParameter> TParamList;
+ TParamList parameters;
+ TType returnType;
+ TBuiltInVariable declaredBuiltIn;
+
+ TString mangledName;
+ TOperator op;
+ bool defined;
+ bool prototyped;
+ bool implicitThis; // True if this function is allowed to see all members of 'this'
+ bool illegalImplicitThis; // True if this function is not supposed to have access to dynamic members of 'this',
+ // even if it finds member variables in the symbol table.
+ // This is important for a static member function that has member variables in scope,
+ // but is not allowed to use them, or see hidden symbols instead.
+ int defaultParamCount;
+};
+
+//
+// Members of anonymous blocks are a kind of TSymbol. They are not hidden in
+// the symbol table behind a container; rather they are visible and point to
+// their anonymous container. (The anonymous container is found through the
+// member, not the other way around.)
+//
+class TAnonMember : public TSymbol {
+public:
+ TAnonMember(const TString* n, unsigned int m, TVariable& a, int an) : TSymbol(n), anonContainer(a), memberNumber(m), anonId(an) { }
+ virtual TAnonMember* clone() const override;
+ virtual ~TAnonMember() { }
+
+ virtual const TAnonMember* getAsAnonMember() const override { return this; }
+ virtual const TVariable& getAnonContainer() const { return anonContainer; }
+ virtual unsigned int getMemberNumber() const { return memberNumber; }
+
+ virtual const TType& getType() const override
+ {
+ const TTypeList& types = *anonContainer.getType().getStruct();
+ return *types[memberNumber].type;
+ }
+
+ virtual TType& getWritableType() override
+ {
+ assert(writable);
+ const TTypeList& types = *anonContainer.getType().getStruct();
+ return *types[memberNumber].type;
+ }
+
+ virtual void setExtensions(int numExts, const char* const exts[]) override
+ {
+ anonContainer.setMemberExtensions(memberNumber, numExts, exts);
+ }
+ virtual int getNumExtensions() const override { return anonContainer.getNumMemberExtensions(memberNumber); }
+ virtual const char** getExtensions() const override { return anonContainer.getMemberExtensions(memberNumber); }
+
+ virtual int getAnonId() const { return anonId; }
+ virtual void dump(TInfoSink &infoSink) const override;
+
+protected:
+ explicit TAnonMember(const TAnonMember&);
+ TAnonMember& operator=(const TAnonMember&);
+
+ TVariable& anonContainer;
+ unsigned int memberNumber;
+ int anonId;
+};
+
+class TSymbolTableLevel {
+public:
+ POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator())
+ TSymbolTableLevel() : defaultPrecision(0), anonId(0), thisLevel(false) { }
+ ~TSymbolTableLevel();
+
+ bool insert(TSymbol& symbol, bool separateNameSpaces)
+ {
+ //
+ // returning true means symbol was added to the table with no semantic errors
+ //
+ const TString& name = symbol.getName();
+ if (name == "") {
+ symbol.getAsVariable()->setAnonId(anonId++);
+ // An empty name means an anonymous container, exposing its members to the external scope.
+ // Give it a name and insert its members in the symbol table, pointing to the container.
+ char buf[20];
+ snprintf(buf, 20, "%s%d", AnonymousPrefix, symbol.getAsVariable()->getAnonId());
+ symbol.changeName(NewPoolTString(buf));
+
+ return insertAnonymousMembers(symbol, 0);
+ } else {
+ // Check for redefinition errors:
+ // - STL itself will tell us if there is a direct name collision, with name mangling, at this level
+ // - additionally, check for function-redefining-variable name collisions
+ const TString& insertName = symbol.getMangledName();
+ if (symbol.getAsFunction()) {
+ // make sure there isn't a variable of this name
+ if (! separateNameSpaces && level.find(name) != level.end())
+ return false;
+
+ // insert, and whatever happens is okay
+ level.insert(tLevelPair(insertName, &symbol));
+
+ return true;
+ } else
+ return level.insert(tLevelPair(insertName, &symbol)).second;
+ }
+ }
+
+ // Add more members to an already inserted aggregate object
+ bool amend(TSymbol& symbol, int firstNewMember)
+ {
+ // See insert() for comments on basic explanation of insert.
+ // This operates similarly, but more simply.
+ // Only supporting amend of anonymous blocks so far.
+ if (IsAnonymous(symbol.getName()))
+ return insertAnonymousMembers(symbol, firstNewMember);
+ else
+ return false;
+ }
+
+ bool insertAnonymousMembers(TSymbol& symbol, int firstMember)
+ {
+ const TTypeList& types = *symbol.getAsVariable()->getType().getStruct();
+ for (unsigned int m = firstMember; m < types.size(); ++m) {
+ TAnonMember* member = new TAnonMember(&types[m].type->getFieldName(), m, *symbol.getAsVariable(), symbol.getAsVariable()->getAnonId());
+ if (! level.insert(tLevelPair(member->getMangledName(), member)).second)
+ return false;
+ }
+
+ return true;
+ }
+
+ TSymbol* find(const TString& name) const
+ {
+ tLevel::const_iterator it = level.find(name);
+ if (it == level.end())
+ return 0;
+ else
+ return (*it).second;
+ }
+
+ void findFunctionNameList(const TString& name, TVector<const TFunction*>& list)
+ {
+ size_t parenAt = name.find_first_of('(');
+ TString base(name, 0, parenAt + 1);
+
+ tLevel::const_iterator begin = level.lower_bound(base);
+ base[parenAt] = ')'; // assume ')' is lexically after '('
+ tLevel::const_iterator end = level.upper_bound(base);
+ for (tLevel::const_iterator it = begin; it != end; ++it)
+ list.push_back(it->second->getAsFunction());
+ }
+
+ // See if there is already a function in the table having the given non-function-style name.
+ bool hasFunctionName(const TString& name) const
+ {
+ tLevel::const_iterator candidate = level.lower_bound(name);
+ if (candidate != level.end()) {
+ const TString& candidateName = (*candidate).first;
+ TString::size_type parenAt = candidateName.find_first_of('(');
+ if (parenAt != candidateName.npos && candidateName.compare(0, parenAt, name) == 0)
+
+ return true;
+ }
+
+ return false;
+ }
+
+ // See if there is a variable at this level having the given non-function-style name.
+ // Return true if name is found, and set variable to true if the name was a variable.
+ bool findFunctionVariableName(const TString& name, bool& variable) const
+ {
+ tLevel::const_iterator candidate = level.lower_bound(name);
+ if (candidate != level.end()) {
+ const TString& candidateName = (*candidate).first;
+ TString::size_type parenAt = candidateName.find_first_of('(');
+ if (parenAt == candidateName.npos) {
+ // not a mangled name
+ if (candidateName == name) {
+ // found a variable name match
+ variable = true;
+ return true;
+ }
+ } else {
+ // a mangled name
+ if (candidateName.compare(0, parenAt, name) == 0) {
+ // found a function name match
+ variable = false;
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // Use this to do a lazy 'push' of precision defaults the first time
+ // a precision statement is seen in a new scope. Leave it at 0 for
+ // when no push was needed. Thus, it is not the current defaults,
+ // it is what to restore the defaults to when popping a level.
+ void setPreviousDefaultPrecisions(const TPrecisionQualifier *p)
+ {
+ // can call multiple times at one scope, will only latch on first call,
+ // as we're tracking the previous scope's values, not the current values
+ if (defaultPrecision != 0)
+ return;
+
+ defaultPrecision = new TPrecisionQualifier[EbtNumTypes];
+ for (int t = 0; t < EbtNumTypes; ++t)
+ defaultPrecision[t] = p[t];
+ }
+
+ void getPreviousDefaultPrecisions(TPrecisionQualifier *p)
+ {
+ // can be called for table level pops that didn't set the
+ // defaults
+ if (defaultPrecision == 0 || p == 0)
+ return;
+
+ for (int t = 0; t < EbtNumTypes; ++t)
+ p[t] = defaultPrecision[t];
+ }
+
+ void relateToOperator(const char* name, TOperator op);
+ void setFunctionExtensions(const char* name, int num, const char* const extensions[]);
+ void dump(TInfoSink &infoSink) const;
+ TSymbolTableLevel* clone() const;
+ void readOnly();
+
+ void setThisLevel() { thisLevel = true; }
+ bool isThisLevel() const { return thisLevel; }
+
+protected:
+ explicit TSymbolTableLevel(TSymbolTableLevel&);
+ TSymbolTableLevel& operator=(TSymbolTableLevel&);
+
+ typedef std::map<TString, TSymbol*, std::less<TString>, pool_allocator<std::pair<const TString, TSymbol*> > > tLevel;
+ typedef const tLevel::value_type tLevelPair;
+ typedef std::pair<tLevel::iterator, bool> tInsertResult;
+
+ tLevel level; // named mappings
+ TPrecisionQualifier *defaultPrecision;
+ int anonId;
+ bool thisLevel; // True if this level of the symbol table is a structure scope containing member function
+ // that are supposed to see anonymous access to member variables.
+};
+
+class TSymbolTable {
+public:
+ TSymbolTable() : uniqueId(0), noBuiltInRedeclarations(false), separateNameSpaces(false), adoptedLevels(0)
+ {
+ //
+ // This symbol table cannot be used until push() is called.
+ //
+ }
+ ~TSymbolTable()
+ {
+ // this can be called explicitly; safest to code it so it can be called multiple times
+
+ // don't deallocate levels passed in from elsewhere
+ while (table.size() > adoptedLevels)
+ pop(0);
+ }
+
+ void adoptLevels(TSymbolTable& symTable)
+ {
+ for (unsigned int level = 0; level < symTable.table.size(); ++level) {
+ table.push_back(symTable.table[level]);
+ ++adoptedLevels;
+ }
+ uniqueId = symTable.uniqueId;
+ noBuiltInRedeclarations = symTable.noBuiltInRedeclarations;
+ separateNameSpaces = symTable.separateNameSpaces;
+ }
+
+ //
+ // While level adopting is generic, the methods below enact a the following
+ // convention for levels:
+ // 0: common built-ins shared across all stages, all compiles, only one copy for all symbol tables
+ // 1: per-stage built-ins, shared across all compiles, but a different copy per stage
+ // 2: built-ins specific to a compile, like resources that are context-dependent, or redeclared built-ins
+ // 3: user-shader globals
+ //
+protected:
+ static const int globalLevel = 3;
+ bool isSharedLevel(int level) { return level <= 1; } // exclude all per-compile levels
+ bool isBuiltInLevel(int level) { return level <= 2; } // exclude user globals
+ bool isGlobalLevel(int level) { return level <= globalLevel; } // include user globals
+public:
+ bool isEmpty() { return table.size() == 0; }
+ bool atBuiltInLevel() { return isBuiltInLevel(currentLevel()); }
+ bool atGlobalLevel() { return isGlobalLevel(currentLevel()); }
+
+ void setNoBuiltInRedeclarations() { noBuiltInRedeclarations = true; }
+ void setSeparateNameSpaces() { separateNameSpaces = true; }
+
+ void push()
+ {
+ table.push_back(new TSymbolTableLevel);
+ }
+
+ // Make a new symbol-table level to represent the scope introduced by a structure
+ // containing member functions, such that the member functions can find anonymous
+ // references to member variables.
+ //
+ // 'thisSymbol' should have a name of "" to trigger anonymous structure-member
+ // symbol finds.
+ void pushThis(TSymbol& thisSymbol)
+ {
+ assert(thisSymbol.getName().size() == 0);
+ table.push_back(new TSymbolTableLevel);
+ table.back()->setThisLevel();
+ insert(thisSymbol);
+ }
+
+ void pop(TPrecisionQualifier *p)
+ {
+ table[currentLevel()]->getPreviousDefaultPrecisions(p);
+ delete table.back();
+ table.pop_back();
+ }
+
+ //
+ // Insert a visible symbol into the symbol table so it can
+ // be found later by name.
+ //
+ // Returns false if the was a name collision.
+ //
+ bool insert(TSymbol& symbol)
+ {
+ symbol.setUniqueId(++uniqueId);
+
+ // make sure there isn't a function of this variable name
+ if (! separateNameSpaces && ! symbol.getAsFunction() && table[currentLevel()]->hasFunctionName(symbol.getName()))
+ return false;
+
+ // check for not overloading or redefining a built-in function
+ if (noBuiltInRedeclarations) {
+ if (atGlobalLevel() && currentLevel() > 0) {
+ if (table[0]->hasFunctionName(symbol.getName()))
+ return false;
+ if (currentLevel() > 1 && table[1]->hasFunctionName(symbol.getName()))
+ return false;
+ }
+ }
+
+ return table[currentLevel()]->insert(symbol, separateNameSpaces);
+ }
+
+ // Add more members to an already inserted aggregate object
+ bool amend(TSymbol& symbol, int firstNewMember)
+ {
+ // See insert() for comments on basic explanation of insert.
+ // This operates similarly, but more simply.
+ return table[currentLevel()]->amend(symbol, firstNewMember);
+ }
+
+ //
+ // To allocate an internal temporary, which will need to be uniquely
+ // identified by the consumer of the AST, but never need to
+ // found by doing a symbol table search by name, hence allowed an
+ // arbitrary name in the symbol with no worry of collision.
+ //
+ void makeInternalVariable(TSymbol& symbol)
+ {
+ symbol.setUniqueId(++uniqueId);
+ }
+
+ //
+ // Copy a variable or anonymous member's structure from a shared level so that
+ // it can be added (soon after return) to the symbol table where it can be
+ // modified without impacting other users of the shared table.
+ //
+ TSymbol* copyUpDeferredInsert(TSymbol* shared)
+ {
+ if (shared->getAsVariable()) {
+ TSymbol* copy = shared->clone();
+ copy->setUniqueId(shared->getUniqueId());
+ return copy;
+ } else {
+ const TAnonMember* anon = shared->getAsAnonMember();
+ assert(anon);
+ TVariable* container = anon->getAnonContainer().clone();
+ container->changeName(NewPoolTString(""));
+ container->setUniqueId(anon->getAnonContainer().getUniqueId());
+ return container;
+ }
+ }
+
+ TSymbol* copyUp(TSymbol* shared)
+ {
+ TSymbol* copy = copyUpDeferredInsert(shared);
+ table[globalLevel]->insert(*copy, separateNameSpaces);
+ if (shared->getAsVariable())
+ return copy;
+ else {
+ // return the copy of the anonymous member
+ return table[globalLevel]->find(shared->getName());
+ }
+ }
+
+ // Normal find of a symbol, that can optionally say whether the symbol was found
+ // at a built-in level or the current top-scope level.
+ TSymbol* find(const TString& name, bool* builtIn = 0, bool* currentScope = 0, int* thisDepthP = 0)
+ {
+ int level = currentLevel();
+ TSymbol* symbol;
+ int thisDepth = 0;
+ do {
+ if (table[level]->isThisLevel())
+ ++thisDepth;
+ symbol = table[level]->find(name);
+ --level;
+ } while (symbol == nullptr && level >= 0);
+ level++;
+ if (builtIn)
+ *builtIn = isBuiltInLevel(level);
+ if (currentScope)
+ *currentScope = isGlobalLevel(currentLevel()) || level == currentLevel(); // consider shared levels as "current scope" WRT user globals
+ if (thisDepthP != nullptr) {
+ if (! table[level]->isThisLevel())
+ thisDepth = 0;
+ *thisDepthP = thisDepth;
+ }
+
+ return symbol;
+ }
+
+ // Find of a symbol that returns how many layers deep of nested
+ // structures-with-member-functions ('this' scopes) deep the symbol was
+ // found in.
+ TSymbol* find(const TString& name, int& thisDepth)
+ {
+ int level = currentLevel();
+ TSymbol* symbol;
+ thisDepth = 0;
+ do {
+ if (table[level]->isThisLevel())
+ ++thisDepth;
+ symbol = table[level]->find(name);
+ --level;
+ } while (symbol == 0 && level >= 0);
+
+ if (! table[level + 1]->isThisLevel())
+ thisDepth = 0;
+
+ return symbol;
+ }
+
+ bool isFunctionNameVariable(const TString& name) const
+ {
+ if (separateNameSpaces)
+ return false;
+
+ int level = currentLevel();
+ do {
+ bool variable;
+ bool found = table[level]->findFunctionVariableName(name, variable);
+ if (found)
+ return variable;
+ --level;
+ } while (level >= 0);
+
+ return false;
+ }
+
+ void findFunctionNameList(const TString& name, TVector<const TFunction*>& list, bool& builtIn)
+ {
+ // For user levels, return the set found in the first scope with a match
+ builtIn = false;
+ int level = currentLevel();
+ do {
+ table[level]->findFunctionNameList(name, list);
+ --level;
+ } while (list.empty() && level >= globalLevel);
+
+ if (! list.empty())
+ return;
+
+ // Gather across all built-in levels; they don't hide each other
+ builtIn = true;
+ do {
+ table[level]->findFunctionNameList(name, list);
+ --level;
+ } while (level >= 0);
+ }
+
+ void relateToOperator(const char* name, TOperator op)
+ {
+ for (unsigned int level = 0; level < table.size(); ++level)
+ table[level]->relateToOperator(name, op);
+ }
+
+ void setFunctionExtensions(const char* name, int num, const char* const extensions[])
+ {
+ for (unsigned int level = 0; level < table.size(); ++level)
+ table[level]->setFunctionExtensions(name, num, extensions);
+ }
+
+ void setVariableExtensions(const char* name, int numExts, const char* const extensions[])
+ {
+ TSymbol* symbol = find(TString(name));
+ if (symbol == nullptr)
+ return;
+
+ symbol->setExtensions(numExts, extensions);
+ }
+
+ void setVariableExtensions(const char* blockName, const char* name, int numExts, const char* const extensions[])
+ {
+ TSymbol* symbol = find(TString(blockName));
+ if (symbol == nullptr)
+ return;
+ TVariable* variable = symbol->getAsVariable();
+ assert(variable != nullptr);
+
+ const TTypeList& structure = *variable->getAsVariable()->getType().getStruct();
+ for (int member = 0; member < (int)structure.size(); ++member) {
+ if (structure[member].type->getFieldName().compare(name) == 0) {
+ variable->setMemberExtensions(member, numExts, extensions);
+ return;
+ }
+ }
+ }
+
+ int getMaxSymbolId() { return uniqueId; }
+ void dump(TInfoSink &infoSink) const;
+ void copyTable(const TSymbolTable& copyOf);
+
+ void setPreviousDefaultPrecisions(TPrecisionQualifier *p) { table[currentLevel()]->setPreviousDefaultPrecisions(p); }
+
+ void readOnly()
+ {
+ for (unsigned int level = 0; level < table.size(); ++level)
+ table[level]->readOnly();
+ }
+
+protected:
+ TSymbolTable(TSymbolTable&);
+ TSymbolTable& operator=(TSymbolTableLevel&);
+
+ int currentLevel() const { return static_cast<int>(table.size()) - 1; }
+
+ std::vector<TSymbolTableLevel*> table;
+ int uniqueId; // for unique identification in code generation
+ bool noBuiltInRedeclarations;
+ bool separateNameSpaces;
+ unsigned int adoptedLevels;
+};
+
+} // end namespace glslang
+
+#endif // _SYMBOL_TABLE_INCLUDED_
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/Versions.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/Versions.cpp
new file mode 100644
index 0000000..0d4b994
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/Versions.cpp
@@ -0,0 +1,1126 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2012-2013 LunarG, Inc.
+// Copyright (C) 2017 ARM Limited.
+// Copyright (C) 2015-2018 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+//
+// Help manage multiple profiles, versions, extensions etc.
+//
+// These don't return error codes, as the presumption is parsing will
+// always continue as if the tested feature were enabled, and thus there
+// is no error recovery needed.
+//
+
+//
+// HOW TO add a feature enabled by an extension.
+//
+// To add a new hypothetical "Feature F" to the front end, where an extension
+// "XXX_extension_X" can be used to enable the feature, do the following.
+//
+// OVERVIEW: Specific features are what are error-checked for, not
+// extensions: A specific Feature F might be enabled by an extension, or a
+// particular version in a particular profile, or a stage, or combinations, etc.
+//
+// The basic mechanism is to use the following to "declare" all the things that
+// enable/disable Feature F, in a code path that implements Feature F:
+//
+// requireProfile()
+// profileRequires()
+// requireStage()
+// checkDeprecated()
+// requireNotRemoved()
+// requireExtensions()
+//
+// Typically, only the first two calls are needed. They go into a code path that
+// implements Feature F, and will log the proper error/warning messages. Parsing
+// will then always continue as if the tested feature was enabled.
+//
+// There is typically no if-testing or conditional parsing, just insertion of the calls above.
+// However, if symbols specific to the extension are added (step 5), they will
+// only be added under tests that the minimum version and profile are present.
+//
+// 1) Add a symbol name for the extension string at the bottom of Versions.h:
+//
+// const char* const XXX_extension_X = "XXX_extension_X";
+//
+// 2) Add extension initialization to TParseVersions::initializeExtensionBehavior(),
+// the first function below:
+//
+// extensionBehavior[XXX_extension_X] = EBhDisable;
+//
+// 3) Add any preprocessor directives etc. in the next function, TParseVersions::getPreamble():
+//
+// "#define XXX_extension_X 1\n"
+//
+// The new-line is important, as that ends preprocess tokens.
+//
+// 4) Insert a profile check in the feature's path (unless all profiles support the feature,
+// for some version level). That is, call requireProfile() to constrain the profiles, e.g.:
+//
+// // ... in a path specific to Feature F...
+// requireProfile(loc,
+// ECoreProfile | ECompatibilityProfile,
+// "Feature F");
+//
+// 5) For each profile that supports the feature, insert version/extension checks:
+//
+// The mostly likely scenario is that Feature F can only be used with a
+// particular profile if XXX_extension_X is present or the version is
+// high enough that the core specification already incorporated it.
+//
+// // following the requireProfile() call...
+// profileRequires(loc,
+// ECoreProfile | ECompatibilityProfile,
+// 420, // 0 if no version incorporated the feature into the core spec.
+// XXX_extension_X, // can be a list of extensions that all add the feature
+// "Feature F Description");
+//
+// This allows the feature if either A) one of the extensions is enabled or
+// B) the version is high enough. If no version yet incorporates the feature
+// into core, pass in 0.
+//
+// This can be called multiple times, if different profiles support the
+// feature starting at different version numbers or with different
+// extensions.
+//
+// This must be called for each profile allowed by the initial call to requireProfile().
+//
+// Profiles are all masks, which can be "or"-ed together.
+//
+// ENoProfile
+// ECoreProfile
+// ECompatibilityProfile
+// EEsProfile
+//
+// The ENoProfile profile is only for desktop, before profiles showed up in version 150;
+// All other #version with no profile default to either es or core, and so have profiles.
+//
+// You can select all but a particular profile using ~. The following basically means "desktop":
+//
+// ~EEsProfile
+//
+// 6) If built-in symbols are added by the extension, add them in Initialize.cpp: Their use
+// will be automatically error checked against the extensions enabled at that moment.
+// see the comment at the top of Initialize.cpp for where to put them. Establish them at
+// the earliest release that supports the extension. Then, tag them with the
+// set of extensions that both enable them and are necessary, given the version of the symbol
+// table. (There is a different symbol table for each version.)
+//
+
+#include "parseVersions.h"
+#include "localintermediate.h"
+
+namespace glslang {
+
+//
+// Initialize all extensions, almost always to 'disable', as once their features
+// are incorporated into a core version, their features are supported through allowing that
+// core version, not through a pseudo-enablement of the extension.
+//
+void TParseVersions::initializeExtensionBehavior()
+{
+ extensionBehavior[E_GL_OES_texture_3D] = EBhDisable;
+ extensionBehavior[E_GL_OES_standard_derivatives] = EBhDisable;
+ extensionBehavior[E_GL_EXT_frag_depth] = EBhDisable;
+ extensionBehavior[E_GL_OES_EGL_image_external] = EBhDisable;
+ extensionBehavior[E_GL_OES_EGL_image_external_essl3] = EBhDisable;
+ extensionBehavior[E_GL_EXT_YUV_target] = EBhDisable;
+ extensionBehavior[E_GL_EXT_shader_texture_lod] = EBhDisable;
+ extensionBehavior[E_GL_EXT_shadow_samplers] = EBhDisable;
+ extensionBehavior[E_GL_ARB_texture_rectangle] = EBhDisable;
+ extensionBehavior[E_GL_3DL_array_objects] = EBhDisable;
+ extensionBehavior[E_GL_ARB_shading_language_420pack] = EBhDisable;
+ extensionBehavior[E_GL_ARB_texture_gather] = EBhDisable;
+ extensionBehavior[E_GL_ARB_gpu_shader5] = EBhDisablePartial;
+ extensionBehavior[E_GL_ARB_separate_shader_objects] = EBhDisable;
+ extensionBehavior[E_GL_ARB_compute_shader] = EBhDisable;
+ extensionBehavior[E_GL_ARB_tessellation_shader] = EBhDisable;
+ extensionBehavior[E_GL_ARB_enhanced_layouts] = EBhDisable;
+ extensionBehavior[E_GL_ARB_texture_cube_map_array] = EBhDisable;
+ extensionBehavior[E_GL_ARB_shader_texture_lod] = EBhDisable;
+ extensionBehavior[E_GL_ARB_explicit_attrib_location] = EBhDisable;
+ extensionBehavior[E_GL_ARB_shader_image_load_store] = EBhDisable;
+ extensionBehavior[E_GL_ARB_shader_atomic_counters] = EBhDisable;
+ extensionBehavior[E_GL_ARB_shader_draw_parameters] = EBhDisable;
+ extensionBehavior[E_GL_ARB_shader_group_vote] = EBhDisable;
+ extensionBehavior[E_GL_ARB_derivative_control] = EBhDisable;
+ extensionBehavior[E_GL_ARB_shader_texture_image_samples] = EBhDisable;
+ extensionBehavior[E_GL_ARB_viewport_array] = EBhDisable;
+ extensionBehavior[E_GL_ARB_gpu_shader_int64] = EBhDisable;
+ extensionBehavior[E_GL_ARB_shader_ballot] = EBhDisable;
+ extensionBehavior[E_GL_ARB_sparse_texture2] = EBhDisable;
+ extensionBehavior[E_GL_ARB_sparse_texture_clamp] = EBhDisable;
+ extensionBehavior[E_GL_ARB_shader_stencil_export] = EBhDisable;
+// extensionBehavior[E_GL_ARB_cull_distance] = EBhDisable; // present for 4.5, but need extension control over block members
+ extensionBehavior[E_GL_ARB_post_depth_coverage] = EBhDisable;
+ extensionBehavior[E_GL_ARB_shader_viewport_layer_array] = EBhDisable;
+
+ extensionBehavior[E_GL_KHR_shader_subgroup_basic] = EBhDisable;
+ extensionBehavior[E_GL_KHR_shader_subgroup_vote] = EBhDisable;
+ extensionBehavior[E_GL_KHR_shader_subgroup_arithmetic] = EBhDisable;
+ extensionBehavior[E_GL_KHR_shader_subgroup_ballot] = EBhDisable;
+ extensionBehavior[E_GL_KHR_shader_subgroup_shuffle] = EBhDisable;
+ extensionBehavior[E_GL_KHR_shader_subgroup_shuffle_relative] = EBhDisable;
+ extensionBehavior[E_GL_KHR_shader_subgroup_clustered] = EBhDisable;
+ extensionBehavior[E_GL_KHR_shader_subgroup_quad] = EBhDisable;
+ extensionBehavior[E_GL_KHR_memory_scope_semantics] = EBhDisable;
+
+ extensionBehavior[E_GL_EXT_shader_atomic_int64] = EBhDisable;
+
+ extensionBehavior[E_GL_EXT_shader_non_constant_global_initializers] = EBhDisable;
+ extensionBehavior[E_GL_EXT_shader_image_load_formatted] = EBhDisable;
+ extensionBehavior[E_GL_EXT_post_depth_coverage] = EBhDisable;
+ extensionBehavior[E_GL_EXT_control_flow_attributes] = EBhDisable;
+ extensionBehavior[E_GL_EXT_nonuniform_qualifier] = EBhDisable;
+ extensionBehavior[E_GL_EXT_samplerless_texture_functions] = EBhDisable;
+ extensionBehavior[E_GL_EXT_scalar_block_layout] = EBhDisable;
+ extensionBehavior[E_GL_EXT_fragment_invocation_density] = EBhDisable;
+ extensionBehavior[E_GL_EXT_buffer_reference] = EBhDisable;
+
+ extensionBehavior[E_GL_EXT_shader_16bit_storage] = EBhDisable;
+ extensionBehavior[E_GL_EXT_shader_8bit_storage] = EBhDisable;
+
+ // #line and #include
+ extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhDisable;
+ extensionBehavior[E_GL_GOOGLE_include_directive] = EBhDisable;
+
+#ifdef AMD_EXTENSIONS
+ extensionBehavior[E_GL_AMD_shader_ballot] = EBhDisable;
+ extensionBehavior[E_GL_AMD_shader_trinary_minmax] = EBhDisable;
+ extensionBehavior[E_GL_AMD_shader_explicit_vertex_parameter] = EBhDisable;
+ extensionBehavior[E_GL_AMD_gcn_shader] = EBhDisable;
+ extensionBehavior[E_GL_AMD_gpu_shader_half_float] = EBhDisable;
+ extensionBehavior[E_GL_AMD_texture_gather_bias_lod] = EBhDisable;
+ extensionBehavior[E_GL_AMD_gpu_shader_int16] = EBhDisable;
+ extensionBehavior[E_GL_AMD_shader_image_load_store_lod] = EBhDisable;
+ extensionBehavior[E_GL_AMD_shader_fragment_mask] = EBhDisable;
+ extensionBehavior[E_GL_AMD_gpu_shader_half_float_fetch] = EBhDisable;
+#endif
+
+#ifdef NV_EXTENSIONS
+ extensionBehavior[E_GL_NV_sample_mask_override_coverage] = EBhDisable;
+ extensionBehavior[E_SPV_NV_geometry_shader_passthrough] = EBhDisable;
+ extensionBehavior[E_GL_NV_viewport_array2] = EBhDisable;
+ extensionBehavior[E_GL_NV_stereo_view_rendering] = EBhDisable;
+ extensionBehavior[E_GL_NVX_multiview_per_view_attributes] = EBhDisable;
+ extensionBehavior[E_GL_NV_shader_atomic_int64] = EBhDisable;
+ extensionBehavior[E_GL_NV_conservative_raster_underestimation] = EBhDisable;
+ extensionBehavior[E_GL_NV_shader_noperspective_interpolation] = EBhDisable;
+ extensionBehavior[E_GL_NV_shader_subgroup_partitioned] = EBhDisable;
+ extensionBehavior[E_GL_NV_shading_rate_image] = EBhDisable;
+ extensionBehavior[E_GL_NV_ray_tracing] = EBhDisable;
+ extensionBehavior[E_GL_NV_fragment_shader_barycentric] = EBhDisable;
+ extensionBehavior[E_GL_NV_compute_shader_derivatives] = EBhDisable;
+ extensionBehavior[E_GL_NV_shader_texture_footprint] = EBhDisable;
+ extensionBehavior[E_GL_NV_mesh_shader] = EBhDisable;
+#endif
+
+ extensionBehavior[E_GL_NV_cooperative_matrix] = EBhDisable;
+
+ // AEP
+ extensionBehavior[E_GL_ANDROID_extension_pack_es31a] = EBhDisable;
+ extensionBehavior[E_GL_KHR_blend_equation_advanced] = EBhDisable;
+ extensionBehavior[E_GL_OES_sample_variables] = EBhDisable;
+ extensionBehavior[E_GL_OES_shader_image_atomic] = EBhDisable;
+ extensionBehavior[E_GL_OES_shader_multisample_interpolation] = EBhDisable;
+ extensionBehavior[E_GL_OES_texture_storage_multisample_2d_array] = EBhDisable;
+ extensionBehavior[E_GL_EXT_geometry_shader] = EBhDisable;
+ extensionBehavior[E_GL_EXT_geometry_point_size] = EBhDisable;
+ extensionBehavior[E_GL_EXT_gpu_shader5] = EBhDisable;
+ extensionBehavior[E_GL_EXT_primitive_bounding_box] = EBhDisable;
+ extensionBehavior[E_GL_EXT_shader_io_blocks] = EBhDisable;
+ extensionBehavior[E_GL_EXT_tessellation_shader] = EBhDisable;
+ extensionBehavior[E_GL_EXT_tessellation_point_size] = EBhDisable;
+ extensionBehavior[E_GL_EXT_texture_buffer] = EBhDisable;
+ extensionBehavior[E_GL_EXT_texture_cube_map_array] = EBhDisable;
+
+ // OES matching AEP
+ extensionBehavior[E_GL_OES_geometry_shader] = EBhDisable;
+ extensionBehavior[E_GL_OES_geometry_point_size] = EBhDisable;
+ extensionBehavior[E_GL_OES_gpu_shader5] = EBhDisable;
+ extensionBehavior[E_GL_OES_primitive_bounding_box] = EBhDisable;
+ extensionBehavior[E_GL_OES_shader_io_blocks] = EBhDisable;
+ extensionBehavior[E_GL_OES_tessellation_shader] = EBhDisable;
+ extensionBehavior[E_GL_OES_tessellation_point_size] = EBhDisable;
+ extensionBehavior[E_GL_OES_texture_buffer] = EBhDisable;
+ extensionBehavior[E_GL_OES_texture_cube_map_array] = EBhDisable;
+
+ // EXT extensions
+ extensionBehavior[E_GL_EXT_device_group] = EBhDisable;
+ extensionBehavior[E_GL_EXT_multiview] = EBhDisable;
+
+ // OVR extensions
+ extensionBehavior[E_GL_OVR_multiview] = EBhDisable;
+ extensionBehavior[E_GL_OVR_multiview2] = EBhDisable;
+
+ // explicit types
+ extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types] = EBhDisable;
+ extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_int8] = EBhDisable;
+ extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_int16] = EBhDisable;
+ extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_int32] = EBhDisable;
+ extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_int64] = EBhDisable;
+ extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_float16] = EBhDisable;
+ extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_float32] = EBhDisable;
+ extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_float64] = EBhDisable;
+}
+
+// Get code that is not part of a shared symbol table, is specific to this shader,
+// or needed by the preprocessor (which does not use a shared symbol table).
+void TParseVersions::getPreamble(std::string& preamble)
+{
+ if (profile == EEsProfile) {
+ preamble =
+ "#define GL_ES 1\n"
+ "#define GL_FRAGMENT_PRECISION_HIGH 1\n"
+ "#define GL_OES_texture_3D 1\n"
+ "#define GL_OES_standard_derivatives 1\n"
+ "#define GL_EXT_frag_depth 1\n"
+ "#define GL_OES_EGL_image_external 1\n"
+ "#define GL_OES_EGL_image_external_essl3 1\n"
+ "#define GL_EXT_YUV_target 1\n"
+ "#define GL_EXT_shader_texture_lod 1\n"
+ "#define GL_EXT_shadow_samplers 1\n"
+
+ // AEP
+ "#define GL_ANDROID_extension_pack_es31a 1\n"
+ "#define GL_KHR_blend_equation_advanced 1\n"
+ "#define GL_OES_sample_variables 1\n"
+ "#define GL_OES_shader_image_atomic 1\n"
+ "#define GL_OES_shader_multisample_interpolation 1\n"
+ "#define GL_OES_texture_storage_multisample_2d_array 1\n"
+ "#define GL_EXT_geometry_shader 1\n"
+ "#define GL_EXT_geometry_point_size 1\n"
+ "#define GL_EXT_gpu_shader5 1\n"
+ "#define GL_EXT_primitive_bounding_box 1\n"
+ "#define GL_EXT_shader_io_blocks 1\n"
+ "#define GL_EXT_tessellation_shader 1\n"
+ "#define GL_EXT_tessellation_point_size 1\n"
+ "#define GL_EXT_texture_buffer 1\n"
+ "#define GL_EXT_texture_cube_map_array 1\n"
+
+ // OES matching AEP
+ "#define GL_OES_geometry_shader 1\n"
+ "#define GL_OES_geometry_point_size 1\n"
+ "#define GL_OES_gpu_shader5 1\n"
+ "#define GL_OES_primitive_bounding_box 1\n"
+ "#define GL_OES_shader_io_blocks 1\n"
+ "#define GL_OES_tessellation_shader 1\n"
+ "#define GL_OES_tessellation_point_size 1\n"
+ "#define GL_OES_texture_buffer 1\n"
+ "#define GL_OES_texture_cube_map_array 1\n"
+ "#define GL_EXT_shader_non_constant_global_initializers 1\n"
+ ;
+
+#ifdef NV_EXTENSIONS
+ if (profile == EEsProfile && version >= 300) {
+ preamble += "#define GL_NV_shader_noperspective_interpolation 1\n";
+ }
+#endif
+
+ } else {
+ preamble =
+ "#define GL_FRAGMENT_PRECISION_HIGH 1\n"
+ "#define GL_ARB_texture_rectangle 1\n"
+ "#define GL_ARB_shading_language_420pack 1\n"
+ "#define GL_ARB_texture_gather 1\n"
+ "#define GL_ARB_gpu_shader5 1\n"
+ "#define GL_ARB_separate_shader_objects 1\n"
+ "#define GL_ARB_compute_shader 1\n"
+ "#define GL_ARB_tessellation_shader 1\n"
+ "#define GL_ARB_enhanced_layouts 1\n"
+ "#define GL_ARB_texture_cube_map_array 1\n"
+ "#define GL_ARB_shader_texture_lod 1\n"
+ "#define GL_ARB_explicit_attrib_location 1\n"
+ "#define GL_ARB_shader_image_load_store 1\n"
+ "#define GL_ARB_shader_atomic_counters 1\n"
+ "#define GL_ARB_shader_draw_parameters 1\n"
+ "#define GL_ARB_shader_group_vote 1\n"
+ "#define GL_ARB_derivative_control 1\n"
+ "#define GL_ARB_shader_texture_image_samples 1\n"
+ "#define GL_ARB_viewport_array 1\n"
+ "#define GL_ARB_gpu_shader_int64 1\n"
+ "#define GL_ARB_shader_ballot 1\n"
+ "#define GL_ARB_sparse_texture2 1\n"
+ "#define GL_ARB_sparse_texture_clamp 1\n"
+ "#define GL_ARB_shader_stencil_export 1\n"
+// "#define GL_ARB_cull_distance 1\n" // present for 4.5, but need extension control over block members
+ "#define GL_ARB_post_depth_coverage 1\n"
+ "#define GL_EXT_shader_non_constant_global_initializers 1\n"
+ "#define GL_EXT_shader_image_load_formatted 1\n"
+ "#define GL_EXT_post_depth_coverage 1\n"
+ "#define GL_EXT_control_flow_attributes 1\n"
+ "#define GL_EXT_nonuniform_qualifier 1\n"
+ "#define GL_EXT_shader_16bit_storage 1\n"
+ "#define GL_EXT_shader_8bit_storage 1\n"
+ "#define GL_EXT_samplerless_texture_functions 1\n"
+ "#define GL_EXT_scalar_block_layout 1\n"
+ "#define GL_EXT_fragment_invocation_density 1\n"
+ "#define GL_EXT_buffer_reference 1\n"
+
+ // GL_KHR_shader_subgroup
+ "#define GL_KHR_shader_subgroup_basic 1\n"
+ "#define GL_KHR_shader_subgroup_vote 1\n"
+ "#define GL_KHR_shader_subgroup_arithmetic 1\n"
+ "#define GL_KHR_shader_subgroup_ballot 1\n"
+ "#define GL_KHR_shader_subgroup_shuffle 1\n"
+ "#define GL_KHR_shader_subgroup_shuffle_relative 1\n"
+ "#define GL_KHR_shader_subgroup_clustered 1\n"
+ "#define GL_KHR_shader_subgroup_quad 1\n"
+
+ "#define E_GL_EXT_shader_atomic_int64 1\n"
+
+#ifdef AMD_EXTENSIONS
+ "#define GL_AMD_shader_ballot 1\n"
+ "#define GL_AMD_shader_trinary_minmax 1\n"
+ "#define GL_AMD_shader_explicit_vertex_parameter 1\n"
+ "#define GL_AMD_gcn_shader 1\n"
+ "#define GL_AMD_gpu_shader_half_float 1\n"
+ "#define GL_AMD_texture_gather_bias_lod 1\n"
+ "#define GL_AMD_gpu_shader_int16 1\n"
+ "#define GL_AMD_shader_image_load_store_lod 1\n"
+ "#define GL_AMD_shader_fragment_mask 1\n"
+ "#define GL_AMD_gpu_shader_half_float_fetch 1\n"
+#endif
+
+#ifdef NV_EXTENSIONS
+ "#define GL_NV_sample_mask_override_coverage 1\n"
+ "#define GL_NV_geometry_shader_passthrough 1\n"
+ "#define GL_NV_viewport_array2 1\n"
+ "#define GL_NV_shader_atomic_int64 1\n"
+ "#define GL_NV_conservative_raster_underestimation 1\n"
+ "#define GL_NV_shader_subgroup_partitioned 1\n"
+ "#define GL_NV_shading_rate_image 1\n"
+ "#define GL_NV_ray_tracing 1\n"
+ "#define GL_NV_fragment_shader_barycentric 1\n"
+ "#define GL_NV_compute_shader_derivatives 1\n"
+ "#define GL_NV_shader_texture_footprint 1\n"
+ "#define GL_NV_mesh_shader 1\n"
+#endif
+ "#define GL_NV_cooperative_matrix 1\n"
+
+ "#define GL_EXT_shader_explicit_arithmetic_types 1\n"
+ "#define GL_EXT_shader_explicit_arithmetic_types_int8 1\n"
+ "#define GL_EXT_shader_explicit_arithmetic_types_int16 1\n"
+ "#define GL_EXT_shader_explicit_arithmetic_types_int32 1\n"
+ "#define GL_EXT_shader_explicit_arithmetic_types_int64 1\n"
+ "#define GL_EXT_shader_explicit_arithmetic_types_float16 1\n"
+ "#define GL_EXT_shader_explicit_arithmetic_types_float32 1\n"
+ "#define GL_EXT_shader_explicit_arithmetic_types_float64 1\n"
+ ;
+
+ if (version >= 150) {
+ // define GL_core_profile and GL_compatibility_profile
+ preamble += "#define GL_core_profile 1\n";
+
+ if (profile == ECompatibilityProfile)
+ preamble += "#define GL_compatibility_profile 1\n";
+ }
+ }
+
+ if ((profile != EEsProfile && version >= 140) ||
+ (profile == EEsProfile && version >= 310)) {
+ preamble +=
+ "#define GL_EXT_device_group 1\n"
+ "#define GL_EXT_multiview 1\n"
+ ;
+ }
+
+ if (version >= 300 /* both ES and non-ES */) {
+ preamble +=
+ "#define GL_OVR_multiview 1\n"
+ "#define GL_OVR_multiview2 1\n"
+ ;
+ }
+
+ // #line and #include
+ preamble +=
+ "#define GL_GOOGLE_cpp_style_line_directive 1\n"
+ "#define GL_GOOGLE_include_directive 1\n"
+ ;
+
+ // #define VULKAN XXXX
+ const int numberBufSize = 12;
+ char numberBuf[numberBufSize];
+ if (spvVersion.vulkanGlsl > 0) {
+ preamble += "#define VULKAN ";
+ snprintf(numberBuf, numberBufSize, "%d", spvVersion.vulkanGlsl);
+ preamble += numberBuf;
+ preamble += "\n";
+ }
+ // #define GL_SPIRV XXXX
+ if (spvVersion.openGl > 0) {
+ preamble += "#define GL_SPIRV ";
+ snprintf(numberBuf, numberBufSize, "%d", spvVersion.openGl);
+ preamble += numberBuf;
+ preamble += "\n";
+ }
+
+}
+
+//
+// When to use requireProfile():
+//
+// Use if only some profiles support a feature. However, if within a profile the feature
+// is version or extension specific, follow this call with calls to profileRequires().
+//
+// Operation: If the current profile is not one of the profileMask,
+// give an error message.
+//
+void TParseVersions::requireProfile(const TSourceLoc& loc, int profileMask, const char* featureDesc)
+{
+ if (! (profile & profileMask))
+ error(loc, "not supported with this profile:", featureDesc, ProfileName(profile));
+}
+
+//
+// Map from stage enum to externally readable text name.
+//
+const char* StageName(EShLanguage stage)
+{
+ switch(stage) {
+ case EShLangVertex: return "vertex";
+ case EShLangTessControl: return "tessellation control";
+ case EShLangTessEvaluation: return "tessellation evaluation";
+ case EShLangGeometry: return "geometry";
+ case EShLangFragment: return "fragment";
+ case EShLangCompute: return "compute";
+#ifdef NV_EXTENSIONS
+ case EShLangRayGenNV: return "ray-generation";
+ case EShLangIntersectNV: return "intersection";
+ case EShLangAnyHitNV: return "any-hit";
+ case EShLangClosestHitNV: return "closest-hit";
+ case EShLangMissNV: return "miss";
+ case EShLangCallableNV: return "callable";
+ case EShLangMeshNV: return "mesh";
+ case EShLangTaskNV: return "task";
+#endif
+ default: return "unknown stage";
+ }
+}
+
+//
+// When to use profileRequires():
+//
+// If a set of profiles have the same requirements for what version or extensions
+// are needed to support a feature.
+//
+// It must be called for each profile that needs protection. Use requireProfile() first
+// to reduce that set of profiles.
+//
+// Operation: Will issue warnings/errors based on the current profile, version, and extension
+// behaviors. It only checks extensions when the current profile is one of the profileMask.
+//
+// A minVersion of 0 means no version of the profileMask support this in core,
+// the extension must be present.
+//
+
+// entry point that takes multiple extensions
+void TParseVersions::profileRequires(const TSourceLoc& loc, int profileMask, int minVersion, int numExtensions, const char* const extensions[], const char* featureDesc)
+{
+ if (profile & profileMask) {
+ bool okay = false;
+ if (minVersion > 0 && version >= minVersion)
+ okay = true;
+ for (int i = 0; i < numExtensions; ++i) {
+ switch (getExtensionBehavior(extensions[i])) {
+ case EBhWarn:
+ infoSink.info.message(EPrefixWarning, ("extension " + TString(extensions[i]) + " is being used for " + featureDesc).c_str(), loc);
+ // fall through
+ case EBhRequire:
+ case EBhEnable:
+ okay = true;
+ break;
+ default: break; // some compilers want this
+ }
+ }
+
+ if (! okay)
+ error(loc, "not supported for this version or the enabled extensions", featureDesc, "");
+ }
+}
+
+// entry point for the above that takes a single extension
+void TParseVersions::profileRequires(const TSourceLoc& loc, int profileMask, int minVersion, const char* extension, const char* featureDesc)
+{
+ profileRequires(loc, profileMask, minVersion, extension ? 1 : 0, &extension, featureDesc);
+}
+
+//
+// When to use requireStage()
+//
+// If only some stages support a feature.
+//
+// Operation: If the current stage is not present, give an error message.
+//
+void TParseVersions::requireStage(const TSourceLoc& loc, EShLanguageMask languageMask, const char* featureDesc)
+{
+ if (((1 << language) & languageMask) == 0)
+ error(loc, "not supported in this stage:", featureDesc, StageName(language));
+}
+
+// If only one stage supports a feature, this can be called. But, all supporting stages
+// must be specified with one call.
+void TParseVersions::requireStage(const TSourceLoc& loc, EShLanguage stage, const char* featureDesc)
+{
+ requireStage(loc, static_cast<EShLanguageMask>(1 << stage), featureDesc);
+}
+
+//
+// Within a set of profiles, see if a feature is deprecated and give an error or warning based on whether
+// a future compatibility context is being use.
+//
+void TParseVersions::checkDeprecated(const TSourceLoc& loc, int profileMask, int depVersion, const char* featureDesc)
+{
+ if (profile & profileMask) {
+ if (version >= depVersion) {
+ if (forwardCompatible)
+ error(loc, "deprecated, may be removed in future release", featureDesc, "");
+ else if (! suppressWarnings())
+ infoSink.info.message(EPrefixWarning, (TString(featureDesc) + " deprecated in version " +
+ String(depVersion) + "; may be removed in future release").c_str(), loc);
+ }
+ }
+}
+
+//
+// Within a set of profiles, see if a feature has now been removed and if so, give an error.
+// The version argument is the first version no longer having the feature.
+//
+void TParseVersions::requireNotRemoved(const TSourceLoc& loc, int profileMask, int removedVersion, const char* featureDesc)
+{
+ if (profile & profileMask) {
+ if (version >= removedVersion) {
+ const int maxSize = 60;
+ char buf[maxSize];
+ snprintf(buf, maxSize, "%s profile; removed in version %d", ProfileName(profile), removedVersion);
+ error(loc, "no longer supported in", featureDesc, buf);
+ }
+ }
+}
+
+void TParseVersions::unimplemented(const TSourceLoc& loc, const char* featureDesc)
+{
+ error(loc, "feature not yet implemented", featureDesc, "");
+}
+
+// Returns true if at least one of the extensions in the extensions parameter is requested. Otherwise, returns false.
+// Warns appropriately if the requested behavior of an extension is "warn".
+bool TParseVersions::checkExtensionsRequested(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc)
+{
+ // First, see if any of the extensions are enabled
+ for (int i = 0; i < numExtensions; ++i) {
+ TExtensionBehavior behavior = getExtensionBehavior(extensions[i]);
+ if (behavior == EBhEnable || behavior == EBhRequire)
+ return true;
+ }
+
+ // See if any extensions want to give a warning on use; give warnings for all such extensions
+ bool warned = false;
+ for (int i = 0; i < numExtensions; ++i) {
+ TExtensionBehavior behavior = getExtensionBehavior(extensions[i]);
+ if (behavior == EBhDisable && relaxedErrors()) {
+ infoSink.info.message(EPrefixWarning, "The following extension must be enabled to use this feature:", loc);
+ behavior = EBhWarn;
+ }
+ if (behavior == EBhWarn) {
+ infoSink.info.message(EPrefixWarning, ("extension " + TString(extensions[i]) + " is being used for " + featureDesc).c_str(), loc);
+ warned = true;
+ }
+ }
+ if (warned)
+ return true;
+ return false;
+}
+
+//
+// Use when there are no profile/version to check, it's just an error if one of the
+// extensions is not present.
+//
+void TParseVersions::requireExtensions(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc)
+{
+ if (checkExtensionsRequested(loc, numExtensions, extensions, featureDesc))
+ return;
+
+ // If we get this far, give errors explaining what extensions are needed
+ if (numExtensions == 1)
+ error(loc, "required extension not requested:", featureDesc, extensions[0]);
+ else {
+ error(loc, "required extension not requested:", featureDesc, "Possible extensions include:");
+ for (int i = 0; i < numExtensions; ++i)
+ infoSink.info.message(EPrefixNone, extensions[i]);
+ }
+}
+
+//
+// Use by preprocessor when there are no profile/version to check, it's just an error if one of the
+// extensions is not present.
+//
+void TParseVersions::ppRequireExtensions(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc)
+{
+ if (checkExtensionsRequested(loc, numExtensions, extensions, featureDesc))
+ return;
+
+ // If we get this far, give errors explaining what extensions are needed
+ if (numExtensions == 1)
+ ppError(loc, "required extension not requested:", featureDesc, extensions[0]);
+ else {
+ ppError(loc, "required extension not requested:", featureDesc, "Possible extensions include:");
+ for (int i = 0; i < numExtensions; ++i)
+ infoSink.info.message(EPrefixNone, extensions[i]);
+ }
+}
+
+TExtensionBehavior TParseVersions::getExtensionBehavior(const char* extension)
+{
+ auto iter = extensionBehavior.find(TString(extension));
+ if (iter == extensionBehavior.end())
+ return EBhMissing;
+ else
+ return iter->second;
+}
+
+// Returns true if the given extension is set to enable, require, or warn.
+bool TParseVersions::extensionTurnedOn(const char* const extension)
+{
+ switch (getExtensionBehavior(extension)) {
+ case EBhEnable:
+ case EBhRequire:
+ case EBhWarn:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+// See if any of the extensions are set to enable, require, or warn.
+bool TParseVersions::extensionsTurnedOn(int numExtensions, const char* const extensions[])
+{
+ for (int i = 0; i < numExtensions; ++i) {
+ if (extensionTurnedOn(extensions[i]))
+ return true;
+ }
+ return false;
+}
+
+//
+// Change the current state of an extension's behavior.
+//
+void TParseVersions::updateExtensionBehavior(int line, const char* extension, const char* behaviorString)
+{
+ // Translate from text string of extension's behavior to an enum.
+ TExtensionBehavior behavior = EBhDisable;
+ if (! strcmp("require", behaviorString))
+ behavior = EBhRequire;
+ else if (! strcmp("enable", behaviorString))
+ behavior = EBhEnable;
+ else if (! strcmp("disable", behaviorString))
+ behavior = EBhDisable;
+ else if (! strcmp("warn", behaviorString))
+ behavior = EBhWarn;
+ else {
+ error(getCurrentLoc(), "behavior not supported:", "#extension", behaviorString);
+ return;
+ }
+
+ // check if extension is used with correct shader stage
+ checkExtensionStage(getCurrentLoc(), extension);
+
+ // update the requested extension
+ updateExtensionBehavior(extension, behavior);
+
+ // see if need to propagate to implicitly modified things
+ if (strcmp(extension, "GL_ANDROID_extension_pack_es31a") == 0) {
+ // to everything in AEP
+ updateExtensionBehavior(line, "GL_KHR_blend_equation_advanced", behaviorString);
+ updateExtensionBehavior(line, "GL_OES_sample_variables", behaviorString);
+ updateExtensionBehavior(line, "GL_OES_shader_image_atomic", behaviorString);
+ updateExtensionBehavior(line, "GL_OES_shader_multisample_interpolation", behaviorString);
+ updateExtensionBehavior(line, "GL_OES_texture_storage_multisample_2d_array", behaviorString);
+ updateExtensionBehavior(line, "GL_EXT_geometry_shader", behaviorString);
+ updateExtensionBehavior(line, "GL_EXT_gpu_shader5", behaviorString);
+ updateExtensionBehavior(line, "GL_EXT_primitive_bounding_box", behaviorString);
+ updateExtensionBehavior(line, "GL_EXT_shader_io_blocks", behaviorString);
+ updateExtensionBehavior(line, "GL_EXT_tessellation_shader", behaviorString);
+ updateExtensionBehavior(line, "GL_EXT_texture_buffer", behaviorString);
+ updateExtensionBehavior(line, "GL_EXT_texture_cube_map_array", behaviorString);
+ }
+ // geometry to io_blocks
+ else if (strcmp(extension, "GL_EXT_geometry_shader") == 0)
+ updateExtensionBehavior(line, "GL_EXT_shader_io_blocks", behaviorString);
+ else if (strcmp(extension, "GL_OES_geometry_shader") == 0)
+ updateExtensionBehavior(line, "GL_OES_shader_io_blocks", behaviorString);
+ // tessellation to io_blocks
+ else if (strcmp(extension, "GL_EXT_tessellation_shader") == 0)
+ updateExtensionBehavior(line, "GL_EXT_shader_io_blocks", behaviorString);
+ else if (strcmp(extension, "GL_OES_tessellation_shader") == 0)
+ updateExtensionBehavior(line, "GL_OES_shader_io_blocks", behaviorString);
+ else if (strcmp(extension, "GL_GOOGLE_include_directive") == 0)
+ updateExtensionBehavior(line, "GL_GOOGLE_cpp_style_line_directive", behaviorString);
+ // subgroup_* to subgroup_basic
+ else if (strcmp(extension, "GL_KHR_shader_subgroup_vote") == 0)
+ updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString);
+ else if (strcmp(extension, "GL_KHR_shader_subgroup_arithmetic") == 0)
+ updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString);
+ else if (strcmp(extension, "GL_KHR_shader_subgroup_ballot") == 0)
+ updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString);
+ else if (strcmp(extension, "GL_KHR_shader_subgroup_shuffle") == 0)
+ updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString);
+ else if (strcmp(extension, "GL_KHR_shader_subgroup_shuffle_relative") == 0)
+ updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString);
+ else if (strcmp(extension, "GL_KHR_shader_subgroup_clustered") == 0)
+ updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString);
+ else if (strcmp(extension, "GL_KHR_shader_subgroup_quad") == 0)
+ updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString);
+#ifdef NV_EXTENSIONS
+ else if (strcmp(extension, "GL_NV_shader_subgroup_partitioned") == 0)
+ updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString);
+#endif
+}
+
+void TParseVersions::updateExtensionBehavior(const char* extension, TExtensionBehavior behavior)
+{
+ // Update the current behavior
+ if (strcmp(extension, "all") == 0) {
+ // special case for the 'all' extension; apply it to every extension present
+ if (behavior == EBhRequire || behavior == EBhEnable) {
+ error(getCurrentLoc(), "extension 'all' cannot have 'require' or 'enable' behavior", "#extension", "");
+ return;
+ } else {
+ for (auto iter = extensionBehavior.begin(); iter != extensionBehavior.end(); ++iter)
+ iter->second = behavior;
+ }
+ } else {
+ // Do the update for this single extension
+ auto iter = extensionBehavior.find(TString(extension));
+ if (iter == extensionBehavior.end()) {
+ switch (behavior) {
+ case EBhRequire:
+ error(getCurrentLoc(), "extension not supported:", "#extension", extension);
+ break;
+ case EBhEnable:
+ case EBhWarn:
+ case EBhDisable:
+ warn(getCurrentLoc(), "extension not supported:", "#extension", extension);
+ break;
+ default:
+ assert(0 && "unexpected behavior");
+ }
+
+ return;
+ } else {
+ if (iter->second == EBhDisablePartial)
+ warn(getCurrentLoc(), "extension is only partially supported:", "#extension", extension);
+ if (behavior == EBhEnable || behavior == EBhRequire)
+ intermediate.addRequestedExtension(extension);
+ iter->second = behavior;
+ }
+ }
+}
+
+// Check if extension is used with correct shader stage.
+void TParseVersions::checkExtensionStage(const TSourceLoc& loc, const char * const extension)
+{
+#ifdef NV_EXTENSIONS
+ // GL_NV_mesh_shader extension is only allowed in task/mesh shaders
+ if (strcmp(extension, "GL_NV_mesh_shader") == 0) {
+ requireStage(loc, (EShLanguageMask)(EShLangTaskNVMask | EShLangMeshNVMask | EShLangFragmentMask),
+ "#extension GL_NV_mesh_shader");
+ profileRequires(loc, ECoreProfile, 450, 0, "#extension GL_NV_mesh_shader");
+ profileRequires(loc, EEsProfile, 320, 0, "#extension GL_NV_mesh_shader");
+ }
+#endif
+}
+
+// Call for any operation needing full GLSL integer data-type support.
+void TParseVersions::fullIntegerCheck(const TSourceLoc& loc, const char* op)
+{
+ profileRequires(loc, ENoProfile, 130, nullptr, op);
+ profileRequires(loc, EEsProfile, 300, nullptr, op);
+}
+
+// Call for any operation needing GLSL double data-type support.
+void TParseVersions::doubleCheck(const TSourceLoc& loc, const char* op)
+{
+ requireProfile(loc, ECoreProfile | ECompatibilityProfile, op);
+ profileRequires(loc, ECoreProfile | ECompatibilityProfile, 400, nullptr, op);
+}
+
+// Call for any operation needing GLSL float16 data-type support.
+void TParseVersions::float16Check(const TSourceLoc& loc, const char* op, bool builtIn)
+{
+ if (!builtIn) {
+ const char* const extensions[] = {
+#if AMD_EXTENSIONS
+ E_GL_AMD_gpu_shader_half_float,
+#endif
+ E_GL_EXT_shader_explicit_arithmetic_types,
+ E_GL_EXT_shader_explicit_arithmetic_types_float16};
+ requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op);
+ }
+}
+
+bool TParseVersions::float16Arithmetic()
+{
+ const char* const extensions[] = {
+#if AMD_EXTENSIONS
+ E_GL_AMD_gpu_shader_half_float,
+#endif
+ E_GL_EXT_shader_explicit_arithmetic_types,
+ E_GL_EXT_shader_explicit_arithmetic_types_float16};
+ return extensionsTurnedOn(sizeof(extensions)/sizeof(extensions[0]), extensions);
+}
+
+bool TParseVersions::int16Arithmetic()
+{
+ const char* const extensions[] = {
+#if AMD_EXTENSIONS
+ E_GL_AMD_gpu_shader_int16,
+#endif
+ E_GL_EXT_shader_explicit_arithmetic_types,
+ E_GL_EXT_shader_explicit_arithmetic_types_int16};
+ return extensionsTurnedOn(sizeof(extensions)/sizeof(extensions[0]), extensions);
+}
+
+bool TParseVersions::int8Arithmetic()
+{
+ const char* const extensions[] = {
+ E_GL_EXT_shader_explicit_arithmetic_types,
+ E_GL_EXT_shader_explicit_arithmetic_types_int8};
+ return extensionsTurnedOn(sizeof(extensions)/sizeof(extensions[0]), extensions);
+}
+
+void TParseVersions::requireFloat16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc)
+{
+ TString combined;
+ combined = op;
+ combined += ": ";
+ combined += featureDesc;
+
+ const char* const extensions[] = {
+#if AMD_EXTENSIONS
+ E_GL_AMD_gpu_shader_half_float,
+#endif
+ E_GL_EXT_shader_explicit_arithmetic_types,
+ E_GL_EXT_shader_explicit_arithmetic_types_float16};
+ requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, combined.c_str());
+}
+
+void TParseVersions::requireInt16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc)
+{
+ TString combined;
+ combined = op;
+ combined += ": ";
+ combined += featureDesc;
+
+ const char* const extensions[] = {
+#if AMD_EXTENSIONS
+ E_GL_AMD_gpu_shader_int16,
+#endif
+ E_GL_EXT_shader_explicit_arithmetic_types,
+ E_GL_EXT_shader_explicit_arithmetic_types_int16};
+ requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, combined.c_str());
+}
+
+void TParseVersions::requireInt8Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc)
+{
+ TString combined;
+ combined = op;
+ combined += ": ";
+ combined += featureDesc;
+
+ const char* const extensions[] = {
+ E_GL_EXT_shader_explicit_arithmetic_types,
+ E_GL_EXT_shader_explicit_arithmetic_types_int8};
+ requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, combined.c_str());
+}
+
+void TParseVersions::float16ScalarVectorCheck(const TSourceLoc& loc, const char* op, bool builtIn)
+{
+ if (!builtIn) {
+ const char* const extensions[] = {
+#if AMD_EXTENSIONS
+ E_GL_AMD_gpu_shader_half_float,
+#endif
+ E_GL_EXT_shader_16bit_storage,
+ E_GL_EXT_shader_explicit_arithmetic_types,
+ E_GL_EXT_shader_explicit_arithmetic_types_float16};
+ requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op);
+ }
+}
+
+// Call for any operation needing GLSL float32 data-type support.
+void TParseVersions::explicitFloat32Check(const TSourceLoc& loc, const char* op, bool builtIn)
+{
+ if (!builtIn) {
+ const char* const extensions[2] = {E_GL_EXT_shader_explicit_arithmetic_types,
+ E_GL_EXT_shader_explicit_arithmetic_types_float32};
+ requireExtensions(loc, 2, extensions, op);
+ }
+}
+
+// Call for any operation needing GLSL float64 data-type support.
+void TParseVersions::explicitFloat64Check(const TSourceLoc& loc, const char* op, bool builtIn)
+{
+ if (!builtIn) {
+ const char* const extensions[2] = {E_GL_EXT_shader_explicit_arithmetic_types,
+ E_GL_EXT_shader_explicit_arithmetic_types_float64};
+ requireExtensions(loc, 2, extensions, op);
+ requireProfile(loc, ECoreProfile | ECompatibilityProfile, op);
+ profileRequires(loc, ECoreProfile | ECompatibilityProfile, 400, nullptr, op);
+ }
+}
+
+// Call for any operation needing GLSL explicit int8 data-type support.
+void TParseVersions::explicitInt8Check(const TSourceLoc& loc, const char* op, bool builtIn)
+{
+ if (! builtIn) {
+ const char* const extensions[2] = {E_GL_EXT_shader_explicit_arithmetic_types,
+ E_GL_EXT_shader_explicit_arithmetic_types_int8};
+ requireExtensions(loc, 2, extensions, op);
+ }
+}
+
+#ifdef AMD_EXTENSIONS
+// Call for any operation needing GLSL float16 opaque-type support
+void TParseVersions::float16OpaqueCheck(const TSourceLoc& loc, const char* op, bool builtIn)
+{
+ if (! builtIn) {
+ requireExtensions(loc, 1, &E_GL_AMD_gpu_shader_half_float_fetch, op);
+ requireProfile(loc, ECoreProfile | ECompatibilityProfile, op);
+ profileRequires(loc, ECoreProfile | ECompatibilityProfile, 400, nullptr, op);
+ }
+}
+#endif
+
+// Call for any operation needing GLSL explicit int16 data-type support.
+void TParseVersions::explicitInt16Check(const TSourceLoc& loc, const char* op, bool builtIn)
+{
+ if (! builtIn) {
+ const char* const extensions[] = {
+#if AMD_EXTENSIONS
+ E_GL_AMD_gpu_shader_int16,
+#endif
+ E_GL_EXT_shader_explicit_arithmetic_types,
+ E_GL_EXT_shader_explicit_arithmetic_types_int16};
+ requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op);
+ }
+}
+
+void TParseVersions::int16ScalarVectorCheck(const TSourceLoc& loc, const char* op, bool builtIn)
+{
+ if (! builtIn) {
+ const char* const extensions[] = {
+#if AMD_EXTENSIONS
+ E_GL_AMD_gpu_shader_int16,
+#endif
+ E_GL_EXT_shader_16bit_storage,
+ E_GL_EXT_shader_explicit_arithmetic_types,
+ E_GL_EXT_shader_explicit_arithmetic_types_int16};
+ requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op);
+ }
+}
+
+void TParseVersions::int8ScalarVectorCheck(const TSourceLoc& loc, const char* op, bool builtIn)
+{
+ if (! builtIn) {
+ const char* const extensions[] = {
+ E_GL_EXT_shader_8bit_storage,
+ E_GL_EXT_shader_explicit_arithmetic_types,
+ E_GL_EXT_shader_explicit_arithmetic_types_int8};
+ requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op);
+ }
+}
+
+// Call for any operation needing GLSL explicit int32 data-type support.
+void TParseVersions::explicitInt32Check(const TSourceLoc& loc, const char* op, bool builtIn)
+{
+ if (! builtIn) {
+ const char* const extensions[2] = {E_GL_EXT_shader_explicit_arithmetic_types,
+ E_GL_EXT_shader_explicit_arithmetic_types_int32};
+ requireExtensions(loc, 2, extensions, op);
+ }
+}
+
+// Call for any operation needing GLSL 64-bit integer data-type support.
+void TParseVersions::int64Check(const TSourceLoc& loc, const char* op, bool builtIn)
+{
+ if (! builtIn) {
+ const char* const extensions[3] = {E_GL_ARB_gpu_shader_int64,
+ E_GL_EXT_shader_explicit_arithmetic_types,
+ E_GL_EXT_shader_explicit_arithmetic_types_int64};
+ requireExtensions(loc, 3, extensions, op);
+ requireProfile(loc, ECoreProfile | ECompatibilityProfile, op);
+ profileRequires(loc, ECoreProfile | ECompatibilityProfile, 400, nullptr, op);
+ }
+}
+
+void TParseVersions::fcoopmatCheck(const TSourceLoc& loc, const char* op, bool builtIn)
+{
+ if (!builtIn) {
+ const char* const extensions[] = {E_GL_NV_cooperative_matrix};
+ requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op);
+ }
+}
+
+// Call for any operation removed because SPIR-V is in use.
+void TParseVersions::spvRemoved(const TSourceLoc& loc, const char* op)
+{
+ if (spvVersion.spv != 0)
+ error(loc, "not allowed when generating SPIR-V", op, "");
+}
+
+// Call for any operation removed because Vulkan SPIR-V is being generated.
+void TParseVersions::vulkanRemoved(const TSourceLoc& loc, const char* op)
+{
+ if (spvVersion.vulkan > 0)
+ error(loc, "not allowed when using GLSL for Vulkan", op, "");
+}
+
+// Call for any operation that requires Vulkan.
+void TParseVersions::requireVulkan(const TSourceLoc& loc, const char* op)
+{
+ if (spvVersion.vulkan == 0)
+ error(loc, "only allowed when using GLSL for Vulkan", op, "");
+}
+
+// Call for any operation that requires SPIR-V.
+void TParseVersions::requireSpv(const TSourceLoc& loc, const char* op)
+{
+ if (spvVersion.spv == 0)
+ error(loc, "only allowed when generating SPIR-V", op, "");
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/Versions.h b/src/3rdparty/glslang/glslang/MachineIndependent/Versions.h
new file mode 100644
index 0000000..e571b51
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/Versions.h
@@ -0,0 +1,299 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2012-2013 LunarG, Inc.
+// Copyright (C) 2017 ARM Limited.
+// Copyright (C) 2015-2018 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+#ifndef _VERSIONS_INCLUDED_
+#define _VERSIONS_INCLUDED_
+
+//
+// Help manage multiple profiles, versions, extensions etc.
+//
+
+//
+// Profiles are set up for masking operations, so queries can be done on multiple
+// profiles at the same time.
+//
+// Don't maintain an ordinal set of enums (0,1,2,3...) to avoid all possible
+// defects from mixing the two different forms.
+//
+typedef enum {
+ EBadProfile = 0,
+ ENoProfile = (1 << 0), // only for desktop, before profiles showed up
+ ECoreProfile = (1 << 1),
+ ECompatibilityProfile = (1 << 2),
+ EEsProfile = (1 << 3)
+} EProfile;
+
+namespace glslang {
+
+//
+// Map from profile enum to externally readable text name.
+//
+inline const char* ProfileName(EProfile profile)
+{
+ switch (profile) {
+ case ENoProfile: return "none";
+ case ECoreProfile: return "core";
+ case ECompatibilityProfile: return "compatibility";
+ case EEsProfile: return "es";
+ default: return "unknown profile";
+ }
+}
+
+//
+// What source rules, validation rules, target language, etc. are needed or
+// desired for SPIR-V?
+//
+// 0 means a target or rule set is not enabled (ignore rules from that entity).
+// Non-0 means to apply semantic rules arising from that version of its rule set.
+// The union of all requested rule sets will be applied.
+//
+struct SpvVersion {
+ SpvVersion() : spv(0), vulkanGlsl(0), vulkan(0), openGl(0) {}
+ unsigned int spv; // the version of SPIR-V to target, as defined by "word 1" of the SPIR-V binary header
+ int vulkanGlsl; // the version of GLSL semantics for Vulkan, from GL_KHR_vulkan_glsl, for "#define VULKAN XXX"
+ int vulkan; // the version of Vulkan, for which SPIR-V execution environment rules to use
+ int openGl; // the version of GLSL semantics for OpenGL, from GL_ARB_gl_spirv, for "#define GL_SPIRV XXX"
+};
+
+//
+// The behaviors from the GLSL "#extension extension_name : behavior"
+//
+typedef enum {
+ EBhMissing = 0,
+ EBhRequire,
+ EBhEnable,
+ EBhWarn,
+ EBhDisable,
+ EBhDisablePartial // use as initial state of an extension that is only partially implemented
+} TExtensionBehavior;
+
+//
+// Symbolic names for extensions. Strings may be directly used when calling the
+// functions, but better to have the compiler do spelling checks.
+//
+const char* const E_GL_OES_texture_3D = "GL_OES_texture_3D";
+const char* const E_GL_OES_standard_derivatives = "GL_OES_standard_derivatives";
+const char* const E_GL_EXT_frag_depth = "GL_EXT_frag_depth";
+const char* const E_GL_OES_EGL_image_external = "GL_OES_EGL_image_external";
+const char* const E_GL_OES_EGL_image_external_essl3 = "GL_OES_EGL_image_external_essl3";
+const char* const E_GL_EXT_YUV_target = "GL_EXT_YUV_target";
+const char* const E_GL_EXT_shader_texture_lod = "GL_EXT_shader_texture_lod";
+const char* const E_GL_EXT_shadow_samplers = "GL_EXT_shadow_samplers";
+
+const char* const E_GL_ARB_texture_rectangle = "GL_ARB_texture_rectangle";
+const char* const E_GL_3DL_array_objects = "GL_3DL_array_objects";
+const char* const E_GL_ARB_shading_language_420pack = "GL_ARB_shading_language_420pack";
+const char* const E_GL_ARB_texture_gather = "GL_ARB_texture_gather";
+const char* const E_GL_ARB_gpu_shader5 = "GL_ARB_gpu_shader5";
+const char* const E_GL_ARB_separate_shader_objects = "GL_ARB_separate_shader_objects";
+const char* const E_GL_ARB_compute_shader = "GL_ARB_compute_shader";
+const char* const E_GL_ARB_tessellation_shader = "GL_ARB_tessellation_shader";
+const char* const E_GL_ARB_enhanced_layouts = "GL_ARB_enhanced_layouts";
+const char* const E_GL_ARB_texture_cube_map_array = "GL_ARB_texture_cube_map_array";
+const char* const E_GL_ARB_shader_texture_lod = "GL_ARB_shader_texture_lod";
+const char* const E_GL_ARB_explicit_attrib_location = "GL_ARB_explicit_attrib_location";
+const char* const E_GL_ARB_shader_image_load_store = "GL_ARB_shader_image_load_store";
+const char* const E_GL_ARB_shader_atomic_counters = "GL_ARB_shader_atomic_counters";
+const char* const E_GL_ARB_shader_draw_parameters = "GL_ARB_shader_draw_parameters";
+const char* const E_GL_ARB_shader_group_vote = "GL_ARB_shader_group_vote";
+const char* const E_GL_ARB_derivative_control = "GL_ARB_derivative_control";
+const char* const E_GL_ARB_shader_texture_image_samples = "GL_ARB_shader_texture_image_samples";
+const char* const E_GL_ARB_viewport_array = "GL_ARB_viewport_array";
+const char* const E_GL_ARB_gpu_shader_int64 = "GL_ARB_gpu_shader_int64";
+const char* const E_GL_ARB_shader_ballot = "GL_ARB_shader_ballot";
+const char* const E_GL_ARB_sparse_texture2 = "GL_ARB_sparse_texture2";
+const char* const E_GL_ARB_sparse_texture_clamp = "GL_ARB_sparse_texture_clamp";
+const char* const E_GL_ARB_shader_stencil_export = "GL_ARB_shader_stencil_export";
+// const char* const E_GL_ARB_cull_distance = "GL_ARB_cull_distance"; // present for 4.5, but need extension control over block members
+const char* const E_GL_ARB_post_depth_coverage = "GL_ARB_post_depth_coverage";
+const char* const E_GL_ARB_shader_viewport_layer_array = "GL_ARB_shader_viewport_layer_array";
+
+const char* const E_GL_KHR_shader_subgroup_basic = "GL_KHR_shader_subgroup_basic";
+const char* const E_GL_KHR_shader_subgroup_vote = "GL_KHR_shader_subgroup_vote";
+const char* const E_GL_KHR_shader_subgroup_arithmetic = "GL_KHR_shader_subgroup_arithmetic";
+const char* const E_GL_KHR_shader_subgroup_ballot = "GL_KHR_shader_subgroup_ballot";
+const char* const E_GL_KHR_shader_subgroup_shuffle = "GL_KHR_shader_subgroup_shuffle";
+const char* const E_GL_KHR_shader_subgroup_shuffle_relative = "GL_KHR_shader_subgroup_shuffle_relative";
+const char* const E_GL_KHR_shader_subgroup_clustered = "GL_KHR_shader_subgroup_clustered";
+const char* const E_GL_KHR_shader_subgroup_quad = "GL_KHR_shader_subgroup_quad";
+const char* const E_GL_KHR_memory_scope_semantics = "GL_KHR_memory_scope_semantics";
+
+const char* const E_GL_EXT_shader_atomic_int64 = "GL_EXT_shader_atomic_int64";
+
+const char* const E_GL_EXT_shader_non_constant_global_initializers = "GL_EXT_shader_non_constant_global_initializers";
+const char* const E_GL_EXT_shader_image_load_formatted = "GL_EXT_shader_image_load_formatted";
+
+const char* const E_GL_EXT_shader_16bit_storage = "GL_EXT_shader_16bit_storage";
+const char* const E_GL_EXT_shader_8bit_storage = "GL_EXT_shader_8bit_storage";
+
+
+// EXT extensions
+const char* const E_GL_EXT_device_group = "GL_EXT_device_group";
+const char* const E_GL_EXT_multiview = "GL_EXT_multiview";
+const char* const E_GL_EXT_post_depth_coverage = "GL_EXT_post_depth_coverage";
+const char* const E_GL_EXT_control_flow_attributes = "GL_EXT_control_flow_attributes";
+const char* const E_GL_EXT_nonuniform_qualifier = "GL_EXT_nonuniform_qualifier";
+const char* const E_GL_EXT_samplerless_texture_functions = "GL_EXT_samplerless_texture_functions";
+const char* const E_GL_EXT_scalar_block_layout = "GL_EXT_scalar_block_layout";
+const char* const E_GL_EXT_fragment_invocation_density = "GL_EXT_fragment_invocation_density";
+const char* const E_GL_EXT_buffer_reference = "GL_EXT_buffer_reference";
+
+// Arrays of extensions for the above viewportEXTs duplications
+
+const char* const post_depth_coverageEXTs[] = { E_GL_ARB_post_depth_coverage, E_GL_EXT_post_depth_coverage };
+const int Num_post_depth_coverageEXTs = sizeof(post_depth_coverageEXTs) / sizeof(post_depth_coverageEXTs[0]);
+
+// OVR extensions
+const char* const E_GL_OVR_multiview = "GL_OVR_multiview";
+const char* const E_GL_OVR_multiview2 = "GL_OVR_multiview2";
+
+const char* const OVR_multiview_EXTs[] = { E_GL_OVR_multiview, E_GL_OVR_multiview2 };
+const int Num_OVR_multiview_EXTs = sizeof(OVR_multiview_EXTs) / sizeof(OVR_multiview_EXTs[0]);
+
+// #line and #include
+const char* const E_GL_GOOGLE_cpp_style_line_directive = "GL_GOOGLE_cpp_style_line_directive";
+const char* const E_GL_GOOGLE_include_directive = "GL_GOOGLE_include_directive";
+
+#ifdef AMD_EXTENSIONS
+const char* const E_GL_AMD_shader_ballot = "GL_AMD_shader_ballot";
+const char* const E_GL_AMD_shader_trinary_minmax = "GL_AMD_shader_trinary_minmax";
+const char* const E_GL_AMD_shader_explicit_vertex_parameter = "GL_AMD_shader_explicit_vertex_parameter";
+const char* const E_GL_AMD_gcn_shader = "GL_AMD_gcn_shader";
+const char* const E_GL_AMD_gpu_shader_half_float = "GL_AMD_gpu_shader_half_float";
+const char* const E_GL_AMD_texture_gather_bias_lod = "GL_AMD_texture_gather_bias_lod";
+const char* const E_GL_AMD_gpu_shader_int16 = "GL_AMD_gpu_shader_int16";
+const char* const E_GL_AMD_shader_image_load_store_lod = "GL_AMD_shader_image_load_store_lod";
+const char* const E_GL_AMD_shader_fragment_mask = "GL_AMD_shader_fragment_mask";
+const char* const E_GL_AMD_gpu_shader_half_float_fetch = "GL_AMD_gpu_shader_half_float_fetch";
+#endif
+
+#ifdef NV_EXTENSIONS
+
+const char* const E_GL_NV_sample_mask_override_coverage = "GL_NV_sample_mask_override_coverage";
+const char* const E_SPV_NV_geometry_shader_passthrough = "GL_NV_geometry_shader_passthrough";
+const char* const E_GL_NV_viewport_array2 = "GL_NV_viewport_array2";
+const char* const E_GL_NV_stereo_view_rendering = "GL_NV_stereo_view_rendering";
+const char* const E_GL_NVX_multiview_per_view_attributes = "GL_NVX_multiview_per_view_attributes";
+const char* const E_GL_NV_shader_atomic_int64 = "GL_NV_shader_atomic_int64";
+const char* const E_GL_NV_conservative_raster_underestimation = "GL_NV_conservative_raster_underestimation";
+const char* const E_GL_NV_shader_noperspective_interpolation = "GL_NV_shader_noperspective_interpolation";
+const char* const E_GL_NV_shader_subgroup_partitioned = "GL_NV_shader_subgroup_partitioned";
+const char* const E_GL_NV_shading_rate_image = "GL_NV_shading_rate_image";
+const char* const E_GL_NV_ray_tracing = "GL_NV_ray_tracing";
+const char* const E_GL_NV_fragment_shader_barycentric = "GL_NV_fragment_shader_barycentric";
+const char* const E_GL_NV_compute_shader_derivatives = "GL_NV_compute_shader_derivatives";
+const char* const E_GL_NV_shader_texture_footprint = "GL_NV_shader_texture_footprint";
+const char* const E_GL_NV_mesh_shader = "GL_NV_mesh_shader";
+
+// Arrays of extensions for the above viewportEXTs duplications
+
+const char* const viewportEXTs[] = { E_GL_ARB_shader_viewport_layer_array, E_GL_NV_viewport_array2 };
+const int Num_viewportEXTs = sizeof(viewportEXTs) / sizeof(viewportEXTs[0]);
+#endif
+
+const char* const E_GL_NV_cooperative_matrix = "GL_NV_cooperative_matrix";
+
+// AEP
+const char* const E_GL_ANDROID_extension_pack_es31a = "GL_ANDROID_extension_pack_es31a";
+const char* const E_GL_KHR_blend_equation_advanced = "GL_KHR_blend_equation_advanced";
+const char* const E_GL_OES_sample_variables = "GL_OES_sample_variables";
+const char* const E_GL_OES_shader_image_atomic = "GL_OES_shader_image_atomic";
+const char* const E_GL_OES_shader_multisample_interpolation = "GL_OES_shader_multisample_interpolation";
+const char* const E_GL_OES_texture_storage_multisample_2d_array = "GL_OES_texture_storage_multisample_2d_array";
+const char* const E_GL_EXT_geometry_shader = "GL_EXT_geometry_shader";
+const char* const E_GL_EXT_geometry_point_size = "GL_EXT_geometry_point_size";
+const char* const E_GL_EXT_gpu_shader5 = "GL_EXT_gpu_shader5";
+const char* const E_GL_EXT_primitive_bounding_box = "GL_EXT_primitive_bounding_box";
+const char* const E_GL_EXT_shader_io_blocks = "GL_EXT_shader_io_blocks";
+const char* const E_GL_EXT_tessellation_shader = "GL_EXT_tessellation_shader";
+const char* const E_GL_EXT_tessellation_point_size = "GL_EXT_tessellation_point_size";
+const char* const E_GL_EXT_texture_buffer = "GL_EXT_texture_buffer";
+const char* const E_GL_EXT_texture_cube_map_array = "GL_EXT_texture_cube_map_array";
+
+// OES matching AEP
+const char* const E_GL_OES_geometry_shader = "GL_OES_geometry_shader";
+const char* const E_GL_OES_geometry_point_size = "GL_OES_geometry_point_size";
+const char* const E_GL_OES_gpu_shader5 = "GL_OES_gpu_shader5";
+const char* const E_GL_OES_primitive_bounding_box = "GL_OES_primitive_bounding_box";
+const char* const E_GL_OES_shader_io_blocks = "GL_OES_shader_io_blocks";
+const char* const E_GL_OES_tessellation_shader = "GL_OES_tessellation_shader";
+const char* const E_GL_OES_tessellation_point_size = "GL_OES_tessellation_point_size";
+const char* const E_GL_OES_texture_buffer = "GL_OES_texture_buffer";
+const char* const E_GL_OES_texture_cube_map_array = "GL_OES_texture_cube_map_array";
+
+// KHX
+const char* const E_GL_EXT_shader_explicit_arithmetic_types = "GL_EXT_shader_explicit_arithmetic_types";
+const char* const E_GL_EXT_shader_explicit_arithmetic_types_int8 = "GL_EXT_shader_explicit_arithmetic_types_int8";
+const char* const E_GL_EXT_shader_explicit_arithmetic_types_int16 = "GL_EXT_shader_explicit_arithmetic_types_int16";
+const char* const E_GL_EXT_shader_explicit_arithmetic_types_int32 = "GL_EXT_shader_explicit_arithmetic_types_int32";
+const char* const E_GL_EXT_shader_explicit_arithmetic_types_int64 = "GL_EXT_shader_explicit_arithmetic_types_int64";
+const char* const E_GL_EXT_shader_explicit_arithmetic_types_float16 = "GL_EXT_shader_explicit_arithmetic_types_float16";
+const char* const E_GL_EXT_shader_explicit_arithmetic_types_float32 = "GL_EXT_shader_explicit_arithmetic_types_float32";
+const char* const E_GL_EXT_shader_explicit_arithmetic_types_float64 = "GL_EXT_shader_explicit_arithmetic_types_float64";
+
+// Arrays of extensions for the above AEP duplications
+
+const char* const AEP_geometry_shader[] = { E_GL_EXT_geometry_shader, E_GL_OES_geometry_shader };
+const int Num_AEP_geometry_shader = sizeof(AEP_geometry_shader)/sizeof(AEP_geometry_shader[0]);
+
+const char* const AEP_geometry_point_size[] = { E_GL_EXT_geometry_point_size, E_GL_OES_geometry_point_size };
+const int Num_AEP_geometry_point_size = sizeof(AEP_geometry_point_size)/sizeof(AEP_geometry_point_size[0]);
+
+const char* const AEP_gpu_shader5[] = { E_GL_EXT_gpu_shader5, E_GL_OES_gpu_shader5 };
+const int Num_AEP_gpu_shader5 = sizeof(AEP_gpu_shader5)/sizeof(AEP_gpu_shader5[0]);
+
+const char* const AEP_primitive_bounding_box[] = { E_GL_EXT_primitive_bounding_box, E_GL_OES_primitive_bounding_box };
+const int Num_AEP_primitive_bounding_box = sizeof(AEP_primitive_bounding_box)/sizeof(AEP_primitive_bounding_box[0]);
+
+const char* const AEP_shader_io_blocks[] = { E_GL_EXT_shader_io_blocks, E_GL_OES_shader_io_blocks };
+const int Num_AEP_shader_io_blocks = sizeof(AEP_shader_io_blocks)/sizeof(AEP_shader_io_blocks[0]);
+
+const char* const AEP_tessellation_shader[] = { E_GL_EXT_tessellation_shader, E_GL_OES_tessellation_shader };
+const int Num_AEP_tessellation_shader = sizeof(AEP_tessellation_shader)/sizeof(AEP_tessellation_shader[0]);
+
+const char* const AEP_tessellation_point_size[] = { E_GL_EXT_tessellation_point_size, E_GL_OES_tessellation_point_size };
+const int Num_AEP_tessellation_point_size = sizeof(AEP_tessellation_point_size)/sizeof(AEP_tessellation_point_size[0]);
+
+const char* const AEP_texture_buffer[] = { E_GL_EXT_texture_buffer, E_GL_OES_texture_buffer };
+const int Num_AEP_texture_buffer = sizeof(AEP_texture_buffer)/sizeof(AEP_texture_buffer[0]);
+
+const char* const AEP_texture_cube_map_array[] = { E_GL_EXT_texture_cube_map_array, E_GL_OES_texture_cube_map_array };
+const int Num_AEP_texture_cube_map_array = sizeof(AEP_texture_cube_map_array)/sizeof(AEP_texture_cube_map_array[0]);
+
+} // end namespace glslang
+
+#endif // _VERSIONS_INCLUDED_
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/attribute.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/attribute.cpp
new file mode 100644
index 0000000..73b665d
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/attribute.cpp
@@ -0,0 +1,257 @@
+//
+// Copyright (C) 2017 LunarG, Inc.
+// Copyright (C) 2018 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of Google, Inc., nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "attribute.h"
+#include "../Include/intermediate.h"
+#include "ParseHelper.h"
+
+namespace glslang {
+
+// extract integers out of attribute arguments stored in attribute aggregate
+bool TAttributeArgs::getInt(int& value, int argNum) const
+{
+ const TConstUnion* intConst = getConstUnion(EbtInt, argNum);
+
+ if (intConst == nullptr)
+ return false;
+
+ value = intConst->getIConst();
+ return true;
+}
+
+// extract strings out of attribute arguments stored in attribute aggregate.
+// convert to lower case if converToLower is true (for case-insensitive compare convenience)
+bool TAttributeArgs::getString(TString& value, int argNum, bool convertToLower) const
+{
+ const TConstUnion* stringConst = getConstUnion(EbtString, argNum);
+
+ if (stringConst == nullptr)
+ return false;
+
+ value = *stringConst->getSConst();
+
+ // Convenience.
+ if (convertToLower)
+ std::transform(value.begin(), value.end(), value.begin(), ::tolower);
+
+ return true;
+}
+
+// How many arguments were supplied?
+int TAttributeArgs::size() const
+{
+ return args == nullptr ? 0 : (int)args->getSequence().size();
+}
+
+// Helper to get attribute const union. Returns nullptr on failure.
+const TConstUnion* TAttributeArgs::getConstUnion(TBasicType basicType, int argNum) const
+{
+ if (args == nullptr)
+ return nullptr;
+
+ if (argNum >= (int)args->getSequence().size())
+ return nullptr;
+
+ const TConstUnion* constVal = &args->getSequence()[argNum]->getAsConstantUnion()->getConstArray()[0];
+ if (constVal == nullptr || constVal->getType() != basicType)
+ return nullptr;
+
+ return constVal;
+}
+
+// Implementation of TParseContext parts of attributes
+TAttributeType TParseContext::attributeFromName(const TString& name) const
+{
+ if (name == "branch" || name == "dont_flatten")
+ return EatBranch;
+ else if (name == "flatten")
+ return EatFlatten;
+ else if (name == "unroll")
+ return EatUnroll;
+ else if (name == "loop" || name == "dont_unroll")
+ return EatLoop;
+ else if (name == "dependency_infinite")
+ return EatDependencyInfinite;
+ else if (name == "dependency_length")
+ return EatDependencyLength;
+ else
+ return EatNone;
+}
+
+// Make an initial leaf for the grammar from a no-argument attribute
+TAttributes* TParseContext::makeAttributes(const TString& identifier) const
+{
+ TAttributes *attributes = nullptr;
+ attributes = NewPoolObject(attributes);
+ TAttributeArgs args = { attributeFromName(identifier), nullptr };
+ attributes->push_back(args);
+ return attributes;
+}
+
+// Make an initial leaf for the grammar from a one-argument attribute
+TAttributes* TParseContext::makeAttributes(const TString& identifier, TIntermNode* node) const
+{
+ TAttributes *attributes = nullptr;
+ attributes = NewPoolObject(attributes);
+
+ // for now, node is always a simple single expression, but other code expects
+ // a list, so make it so
+ TIntermAggregate* agg = intermediate.makeAggregate(node);
+ TAttributeArgs args = { attributeFromName(identifier), agg };
+ attributes->push_back(args);
+ return attributes;
+}
+
+// Merge two sets of attributes into a single set.
+// The second argument is destructively consumed.
+TAttributes* TParseContext::mergeAttributes(TAttributes* attr1, TAttributes* attr2) const
+{
+ attr1->splice(attr1->end(), *attr2);
+ return attr1;
+}
+
+//
+// Selection attributes
+//
+void TParseContext::handleSelectionAttributes(const TAttributes& attributes, TIntermNode* node)
+{
+ TIntermSelection* selection = node->getAsSelectionNode();
+ if (selection == nullptr)
+ return;
+
+ for (auto it = attributes.begin(); it != attributes.end(); ++it) {
+ if (it->size() > 0) {
+ warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", "");
+ continue;
+ }
+
+ switch (it->name) {
+ case EatFlatten:
+ selection->setFlatten();
+ break;
+ case EatBranch:
+ selection->setDontFlatten();
+ break;
+ default:
+ warn(node->getLoc(), "attribute does not apply to a selection", "", "");
+ break;
+ }
+ }
+}
+
+//
+// Switch attributes
+//
+void TParseContext::handleSwitchAttributes(const TAttributes& attributes, TIntermNode* node)
+{
+ TIntermSwitch* selection = node->getAsSwitchNode();
+ if (selection == nullptr)
+ return;
+
+ for (auto it = attributes.begin(); it != attributes.end(); ++it) {
+ if (it->size() > 0) {
+ warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", "");
+ continue;
+ }
+
+ switch (it->name) {
+ case EatFlatten:
+ selection->setFlatten();
+ break;
+ case EatBranch:
+ selection->setDontFlatten();
+ break;
+ default:
+ warn(node->getLoc(), "attribute does not apply to a switch", "", "");
+ break;
+ }
+ }
+}
+
+//
+// Loop attributes
+//
+void TParseContext::handleLoopAttributes(const TAttributes& attributes, TIntermNode* node)
+{
+ TIntermLoop* loop = node->getAsLoopNode();
+ if (loop == nullptr) {
+ // the actual loop might be part of a sequence
+ TIntermAggregate* agg = node->getAsAggregate();
+ if (agg == nullptr)
+ return;
+ for (auto it = agg->getSequence().begin(); it != agg->getSequence().end(); ++it) {
+ loop = (*it)->getAsLoopNode();
+ if (loop != nullptr)
+ break;
+ }
+ if (loop == nullptr)
+ return;
+ }
+
+ for (auto it = attributes.begin(); it != attributes.end(); ++it) {
+ if (it->name != EatDependencyLength && it->size() > 0) {
+ warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", "");
+ continue;
+ }
+
+ int value;
+ switch (it->name) {
+ case EatUnroll:
+ loop->setUnroll();
+ break;
+ case EatLoop:
+ loop->setDontUnroll();
+ break;
+ case EatDependencyInfinite:
+ loop->setLoopDependency(TIntermLoop::dependencyInfinite);
+ break;
+ case EatDependencyLength:
+ if (it->size() == 1 && it->getInt(value)) {
+ if (value <= 0)
+ error(node->getLoc(), "must be positive", "dependency_length", "");
+ loop->setLoopDependency(value);
+ } else
+ warn(node->getLoc(), "expected a single integer argument", "dependency_length", "");
+ break;
+ default:
+ warn(node->getLoc(), "attribute does not apply to a loop", "", "");
+ break;
+ }
+ }
+}
+
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/attribute.h b/src/3rdparty/glslang/glslang/MachineIndependent/attribute.h
new file mode 100644
index 0000000..8d0c5bc
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/attribute.h
@@ -0,0 +1,102 @@
+//
+// Copyright (C) 2017 LunarG, Inc.
+// Copyright (C) 2018 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef _ATTRIBUTE_INCLUDED_
+#define _ATTRIBUTE_INCLUDED_
+
+#include "../Include/Common.h"
+#include "../Include/ConstantUnion.h"
+
+namespace glslang {
+
+ enum TAttributeType {
+ EatNone,
+ EatAllow_uav_condition,
+ EatBranch,
+ EatCall,
+ EatDomain,
+ EatEarlyDepthStencil,
+ EatFastOpt,
+ EatFlatten,
+ EatForceCase,
+ EatInstance,
+ EatMaxTessFactor,
+ EatNumThreads,
+ EatMaxVertexCount,
+ EatOutputControlPoints,
+ EatOutputTopology,
+ EatPartitioning,
+ EatPatchConstantFunc,
+ EatPatchSize,
+ EatUnroll,
+ EatLoop,
+ EatBinding,
+ EatGlobalBinding,
+ EatLocation,
+ EatInputAttachment,
+ EatBuiltIn,
+ EatPushConstant,
+ EatConstantId,
+ EatDependencyInfinite,
+ EatDependencyLength
+ };
+
+ class TIntermAggregate;
+
+ struct TAttributeArgs {
+ TAttributeType name;
+ const TIntermAggregate* args;
+
+ // Obtain attribute as integer
+ // Return false if it cannot be obtained
+ bool getInt(int& value, int argNum = 0) const;
+
+ // Obtain attribute as string, with optional to-lower transform
+ // Return false if it cannot be obtained
+ bool getString(TString& value, int argNum = 0, bool convertToLower = true) const;
+
+ // How many arguments were provided to the attribute?
+ int size() const;
+
+ protected:
+ const TConstUnion* getConstUnion(TBasicType basicType, int argNum) const;
+ };
+
+ typedef TList<TAttributeArgs> TAttributes;
+
+} // end namespace glslang
+
+#endif // _ATTRIBUTE_INCLUDED_
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/gl_types.h b/src/3rdparty/glslang/glslang/MachineIndependent/gl_types.h
new file mode 100644
index 0000000..c9fee9e
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/gl_types.h
@@ -0,0 +1,214 @@
+/*
+** Copyright (c) 2013 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+#pragma once
+
+#define GL_FLOAT 0x1406
+#define GL_FLOAT_VEC2 0x8B50
+#define GL_FLOAT_VEC3 0x8B51
+#define GL_FLOAT_VEC4 0x8B52
+
+#define GL_DOUBLE 0x140A
+#define GL_DOUBLE_VEC2 0x8FFC
+#define GL_DOUBLE_VEC3 0x8FFD
+#define GL_DOUBLE_VEC4 0x8FFE
+
+#define GL_INT 0x1404
+#define GL_INT_VEC2 0x8B53
+#define GL_INT_VEC3 0x8B54
+#define GL_INT_VEC4 0x8B55
+
+#define GL_UNSIGNED_INT 0x1405
+#define GL_UNSIGNED_INT_VEC2 0x8DC6
+#define GL_UNSIGNED_INT_VEC3 0x8DC7
+#define GL_UNSIGNED_INT_VEC4 0x8DC8
+
+#define GL_INT64_ARB 0x140E
+#define GL_INT64_VEC2_ARB 0x8FE9
+#define GL_INT64_VEC3_ARB 0x8FEA
+#define GL_INT64_VEC4_ARB 0x8FEB
+
+#define GL_UNSIGNED_INT64_ARB 0x140F
+#define GL_UNSIGNED_INT64_VEC2_ARB 0x8FE5
+#define GL_UNSIGNED_INT64_VEC3_ARB 0x8FE6
+#define GL_UNSIGNED_INT64_VEC4_ARB 0x8FE7
+
+#define GL_BOOL 0x8B56
+#define GL_BOOL_VEC2 0x8B57
+#define GL_BOOL_VEC3 0x8B58
+#define GL_BOOL_VEC4 0x8B59
+
+#define GL_FLOAT_MAT2 0x8B5A
+#define GL_FLOAT_MAT3 0x8B5B
+#define GL_FLOAT_MAT4 0x8B5C
+#define GL_FLOAT_MAT2x3 0x8B65
+#define GL_FLOAT_MAT2x4 0x8B66
+#define GL_FLOAT_MAT3x2 0x8B67
+#define GL_FLOAT_MAT3x4 0x8B68
+#define GL_FLOAT_MAT4x2 0x8B69
+#define GL_FLOAT_MAT4x3 0x8B6A
+
+#define GL_DOUBLE_MAT2 0x8F46
+#define GL_DOUBLE_MAT3 0x8F47
+#define GL_DOUBLE_MAT4 0x8F48
+#define GL_DOUBLE_MAT2x3 0x8F49
+#define GL_DOUBLE_MAT2x4 0x8F4A
+#define GL_DOUBLE_MAT3x2 0x8F4B
+#define GL_DOUBLE_MAT3x4 0x8F4C
+#define GL_DOUBLE_MAT4x2 0x8F4D
+#define GL_DOUBLE_MAT4x3 0x8F4E
+
+#ifdef AMD_EXTENSIONS
+// Those constants are borrowed from extension NV_gpu_shader5
+#define GL_FLOAT16_NV 0x8FF8
+#define GL_FLOAT16_VEC2_NV 0x8FF9
+#define GL_FLOAT16_VEC3_NV 0x8FFA
+#define GL_FLOAT16_VEC4_NV 0x8FFB
+
+#define GL_FLOAT16_MAT2_AMD 0x91C5
+#define GL_FLOAT16_MAT3_AMD 0x91C6
+#define GL_FLOAT16_MAT4_AMD 0x91C7
+#define GL_FLOAT16_MAT2x3_AMD 0x91C8
+#define GL_FLOAT16_MAT2x4_AMD 0x91C9
+#define GL_FLOAT16_MAT3x2_AMD 0x91CA
+#define GL_FLOAT16_MAT3x4_AMD 0x91CB
+#define GL_FLOAT16_MAT4x2_AMD 0x91CC
+#define GL_FLOAT16_MAT4x3_AMD 0x91CD
+#endif
+
+#define GL_SAMPLER_1D 0x8B5D
+#define GL_SAMPLER_2D 0x8B5E
+#define GL_SAMPLER_3D 0x8B5F
+#define GL_SAMPLER_CUBE 0x8B60
+#define GL_SAMPLER_BUFFER 0x8DC2
+#define GL_SAMPLER_1D_ARRAY 0x8DC0
+#define GL_SAMPLER_2D_ARRAY 0x8DC1
+#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3
+#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4
+#define GL_SAMPLER_CUBE_SHADOW 0x8DC5
+#define GL_SAMPLER_1D_SHADOW 0x8B61
+#define GL_SAMPLER_2D_SHADOW 0x8B62
+#define GL_SAMPLER_2D_RECT 0x8B63
+#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64
+#define GL_SAMPLER_2D_MULTISAMPLE 0x9108
+#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B
+#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C
+#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D
+#define GL_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900C
+#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D
+
+#ifdef AMD_EXTENSIONS
+#define GL_FLOAT16_SAMPLER_1D_AMD 0x91CE
+#define GL_FLOAT16_SAMPLER_2D_AMD 0x91CF
+#define GL_FLOAT16_SAMPLER_3D_AMD 0x91D0
+#define GL_FLOAT16_SAMPLER_CUBE_AMD 0x91D1
+#define GL_FLOAT16_SAMPLER_2D_RECT_AMD 0x91D2
+#define GL_FLOAT16_SAMPLER_1D_ARRAY_AMD 0x91D3
+#define GL_FLOAT16_SAMPLER_2D_ARRAY_AMD 0x91D4
+#define GL_FLOAT16_SAMPLER_CUBE_MAP_ARRAY_AMD 0x91D5
+#define GL_FLOAT16_SAMPLER_BUFFER_AMD 0x91D6
+#define GL_FLOAT16_SAMPLER_2D_MULTISAMPLE_AMD 0x91D7
+#define GL_FLOAT16_SAMPLER_2D_MULTISAMPLE_ARRAY_AMD 0x91D8
+
+#define GL_FLOAT16_SAMPLER_1D_SHADOW_AMD 0x91D9
+#define GL_FLOAT16_SAMPLER_2D_SHADOW_AMD 0x91DA
+#define GL_FLOAT16_SAMPLER_2D_RECT_SHADOW_AMD 0x91DB
+#define GL_FLOAT16_SAMPLER_1D_ARRAY_SHADOW_AMD 0x91DC
+#define GL_FLOAT16_SAMPLER_2D_ARRAY_SHADOW_AMD 0x91DD
+#define GL_FLOAT16_SAMPLER_CUBE_SHADOW_AMD 0x91DE
+#define GL_FLOAT16_SAMPLER_CUBE_MAP_ARRAY_SHADOW_AMD 0x91DF
+
+#define GL_FLOAT16_IMAGE_1D_AMD 0x91E0
+#define GL_FLOAT16_IMAGE_2D_AMD 0x91E1
+#define GL_FLOAT16_IMAGE_3D_AMD 0x91E2
+#define GL_FLOAT16_IMAGE_2D_RECT_AMD 0x91E3
+#define GL_FLOAT16_IMAGE_CUBE_AMD 0x91E4
+#define GL_FLOAT16_IMAGE_1D_ARRAY_AMD 0x91E5
+#define GL_FLOAT16_IMAGE_2D_ARRAY_AMD 0x91E6
+#define GL_FLOAT16_IMAGE_CUBE_MAP_ARRAY_AMD 0x91E7
+#define GL_FLOAT16_IMAGE_BUFFER_AMD 0x91E8
+#define GL_FLOAT16_IMAGE_2D_MULTISAMPLE_AMD 0x91E9
+#define GL_FLOAT16_IMAGE_2D_MULTISAMPLE_ARRAY_AMD 0x91EA
+#endif
+
+#define GL_INT_SAMPLER_1D 0x8DC9
+#define GL_INT_SAMPLER_2D 0x8DCA
+#define GL_INT_SAMPLER_3D 0x8DCB
+#define GL_INT_SAMPLER_CUBE 0x8DCC
+#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE
+#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF
+#define GL_INT_SAMPLER_2D_RECT 0x8DCD
+#define GL_INT_SAMPLER_BUFFER 0x8DD0
+#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109
+#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C
+#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E
+#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E
+
+#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1
+#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2
+#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3
+#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4
+#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6
+#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7
+#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5
+#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8
+#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D
+#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F
+#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F
+#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A
+
+#define GL_IMAGE_1D 0x904C
+#define GL_IMAGE_2D 0x904D
+#define GL_IMAGE_3D 0x904E
+#define GL_IMAGE_2D_RECT 0x904F
+#define GL_IMAGE_CUBE 0x9050
+#define GL_IMAGE_BUFFER 0x9051
+#define GL_IMAGE_1D_ARRAY 0x9052
+#define GL_IMAGE_2D_ARRAY 0x9053
+#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054
+#define GL_IMAGE_2D_MULTISAMPLE 0x9055
+#define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056
+#define GL_INT_IMAGE_1D 0x9057
+#define GL_INT_IMAGE_2D 0x9058
+#define GL_INT_IMAGE_3D 0x9059
+#define GL_INT_IMAGE_2D_RECT 0x905A
+#define GL_INT_IMAGE_CUBE 0x905B
+#define GL_INT_IMAGE_BUFFER 0x905C
+#define GL_INT_IMAGE_1D_ARRAY 0x905D
+#define GL_INT_IMAGE_2D_ARRAY 0x905E
+#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F
+#define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060
+#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061
+#define GL_UNSIGNED_INT_IMAGE_1D 0x9062
+#define GL_UNSIGNED_INT_IMAGE_2D 0x9063
+#define GL_UNSIGNED_INT_IMAGE_3D 0x9064
+#define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065
+#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066
+#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067
+#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068
+#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069
+#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A
+#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B
+#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C
+
+#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/glslang.y b/src/3rdparty/glslang/glslang/MachineIndependent/glslang.y
new file mode 100644
index 0000000..b5691a2
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/glslang.y
@@ -0,0 +1,3796 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2012-2013 LunarG, Inc.
+// Copyright (C) 2017 ARM Limited.
+// Copyright (C) 2015-2018 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+/**
+ * This is bison grammar and productions for parsing all versions of the
+ * GLSL shading languages.
+ */
+%{
+
+/* Based on:
+ANSI C Yacc grammar
+
+In 1985, Jeff Lee published his Yacc grammar (which is accompanied by a
+matching Lex specification) for the April 30, 1985 draft version of the
+ANSI C standard. Tom Stockfisch reposted it to net.sources in 1987; that
+original, as mentioned in the answer to question 17.25 of the comp.lang.c
+FAQ, can be ftp'ed from ftp.uu.net, file usenet/net.sources/ansi.c.grammar.Z.
+
+I intend to keep this version as close to the current C Standard grammar as
+possible; please let me know if you discover discrepancies.
+
+Jutta Degener, 1995
+*/
+
+#include "SymbolTable.h"
+#include "ParseHelper.h"
+#include "../Public/ShaderLang.h"
+#include "attribute.h"
+
+using namespace glslang;
+
+%}
+
+%define parse.error verbose
+
+%union {
+ struct {
+ glslang::TSourceLoc loc;
+ union {
+ glslang::TString *string;
+ int i;
+ unsigned int u;
+ long long i64;
+ unsigned long long u64;
+ bool b;
+ double d;
+ };
+ glslang::TSymbol* symbol;
+ } lex;
+ struct {
+ glslang::TSourceLoc loc;
+ glslang::TOperator op;
+ union {
+ TIntermNode* intermNode;
+ glslang::TIntermNodePair nodePair;
+ glslang::TIntermTyped* intermTypedNode;
+ glslang::TAttributes* attributes;
+ };
+ union {
+ glslang::TPublicType type;
+ glslang::TFunction* function;
+ glslang::TParameter param;
+ glslang::TTypeLoc typeLine;
+ glslang::TTypeList* typeList;
+ glslang::TArraySizes* arraySizes;
+ glslang::TIdentifierList* identifierList;
+ };
+ glslang::TArraySizes* typeParameters;
+ } interm;
+}
+
+%{
+
+/* windows only pragma */
+#ifdef _MSC_VER
+ #pragma warning(disable : 4065)
+ #pragma warning(disable : 4127)
+ #pragma warning(disable : 4244)
+#endif
+
+#define parseContext (*pParseContext)
+#define yyerror(context, msg) context->parserError(msg)
+
+extern int yylex(YYSTYPE*, TParseContext&);
+
+%}
+
+%parse-param {glslang::TParseContext* pParseContext}
+%lex-param {parseContext}
+%pure-parser // enable thread safety
+%expect 1 // One shift reduce conflict because of if | else
+
+%token <lex> ATTRIBUTE VARYING
+%token <lex> FLOAT16_T FLOAT FLOAT32_T DOUBLE FLOAT64_T
+%token <lex> CONST BOOL INT UINT INT64_T UINT64_T INT32_T UINT32_T INT16_T UINT16_T INT8_T UINT8_T
+%token <lex> BREAK CONTINUE DO ELSE FOR IF DISCARD RETURN SWITCH CASE DEFAULT SUBROUTINE
+%token <lex> BVEC2 BVEC3 BVEC4
+%token <lex> IVEC2 IVEC3 IVEC4
+%token <lex> UVEC2 UVEC3 UVEC4
+%token <lex> I64VEC2 I64VEC3 I64VEC4
+%token <lex> U64VEC2 U64VEC3 U64VEC4
+%token <lex> I32VEC2 I32VEC3 I32VEC4
+%token <lex> U32VEC2 U32VEC3 U32VEC4
+%token <lex> I16VEC2 I16VEC3 I16VEC4
+%token <lex> U16VEC2 U16VEC3 U16VEC4
+%token <lex> I8VEC2 I8VEC3 I8VEC4
+%token <lex> U8VEC2 U8VEC3 U8VEC4
+%token <lex> VEC2 VEC3 VEC4
+%token <lex> MAT2 MAT3 MAT4 CENTROID IN OUT INOUT
+%token <lex> UNIFORM PATCH SAMPLE BUFFER SHARED NONUNIFORM PAYLOADNV PAYLOADINNV HITATTRNV CALLDATANV CALLDATAINNV
+%token <lex> COHERENT VOLATILE RESTRICT READONLY WRITEONLY DEVICECOHERENT QUEUEFAMILYCOHERENT WORKGROUPCOHERENT SUBGROUPCOHERENT NONPRIVATE
+%token <lex> DVEC2 DVEC3 DVEC4 DMAT2 DMAT3 DMAT4
+%token <lex> F16VEC2 F16VEC3 F16VEC4 F16MAT2 F16MAT3 F16MAT4
+%token <lex> F32VEC2 F32VEC3 F32VEC4 F32MAT2 F32MAT3 F32MAT4
+%token <lex> F64VEC2 F64VEC3 F64VEC4 F64MAT2 F64MAT3 F64MAT4
+%token <lex> NOPERSPECTIVE FLAT SMOOTH LAYOUT EXPLICITINTERPAMD PERVERTEXNV PERPRIMITIVENV PERVIEWNV PERTASKNV
+
+%token <lex> MAT2X2 MAT2X3 MAT2X4
+%token <lex> MAT3X2 MAT3X3 MAT3X4
+%token <lex> MAT4X2 MAT4X3 MAT4X4
+%token <lex> DMAT2X2 DMAT2X3 DMAT2X4
+%token <lex> DMAT3X2 DMAT3X3 DMAT3X4
+%token <lex> DMAT4X2 DMAT4X3 DMAT4X4
+%token <lex> F16MAT2X2 F16MAT2X3 F16MAT2X4
+%token <lex> F16MAT3X2 F16MAT3X3 F16MAT3X4
+%token <lex> F16MAT4X2 F16MAT4X3 F16MAT4X4
+%token <lex> F32MAT2X2 F32MAT2X3 F32MAT2X4
+%token <lex> F32MAT3X2 F32MAT3X3 F32MAT3X4
+%token <lex> F32MAT4X2 F32MAT4X3 F32MAT4X4
+%token <lex> F64MAT2X2 F64MAT2X3 F64MAT2X4
+%token <lex> F64MAT3X2 F64MAT3X3 F64MAT3X4
+%token <lex> F64MAT4X2 F64MAT4X3 F64MAT4X4
+%token <lex> ATOMIC_UINT
+%token <lex> ACCSTRUCTNV
+%token <lex> FCOOPMATNV
+
+// combined image/sampler
+%token <lex> SAMPLER1D SAMPLER2D SAMPLER3D SAMPLERCUBE SAMPLER1DSHADOW SAMPLER2DSHADOW
+%token <lex> SAMPLERCUBESHADOW SAMPLER1DARRAY SAMPLER2DARRAY SAMPLER1DARRAYSHADOW
+%token <lex> SAMPLER2DARRAYSHADOW ISAMPLER1D ISAMPLER2D ISAMPLER3D ISAMPLERCUBE
+%token <lex> ISAMPLER1DARRAY ISAMPLER2DARRAY USAMPLER1D USAMPLER2D USAMPLER3D
+%token <lex> USAMPLERCUBE USAMPLER1DARRAY USAMPLER2DARRAY
+%token <lex> SAMPLER2DRECT SAMPLER2DRECTSHADOW ISAMPLER2DRECT USAMPLER2DRECT
+%token <lex> SAMPLERBUFFER ISAMPLERBUFFER USAMPLERBUFFER
+%token <lex> SAMPLERCUBEARRAY SAMPLERCUBEARRAYSHADOW
+%token <lex> ISAMPLERCUBEARRAY USAMPLERCUBEARRAY
+%token <lex> SAMPLER2DMS ISAMPLER2DMS USAMPLER2DMS
+%token <lex> SAMPLER2DMSARRAY ISAMPLER2DMSARRAY USAMPLER2DMSARRAY
+%token <lex> SAMPLEREXTERNALOES
+%token <lex> SAMPLEREXTERNAL2DY2YEXT
+
+%token <lex> F16SAMPLER1D F16SAMPLER2D F16SAMPLER3D F16SAMPLER2DRECT F16SAMPLERCUBE
+%token <lex> F16SAMPLER1DARRAY F16SAMPLER2DARRAY F16SAMPLERCUBEARRAY
+%token <lex> F16SAMPLERBUFFER F16SAMPLER2DMS F16SAMPLER2DMSARRAY
+%token <lex> F16SAMPLER1DSHADOW F16SAMPLER2DSHADOW F16SAMPLER1DARRAYSHADOW F16SAMPLER2DARRAYSHADOW
+%token <lex> F16SAMPLER2DRECTSHADOW F16SAMPLERCUBESHADOW F16SAMPLERCUBEARRAYSHADOW
+
+// pure sampler
+%token <lex> SAMPLER SAMPLERSHADOW
+
+// texture without sampler
+%token <lex> TEXTURE1D TEXTURE2D TEXTURE3D TEXTURECUBE
+%token <lex> TEXTURE1DARRAY TEXTURE2DARRAY
+%token <lex> ITEXTURE1D ITEXTURE2D ITEXTURE3D ITEXTURECUBE
+%token <lex> ITEXTURE1DARRAY ITEXTURE2DARRAY UTEXTURE1D UTEXTURE2D UTEXTURE3D
+%token <lex> UTEXTURECUBE UTEXTURE1DARRAY UTEXTURE2DARRAY
+%token <lex> TEXTURE2DRECT ITEXTURE2DRECT UTEXTURE2DRECT
+%token <lex> TEXTUREBUFFER ITEXTUREBUFFER UTEXTUREBUFFER
+%token <lex> TEXTURECUBEARRAY ITEXTURECUBEARRAY UTEXTURECUBEARRAY
+%token <lex> TEXTURE2DMS ITEXTURE2DMS UTEXTURE2DMS
+%token <lex> TEXTURE2DMSARRAY ITEXTURE2DMSARRAY UTEXTURE2DMSARRAY
+
+%token <lex> F16TEXTURE1D F16TEXTURE2D F16TEXTURE3D F16TEXTURE2DRECT F16TEXTURECUBE
+%token <lex> F16TEXTURE1DARRAY F16TEXTURE2DARRAY F16TEXTURECUBEARRAY
+%token <lex> F16TEXTUREBUFFER F16TEXTURE2DMS F16TEXTURE2DMSARRAY
+
+// input attachments
+%token <lex> SUBPASSINPUT SUBPASSINPUTMS ISUBPASSINPUT ISUBPASSINPUTMS USUBPASSINPUT USUBPASSINPUTMS
+%token <lex> F16SUBPASSINPUT F16SUBPASSINPUTMS
+
+%token <lex> IMAGE1D IIMAGE1D UIMAGE1D IMAGE2D IIMAGE2D
+%token <lex> UIMAGE2D IMAGE3D IIMAGE3D UIMAGE3D
+%token <lex> IMAGE2DRECT IIMAGE2DRECT UIMAGE2DRECT
+%token <lex> IMAGECUBE IIMAGECUBE UIMAGECUBE
+%token <lex> IMAGEBUFFER IIMAGEBUFFER UIMAGEBUFFER
+%token <lex> IMAGE1DARRAY IIMAGE1DARRAY UIMAGE1DARRAY
+%token <lex> IMAGE2DARRAY IIMAGE2DARRAY UIMAGE2DARRAY
+%token <lex> IMAGECUBEARRAY IIMAGECUBEARRAY UIMAGECUBEARRAY
+%token <lex> IMAGE2DMS IIMAGE2DMS UIMAGE2DMS
+%token <lex> IMAGE2DMSARRAY IIMAGE2DMSARRAY UIMAGE2DMSARRAY
+
+%token <lex> F16IMAGE1D F16IMAGE2D F16IMAGE3D F16IMAGE2DRECT
+%token <lex> F16IMAGECUBE F16IMAGE1DARRAY F16IMAGE2DARRAY F16IMAGECUBEARRAY
+%token <lex> F16IMAGEBUFFER F16IMAGE2DMS F16IMAGE2DMSARRAY
+
+%token <lex> STRUCT VOID WHILE
+
+%token <lex> IDENTIFIER TYPE_NAME
+%token <lex> FLOATCONSTANT DOUBLECONSTANT INT16CONSTANT UINT16CONSTANT INT32CONSTANT UINT32CONSTANT INTCONSTANT UINTCONSTANT INT64CONSTANT UINT64CONSTANT BOOLCONSTANT FLOAT16CONSTANT
+%token <lex> LEFT_OP RIGHT_OP
+%token <lex> INC_OP DEC_OP LE_OP GE_OP EQ_OP NE_OP
+%token <lex> AND_OP OR_OP XOR_OP MUL_ASSIGN DIV_ASSIGN ADD_ASSIGN
+%token <lex> MOD_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN XOR_ASSIGN OR_ASSIGN
+%token <lex> SUB_ASSIGN
+
+%token <lex> LEFT_PAREN RIGHT_PAREN LEFT_BRACKET RIGHT_BRACKET LEFT_BRACE RIGHT_BRACE DOT
+%token <lex> COMMA COLON EQUAL SEMICOLON BANG DASH TILDE PLUS STAR SLASH PERCENT
+%token <lex> LEFT_ANGLE RIGHT_ANGLE VERTICAL_BAR CARET AMPERSAND QUESTION
+
+%token <lex> INVARIANT PRECISE
+%token <lex> HIGH_PRECISION MEDIUM_PRECISION LOW_PRECISION PRECISION
+
+%token <lex> PACKED RESOURCE SUPERP
+
+%type <interm> assignment_operator unary_operator
+%type <interm.intermTypedNode> variable_identifier primary_expression postfix_expression
+%type <interm.intermTypedNode> expression integer_expression assignment_expression
+%type <interm.intermTypedNode> unary_expression multiplicative_expression additive_expression
+%type <interm.intermTypedNode> relational_expression equality_expression
+%type <interm.intermTypedNode> conditional_expression constant_expression
+%type <interm.intermTypedNode> logical_or_expression logical_xor_expression logical_and_expression
+%type <interm.intermTypedNode> shift_expression and_expression exclusive_or_expression inclusive_or_expression
+%type <interm.intermTypedNode> function_call initializer initializer_list condition conditionopt
+
+%type <interm.intermNode> translation_unit function_definition
+%type <interm.intermNode> statement simple_statement
+%type <interm.intermNode> statement_list switch_statement_list compound_statement
+%type <interm.intermNode> declaration_statement selection_statement selection_statement_nonattributed expression_statement
+%type <interm.intermNode> switch_statement switch_statement_nonattributed case_label
+%type <interm.intermNode> declaration external_declaration
+%type <interm.intermNode> for_init_statement compound_statement_no_new_scope
+%type <interm.nodePair> selection_rest_statement for_rest_statement
+%type <interm.intermNode> iteration_statement iteration_statement_nonattributed jump_statement statement_no_new_scope statement_scoped
+%type <interm> single_declaration init_declarator_list
+
+%type <interm> parameter_declaration parameter_declarator parameter_type_specifier
+
+%type <interm> array_specifier
+%type <interm.type> precise_qualifier invariant_qualifier interpolation_qualifier storage_qualifier precision_qualifier
+%type <interm.type> layout_qualifier layout_qualifier_id_list layout_qualifier_id
+%type <interm.type> non_uniform_qualifier
+
+%type <interm.typeParameters> type_parameter_specifier
+%type <interm.typeParameters> type_parameter_specifier_opt
+%type <interm.typeParameters> type_parameter_specifier_list
+
+%type <interm.type> type_qualifier fully_specified_type type_specifier
+%type <interm.type> single_type_qualifier
+%type <interm.type> type_specifier_nonarray
+%type <interm.type> struct_specifier
+%type <interm.typeLine> struct_declarator
+%type <interm.typeList> struct_declarator_list struct_declaration struct_declaration_list type_name_list
+%type <interm> block_structure
+%type <interm.function> function_header function_declarator
+%type <interm.function> function_header_with_parameters
+%type <interm> function_call_header_with_parameters function_call_header_no_parameters function_call_generic function_prototype
+%type <interm> function_call_or_method function_identifier function_call_header
+
+%type <interm.identifierList> identifier_list
+
+%type <interm.attributes> attribute attribute_list single_attribute
+
+%start translation_unit
+%%
+
+variable_identifier
+ : IDENTIFIER {
+ $$ = parseContext.handleVariable($1.loc, $1.symbol, $1.string);
+ }
+ ;
+
+primary_expression
+ : variable_identifier {
+ $$ = $1;
+ }
+ | INT32CONSTANT {
+ parseContext.explicitInt32Check($1.loc, "32-bit signed literal");
+ $$ = parseContext.intermediate.addConstantUnion($1.i, $1.loc, true);
+ }
+ | UINT32CONSTANT {
+ parseContext.explicitInt32Check($1.loc, "32-bit signed literal");
+ $$ = parseContext.intermediate.addConstantUnion($1.u, $1.loc, true);
+ }
+ | INTCONSTANT {
+ $$ = parseContext.intermediate.addConstantUnion($1.i, $1.loc, true);
+ }
+ | UINTCONSTANT {
+ parseContext.fullIntegerCheck($1.loc, "unsigned literal");
+ $$ = parseContext.intermediate.addConstantUnion($1.u, $1.loc, true);
+ }
+ | INT64CONSTANT {
+ parseContext.int64Check($1.loc, "64-bit integer literal");
+ $$ = parseContext.intermediate.addConstantUnion($1.i64, $1.loc, true);
+ }
+ | UINT64CONSTANT {
+ parseContext.int64Check($1.loc, "64-bit unsigned integer literal");
+ $$ = parseContext.intermediate.addConstantUnion($1.u64, $1.loc, true);
+ }
+ | INT16CONSTANT {
+ parseContext.explicitInt16Check($1.loc, "16-bit integer literal");
+ $$ = parseContext.intermediate.addConstantUnion((short)$1.i, $1.loc, true);
+ }
+ | UINT16CONSTANT {
+ parseContext.explicitInt16Check($1.loc, "16-bit unsigned integer literal");
+ $$ = parseContext.intermediate.addConstantUnion((unsigned short)$1.u, $1.loc, true);
+ }
+ | FLOATCONSTANT {
+ $$ = parseContext.intermediate.addConstantUnion($1.d, EbtFloat, $1.loc, true);
+ }
+ | DOUBLECONSTANT {
+ parseContext.doubleCheck($1.loc, "double literal");
+ $$ = parseContext.intermediate.addConstantUnion($1.d, EbtDouble, $1.loc, true);
+ }
+ | FLOAT16CONSTANT {
+ parseContext.float16Check($1.loc, "half float literal");
+ $$ = parseContext.intermediate.addConstantUnion($1.d, EbtFloat16, $1.loc, true);
+ }
+ | BOOLCONSTANT {
+ $$ = parseContext.intermediate.addConstantUnion($1.b, $1.loc, true);
+ }
+ | LEFT_PAREN expression RIGHT_PAREN {
+ $$ = $2;
+ if ($$->getAsConstantUnion())
+ $$->getAsConstantUnion()->setExpression();
+ }
+ ;
+
+postfix_expression
+ : primary_expression {
+ $$ = $1;
+ }
+ | postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET {
+ $$ = parseContext.handleBracketDereference($2.loc, $1, $3);
+ }
+ | function_call {
+ $$ = $1;
+ }
+ | postfix_expression DOT IDENTIFIER {
+ $$ = parseContext.handleDotDereference($3.loc, $1, *$3.string);
+ }
+ | postfix_expression INC_OP {
+ parseContext.variableCheck($1);
+ parseContext.lValueErrorCheck($2.loc, "++", $1);
+ $$ = parseContext.handleUnaryMath($2.loc, "++", EOpPostIncrement, $1);
+ }
+ | postfix_expression DEC_OP {
+ parseContext.variableCheck($1);
+ parseContext.lValueErrorCheck($2.loc, "--", $1);
+ $$ = parseContext.handleUnaryMath($2.loc, "--", EOpPostDecrement, $1);
+ }
+ ;
+
+integer_expression
+ : expression {
+ parseContext.integerCheck($1, "[]");
+ $$ = $1;
+ }
+ ;
+
+function_call
+ : function_call_or_method {
+ $$ = parseContext.handleFunctionCall($1.loc, $1.function, $1.intermNode);
+ delete $1.function;
+ }
+ ;
+
+function_call_or_method
+ : function_call_generic {
+ $$ = $1;
+ }
+ ;
+
+function_call_generic
+ : function_call_header_with_parameters RIGHT_PAREN {
+ $$ = $1;
+ $$.loc = $2.loc;
+ }
+ | function_call_header_no_parameters RIGHT_PAREN {
+ $$ = $1;
+ $$.loc = $2.loc;
+ }
+ ;
+
+function_call_header_no_parameters
+ : function_call_header VOID {
+ $$ = $1;
+ }
+ | function_call_header {
+ $$ = $1;
+ }
+ ;
+
+function_call_header_with_parameters
+ : function_call_header assignment_expression {
+ TParameter param = { 0, new TType };
+ param.type->shallowCopy($2->getType());
+ $1.function->addParameter(param);
+ $$.function = $1.function;
+ $$.intermNode = $2;
+ }
+ | function_call_header_with_parameters COMMA assignment_expression {
+ TParameter param = { 0, new TType };
+ param.type->shallowCopy($3->getType());
+ $1.function->addParameter(param);
+ $$.function = $1.function;
+ $$.intermNode = parseContext.intermediate.growAggregate($1.intermNode, $3, $2.loc);
+ }
+ ;
+
+function_call_header
+ : function_identifier LEFT_PAREN {
+ $$ = $1;
+ }
+ ;
+
+// Grammar Note: Constructors look like functions, but are recognized as types.
+
+function_identifier
+ : type_specifier {
+ // Constructor
+ $$.intermNode = 0;
+ $$.function = parseContext.handleConstructorCall($1.loc, $1);
+ }
+ | postfix_expression {
+ //
+ // Should be a method or subroutine call, but we haven't recognized the arguments yet.
+ //
+ $$.function = 0;
+ $$.intermNode = 0;
+
+ TIntermMethod* method = $1->getAsMethodNode();
+ if (method) {
+ $$.function = new TFunction(&method->getMethodName(), TType(EbtInt), EOpArrayLength);
+ $$.intermNode = method->getObject();
+ } else {
+ TIntermSymbol* symbol = $1->getAsSymbolNode();
+ if (symbol) {
+ parseContext.reservedErrorCheck(symbol->getLoc(), symbol->getName());
+ TFunction *function = new TFunction(&symbol->getName(), TType(EbtVoid));
+ $$.function = function;
+ } else
+ parseContext.error($1->getLoc(), "function call, method, or subroutine call expected", "", "");
+ }
+
+ if ($$.function == 0) {
+ // error recover
+ TString* empty = NewPoolTString("");
+ $$.function = new TFunction(empty, TType(EbtVoid), EOpNull);
+ }
+ }
+ | non_uniform_qualifier {
+ // Constructor
+ $$.intermNode = 0;
+ $$.function = parseContext.handleConstructorCall($1.loc, $1);
+ }
+ ;
+
+unary_expression
+ : postfix_expression {
+ parseContext.variableCheck($1);
+ $$ = $1;
+ if (TIntermMethod* method = $1->getAsMethodNode())
+ parseContext.error($1->getLoc(), "incomplete method syntax", method->getMethodName().c_str(), "");
+ }
+ | INC_OP unary_expression {
+ parseContext.lValueErrorCheck($1.loc, "++", $2);
+ $$ = parseContext.handleUnaryMath($1.loc, "++", EOpPreIncrement, $2);
+ }
+ | DEC_OP unary_expression {
+ parseContext.lValueErrorCheck($1.loc, "--", $2);
+ $$ = parseContext.handleUnaryMath($1.loc, "--", EOpPreDecrement, $2);
+ }
+ | unary_operator unary_expression {
+ if ($1.op != EOpNull) {
+ char errorOp[2] = {0, 0};
+ switch($1.op) {
+ case EOpNegative: errorOp[0] = '-'; break;
+ case EOpLogicalNot: errorOp[0] = '!'; break;
+ case EOpBitwiseNot: errorOp[0] = '~'; break;
+ default: break; // some compilers want this
+ }
+ $$ = parseContext.handleUnaryMath($1.loc, errorOp, $1.op, $2);
+ } else {
+ $$ = $2;
+ if ($$->getAsConstantUnion())
+ $$->getAsConstantUnion()->setExpression();
+ }
+ }
+ ;
+// Grammar Note: No traditional style type casts.
+
+unary_operator
+ : PLUS { $$.loc = $1.loc; $$.op = EOpNull; }
+ | DASH { $$.loc = $1.loc; $$.op = EOpNegative; }
+ | BANG { $$.loc = $1.loc; $$.op = EOpLogicalNot; }
+ | TILDE { $$.loc = $1.loc; $$.op = EOpBitwiseNot;
+ parseContext.fullIntegerCheck($1.loc, "bitwise not"); }
+ ;
+// Grammar Note: No '*' or '&' unary ops. Pointers are not supported.
+
+multiplicative_expression
+ : unary_expression { $$ = $1; }
+ | multiplicative_expression STAR unary_expression {
+ $$ = parseContext.handleBinaryMath($2.loc, "*", EOpMul, $1, $3);
+ if ($$ == 0)
+ $$ = $1;
+ }
+ | multiplicative_expression SLASH unary_expression {
+ $$ = parseContext.handleBinaryMath($2.loc, "/", EOpDiv, $1, $3);
+ if ($$ == 0)
+ $$ = $1;
+ }
+ | multiplicative_expression PERCENT unary_expression {
+ parseContext.fullIntegerCheck($2.loc, "%");
+ $$ = parseContext.handleBinaryMath($2.loc, "%", EOpMod, $1, $3);
+ if ($$ == 0)
+ $$ = $1;
+ }
+ ;
+
+additive_expression
+ : multiplicative_expression { $$ = $1; }
+ | additive_expression PLUS multiplicative_expression {
+ $$ = parseContext.handleBinaryMath($2.loc, "+", EOpAdd, $1, $3);
+ if ($$ == 0)
+ $$ = $1;
+ }
+ | additive_expression DASH multiplicative_expression {
+ $$ = parseContext.handleBinaryMath($2.loc, "-", EOpSub, $1, $3);
+ if ($$ == 0)
+ $$ = $1;
+ }
+ ;
+
+shift_expression
+ : additive_expression { $$ = $1; }
+ | shift_expression LEFT_OP additive_expression {
+ parseContext.fullIntegerCheck($2.loc, "bit shift left");
+ $$ = parseContext.handleBinaryMath($2.loc, "<<", EOpLeftShift, $1, $3);
+ if ($$ == 0)
+ $$ = $1;
+ }
+ | shift_expression RIGHT_OP additive_expression {
+ parseContext.fullIntegerCheck($2.loc, "bit shift right");
+ $$ = parseContext.handleBinaryMath($2.loc, ">>", EOpRightShift, $1, $3);
+ if ($$ == 0)
+ $$ = $1;
+ }
+ ;
+
+relational_expression
+ : shift_expression { $$ = $1; }
+ | relational_expression LEFT_ANGLE shift_expression {
+ $$ = parseContext.handleBinaryMath($2.loc, "<", EOpLessThan, $1, $3);
+ if ($$ == 0)
+ $$ = parseContext.intermediate.addConstantUnion(false, $2.loc);
+ }
+ | relational_expression RIGHT_ANGLE shift_expression {
+ $$ = parseContext.handleBinaryMath($2.loc, ">", EOpGreaterThan, $1, $3);
+ if ($$ == 0)
+ $$ = parseContext.intermediate.addConstantUnion(false, $2.loc);
+ }
+ | relational_expression LE_OP shift_expression {
+ $$ = parseContext.handleBinaryMath($2.loc, "<=", EOpLessThanEqual, $1, $3);
+ if ($$ == 0)
+ $$ = parseContext.intermediate.addConstantUnion(false, $2.loc);
+ }
+ | relational_expression GE_OP shift_expression {
+ $$ = parseContext.handleBinaryMath($2.loc, ">=", EOpGreaterThanEqual, $1, $3);
+ if ($$ == 0)
+ $$ = parseContext.intermediate.addConstantUnion(false, $2.loc);
+ }
+ ;
+
+equality_expression
+ : relational_expression { $$ = $1; }
+ | equality_expression EQ_OP relational_expression {
+ parseContext.arrayObjectCheck($2.loc, $1->getType(), "array comparison");
+ parseContext.opaqueCheck($2.loc, $1->getType(), "==");
+ parseContext.specializationCheck($2.loc, $1->getType(), "==");
+ parseContext.referenceCheck($2.loc, $1->getType(), "==");
+ $$ = parseContext.handleBinaryMath($2.loc, "==", EOpEqual, $1, $3);
+ if ($$ == 0)
+ $$ = parseContext.intermediate.addConstantUnion(false, $2.loc);
+ }
+ | equality_expression NE_OP relational_expression {
+ parseContext.arrayObjectCheck($2.loc, $1->getType(), "array comparison");
+ parseContext.opaqueCheck($2.loc, $1->getType(), "!=");
+ parseContext.specializationCheck($2.loc, $1->getType(), "!=");
+ parseContext.referenceCheck($2.loc, $1->getType(), "!=");
+ $$ = parseContext.handleBinaryMath($2.loc, "!=", EOpNotEqual, $1, $3);
+ if ($$ == 0)
+ $$ = parseContext.intermediate.addConstantUnion(false, $2.loc);
+ }
+ ;
+
+and_expression
+ : equality_expression { $$ = $1; }
+ | and_expression AMPERSAND equality_expression {
+ parseContext.fullIntegerCheck($2.loc, "bitwise and");
+ $$ = parseContext.handleBinaryMath($2.loc, "&", EOpAnd, $1, $3);
+ if ($$ == 0)
+ $$ = $1;
+ }
+ ;
+
+exclusive_or_expression
+ : and_expression { $$ = $1; }
+ | exclusive_or_expression CARET and_expression {
+ parseContext.fullIntegerCheck($2.loc, "bitwise exclusive or");
+ $$ = parseContext.handleBinaryMath($2.loc, "^", EOpExclusiveOr, $1, $3);
+ if ($$ == 0)
+ $$ = $1;
+ }
+ ;
+
+inclusive_or_expression
+ : exclusive_or_expression { $$ = $1; }
+ | inclusive_or_expression VERTICAL_BAR exclusive_or_expression {
+ parseContext.fullIntegerCheck($2.loc, "bitwise inclusive or");
+ $$ = parseContext.handleBinaryMath($2.loc, "|", EOpInclusiveOr, $1, $3);
+ if ($$ == 0)
+ $$ = $1;
+ }
+ ;
+
+logical_and_expression
+ : inclusive_or_expression { $$ = $1; }
+ | logical_and_expression AND_OP inclusive_or_expression {
+ $$ = parseContext.handleBinaryMath($2.loc, "&&", EOpLogicalAnd, $1, $3);
+ if ($$ == 0)
+ $$ = parseContext.intermediate.addConstantUnion(false, $2.loc);
+ }
+ ;
+
+logical_xor_expression
+ : logical_and_expression { $$ = $1; }
+ | logical_xor_expression XOR_OP logical_and_expression {
+ $$ = parseContext.handleBinaryMath($2.loc, "^^", EOpLogicalXor, $1, $3);
+ if ($$ == 0)
+ $$ = parseContext.intermediate.addConstantUnion(false, $2.loc);
+ }
+ ;
+
+logical_or_expression
+ : logical_xor_expression { $$ = $1; }
+ | logical_or_expression OR_OP logical_xor_expression {
+ $$ = parseContext.handleBinaryMath($2.loc, "||", EOpLogicalOr, $1, $3);
+ if ($$ == 0)
+ $$ = parseContext.intermediate.addConstantUnion(false, $2.loc);
+ }
+ ;
+
+conditional_expression
+ : logical_or_expression { $$ = $1; }
+ | logical_or_expression QUESTION {
+ ++parseContext.controlFlowNestingLevel;
+ }
+ expression COLON assignment_expression {
+ --parseContext.controlFlowNestingLevel;
+ parseContext.boolCheck($2.loc, $1);
+ parseContext.rValueErrorCheck($2.loc, "?", $1);
+ parseContext.rValueErrorCheck($5.loc, ":", $4);
+ parseContext.rValueErrorCheck($5.loc, ":", $6);
+ $$ = parseContext.intermediate.addSelection($1, $4, $6, $2.loc);
+ if ($$ == 0) {
+ parseContext.binaryOpError($2.loc, ":", $4->getCompleteString(), $6->getCompleteString());
+ $$ = $6;
+ }
+ }
+ ;
+
+assignment_expression
+ : conditional_expression { $$ = $1; }
+ | unary_expression assignment_operator assignment_expression {
+ parseContext.arrayObjectCheck($2.loc, $1->getType(), "array assignment");
+ parseContext.opaqueCheck($2.loc, $1->getType(), "=");
+ parseContext.storage16BitAssignmentCheck($2.loc, $1->getType(), "=");
+ parseContext.specializationCheck($2.loc, $1->getType(), "=");
+ parseContext.lValueErrorCheck($2.loc, "assign", $1);
+ parseContext.rValueErrorCheck($2.loc, "assign", $3);
+ $$ = parseContext.intermediate.addAssign($2.op, $1, $3, $2.loc);
+ if ($$ == 0) {
+ parseContext.assignError($2.loc, "assign", $1->getCompleteString(), $3->getCompleteString());
+ $$ = $1;
+ }
+ }
+ ;
+
+assignment_operator
+ : EQUAL {
+ $$.loc = $1.loc;
+ $$.op = EOpAssign;
+ }
+ | MUL_ASSIGN {
+ $$.loc = $1.loc;
+ $$.op = EOpMulAssign;
+ }
+ | DIV_ASSIGN {
+ $$.loc = $1.loc;
+ $$.op = EOpDivAssign;
+ }
+ | MOD_ASSIGN {
+ parseContext.fullIntegerCheck($1.loc, "%=");
+ $$.loc = $1.loc;
+ $$.op = EOpModAssign;
+ }
+ | ADD_ASSIGN {
+ $$.loc = $1.loc;
+ $$.op = EOpAddAssign;
+ }
+ | SUB_ASSIGN {
+ $$.loc = $1.loc;
+ $$.op = EOpSubAssign;
+ }
+ | LEFT_ASSIGN {
+ parseContext.fullIntegerCheck($1.loc, "bit-shift left assign");
+ $$.loc = $1.loc; $$.op = EOpLeftShiftAssign;
+ }
+ | RIGHT_ASSIGN {
+ parseContext.fullIntegerCheck($1.loc, "bit-shift right assign");
+ $$.loc = $1.loc; $$.op = EOpRightShiftAssign;
+ }
+ | AND_ASSIGN {
+ parseContext.fullIntegerCheck($1.loc, "bitwise-and assign");
+ $$.loc = $1.loc; $$.op = EOpAndAssign;
+ }
+ | XOR_ASSIGN {
+ parseContext.fullIntegerCheck($1.loc, "bitwise-xor assign");
+ $$.loc = $1.loc; $$.op = EOpExclusiveOrAssign;
+ }
+ | OR_ASSIGN {
+ parseContext.fullIntegerCheck($1.loc, "bitwise-or assign");
+ $$.loc = $1.loc; $$.op = EOpInclusiveOrAssign;
+ }
+ ;
+
+expression
+ : assignment_expression {
+ $$ = $1;
+ }
+ | expression COMMA assignment_expression {
+ parseContext.samplerConstructorLocationCheck($2.loc, ",", $3);
+ $$ = parseContext.intermediate.addComma($1, $3, $2.loc);
+ if ($$ == 0) {
+ parseContext.binaryOpError($2.loc, ",", $1->getCompleteString(), $3->getCompleteString());
+ $$ = $3;
+ }
+ }
+ ;
+
+constant_expression
+ : conditional_expression {
+ parseContext.constantValueCheck($1, "");
+ $$ = $1;
+ }
+ ;
+
+declaration
+ : function_prototype SEMICOLON {
+ parseContext.handleFunctionDeclarator($1.loc, *$1.function, true /* prototype */);
+ $$ = 0;
+ // TODO: 4.0 functionality: subroutines: make the identifier a user type for this signature
+ }
+ | init_declarator_list SEMICOLON {
+ if ($1.intermNode && $1.intermNode->getAsAggregate())
+ $1.intermNode->getAsAggregate()->setOperator(EOpSequence);
+ $$ = $1.intermNode;
+ }
+ | PRECISION precision_qualifier type_specifier SEMICOLON {
+ parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "precision statement");
+
+ // lazy setting of the previous scope's defaults, has effect only the first time it is called in a particular scope
+ parseContext.symbolTable.setPreviousDefaultPrecisions(&parseContext.defaultPrecision[0]);
+ parseContext.setDefaultPrecision($1.loc, $3, $2.qualifier.precision);
+ $$ = 0;
+ }
+ | block_structure SEMICOLON {
+ parseContext.declareBlock($1.loc, *$1.typeList);
+ $$ = 0;
+ }
+ | block_structure IDENTIFIER SEMICOLON {
+ parseContext.declareBlock($1.loc, *$1.typeList, $2.string);
+ $$ = 0;
+ }
+ | block_structure IDENTIFIER array_specifier SEMICOLON {
+ parseContext.declareBlock($1.loc, *$1.typeList, $2.string, $3.arraySizes);
+ $$ = 0;
+ }
+ | type_qualifier SEMICOLON {
+ parseContext.globalQualifierFixCheck($1.loc, $1.qualifier);
+ parseContext.updateStandaloneQualifierDefaults($1.loc, $1);
+ $$ = 0;
+ }
+ | type_qualifier IDENTIFIER SEMICOLON {
+ parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers);
+ parseContext.addQualifierToExisting($1.loc, $1.qualifier, *$2.string);
+ $$ = 0;
+ }
+ | type_qualifier IDENTIFIER identifier_list SEMICOLON {
+ parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers);
+ $3->push_back($2.string);
+ parseContext.addQualifierToExisting($1.loc, $1.qualifier, *$3);
+ $$ = 0;
+ }
+ ;
+
+block_structure
+ : type_qualifier IDENTIFIER LEFT_BRACE { parseContext.nestedBlockCheck($1.loc); } struct_declaration_list RIGHT_BRACE {
+ --parseContext.structNestingLevel;
+ parseContext.blockName = $2.string;
+ parseContext.globalQualifierFixCheck($1.loc, $1.qualifier);
+ parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers);
+ parseContext.currentBlockQualifier = $1.qualifier;
+ $$.loc = $1.loc;
+ $$.typeList = $5;
+ }
+
+identifier_list
+ : COMMA IDENTIFIER {
+ $$ = new TIdentifierList;
+ $$->push_back($2.string);
+ }
+ | identifier_list COMMA IDENTIFIER {
+ $$ = $1;
+ $$->push_back($3.string);
+ }
+ ;
+
+function_prototype
+ : function_declarator RIGHT_PAREN {
+ $$.function = $1;
+ $$.loc = $2.loc;
+ }
+ ;
+
+function_declarator
+ : function_header {
+ $$ = $1;
+ }
+ | function_header_with_parameters {
+ $$ = $1;
+ }
+ ;
+
+
+function_header_with_parameters
+ : function_header parameter_declaration {
+ // Add the parameter
+ $$ = $1;
+ if ($2.param.type->getBasicType() != EbtVoid)
+ $1->addParameter($2.param);
+ else
+ delete $2.param.type;
+ }
+ | function_header_with_parameters COMMA parameter_declaration {
+ //
+ // Only first parameter of one-parameter functions can be void
+ // The check for named parameters not being void is done in parameter_declarator
+ //
+ if ($3.param.type->getBasicType() == EbtVoid) {
+ //
+ // This parameter > first is void
+ //
+ parseContext.error($2.loc, "cannot be an argument type except for '(void)'", "void", "");
+ delete $3.param.type;
+ } else {
+ // Add the parameter
+ $$ = $1;
+ $1->addParameter($3.param);
+ }
+ }
+ ;
+
+function_header
+ : fully_specified_type IDENTIFIER LEFT_PAREN {
+ if ($1.qualifier.storage != EvqGlobal && $1.qualifier.storage != EvqTemporary) {
+ parseContext.error($2.loc, "no qualifiers allowed for function return",
+ GetStorageQualifierString($1.qualifier.storage), "");
+ }
+ if ($1.arraySizes)
+ parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes);
+
+ // Add the function as a prototype after parsing it (we do not support recursion)
+ TFunction *function;
+ TType type($1);
+
+ // Potentially rename shader entry point function. No-op most of the time.
+ parseContext.renameShaderFunction($2.string);
+
+ // Make the function
+ function = new TFunction($2.string, type);
+ $$ = function;
+ }
+ ;
+
+parameter_declarator
+ // Type + name
+ : type_specifier IDENTIFIER {
+ if ($1.arraySizes) {
+ parseContext.profileRequires($1.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type");
+ parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "arrayed type");
+ parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes);
+ }
+ if ($1.basicType == EbtVoid) {
+ parseContext.error($2.loc, "illegal use of type 'void'", $2.string->c_str(), "");
+ }
+ parseContext.reservedErrorCheck($2.loc, *$2.string);
+
+ TParameter param = {$2.string, new TType($1)};
+ $$.loc = $2.loc;
+ $$.param = param;
+ }
+ | type_specifier IDENTIFIER array_specifier {
+ if ($1.arraySizes) {
+ parseContext.profileRequires($1.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type");
+ parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "arrayed type");
+ parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes);
+ }
+ TType* type = new TType($1);
+ type->transferArraySizes($3.arraySizes);
+ type->copyArrayInnerSizes($1.arraySizes);
+
+ parseContext.arrayOfArrayVersionCheck($2.loc, type->getArraySizes());
+ parseContext.arraySizeRequiredCheck($3.loc, *$3.arraySizes);
+ parseContext.reservedErrorCheck($2.loc, *$2.string);
+
+ TParameter param = { $2.string, type };
+
+ $$.loc = $2.loc;
+ $$.param = param;
+ }
+ ;
+
+parameter_declaration
+ //
+ // With name
+ //
+ : type_qualifier parameter_declarator {
+ $$ = $2;
+ if ($1.qualifier.precision != EpqNone)
+ $$.param.type->getQualifier().precision = $1.qualifier.precision;
+ parseContext.precisionQualifierCheck($$.loc, $$.param.type->getBasicType(), $$.param.type->getQualifier());
+
+ parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers);
+ parseContext.parameterTypeCheck($2.loc, $1.qualifier.storage, *$$.param.type);
+ parseContext.paramCheckFix($1.loc, $1.qualifier, *$$.param.type);
+
+ }
+ | parameter_declarator {
+ $$ = $1;
+
+ parseContext.parameterTypeCheck($1.loc, EvqIn, *$1.param.type);
+ parseContext.paramCheckFixStorage($1.loc, EvqTemporary, *$$.param.type);
+ parseContext.precisionQualifierCheck($$.loc, $$.param.type->getBasicType(), $$.param.type->getQualifier());
+ }
+ //
+ // Without name
+ //
+ | type_qualifier parameter_type_specifier {
+ $$ = $2;
+ if ($1.qualifier.precision != EpqNone)
+ $$.param.type->getQualifier().precision = $1.qualifier.precision;
+ parseContext.precisionQualifierCheck($1.loc, $$.param.type->getBasicType(), $$.param.type->getQualifier());
+
+ parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers);
+ parseContext.parameterTypeCheck($2.loc, $1.qualifier.storage, *$$.param.type);
+ parseContext.paramCheckFix($1.loc, $1.qualifier, *$$.param.type);
+ }
+ | parameter_type_specifier {
+ $$ = $1;
+
+ parseContext.parameterTypeCheck($1.loc, EvqIn, *$1.param.type);
+ parseContext.paramCheckFixStorage($1.loc, EvqTemporary, *$$.param.type);
+ parseContext.precisionQualifierCheck($$.loc, $$.param.type->getBasicType(), $$.param.type->getQualifier());
+ }
+ ;
+
+parameter_type_specifier
+ : type_specifier {
+ TParameter param = { 0, new TType($1) };
+ $$.param = param;
+ if ($1.arraySizes)
+ parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes);
+ }
+ ;
+
+init_declarator_list
+ : single_declaration {
+ $$ = $1;
+ }
+ | init_declarator_list COMMA IDENTIFIER {
+ $$ = $1;
+ parseContext.declareVariable($3.loc, *$3.string, $1.type);
+ }
+ | init_declarator_list COMMA IDENTIFIER array_specifier {
+ $$ = $1;
+ parseContext.declareVariable($3.loc, *$3.string, $1.type, $4.arraySizes);
+ }
+ | init_declarator_list COMMA IDENTIFIER array_specifier EQUAL initializer {
+ $$.type = $1.type;
+ TIntermNode* initNode = parseContext.declareVariable($3.loc, *$3.string, $1.type, $4.arraySizes, $6);
+ $$.intermNode = parseContext.intermediate.growAggregate($1.intermNode, initNode, $5.loc);
+ }
+ | init_declarator_list COMMA IDENTIFIER EQUAL initializer {
+ $$.type = $1.type;
+ TIntermNode* initNode = parseContext.declareVariable($3.loc, *$3.string, $1.type, 0, $5);
+ $$.intermNode = parseContext.intermediate.growAggregate($1.intermNode, initNode, $4.loc);
+ }
+ ;
+
+single_declaration
+ : fully_specified_type {
+ $$.type = $1;
+ $$.intermNode = 0;
+ parseContext.declareTypeDefaults($$.loc, $$.type);
+ }
+ | fully_specified_type IDENTIFIER {
+ $$.type = $1;
+ $$.intermNode = 0;
+ parseContext.declareVariable($2.loc, *$2.string, $1);
+ }
+ | fully_specified_type IDENTIFIER array_specifier {
+ $$.type = $1;
+ $$.intermNode = 0;
+ parseContext.declareVariable($2.loc, *$2.string, $1, $3.arraySizes);
+ }
+ | fully_specified_type IDENTIFIER array_specifier EQUAL initializer {
+ $$.type = $1;
+ TIntermNode* initNode = parseContext.declareVariable($2.loc, *$2.string, $1, $3.arraySizes, $5);
+ $$.intermNode = parseContext.intermediate.growAggregate(0, initNode, $4.loc);
+ }
+ | fully_specified_type IDENTIFIER EQUAL initializer {
+ $$.type = $1;
+ TIntermNode* initNode = parseContext.declareVariable($2.loc, *$2.string, $1, 0, $4);
+ $$.intermNode = parseContext.intermediate.growAggregate(0, initNode, $3.loc);
+ }
+
+// Grammar Note: No 'enum', or 'typedef'.
+
+fully_specified_type
+ : type_specifier {
+ $$ = $1;
+
+ parseContext.globalQualifierTypeCheck($1.loc, $1.qualifier, $$);
+ if ($1.arraySizes) {
+ parseContext.profileRequires($1.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type");
+ parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "arrayed type");
+ }
+
+ parseContext.precisionQualifierCheck($$.loc, $$.basicType, $$.qualifier);
+ }
+ | type_qualifier type_specifier {
+ parseContext.globalQualifierFixCheck($1.loc, $1.qualifier);
+ parseContext.globalQualifierTypeCheck($1.loc, $1.qualifier, $2);
+
+ if ($2.arraySizes) {
+ parseContext.profileRequires($2.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type");
+ parseContext.profileRequires($2.loc, EEsProfile, 300, 0, "arrayed type");
+ }
+
+ if ($2.arraySizes && parseContext.arrayQualifierError($2.loc, $1.qualifier))
+ $2.arraySizes = nullptr;
+
+ parseContext.checkNoShaderLayouts($2.loc, $1.shaderQualifiers);
+ $2.shaderQualifiers.merge($1.shaderQualifiers);
+ parseContext.mergeQualifiers($2.loc, $2.qualifier, $1.qualifier, true);
+ parseContext.precisionQualifierCheck($2.loc, $2.basicType, $2.qualifier);
+
+ $$ = $2;
+
+ if (! $$.qualifier.isInterpolation() &&
+ ((parseContext.language == EShLangVertex && $$.qualifier.storage == EvqVaryingOut) ||
+ (parseContext.language == EShLangFragment && $$.qualifier.storage == EvqVaryingIn)))
+ $$.qualifier.smooth = true;
+ }
+ ;
+
+invariant_qualifier
+ : INVARIANT {
+ parseContext.globalCheck($1.loc, "invariant");
+ parseContext.profileRequires($$.loc, ENoProfile, 120, 0, "invariant");
+ $$.init($1.loc);
+ $$.qualifier.invariant = true;
+ }
+ ;
+
+interpolation_qualifier
+ : SMOOTH {
+ parseContext.globalCheck($1.loc, "smooth");
+ parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "smooth");
+ parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "smooth");
+ $$.init($1.loc);
+ $$.qualifier.smooth = true;
+ }
+ | FLAT {
+ parseContext.globalCheck($1.loc, "flat");
+ parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "flat");
+ parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "flat");
+ $$.init($1.loc);
+ $$.qualifier.flat = true;
+ }
+ | NOPERSPECTIVE {
+ parseContext.globalCheck($1.loc, "noperspective");
+#ifdef NV_EXTENSIONS
+ parseContext.profileRequires($1.loc, EEsProfile, 0, E_GL_NV_shader_noperspective_interpolation, "noperspective");
+#else
+ parseContext.requireProfile($1.loc, ~EEsProfile, "noperspective");
+#endif
+ parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "noperspective");
+ $$.init($1.loc);
+ $$.qualifier.nopersp = true;
+ }
+ | EXPLICITINTERPAMD {
+#ifdef AMD_EXTENSIONS
+ parseContext.globalCheck($1.loc, "__explicitInterpAMD");
+ parseContext.profileRequires($1.loc, ECoreProfile, 450, E_GL_AMD_shader_explicit_vertex_parameter, "explicit interpolation");
+ parseContext.profileRequires($1.loc, ECompatibilityProfile, 450, E_GL_AMD_shader_explicit_vertex_parameter, "explicit interpolation");
+ $$.init($1.loc);
+ $$.qualifier.explicitInterp = true;
+#endif
+ }
+ | PERVERTEXNV {
+#ifdef NV_EXTENSIONS
+ parseContext.globalCheck($1.loc, "pervertexNV");
+ parseContext.profileRequires($1.loc, ECoreProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric");
+ parseContext.profileRequires($1.loc, ECompatibilityProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric");
+ parseContext.profileRequires($1.loc, EEsProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric");
+ $$.init($1.loc);
+ $$.qualifier.pervertexNV = true;
+#endif
+ }
+ | PERPRIMITIVENV {
+#ifdef NV_EXTENSIONS
+ // No need for profile version or extension check. Shader stage already checks both.
+ parseContext.globalCheck($1.loc, "perprimitiveNV");
+ parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangFragmentMask | EShLangMeshNVMask), "perprimitiveNV");
+ // Fragment shader stage doesn't check for extension. So we explicitly add below extension check.
+ if (parseContext.language == EShLangFragment)
+ parseContext.requireExtensions($1.loc, 1, &E_GL_NV_mesh_shader, "perprimitiveNV");
+ $$.init($1.loc);
+ $$.qualifier.perPrimitiveNV = true;
+#endif
+ }
+ | PERVIEWNV {
+#ifdef NV_EXTENSIONS
+ // No need for profile version or extension check. Shader stage already checks both.
+ parseContext.globalCheck($1.loc, "perviewNV");
+ parseContext.requireStage($1.loc, EShLangMeshNV, "perviewNV");
+ $$.init($1.loc);
+ $$.qualifier.perViewNV = true;
+#endif
+ }
+ | PERTASKNV {
+#ifdef NV_EXTENSIONS
+ // No need for profile version or extension check. Shader stage already checks both.
+ parseContext.globalCheck($1.loc, "taskNV");
+ parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangTaskNVMask | EShLangMeshNVMask), "taskNV");
+ $$.init($1.loc);
+ $$.qualifier.perTaskNV = true;
+#endif
+ }
+ ;
+
+layout_qualifier
+ : LAYOUT LEFT_PAREN layout_qualifier_id_list RIGHT_PAREN {
+ $$ = $3;
+ }
+ ;
+
+layout_qualifier_id_list
+ : layout_qualifier_id {
+ $$ = $1;
+ }
+ | layout_qualifier_id_list COMMA layout_qualifier_id {
+ $$ = $1;
+ $$.shaderQualifiers.merge($3.shaderQualifiers);
+ parseContext.mergeObjectLayoutQualifiers($$.qualifier, $3.qualifier, false);
+ }
+
+layout_qualifier_id
+ : IDENTIFIER {
+ $$.init($1.loc);
+ parseContext.setLayoutQualifier($1.loc, $$, *$1.string);
+ }
+ | IDENTIFIER EQUAL constant_expression {
+ $$.init($1.loc);
+ parseContext.setLayoutQualifier($1.loc, $$, *$1.string, $3);
+ }
+ | SHARED { // because "shared" is both an identifier and a keyword
+ $$.init($1.loc);
+ TString strShared("shared");
+ parseContext.setLayoutQualifier($1.loc, $$, strShared);
+ }
+ ;
+
+precise_qualifier
+ : PRECISE {
+ parseContext.profileRequires($$.loc, ECoreProfile | ECompatibilityProfile, 400, E_GL_ARB_gpu_shader5, "precise");
+ parseContext.profileRequires($1.loc, EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, "precise");
+ $$.init($1.loc);
+ $$.qualifier.noContraction = true;
+ }
+ ;
+
+type_qualifier
+ : single_type_qualifier {
+ $$ = $1;
+ }
+ | type_qualifier single_type_qualifier {
+ $$ = $1;
+ if ($$.basicType == EbtVoid)
+ $$.basicType = $2.basicType;
+
+ $$.shaderQualifiers.merge($2.shaderQualifiers);
+ parseContext.mergeQualifiers($$.loc, $$.qualifier, $2.qualifier, false);
+ }
+ ;
+
+single_type_qualifier
+ : storage_qualifier {
+ $$ = $1;
+ }
+ | layout_qualifier {
+ $$ = $1;
+ }
+ | precision_qualifier {
+ parseContext.checkPrecisionQualifier($1.loc, $1.qualifier.precision);
+ $$ = $1;
+ }
+ | interpolation_qualifier {
+ // allow inheritance of storage qualifier from block declaration
+ $$ = $1;
+ }
+ | invariant_qualifier {
+ // allow inheritance of storage qualifier from block declaration
+ $$ = $1;
+ }
+ | precise_qualifier {
+ // allow inheritance of storage qualifier from block declaration
+ $$ = $1;
+ }
+ | non_uniform_qualifier {
+ $$ = $1;
+ }
+ ;
+
+storage_qualifier
+ : CONST {
+ $$.init($1.loc);
+ $$.qualifier.storage = EvqConst; // will later turn into EvqConstReadOnly, if the initializer is not constant
+ }
+ | ATTRIBUTE {
+ parseContext.requireStage($1.loc, EShLangVertex, "attribute");
+ parseContext.checkDeprecated($1.loc, ECoreProfile, 130, "attribute");
+ parseContext.checkDeprecated($1.loc, ENoProfile, 130, "attribute");
+ parseContext.requireNotRemoved($1.loc, ECoreProfile, 420, "attribute");
+ parseContext.requireNotRemoved($1.loc, EEsProfile, 300, "attribute");
+
+ parseContext.globalCheck($1.loc, "attribute");
+
+ $$.init($1.loc);
+ $$.qualifier.storage = EvqVaryingIn;
+ }
+ | VARYING {
+ parseContext.checkDeprecated($1.loc, ENoProfile, 130, "varying");
+ parseContext.checkDeprecated($1.loc, ECoreProfile, 130, "varying");
+ parseContext.requireNotRemoved($1.loc, ECoreProfile, 420, "varying");
+ parseContext.requireNotRemoved($1.loc, EEsProfile, 300, "varying");
+
+ parseContext.globalCheck($1.loc, "varying");
+
+ $$.init($1.loc);
+ if (parseContext.language == EShLangVertex)
+ $$.qualifier.storage = EvqVaryingOut;
+ else
+ $$.qualifier.storage = EvqVaryingIn;
+ }
+ | INOUT {
+ parseContext.globalCheck($1.loc, "inout");
+ $$.init($1.loc);
+ $$.qualifier.storage = EvqInOut;
+ }
+ | IN {
+ parseContext.globalCheck($1.loc, "in");
+ $$.init($1.loc);
+ // whether this is a parameter "in" or a pipeline "in" will get sorted out a bit later
+ $$.qualifier.storage = EvqIn;
+ }
+ | OUT {
+ parseContext.globalCheck($1.loc, "out");
+ $$.init($1.loc);
+ // whether this is a parameter "out" or a pipeline "out" will get sorted out a bit later
+ $$.qualifier.storage = EvqOut;
+ }
+ | CENTROID {
+ parseContext.profileRequires($1.loc, ENoProfile, 120, 0, "centroid");
+ parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "centroid");
+ parseContext.globalCheck($1.loc, "centroid");
+ $$.init($1.loc);
+ $$.qualifier.centroid = true;
+ }
+ | PATCH {
+ parseContext.globalCheck($1.loc, "patch");
+ parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangTessControlMask | EShLangTessEvaluationMask), "patch");
+ $$.init($1.loc);
+ $$.qualifier.patch = true;
+ }
+ | SAMPLE {
+ parseContext.globalCheck($1.loc, "sample");
+ $$.init($1.loc);
+ $$.qualifier.sample = true;
+ }
+ | UNIFORM {
+ parseContext.globalCheck($1.loc, "uniform");
+ $$.init($1.loc);
+ $$.qualifier.storage = EvqUniform;
+ }
+ | BUFFER {
+ parseContext.globalCheck($1.loc, "buffer");
+ $$.init($1.loc);
+ $$.qualifier.storage = EvqBuffer;
+ }
+ | HITATTRNV {
+#ifdef NV_EXTENSIONS
+ parseContext.globalCheck($1.loc, "hitAttributeNV");
+ parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangIntersectNVMask | EShLangClosestHitNVMask
+ | EShLangAnyHitNVMask), "hitAttributeNV");
+ parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "hitAttributeNV");
+ $$.init($1.loc);
+ $$.qualifier.storage = EvqHitAttrNV;
+#endif
+ }
+ | PAYLOADNV {
+#ifdef NV_EXTENSIONS
+ parseContext.globalCheck($1.loc, "rayPayloadNV");
+ parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangRayGenNVMask | EShLangClosestHitNVMask |
+ EShLangAnyHitNVMask | EShLangMissNVMask), "rayPayloadNV");
+ parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "rayPayloadNV");
+ $$.init($1.loc);
+ $$.qualifier.storage = EvqPayloadNV;
+#endif
+ }
+ | PAYLOADINNV {
+#ifdef NV_EXTENSIONS
+ parseContext.globalCheck($1.loc, "rayPayloadInNV");
+ parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangClosestHitNVMask |
+ EShLangAnyHitNVMask | EShLangMissNVMask), "rayPayloadInNV");
+ parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "rayPayloadInNV");
+ $$.init($1.loc);
+ $$.qualifier.storage = EvqPayloadInNV;
+#endif
+ }
+ | CALLDATANV {
+#ifdef NV_EXTENSIONS
+ parseContext.globalCheck($1.loc, "callableDataNV");
+ parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangRayGenNVMask |
+ EShLangClosestHitNVMask | EShLangMissNVMask | EShLangCallableNVMask), "callableDataNV");
+ parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "callableDataNV");
+ $$.init($1.loc);
+ $$.qualifier.storage = EvqCallableDataNV;
+#endif
+ }
+ | CALLDATAINNV {
+#ifdef NV_EXTENSIONS
+ parseContext.globalCheck($1.loc, "callableDataInNV");
+ parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangCallableNVMask), "callableDataInNV");
+ parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "callableDataInNV");
+ $$.init($1.loc);
+ $$.qualifier.storage = EvqCallableDataInNV;
+#endif
+ }
+ | SHARED {
+ parseContext.globalCheck($1.loc, "shared");
+ parseContext.profileRequires($1.loc, ECoreProfile | ECompatibilityProfile, 430, E_GL_ARB_compute_shader, "shared");
+ parseContext.profileRequires($1.loc, EEsProfile, 310, 0, "shared");
+#ifdef NV_EXTENSIONS
+ parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangComputeMask | EShLangMeshNVMask | EShLangTaskNVMask), "shared");
+#else
+ parseContext.requireStage($1.loc, EShLangCompute, "shared");
+#endif
+ $$.init($1.loc);
+ $$.qualifier.storage = EvqShared;
+ }
+ | COHERENT {
+ $$.init($1.loc);
+ $$.qualifier.coherent = true;
+ }
+ | DEVICECOHERENT {
+ $$.init($1.loc);
+ parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "devicecoherent");
+ $$.qualifier.devicecoherent = true;
+ }
+ | QUEUEFAMILYCOHERENT {
+ $$.init($1.loc);
+ parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "queuefamilycoherent");
+ $$.qualifier.queuefamilycoherent = true;
+ }
+ | WORKGROUPCOHERENT {
+ $$.init($1.loc);
+ parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "workgroupcoherent");
+ $$.qualifier.workgroupcoherent = true;
+ }
+ | SUBGROUPCOHERENT {
+ $$.init($1.loc);
+ parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "subgroupcoherent");
+ $$.qualifier.subgroupcoherent = true;
+ }
+ | NONPRIVATE {
+ $$.init($1.loc);
+ parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "nonprivate");
+ $$.qualifier.nonprivate = true;
+ }
+ | VOLATILE {
+ $$.init($1.loc);
+ $$.qualifier.volatil = true;
+ }
+ | RESTRICT {
+ $$.init($1.loc);
+ $$.qualifier.restrict = true;
+ }
+ | READONLY {
+ $$.init($1.loc);
+ $$.qualifier.readonly = true;
+ }
+ | WRITEONLY {
+ $$.init($1.loc);
+ $$.qualifier.writeonly = true;
+ }
+ | SUBROUTINE {
+ parseContext.spvRemoved($1.loc, "subroutine");
+ parseContext.globalCheck($1.loc, "subroutine");
+ parseContext.unimplemented($1.loc, "subroutine");
+ $$.init($1.loc);
+ }
+ | SUBROUTINE LEFT_PAREN type_name_list RIGHT_PAREN {
+ parseContext.spvRemoved($1.loc, "subroutine");
+ parseContext.globalCheck($1.loc, "subroutine");
+ parseContext.unimplemented($1.loc, "subroutine");
+ $$.init($1.loc);
+ }
+ ;
+
+non_uniform_qualifier
+ : NONUNIFORM {
+ $$.init($1.loc);
+ $$.qualifier.nonUniform = true;
+ }
+ ;
+
+type_name_list
+ : IDENTIFIER {
+ // TODO
+ }
+ | type_name_list COMMA IDENTIFIER {
+ // TODO: 4.0 semantics: subroutines
+ // 1) make sure each identifier is a type declared earlier with SUBROUTINE
+ // 2) save all of the identifiers for future comparison with the declared function
+ }
+ ;
+
+type_specifier
+ : type_specifier_nonarray type_parameter_specifier_opt {
+ $$ = $1;
+ $$.qualifier.precision = parseContext.getDefaultPrecision($$);
+ $$.typeParameters = $2;
+ }
+ | type_specifier_nonarray type_parameter_specifier_opt array_specifier {
+ parseContext.arrayOfArrayVersionCheck($3.loc, $3.arraySizes);
+ $$ = $1;
+ $$.qualifier.precision = parseContext.getDefaultPrecision($$);
+ $$.typeParameters = $2;
+ $$.arraySizes = $3.arraySizes;
+ }
+ ;
+
+array_specifier
+ : LEFT_BRACKET RIGHT_BRACKET {
+ $$.loc = $1.loc;
+ $$.arraySizes = new TArraySizes;
+ $$.arraySizes->addInnerSize();
+ }
+ | LEFT_BRACKET conditional_expression RIGHT_BRACKET {
+ $$.loc = $1.loc;
+ $$.arraySizes = new TArraySizes;
+
+ TArraySize size;
+ parseContext.arraySizeCheck($2->getLoc(), $2, size, "array size");
+ $$.arraySizes->addInnerSize(size);
+ }
+ | array_specifier LEFT_BRACKET RIGHT_BRACKET {
+ $$ = $1;
+ $$.arraySizes->addInnerSize();
+ }
+ | array_specifier LEFT_BRACKET conditional_expression RIGHT_BRACKET {
+ $$ = $1;
+
+ TArraySize size;
+ parseContext.arraySizeCheck($3->getLoc(), $3, size, "array size");
+ $$.arraySizes->addInnerSize(size);
+ }
+ ;
+
+type_parameter_specifier_opt
+ : type_parameter_specifier {
+ $$ = $1;
+ }
+ | /* May be null */ {
+ $$ = 0;
+ }
+ ;
+
+type_parameter_specifier
+ : LEFT_ANGLE type_parameter_specifier_list RIGHT_ANGLE {
+ $$ = $2;
+ }
+ ;
+
+type_parameter_specifier_list
+ : unary_expression {
+ $$ = new TArraySizes;
+
+ TArraySize size;
+ parseContext.arraySizeCheck($1->getLoc(), $1, size, "type parameter");
+ $$->addInnerSize(size);
+ }
+ | type_parameter_specifier_list COMMA unary_expression {
+ $$ = $1;
+
+ TArraySize size;
+ parseContext.arraySizeCheck($3->getLoc(), $3, size, "type parameter");
+ $$->addInnerSize(size);
+ }
+ ;
+
+type_specifier_nonarray
+ : VOID {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtVoid;
+ }
+ | FLOAT {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ }
+ | DOUBLE {
+ parseContext.doubleCheck($1.loc, "double");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ }
+ | FLOAT16_T {
+ parseContext.float16ScalarVectorCheck($1.loc, "float16_t", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat16;
+ }
+ | FLOAT32_T {
+ parseContext.explicitFloat32Check($1.loc, "float32_t", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ }
+ | FLOAT64_T {
+ parseContext.explicitFloat64Check($1.loc, "float64_t", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ }
+ | INT {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtInt;
+ }
+ | UINT {
+ parseContext.fullIntegerCheck($1.loc, "unsigned integer");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtUint;
+ }
+ | INT8_T {
+ parseContext.int8ScalarVectorCheck($1.loc, "8-bit signed integer", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtInt8;
+ }
+ | UINT8_T {
+ parseContext.int8ScalarVectorCheck($1.loc, "8-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtUint8;
+ }
+ | INT16_T {
+ parseContext.int16ScalarVectorCheck($1.loc, "16-bit signed integer", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtInt16;
+ }
+ | UINT16_T {
+ parseContext.int16ScalarVectorCheck($1.loc, "16-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtUint16;
+ }
+ | INT32_T {
+ parseContext.explicitInt32Check($1.loc, "32-bit signed integer", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtInt;
+ }
+ | UINT32_T {
+ parseContext.explicitInt32Check($1.loc, "32-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtUint;
+ }
+ | INT64_T {
+ parseContext.int64Check($1.loc, "64-bit integer", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtInt64;
+ }
+ | UINT64_T {
+ parseContext.int64Check($1.loc, "64-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtUint64;
+ }
+ | BOOL {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtBool;
+ }
+ | VEC2 {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setVector(2);
+ }
+ | VEC3 {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setVector(3);
+ }
+ | VEC4 {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setVector(4);
+ }
+ | DVEC2 {
+ parseContext.doubleCheck($1.loc, "double vector");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setVector(2);
+ }
+ | DVEC3 {
+ parseContext.doubleCheck($1.loc, "double vector");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setVector(3);
+ }
+ | DVEC4 {
+ parseContext.doubleCheck($1.loc, "double vector");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setVector(4);
+ }
+ | F16VEC2 {
+ parseContext.float16ScalarVectorCheck($1.loc, "half float vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat16;
+ $$.setVector(2);
+ }
+ | F16VEC3 {
+ parseContext.float16ScalarVectorCheck($1.loc, "half float vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat16;
+ $$.setVector(3);
+ }
+ | F16VEC4 {
+ parseContext.float16ScalarVectorCheck($1.loc, "half float vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat16;
+ $$.setVector(4);
+ }
+ | F32VEC2 {
+ parseContext.explicitFloat32Check($1.loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setVector(2);
+ }
+ | F32VEC3 {
+ parseContext.explicitFloat32Check($1.loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setVector(3);
+ }
+ | F32VEC4 {
+ parseContext.explicitFloat32Check($1.loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setVector(4);
+ }
+ | F64VEC2 {
+ parseContext.explicitFloat64Check($1.loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setVector(2);
+ }
+ | F64VEC3 {
+ parseContext.explicitFloat64Check($1.loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setVector(3);
+ }
+ | F64VEC4 {
+ parseContext.explicitFloat64Check($1.loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setVector(4);
+ }
+ | BVEC2 {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtBool;
+ $$.setVector(2);
+ }
+ | BVEC3 {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtBool;
+ $$.setVector(3);
+ }
+ | BVEC4 {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtBool;
+ $$.setVector(4);
+ }
+ | IVEC2 {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtInt;
+ $$.setVector(2);
+ }
+ | IVEC3 {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtInt;
+ $$.setVector(3);
+ }
+ | IVEC4 {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtInt;
+ $$.setVector(4);
+ }
+ | I8VEC2 {
+ parseContext.int8ScalarVectorCheck($1.loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtInt8;
+ $$.setVector(2);
+ }
+ | I8VEC3 {
+ parseContext.int8ScalarVectorCheck($1.loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtInt8;
+ $$.setVector(3);
+ }
+ | I8VEC4 {
+ parseContext.int8ScalarVectorCheck($1.loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtInt8;
+ $$.setVector(4);
+ }
+ | I16VEC2 {
+ parseContext.int16ScalarVectorCheck($1.loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtInt16;
+ $$.setVector(2);
+ }
+ | I16VEC3 {
+ parseContext.int16ScalarVectorCheck($1.loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtInt16;
+ $$.setVector(3);
+ }
+ | I16VEC4 {
+ parseContext.int16ScalarVectorCheck($1.loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtInt16;
+ $$.setVector(4);
+ }
+ | I32VEC2 {
+ parseContext.explicitInt32Check($1.loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtInt;
+ $$.setVector(2);
+ }
+ | I32VEC3 {
+ parseContext.explicitInt32Check($1.loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtInt;
+ $$.setVector(3);
+ }
+ | I32VEC4 {
+ parseContext.explicitInt32Check($1.loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtInt;
+ $$.setVector(4);
+ }
+ | I64VEC2 {
+ parseContext.int64Check($1.loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtInt64;
+ $$.setVector(2);
+ }
+ | I64VEC3 {
+ parseContext.int64Check($1.loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtInt64;
+ $$.setVector(3);
+ }
+ | I64VEC4 {
+ parseContext.int64Check($1.loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtInt64;
+ $$.setVector(4);
+ }
+ | UVEC2 {
+ parseContext.fullIntegerCheck($1.loc, "unsigned integer vector");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtUint;
+ $$.setVector(2);
+ }
+ | UVEC3 {
+ parseContext.fullIntegerCheck($1.loc, "unsigned integer vector");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtUint;
+ $$.setVector(3);
+ }
+ | UVEC4 {
+ parseContext.fullIntegerCheck($1.loc, "unsigned integer vector");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtUint;
+ $$.setVector(4);
+ }
+ | U8VEC2 {
+ parseContext.int8ScalarVectorCheck($1.loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtUint8;
+ $$.setVector(2);
+ }
+ | U8VEC3 {
+ parseContext.int8ScalarVectorCheck($1.loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtUint8;
+ $$.setVector(3);
+ }
+ | U8VEC4 {
+ parseContext.int8ScalarVectorCheck($1.loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtUint8;
+ $$.setVector(4);
+ }
+ | U16VEC2 {
+ parseContext.int16ScalarVectorCheck($1.loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtUint16;
+ $$.setVector(2);
+ }
+ | U16VEC3 {
+ parseContext.int16ScalarVectorCheck($1.loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtUint16;
+ $$.setVector(3);
+ }
+ | U16VEC4 {
+ parseContext.int16ScalarVectorCheck($1.loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtUint16;
+ $$.setVector(4);
+ }
+ | U32VEC2 {
+ parseContext.explicitInt32Check($1.loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtUint;
+ $$.setVector(2);
+ }
+ | U32VEC3 {
+ parseContext.explicitInt32Check($1.loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtUint;
+ $$.setVector(3);
+ }
+ | U32VEC4 {
+ parseContext.explicitInt32Check($1.loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtUint;
+ $$.setVector(4);
+ }
+ | U64VEC2 {
+ parseContext.int64Check($1.loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtUint64;
+ $$.setVector(2);
+ }
+ | U64VEC3 {
+ parseContext.int64Check($1.loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtUint64;
+ $$.setVector(3);
+ }
+ | U64VEC4 {
+ parseContext.int64Check($1.loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtUint64;
+ $$.setVector(4);
+ }
+ | MAT2 {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(2, 2);
+ }
+ | MAT3 {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(3, 3);
+ }
+ | MAT4 {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(4, 4);
+ }
+ | MAT2X2 {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(2, 2);
+ }
+ | MAT2X3 {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(2, 3);
+ }
+ | MAT2X4 {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(2, 4);
+ }
+ | MAT3X2 {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(3, 2);
+ }
+ | MAT3X3 {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(3, 3);
+ }
+ | MAT3X4 {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(3, 4);
+ }
+ | MAT4X2 {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(4, 2);
+ }
+ | MAT4X3 {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(4, 3);
+ }
+ | MAT4X4 {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(4, 4);
+ }
+ | DMAT2 {
+ parseContext.doubleCheck($1.loc, "double matrix");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(2, 2);
+ }
+ | DMAT3 {
+ parseContext.doubleCheck($1.loc, "double matrix");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(3, 3);
+ }
+ | DMAT4 {
+ parseContext.doubleCheck($1.loc, "double matrix");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(4, 4);
+ }
+ | DMAT2X2 {
+ parseContext.doubleCheck($1.loc, "double matrix");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(2, 2);
+ }
+ | DMAT2X3 {
+ parseContext.doubleCheck($1.loc, "double matrix");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(2, 3);
+ }
+ | DMAT2X4 {
+ parseContext.doubleCheck($1.loc, "double matrix");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(2, 4);
+ }
+ | DMAT3X2 {
+ parseContext.doubleCheck($1.loc, "double matrix");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(3, 2);
+ }
+ | DMAT3X3 {
+ parseContext.doubleCheck($1.loc, "double matrix");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(3, 3);
+ }
+ | DMAT3X4 {
+ parseContext.doubleCheck($1.loc, "double matrix");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(3, 4);
+ }
+ | DMAT4X2 {
+ parseContext.doubleCheck($1.loc, "double matrix");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(4, 2);
+ }
+ | DMAT4X3 {
+ parseContext.doubleCheck($1.loc, "double matrix");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(4, 3);
+ }
+ | DMAT4X4 {
+ parseContext.doubleCheck($1.loc, "double matrix");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(4, 4);
+ }
+ | F16MAT2 {
+ parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat16;
+ $$.setMatrix(2, 2);
+ }
+ | F16MAT3 {
+ parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat16;
+ $$.setMatrix(3, 3);
+ }
+ | F16MAT4 {
+ parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat16;
+ $$.setMatrix(4, 4);
+ }
+ | F16MAT2X2 {
+ parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat16;
+ $$.setMatrix(2, 2);
+ }
+ | F16MAT2X3 {
+ parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat16;
+ $$.setMatrix(2, 3);
+ }
+ | F16MAT2X4 {
+ parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat16;
+ $$.setMatrix(2, 4);
+ }
+ | F16MAT3X2 {
+ parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat16;
+ $$.setMatrix(3, 2);
+ }
+ | F16MAT3X3 {
+ parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat16;
+ $$.setMatrix(3, 3);
+ }
+ | F16MAT3X4 {
+ parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat16;
+ $$.setMatrix(3, 4);
+ }
+ | F16MAT4X2 {
+ parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat16;
+ $$.setMatrix(4, 2);
+ }
+ | F16MAT4X3 {
+ parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat16;
+ $$.setMatrix(4, 3);
+ }
+ | F16MAT4X4 {
+ parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat16;
+ $$.setMatrix(4, 4);
+ }
+ | F32MAT2 {
+ parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(2, 2);
+ }
+ | F32MAT3 {
+ parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(3, 3);
+ }
+ | F32MAT4 {
+ parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(4, 4);
+ }
+ | F32MAT2X2 {
+ parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(2, 2);
+ }
+ | F32MAT2X3 {
+ parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(2, 3);
+ }
+ | F32MAT2X4 {
+ parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(2, 4);
+ }
+ | F32MAT3X2 {
+ parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(3, 2);
+ }
+ | F32MAT3X3 {
+ parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(3, 3);
+ }
+ | F32MAT3X4 {
+ parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(3, 4);
+ }
+ | F32MAT4X2 {
+ parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(4, 2);
+ }
+ | F32MAT4X3 {
+ parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(4, 3);
+ }
+ | F32MAT4X4 {
+ parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.setMatrix(4, 4);
+ }
+ | F64MAT2 {
+ parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(2, 2);
+ }
+ | F64MAT3 {
+ parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(3, 3);
+ }
+ | F64MAT4 {
+ parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(4, 4);
+ }
+ | F64MAT2X2 {
+ parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(2, 2);
+ }
+ | F64MAT2X3 {
+ parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(2, 3);
+ }
+ | F64MAT2X4 {
+ parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(2, 4);
+ }
+ | F64MAT3X2 {
+ parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(3, 2);
+ }
+ | F64MAT3X3 {
+ parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(3, 3);
+ }
+ | F64MAT3X4 {
+ parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(3, 4);
+ }
+ | F64MAT4X2 {
+ parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(4, 2);
+ }
+ | F64MAT4X3 {
+ parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(4, 3);
+ }
+ | F64MAT4X4 {
+ parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtDouble;
+ $$.setMatrix(4, 4);
+ }
+ | ACCSTRUCTNV {
+#ifdef NV_EXTENSIONS
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtAccStructNV;
+#endif
+ }
+ | ATOMIC_UINT {
+ parseContext.vulkanRemoved($1.loc, "atomic counter types");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtAtomicUint;
+ }
+ | SAMPLER1D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat, Esd1D);
+ }
+ | SAMPLER2D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat, Esd2D);
+ }
+ | SAMPLER3D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat, Esd3D);
+ }
+ | SAMPLERCUBE {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat, EsdCube);
+ }
+ | SAMPLER1DSHADOW {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat, Esd1D, false, true);
+ }
+ | SAMPLER2DSHADOW {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat, Esd2D, false, true);
+ }
+ | SAMPLERCUBESHADOW {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat, EsdCube, false, true);
+ }
+ | SAMPLER1DARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat, Esd1D, true);
+ }
+ | SAMPLER2DARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat, Esd2D, true);
+ }
+ | SAMPLER1DARRAYSHADOW {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat, Esd1D, true, true);
+ }
+ | SAMPLER2DARRAYSHADOW {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat, Esd2D, true, true);
+ }
+ | SAMPLERCUBEARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat, EsdCube, true);
+ }
+ | SAMPLERCUBEARRAYSHADOW {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat, EsdCube, true, true);
+ }
+ | F16SAMPLER1D {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat16, Esd1D);
+#endif
+ }
+ | F16SAMPLER2D {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat16, Esd2D);
+#endif
+ }
+ | F16SAMPLER3D {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat16, Esd3D);
+#endif
+ }
+ | F16SAMPLERCUBE {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat16, EsdCube);
+#endif
+ }
+ | F16SAMPLER1DSHADOW {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat16, Esd1D, false, true);
+#endif
+ }
+ | F16SAMPLER2DSHADOW {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat16, Esd2D, false, true);
+#endif
+ }
+ | F16SAMPLERCUBESHADOW {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat16, EsdCube, false, true);
+#endif
+ }
+ | F16SAMPLER1DARRAY {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat16, Esd1D, true);
+#endif
+ }
+ | F16SAMPLER2DARRAY {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat16, Esd2D, true);
+#endif
+ }
+ | F16SAMPLER1DARRAYSHADOW {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat16, Esd1D, true, true);
+#endif
+ }
+ | F16SAMPLER2DARRAYSHADOW {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat16, Esd2D, true, true);
+#endif
+ }
+ | F16SAMPLERCUBEARRAY {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat16, EsdCube, true);
+#endif
+ }
+ | F16SAMPLERCUBEARRAYSHADOW {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat16, EsdCube, true, true);
+#endif
+ }
+ | ISAMPLER1D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtInt, Esd1D);
+ }
+ | ISAMPLER2D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtInt, Esd2D);
+ }
+ | ISAMPLER3D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtInt, Esd3D);
+ }
+ | ISAMPLERCUBE {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtInt, EsdCube);
+ }
+ | ISAMPLER1DARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtInt, Esd1D, true);
+ }
+ | ISAMPLER2DARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtInt, Esd2D, true);
+ }
+ | ISAMPLERCUBEARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtInt, EsdCube, true);
+ }
+ | USAMPLER1D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtUint, Esd1D);
+ }
+ | USAMPLER2D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtUint, Esd2D);
+ }
+ | USAMPLER3D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtUint, Esd3D);
+ }
+ | USAMPLERCUBE {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtUint, EsdCube);
+ }
+ | USAMPLER1DARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtUint, Esd1D, true);
+ }
+ | USAMPLER2DARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtUint, Esd2D, true);
+ }
+ | USAMPLERCUBEARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtUint, EsdCube, true);
+ }
+ | SAMPLER2DRECT {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat, EsdRect);
+ }
+ | SAMPLER2DRECTSHADOW {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat, EsdRect, false, true);
+ }
+ | F16SAMPLER2DRECT {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat16, EsdRect);
+#endif
+ }
+ | F16SAMPLER2DRECTSHADOW {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat16, EsdRect, false, true);
+#endif
+ }
+ | ISAMPLER2DRECT {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtInt, EsdRect);
+ }
+ | USAMPLER2DRECT {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtUint, EsdRect);
+ }
+ | SAMPLERBUFFER {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat, EsdBuffer);
+ }
+ | F16SAMPLERBUFFER {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat16, EsdBuffer);
+#endif
+ }
+ | ISAMPLERBUFFER {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtInt, EsdBuffer);
+ }
+ | USAMPLERBUFFER {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtUint, EsdBuffer);
+ }
+ | SAMPLER2DMS {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat, Esd2D, false, false, true);
+ }
+ | F16SAMPLER2DMS {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat16, Esd2D, false, false, true);
+#endif
+ }
+ | ISAMPLER2DMS {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtInt, Esd2D, false, false, true);
+ }
+ | USAMPLER2DMS {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtUint, Esd2D, false, false, true);
+ }
+ | SAMPLER2DMSARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat, Esd2D, true, false, true);
+ }
+ | F16SAMPLER2DMSARRAY {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat16, Esd2D, true, false, true);
+#endif
+ }
+ | ISAMPLER2DMSARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtInt, Esd2D, true, false, true);
+ }
+ | USAMPLER2DMSARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtUint, Esd2D, true, false, true);
+ }
+ | SAMPLER {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setPureSampler(false);
+ }
+ | SAMPLERSHADOW {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setPureSampler(true);
+ }
+ | TEXTURE1D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat, Esd1D);
+ }
+ | F16TEXTURE1D {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat16, Esd1D);
+#endif
+ }
+ | TEXTURE2D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat, Esd2D);
+ }
+ | F16TEXTURE2D {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat16, Esd2D);
+#endif
+ }
+ | TEXTURE3D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat, Esd3D);
+ }
+ | F16TEXTURE3D {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat16, Esd3D);
+#endif
+ }
+ | TEXTURECUBE {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat, EsdCube);
+ }
+ | F16TEXTURECUBE {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat16, EsdCube);
+#endif
+ }
+ | TEXTURE1DARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat, Esd1D, true);
+ }
+ | F16TEXTURE1DARRAY {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat16, Esd1D, true);
+#endif
+ }
+ | TEXTURE2DARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat, Esd2D, true);
+ }
+ | F16TEXTURE2DARRAY {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat16, Esd2D, true);
+#endif
+ }
+ | TEXTURECUBEARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat, EsdCube, true);
+ }
+ | F16TEXTURECUBEARRAY {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat16, EsdCube, true);
+#endif
+ }
+ | ITEXTURE1D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtInt, Esd1D);
+ }
+ | ITEXTURE2D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtInt, Esd2D);
+ }
+ | ITEXTURE3D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtInt, Esd3D);
+ }
+ | ITEXTURECUBE {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtInt, EsdCube);
+ }
+ | ITEXTURE1DARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtInt, Esd1D, true);
+ }
+ | ITEXTURE2DARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtInt, Esd2D, true);
+ }
+ | ITEXTURECUBEARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtInt, EsdCube, true);
+ }
+ | UTEXTURE1D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtUint, Esd1D);
+ }
+ | UTEXTURE2D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtUint, Esd2D);
+ }
+ | UTEXTURE3D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtUint, Esd3D);
+ }
+ | UTEXTURECUBE {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtUint, EsdCube);
+ }
+ | UTEXTURE1DARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtUint, Esd1D, true);
+ }
+ | UTEXTURE2DARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtUint, Esd2D, true);
+ }
+ | UTEXTURECUBEARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtUint, EsdCube, true);
+ }
+ | TEXTURE2DRECT {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat, EsdRect);
+ }
+ | F16TEXTURE2DRECT {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat16, EsdRect);
+#endif
+ }
+ | ITEXTURE2DRECT {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtInt, EsdRect);
+ }
+ | UTEXTURE2DRECT {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtUint, EsdRect);
+ }
+ | TEXTUREBUFFER {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat, EsdBuffer);
+ }
+ | F16TEXTUREBUFFER {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat16, EsdBuffer);
+#endif
+ }
+ | ITEXTUREBUFFER {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtInt, EsdBuffer);
+ }
+ | UTEXTUREBUFFER {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtUint, EsdBuffer);
+ }
+ | TEXTURE2DMS {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat, Esd2D, false, false, true);
+ }
+ | F16TEXTURE2DMS {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat16, Esd2D, false, false, true);
+#endif
+ }
+ | ITEXTURE2DMS {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtInt, Esd2D, false, false, true);
+ }
+ | UTEXTURE2DMS {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtUint, Esd2D, false, false, true);
+ }
+ | TEXTURE2DMSARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat, Esd2D, true, false, true);
+ }
+ | F16TEXTURE2DMSARRAY {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtFloat16, Esd2D, true, false, true);
+#endif
+ }
+ | ITEXTURE2DMSARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtInt, Esd2D, true, false, true);
+ }
+ | UTEXTURE2DMSARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setTexture(EbtUint, Esd2D, true, false, true);
+ }
+ | IMAGE1D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat, Esd1D);
+ }
+ | F16IMAGE1D {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat16, Esd1D);
+#endif
+ }
+ | IIMAGE1D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtInt, Esd1D);
+ }
+ | UIMAGE1D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtUint, Esd1D);
+ }
+ | IMAGE2D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat, Esd2D);
+ }
+ | F16IMAGE2D {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat16, Esd2D);
+#endif
+ }
+ | IIMAGE2D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtInt, Esd2D);
+ }
+ | UIMAGE2D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtUint, Esd2D);
+ }
+ | IMAGE3D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat, Esd3D);
+ }
+ | F16IMAGE3D {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat16, Esd3D);
+#endif
+ }
+ | IIMAGE3D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtInt, Esd3D);
+ }
+ | UIMAGE3D {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtUint, Esd3D);
+ }
+ | IMAGE2DRECT {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat, EsdRect);
+ }
+ | F16IMAGE2DRECT {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat16, EsdRect);
+#endif
+ }
+ | IIMAGE2DRECT {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtInt, EsdRect);
+ }
+ | UIMAGE2DRECT {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtUint, EsdRect);
+ }
+ | IMAGECUBE {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat, EsdCube);
+ }
+ | F16IMAGECUBE {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat16, EsdCube);
+#endif
+ }
+ | IIMAGECUBE {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtInt, EsdCube);
+ }
+ | UIMAGECUBE {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtUint, EsdCube);
+ }
+ | IMAGEBUFFER {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat, EsdBuffer);
+ }
+ | F16IMAGEBUFFER {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat16, EsdBuffer);
+#endif
+ }
+ | IIMAGEBUFFER {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtInt, EsdBuffer);
+ }
+ | UIMAGEBUFFER {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtUint, EsdBuffer);
+ }
+ | IMAGE1DARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat, Esd1D, true);
+ }
+ | F16IMAGE1DARRAY {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat16, Esd1D, true);
+#endif
+ }
+ | IIMAGE1DARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtInt, Esd1D, true);
+ }
+ | UIMAGE1DARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtUint, Esd1D, true);
+ }
+ | IMAGE2DARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat, Esd2D, true);
+ }
+ | F16IMAGE2DARRAY {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat16, Esd2D, true);
+#endif
+ }
+ | IIMAGE2DARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtInt, Esd2D, true);
+ }
+ | UIMAGE2DARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtUint, Esd2D, true);
+ }
+ | IMAGECUBEARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat, EsdCube, true);
+ }
+ | F16IMAGECUBEARRAY {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat16, EsdCube, true);
+#endif
+ }
+ | IIMAGECUBEARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtInt, EsdCube, true);
+ }
+ | UIMAGECUBEARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtUint, EsdCube, true);
+ }
+ | IMAGE2DMS {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat, Esd2D, false, false, true);
+ }
+ | F16IMAGE2DMS {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat16, Esd2D, false, false, true);
+#endif
+ }
+ | IIMAGE2DMS {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtInt, Esd2D, false, false, true);
+ }
+ | UIMAGE2DMS {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtUint, Esd2D, false, false, true);
+ }
+ | IMAGE2DMSARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat, Esd2D, true, false, true);
+ }
+ | F16IMAGE2DMSARRAY {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtFloat16, Esd2D, true, false, true);
+#endif
+ }
+ | IIMAGE2DMSARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtInt, Esd2D, true, false, true);
+ }
+ | UIMAGE2DMSARRAY {
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setImage(EbtUint, Esd2D, true, false, true);
+ }
+ | SAMPLEREXTERNALOES { // GL_OES_EGL_image_external
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat, Esd2D);
+ $$.sampler.external = true;
+ }
+ | SAMPLEREXTERNAL2DY2YEXT { // GL_EXT_YUV_target
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.set(EbtFloat, Esd2D);
+ $$.sampler.yuv = true;
+ }
+ | SUBPASSINPUT {
+ parseContext.requireStage($1.loc, EShLangFragment, "subpass input");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setSubpass(EbtFloat);
+ }
+ | SUBPASSINPUTMS {
+ parseContext.requireStage($1.loc, EShLangFragment, "subpass input");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setSubpass(EbtFloat, true);
+ }
+ | F16SUBPASSINPUT {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float subpass input", parseContext.symbolTable.atBuiltInLevel());
+ parseContext.requireStage($1.loc, EShLangFragment, "subpass input");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setSubpass(EbtFloat16);
+#endif
+ }
+ | F16SUBPASSINPUTMS {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck($1.loc, "half float subpass input", parseContext.symbolTable.atBuiltInLevel());
+ parseContext.requireStage($1.loc, EShLangFragment, "subpass input");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setSubpass(EbtFloat16, true);
+#endif
+ }
+ | ISUBPASSINPUT {
+ parseContext.requireStage($1.loc, EShLangFragment, "subpass input");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setSubpass(EbtInt);
+ }
+ | ISUBPASSINPUTMS {
+ parseContext.requireStage($1.loc, EShLangFragment, "subpass input");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setSubpass(EbtInt, true);
+ }
+ | USUBPASSINPUT {
+ parseContext.requireStage($1.loc, EShLangFragment, "subpass input");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setSubpass(EbtUint);
+ }
+ | USUBPASSINPUTMS {
+ parseContext.requireStage($1.loc, EShLangFragment, "subpass input");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtSampler;
+ $$.sampler.setSubpass(EbtUint, true);
+ }
+ | FCOOPMATNV {
+ parseContext.fcoopmatCheck($1.loc, "fcoopmatNV", parseContext.symbolTable.atBuiltInLevel());
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtFloat;
+ $$.coopmat = true;
+ }
+ | struct_specifier {
+ $$ = $1;
+ $$.qualifier.storage = parseContext.symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
+ parseContext.structTypeCheck($$.loc, $$);
+ }
+ | TYPE_NAME {
+ //
+ // This is for user defined type names. The lexical phase looked up the
+ // type.
+ //
+ if (const TVariable* variable = ($1.symbol)->getAsVariable()) {
+ const TType& structure = variable->getType();
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ $$.basicType = EbtStruct;
+ $$.userDef = &structure;
+ } else
+ parseContext.error($1.loc, "expected type name", $1.string->c_str(), "");
+ }
+ ;
+
+precision_qualifier
+ : HIGH_PRECISION {
+ parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "highp precision qualifier");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ parseContext.handlePrecisionQualifier($1.loc, $$.qualifier, EpqHigh);
+ }
+ | MEDIUM_PRECISION {
+ parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "mediump precision qualifier");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ parseContext.handlePrecisionQualifier($1.loc, $$.qualifier, EpqMedium);
+ }
+ | LOW_PRECISION {
+ parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "lowp precision qualifier");
+ $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
+ parseContext.handlePrecisionQualifier($1.loc, $$.qualifier, EpqLow);
+ }
+ ;
+
+struct_specifier
+ : STRUCT IDENTIFIER LEFT_BRACE { parseContext.nestedStructCheck($1.loc); } struct_declaration_list RIGHT_BRACE {
+ TType* structure = new TType($5, *$2.string);
+ parseContext.structArrayCheck($2.loc, *structure);
+ TVariable* userTypeDef = new TVariable($2.string, *structure, true);
+ if (! parseContext.symbolTable.insert(*userTypeDef))
+ parseContext.error($2.loc, "redefinition", $2.string->c_str(), "struct");
+ $$.init($1.loc);
+ $$.basicType = EbtStruct;
+ $$.userDef = structure;
+ --parseContext.structNestingLevel;
+ }
+ | STRUCT LEFT_BRACE { parseContext.nestedStructCheck($1.loc); } struct_declaration_list RIGHT_BRACE {
+ TType* structure = new TType($4, TString(""));
+ $$.init($1.loc);
+ $$.basicType = EbtStruct;
+ $$.userDef = structure;
+ --parseContext.structNestingLevel;
+ }
+ ;
+
+struct_declaration_list
+ : struct_declaration {
+ $$ = $1;
+ }
+ | struct_declaration_list struct_declaration {
+ $$ = $1;
+ for (unsigned int i = 0; i < $2->size(); ++i) {
+ for (unsigned int j = 0; j < $$->size(); ++j) {
+ if ((*$$)[j].type->getFieldName() == (*$2)[i].type->getFieldName())
+ parseContext.error((*$2)[i].loc, "duplicate member name:", "", (*$2)[i].type->getFieldName().c_str());
+ }
+ $$->push_back((*$2)[i]);
+ }
+ }
+ ;
+
+struct_declaration
+ : type_specifier struct_declarator_list SEMICOLON {
+ if ($1.arraySizes) {
+ parseContext.profileRequires($1.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type");
+ parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "arrayed type");
+ if (parseContext.profile == EEsProfile)
+ parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes);
+ }
+
+ $$ = $2;
+
+ parseContext.voidErrorCheck($1.loc, (*$2)[0].type->getFieldName(), $1.basicType);
+ parseContext.precisionQualifierCheck($1.loc, $1.basicType, $1.qualifier);
+
+ for (unsigned int i = 0; i < $$->size(); ++i) {
+ TType type($1);
+ type.setFieldName((*$$)[i].type->getFieldName());
+ type.transferArraySizes((*$$)[i].type->getArraySizes());
+ type.copyArrayInnerSizes($1.arraySizes);
+ parseContext.arrayOfArrayVersionCheck((*$$)[i].loc, type.getArraySizes());
+ (*$$)[i].type->shallowCopy(type);
+ }
+ }
+ | type_qualifier type_specifier struct_declarator_list SEMICOLON {
+ if ($2.arraySizes) {
+ parseContext.profileRequires($2.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type");
+ parseContext.profileRequires($2.loc, EEsProfile, 300, 0, "arrayed type");
+ if (parseContext.profile == EEsProfile)
+ parseContext.arraySizeRequiredCheck($2.loc, *$2.arraySizes);
+ }
+
+ $$ = $3;
+
+ parseContext.memberQualifierCheck($1);
+ parseContext.voidErrorCheck($2.loc, (*$3)[0].type->getFieldName(), $2.basicType);
+ parseContext.mergeQualifiers($2.loc, $2.qualifier, $1.qualifier, true);
+ parseContext.precisionQualifierCheck($2.loc, $2.basicType, $2.qualifier);
+
+ for (unsigned int i = 0; i < $$->size(); ++i) {
+ TType type($2);
+ type.setFieldName((*$$)[i].type->getFieldName());
+ type.transferArraySizes((*$$)[i].type->getArraySizes());
+ type.copyArrayInnerSizes($2.arraySizes);
+ parseContext.arrayOfArrayVersionCheck((*$$)[i].loc, type.getArraySizes());
+ (*$$)[i].type->shallowCopy(type);
+ }
+ }
+ ;
+
+struct_declarator_list
+ : struct_declarator {
+ $$ = new TTypeList;
+ $$->push_back($1);
+ }
+ | struct_declarator_list COMMA struct_declarator {
+ $$->push_back($3);
+ }
+ ;
+
+struct_declarator
+ : IDENTIFIER {
+ $$.type = new TType(EbtVoid);
+ $$.loc = $1.loc;
+ $$.type->setFieldName(*$1.string);
+ }
+ | IDENTIFIER array_specifier {
+ parseContext.arrayOfArrayVersionCheck($1.loc, $2.arraySizes);
+
+ $$.type = new TType(EbtVoid);
+ $$.loc = $1.loc;
+ $$.type->setFieldName(*$1.string);
+ $$.type->transferArraySizes($2.arraySizes);
+ }
+ ;
+
+initializer
+ : assignment_expression {
+ $$ = $1;
+ }
+ | LEFT_BRACE initializer_list RIGHT_BRACE {
+ const char* initFeature = "{ } style initializers";
+ parseContext.requireProfile($1.loc, ~EEsProfile, initFeature);
+ parseContext.profileRequires($1.loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature);
+ $$ = $2;
+ }
+ | LEFT_BRACE initializer_list COMMA RIGHT_BRACE {
+ const char* initFeature = "{ } style initializers";
+ parseContext.requireProfile($1.loc, ~EEsProfile, initFeature);
+ parseContext.profileRequires($1.loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature);
+ $$ = $2;
+ }
+ ;
+
+initializer_list
+ : initializer {
+ $$ = parseContext.intermediate.growAggregate(0, $1, $1->getLoc());
+ }
+ | initializer_list COMMA initializer {
+ $$ = parseContext.intermediate.growAggregate($1, $3);
+ }
+ ;
+
+declaration_statement
+ : declaration { $$ = $1; }
+ ;
+
+statement
+ : compound_statement { $$ = $1; }
+ | simple_statement { $$ = $1; }
+ ;
+
+// Grammar Note: labeled statements for switch statements only; 'goto' is not supported.
+
+simple_statement
+ : declaration_statement { $$ = $1; }
+ | expression_statement { $$ = $1; }
+ | selection_statement { $$ = $1; }
+ | switch_statement { $$ = $1; }
+ | case_label { $$ = $1; }
+ | iteration_statement { $$ = $1; }
+ | jump_statement { $$ = $1; }
+ ;
+
+compound_statement
+ : LEFT_BRACE RIGHT_BRACE { $$ = 0; }
+ | LEFT_BRACE {
+ parseContext.symbolTable.push();
+ ++parseContext.statementNestingLevel;
+ }
+ statement_list {
+ parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]);
+ --parseContext.statementNestingLevel;
+ }
+ RIGHT_BRACE {
+ if ($3 && $3->getAsAggregate())
+ $3->getAsAggregate()->setOperator(EOpSequence);
+ $$ = $3;
+ }
+ ;
+
+statement_no_new_scope
+ : compound_statement_no_new_scope { $$ = $1; }
+ | simple_statement { $$ = $1; }
+ ;
+
+statement_scoped
+ : {
+ ++parseContext.controlFlowNestingLevel;
+ }
+ compound_statement {
+ --parseContext.controlFlowNestingLevel;
+ $$ = $2;
+ }
+ | {
+ parseContext.symbolTable.push();
+ ++parseContext.statementNestingLevel;
+ ++parseContext.controlFlowNestingLevel;
+ }
+ simple_statement {
+ parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]);
+ --parseContext.statementNestingLevel;
+ --parseContext.controlFlowNestingLevel;
+ $$ = $2;
+ }
+
+compound_statement_no_new_scope
+ // Statement that doesn't create a new scope, for selection_statement, iteration_statement
+ : LEFT_BRACE RIGHT_BRACE {
+ $$ = 0;
+ }
+ | LEFT_BRACE statement_list RIGHT_BRACE {
+ if ($2 && $2->getAsAggregate())
+ $2->getAsAggregate()->setOperator(EOpSequence);
+ $$ = $2;
+ }
+ ;
+
+statement_list
+ : statement {
+ $$ = parseContext.intermediate.makeAggregate($1);
+ if ($1 && $1->getAsBranchNode() && ($1->getAsBranchNode()->getFlowOp() == EOpCase ||
+ $1->getAsBranchNode()->getFlowOp() == EOpDefault)) {
+ parseContext.wrapupSwitchSubsequence(0, $1);
+ $$ = 0; // start a fresh subsequence for what's after this case
+ }
+ }
+ | statement_list statement {
+ if ($2 && $2->getAsBranchNode() && ($2->getAsBranchNode()->getFlowOp() == EOpCase ||
+ $2->getAsBranchNode()->getFlowOp() == EOpDefault)) {
+ parseContext.wrapupSwitchSubsequence($1 ? $1->getAsAggregate() : 0, $2);
+ $$ = 0; // start a fresh subsequence for what's after this case
+ } else
+ $$ = parseContext.intermediate.growAggregate($1, $2);
+ }
+ ;
+
+expression_statement
+ : SEMICOLON { $$ = 0; }
+ | expression SEMICOLON { $$ = static_cast<TIntermNode*>($1); }
+ ;
+
+selection_statement
+ : selection_statement_nonattributed {
+ $$ = $1;
+ }
+ | attribute selection_statement_nonattributed {
+ parseContext.handleSelectionAttributes(*$1, $2);
+ $$ = $2;
+ }
+
+selection_statement_nonattributed
+ : IF LEFT_PAREN expression RIGHT_PAREN selection_rest_statement {
+ parseContext.boolCheck($1.loc, $3);
+ $$ = parseContext.intermediate.addSelection($3, $5, $1.loc);
+ }
+ ;
+
+selection_rest_statement
+ : statement_scoped ELSE statement_scoped {
+ $$.node1 = $1;
+ $$.node2 = $3;
+ }
+ | statement_scoped {
+ $$.node1 = $1;
+ $$.node2 = 0;
+ }
+ ;
+
+condition
+ // In 1996 c++ draft, conditions can include single declarations
+ : expression {
+ $$ = $1;
+ parseContext.boolCheck($1->getLoc(), $1);
+ }
+ | fully_specified_type IDENTIFIER EQUAL initializer {
+ parseContext.boolCheck($2.loc, $1);
+
+ TType type($1);
+ TIntermNode* initNode = parseContext.declareVariable($2.loc, *$2.string, $1, 0, $4);
+ if (initNode)
+ $$ = initNode->getAsTyped();
+ else
+ $$ = 0;
+ }
+ ;
+
+switch_statement
+ : switch_statement_nonattributed {
+ $$ = $1;
+ }
+ | attribute switch_statement_nonattributed {
+ parseContext.handleSwitchAttributes(*$1, $2);
+ $$ = $2;
+ }
+
+switch_statement_nonattributed
+ : SWITCH LEFT_PAREN expression RIGHT_PAREN {
+ // start new switch sequence on the switch stack
+ ++parseContext.controlFlowNestingLevel;
+ ++parseContext.statementNestingLevel;
+ parseContext.switchSequenceStack.push_back(new TIntermSequence);
+ parseContext.switchLevel.push_back(parseContext.statementNestingLevel);
+ parseContext.symbolTable.push();
+ }
+ LEFT_BRACE switch_statement_list RIGHT_BRACE {
+ $$ = parseContext.addSwitch($1.loc, $3, $7 ? $7->getAsAggregate() : 0);
+ delete parseContext.switchSequenceStack.back();
+ parseContext.switchSequenceStack.pop_back();
+ parseContext.switchLevel.pop_back();
+ parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]);
+ --parseContext.statementNestingLevel;
+ --parseContext.controlFlowNestingLevel;
+ }
+ ;
+
+switch_statement_list
+ : /* nothing */ {
+ $$ = 0;
+ }
+ | statement_list {
+ $$ = $1;
+ }
+ ;
+
+case_label
+ : CASE expression COLON {
+ $$ = 0;
+ if (parseContext.switchLevel.size() == 0)
+ parseContext.error($1.loc, "cannot appear outside switch statement", "case", "");
+ else if (parseContext.switchLevel.back() != parseContext.statementNestingLevel)
+ parseContext.error($1.loc, "cannot be nested inside control flow", "case", "");
+ else {
+ parseContext.constantValueCheck($2, "case");
+ parseContext.integerCheck($2, "case");
+ $$ = parseContext.intermediate.addBranch(EOpCase, $2, $1.loc);
+ }
+ }
+ | DEFAULT COLON {
+ $$ = 0;
+ if (parseContext.switchLevel.size() == 0)
+ parseContext.error($1.loc, "cannot appear outside switch statement", "default", "");
+ else if (parseContext.switchLevel.back() != parseContext.statementNestingLevel)
+ parseContext.error($1.loc, "cannot be nested inside control flow", "default", "");
+ else
+ $$ = parseContext.intermediate.addBranch(EOpDefault, $1.loc);
+ }
+ ;
+
+iteration_statement
+ : iteration_statement_nonattributed {
+ $$ = $1;
+ }
+ | attribute iteration_statement_nonattributed {
+ parseContext.handleLoopAttributes(*$1, $2);
+ $$ = $2;
+ }
+
+iteration_statement_nonattributed
+ : WHILE LEFT_PAREN {
+ if (! parseContext.limits.whileLoops)
+ parseContext.error($1.loc, "while loops not available", "limitation", "");
+ parseContext.symbolTable.push();
+ ++parseContext.loopNestingLevel;
+ ++parseContext.statementNestingLevel;
+ ++parseContext.controlFlowNestingLevel;
+ }
+ condition RIGHT_PAREN statement_no_new_scope {
+ parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]);
+ $$ = parseContext.intermediate.addLoop($6, $4, 0, true, $1.loc);
+ --parseContext.loopNestingLevel;
+ --parseContext.statementNestingLevel;
+ --parseContext.controlFlowNestingLevel;
+ }
+ | DO {
+ ++parseContext.loopNestingLevel;
+ ++parseContext.statementNestingLevel;
+ ++parseContext.controlFlowNestingLevel;
+ }
+ statement WHILE LEFT_PAREN expression RIGHT_PAREN SEMICOLON {
+ if (! parseContext.limits.whileLoops)
+ parseContext.error($1.loc, "do-while loops not available", "limitation", "");
+
+ parseContext.boolCheck($8.loc, $6);
+
+ $$ = parseContext.intermediate.addLoop($3, $6, 0, false, $4.loc);
+ --parseContext.loopNestingLevel;
+ --parseContext.statementNestingLevel;
+ --parseContext.controlFlowNestingLevel;
+ }
+ | FOR LEFT_PAREN {
+ parseContext.symbolTable.push();
+ ++parseContext.loopNestingLevel;
+ ++parseContext.statementNestingLevel;
+ ++parseContext.controlFlowNestingLevel;
+ }
+ for_init_statement for_rest_statement RIGHT_PAREN statement_no_new_scope {
+ parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]);
+ $$ = parseContext.intermediate.makeAggregate($4, $2.loc);
+ TIntermLoop* forLoop = parseContext.intermediate.addLoop($7, reinterpret_cast<TIntermTyped*>($5.node1), reinterpret_cast<TIntermTyped*>($5.node2), true, $1.loc);
+ if (! parseContext.limits.nonInductiveForLoops)
+ parseContext.inductiveLoopCheck($1.loc, $4, forLoop);
+ $$ = parseContext.intermediate.growAggregate($$, forLoop, $1.loc);
+ $$->getAsAggregate()->setOperator(EOpSequence);
+ --parseContext.loopNestingLevel;
+ --parseContext.statementNestingLevel;
+ --parseContext.controlFlowNestingLevel;
+ }
+ ;
+
+for_init_statement
+ : expression_statement {
+ $$ = $1;
+ }
+ | declaration_statement {
+ $$ = $1;
+ }
+ ;
+
+conditionopt
+ : condition {
+ $$ = $1;
+ }
+ | /* May be null */ {
+ $$ = 0;
+ }
+ ;
+
+for_rest_statement
+ : conditionopt SEMICOLON {
+ $$.node1 = $1;
+ $$.node2 = 0;
+ }
+ | conditionopt SEMICOLON expression {
+ $$.node1 = $1;
+ $$.node2 = $3;
+ }
+ ;
+
+jump_statement
+ : CONTINUE SEMICOLON {
+ if (parseContext.loopNestingLevel <= 0)
+ parseContext.error($1.loc, "continue statement only allowed in loops", "", "");
+ $$ = parseContext.intermediate.addBranch(EOpContinue, $1.loc);
+ }
+ | BREAK SEMICOLON {
+ if (parseContext.loopNestingLevel + parseContext.switchSequenceStack.size() <= 0)
+ parseContext.error($1.loc, "break statement only allowed in switch and loops", "", "");
+ $$ = parseContext.intermediate.addBranch(EOpBreak, $1.loc);
+ }
+ | RETURN SEMICOLON {
+ $$ = parseContext.intermediate.addBranch(EOpReturn, $1.loc);
+ if (parseContext.currentFunctionType->getBasicType() != EbtVoid)
+ parseContext.error($1.loc, "non-void function must return a value", "return", "");
+ if (parseContext.inMain)
+ parseContext.postEntryPointReturn = true;
+ }
+ | RETURN expression SEMICOLON {
+ $$ = parseContext.handleReturnValue($1.loc, $2);
+ }
+ | DISCARD SEMICOLON {
+ parseContext.requireStage($1.loc, EShLangFragment, "discard");
+ $$ = parseContext.intermediate.addBranch(EOpKill, $1.loc);
+ }
+ ;
+
+// Grammar Note: No 'goto'. Gotos are not supported.
+
+translation_unit
+ : external_declaration {
+ $$ = $1;
+ parseContext.intermediate.setTreeRoot($$);
+ }
+ | translation_unit external_declaration {
+ if ($2 != nullptr) {
+ $$ = parseContext.intermediate.growAggregate($1, $2);
+ parseContext.intermediate.setTreeRoot($$);
+ }
+ }
+ ;
+
+external_declaration
+ : function_definition {
+ $$ = $1;
+ }
+ | declaration {
+ $$ = $1;
+ }
+ | SEMICOLON {
+ parseContext.requireProfile($1.loc, ~EEsProfile, "extraneous semicolon");
+ parseContext.profileRequires($1.loc, ~EEsProfile, 460, nullptr, "extraneous semicolon");
+ $$ = nullptr;
+ }
+ ;
+
+function_definition
+ : function_prototype {
+ $1.function = parseContext.handleFunctionDeclarator($1.loc, *$1.function, false /* not prototype */);
+ $1.intermNode = parseContext.handleFunctionDefinition($1.loc, *$1.function);
+ }
+ compound_statement_no_new_scope {
+ // May be best done as post process phase on intermediate code
+ if (parseContext.currentFunctionType->getBasicType() != EbtVoid && ! parseContext.functionReturnsValue)
+ parseContext.error($1.loc, "function does not return a value:", "", $1.function->getName().c_str());
+ parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]);
+ $$ = parseContext.intermediate.growAggregate($1.intermNode, $3);
+ parseContext.intermediate.setAggregateOperator($$, EOpFunction, $1.function->getType(), $1.loc);
+ $$->getAsAggregate()->setName($1.function->getMangledName().c_str());
+
+ // store the pragma information for debug and optimize and other vendor specific
+ // information. This information can be queried from the parse tree
+ $$->getAsAggregate()->setOptimize(parseContext.contextPragma.optimize);
+ $$->getAsAggregate()->setDebug(parseContext.contextPragma.debug);
+ $$->getAsAggregate()->setPragmaTable(parseContext.contextPragma.pragmaTable);
+ }
+ ;
+
+attribute
+ : LEFT_BRACKET LEFT_BRACKET attribute_list RIGHT_BRACKET RIGHT_BRACKET {
+ $$ = $3;
+ parseContext.requireExtensions($1.loc, 1, &E_GL_EXT_control_flow_attributes, "attribute");
+ }
+
+attribute_list
+ : single_attribute {
+ $$ = $1;
+ }
+ | attribute_list COMMA single_attribute {
+ $$ = parseContext.mergeAttributes($1, $3);
+ }
+
+single_attribute
+ : IDENTIFIER {
+ $$ = parseContext.makeAttributes(*$1.string);
+ }
+ | IDENTIFIER LEFT_PAREN constant_expression RIGHT_PAREN {
+ $$ = parseContext.makeAttributes(*$1.string, $3);
+ }
+
+%%
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/glslang_tab.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/glslang_tab.cpp
new file mode 100644
index 0000000..07feffe
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/glslang_tab.cpp
@@ -0,0 +1,10468 @@
+/* A Bison parser, made by GNU Bison 3.0.4. */
+
+/* Bison implementation for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "3.0.4"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 1
+
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 43 "MachineIndependent/glslang.y" /* yacc.c:339 */
+
+
+/* Based on:
+ANSI C Yacc grammar
+
+In 1985, Jeff Lee published his Yacc grammar (which is accompanied by a
+matching Lex specification) for the April 30, 1985 draft version of the
+ANSI C standard. Tom Stockfisch reposted it to net.sources in 1987; that
+original, as mentioned in the answer to question 17.25 of the comp.lang.c
+FAQ, can be ftp'ed from ftp.uu.net, file usenet/net.sources/ansi.c.grammar.Z.
+
+I intend to keep this version as close to the current C Standard grammar as
+possible; please let me know if you discover discrepancies.
+
+Jutta Degener, 1995
+*/
+
+#include "SymbolTable.h"
+#include "ParseHelper.h"
+#include "../Public/ShaderLang.h"
+#include "attribute.h"
+
+using namespace glslang;
+
+
+#line 92 "MachineIndependent/glslang_tab.cpp" /* yacc.c:339 */
+
+# ifndef YY_NULLPTR
+# if defined __cplusplus && 201103L <= __cplusplus
+# define YY_NULLPTR nullptr
+# else
+# define YY_NULLPTR 0
+# endif
+# endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 1
+#endif
+
+/* In a future release of Bison, this section will be replaced
+ by #include "glslang_tab.cpp.h". */
+#ifndef YY_YY_MACHINEINDEPENDENT_GLSLANG_TAB_CPP_H_INCLUDED
+# define YY_YY_MACHINEINDEPENDENT_GLSLANG_TAB_CPP_H_INCLUDED
+/* Debug traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 1
+#endif
+#if YYDEBUG
+extern int yydebug;
+#endif
+
+/* Token type. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ enum yytokentype
+ {
+ ATTRIBUTE = 258,
+ VARYING = 259,
+ FLOAT16_T = 260,
+ FLOAT = 261,
+ FLOAT32_T = 262,
+ DOUBLE = 263,
+ FLOAT64_T = 264,
+ CONST = 265,
+ BOOL = 266,
+ INT = 267,
+ UINT = 268,
+ INT64_T = 269,
+ UINT64_T = 270,
+ INT32_T = 271,
+ UINT32_T = 272,
+ INT16_T = 273,
+ UINT16_T = 274,
+ INT8_T = 275,
+ UINT8_T = 276,
+ BREAK = 277,
+ CONTINUE = 278,
+ DO = 279,
+ ELSE = 280,
+ FOR = 281,
+ IF = 282,
+ DISCARD = 283,
+ RETURN = 284,
+ SWITCH = 285,
+ CASE = 286,
+ DEFAULT = 287,
+ SUBROUTINE = 288,
+ BVEC2 = 289,
+ BVEC3 = 290,
+ BVEC4 = 291,
+ IVEC2 = 292,
+ IVEC3 = 293,
+ IVEC4 = 294,
+ UVEC2 = 295,
+ UVEC3 = 296,
+ UVEC4 = 297,
+ I64VEC2 = 298,
+ I64VEC3 = 299,
+ I64VEC4 = 300,
+ U64VEC2 = 301,
+ U64VEC3 = 302,
+ U64VEC4 = 303,
+ I32VEC2 = 304,
+ I32VEC3 = 305,
+ I32VEC4 = 306,
+ U32VEC2 = 307,
+ U32VEC3 = 308,
+ U32VEC4 = 309,
+ I16VEC2 = 310,
+ I16VEC3 = 311,
+ I16VEC4 = 312,
+ U16VEC2 = 313,
+ U16VEC3 = 314,
+ U16VEC4 = 315,
+ I8VEC2 = 316,
+ I8VEC3 = 317,
+ I8VEC4 = 318,
+ U8VEC2 = 319,
+ U8VEC3 = 320,
+ U8VEC4 = 321,
+ VEC2 = 322,
+ VEC3 = 323,
+ VEC4 = 324,
+ MAT2 = 325,
+ MAT3 = 326,
+ MAT4 = 327,
+ CENTROID = 328,
+ IN = 329,
+ OUT = 330,
+ INOUT = 331,
+ UNIFORM = 332,
+ PATCH = 333,
+ SAMPLE = 334,
+ BUFFER = 335,
+ SHARED = 336,
+ NONUNIFORM = 337,
+ PAYLOADNV = 338,
+ PAYLOADINNV = 339,
+ HITATTRNV = 340,
+ CALLDATANV = 341,
+ CALLDATAINNV = 342,
+ COHERENT = 343,
+ VOLATILE = 344,
+ RESTRICT = 345,
+ READONLY = 346,
+ WRITEONLY = 347,
+ DEVICECOHERENT = 348,
+ QUEUEFAMILYCOHERENT = 349,
+ WORKGROUPCOHERENT = 350,
+ SUBGROUPCOHERENT = 351,
+ NONPRIVATE = 352,
+ DVEC2 = 353,
+ DVEC3 = 354,
+ DVEC4 = 355,
+ DMAT2 = 356,
+ DMAT3 = 357,
+ DMAT4 = 358,
+ F16VEC2 = 359,
+ F16VEC3 = 360,
+ F16VEC4 = 361,
+ F16MAT2 = 362,
+ F16MAT3 = 363,
+ F16MAT4 = 364,
+ F32VEC2 = 365,
+ F32VEC3 = 366,
+ F32VEC4 = 367,
+ F32MAT2 = 368,
+ F32MAT3 = 369,
+ F32MAT4 = 370,
+ F64VEC2 = 371,
+ F64VEC3 = 372,
+ F64VEC4 = 373,
+ F64MAT2 = 374,
+ F64MAT3 = 375,
+ F64MAT4 = 376,
+ NOPERSPECTIVE = 377,
+ FLAT = 378,
+ SMOOTH = 379,
+ LAYOUT = 380,
+ EXPLICITINTERPAMD = 381,
+ PERVERTEXNV = 382,
+ PERPRIMITIVENV = 383,
+ PERVIEWNV = 384,
+ PERTASKNV = 385,
+ MAT2X2 = 386,
+ MAT2X3 = 387,
+ MAT2X4 = 388,
+ MAT3X2 = 389,
+ MAT3X3 = 390,
+ MAT3X4 = 391,
+ MAT4X2 = 392,
+ MAT4X3 = 393,
+ MAT4X4 = 394,
+ DMAT2X2 = 395,
+ DMAT2X3 = 396,
+ DMAT2X4 = 397,
+ DMAT3X2 = 398,
+ DMAT3X3 = 399,
+ DMAT3X4 = 400,
+ DMAT4X2 = 401,
+ DMAT4X3 = 402,
+ DMAT4X4 = 403,
+ F16MAT2X2 = 404,
+ F16MAT2X3 = 405,
+ F16MAT2X4 = 406,
+ F16MAT3X2 = 407,
+ F16MAT3X3 = 408,
+ F16MAT3X4 = 409,
+ F16MAT4X2 = 410,
+ F16MAT4X3 = 411,
+ F16MAT4X4 = 412,
+ F32MAT2X2 = 413,
+ F32MAT2X3 = 414,
+ F32MAT2X4 = 415,
+ F32MAT3X2 = 416,
+ F32MAT3X3 = 417,
+ F32MAT3X4 = 418,
+ F32MAT4X2 = 419,
+ F32MAT4X3 = 420,
+ F32MAT4X4 = 421,
+ F64MAT2X2 = 422,
+ F64MAT2X3 = 423,
+ F64MAT2X4 = 424,
+ F64MAT3X2 = 425,
+ F64MAT3X3 = 426,
+ F64MAT3X4 = 427,
+ F64MAT4X2 = 428,
+ F64MAT4X3 = 429,
+ F64MAT4X4 = 430,
+ ATOMIC_UINT = 431,
+ ACCSTRUCTNV = 432,
+ FCOOPMATNV = 433,
+ SAMPLER1D = 434,
+ SAMPLER2D = 435,
+ SAMPLER3D = 436,
+ SAMPLERCUBE = 437,
+ SAMPLER1DSHADOW = 438,
+ SAMPLER2DSHADOW = 439,
+ SAMPLERCUBESHADOW = 440,
+ SAMPLER1DARRAY = 441,
+ SAMPLER2DARRAY = 442,
+ SAMPLER1DARRAYSHADOW = 443,
+ SAMPLER2DARRAYSHADOW = 444,
+ ISAMPLER1D = 445,
+ ISAMPLER2D = 446,
+ ISAMPLER3D = 447,
+ ISAMPLERCUBE = 448,
+ ISAMPLER1DARRAY = 449,
+ ISAMPLER2DARRAY = 450,
+ USAMPLER1D = 451,
+ USAMPLER2D = 452,
+ USAMPLER3D = 453,
+ USAMPLERCUBE = 454,
+ USAMPLER1DARRAY = 455,
+ USAMPLER2DARRAY = 456,
+ SAMPLER2DRECT = 457,
+ SAMPLER2DRECTSHADOW = 458,
+ ISAMPLER2DRECT = 459,
+ USAMPLER2DRECT = 460,
+ SAMPLERBUFFER = 461,
+ ISAMPLERBUFFER = 462,
+ USAMPLERBUFFER = 463,
+ SAMPLERCUBEARRAY = 464,
+ SAMPLERCUBEARRAYSHADOW = 465,
+ ISAMPLERCUBEARRAY = 466,
+ USAMPLERCUBEARRAY = 467,
+ SAMPLER2DMS = 468,
+ ISAMPLER2DMS = 469,
+ USAMPLER2DMS = 470,
+ SAMPLER2DMSARRAY = 471,
+ ISAMPLER2DMSARRAY = 472,
+ USAMPLER2DMSARRAY = 473,
+ SAMPLEREXTERNALOES = 474,
+ SAMPLEREXTERNAL2DY2YEXT = 475,
+ F16SAMPLER1D = 476,
+ F16SAMPLER2D = 477,
+ F16SAMPLER3D = 478,
+ F16SAMPLER2DRECT = 479,
+ F16SAMPLERCUBE = 480,
+ F16SAMPLER1DARRAY = 481,
+ F16SAMPLER2DARRAY = 482,
+ F16SAMPLERCUBEARRAY = 483,
+ F16SAMPLERBUFFER = 484,
+ F16SAMPLER2DMS = 485,
+ F16SAMPLER2DMSARRAY = 486,
+ F16SAMPLER1DSHADOW = 487,
+ F16SAMPLER2DSHADOW = 488,
+ F16SAMPLER1DARRAYSHADOW = 489,
+ F16SAMPLER2DARRAYSHADOW = 490,
+ F16SAMPLER2DRECTSHADOW = 491,
+ F16SAMPLERCUBESHADOW = 492,
+ F16SAMPLERCUBEARRAYSHADOW = 493,
+ SAMPLER = 494,
+ SAMPLERSHADOW = 495,
+ TEXTURE1D = 496,
+ TEXTURE2D = 497,
+ TEXTURE3D = 498,
+ TEXTURECUBE = 499,
+ TEXTURE1DARRAY = 500,
+ TEXTURE2DARRAY = 501,
+ ITEXTURE1D = 502,
+ ITEXTURE2D = 503,
+ ITEXTURE3D = 504,
+ ITEXTURECUBE = 505,
+ ITEXTURE1DARRAY = 506,
+ ITEXTURE2DARRAY = 507,
+ UTEXTURE1D = 508,
+ UTEXTURE2D = 509,
+ UTEXTURE3D = 510,
+ UTEXTURECUBE = 511,
+ UTEXTURE1DARRAY = 512,
+ UTEXTURE2DARRAY = 513,
+ TEXTURE2DRECT = 514,
+ ITEXTURE2DRECT = 515,
+ UTEXTURE2DRECT = 516,
+ TEXTUREBUFFER = 517,
+ ITEXTUREBUFFER = 518,
+ UTEXTUREBUFFER = 519,
+ TEXTURECUBEARRAY = 520,
+ ITEXTURECUBEARRAY = 521,
+ UTEXTURECUBEARRAY = 522,
+ TEXTURE2DMS = 523,
+ ITEXTURE2DMS = 524,
+ UTEXTURE2DMS = 525,
+ TEXTURE2DMSARRAY = 526,
+ ITEXTURE2DMSARRAY = 527,
+ UTEXTURE2DMSARRAY = 528,
+ F16TEXTURE1D = 529,
+ F16TEXTURE2D = 530,
+ F16TEXTURE3D = 531,
+ F16TEXTURE2DRECT = 532,
+ F16TEXTURECUBE = 533,
+ F16TEXTURE1DARRAY = 534,
+ F16TEXTURE2DARRAY = 535,
+ F16TEXTURECUBEARRAY = 536,
+ F16TEXTUREBUFFER = 537,
+ F16TEXTURE2DMS = 538,
+ F16TEXTURE2DMSARRAY = 539,
+ SUBPASSINPUT = 540,
+ SUBPASSINPUTMS = 541,
+ ISUBPASSINPUT = 542,
+ ISUBPASSINPUTMS = 543,
+ USUBPASSINPUT = 544,
+ USUBPASSINPUTMS = 545,
+ F16SUBPASSINPUT = 546,
+ F16SUBPASSINPUTMS = 547,
+ IMAGE1D = 548,
+ IIMAGE1D = 549,
+ UIMAGE1D = 550,
+ IMAGE2D = 551,
+ IIMAGE2D = 552,
+ UIMAGE2D = 553,
+ IMAGE3D = 554,
+ IIMAGE3D = 555,
+ UIMAGE3D = 556,
+ IMAGE2DRECT = 557,
+ IIMAGE2DRECT = 558,
+ UIMAGE2DRECT = 559,
+ IMAGECUBE = 560,
+ IIMAGECUBE = 561,
+ UIMAGECUBE = 562,
+ IMAGEBUFFER = 563,
+ IIMAGEBUFFER = 564,
+ UIMAGEBUFFER = 565,
+ IMAGE1DARRAY = 566,
+ IIMAGE1DARRAY = 567,
+ UIMAGE1DARRAY = 568,
+ IMAGE2DARRAY = 569,
+ IIMAGE2DARRAY = 570,
+ UIMAGE2DARRAY = 571,
+ IMAGECUBEARRAY = 572,
+ IIMAGECUBEARRAY = 573,
+ UIMAGECUBEARRAY = 574,
+ IMAGE2DMS = 575,
+ IIMAGE2DMS = 576,
+ UIMAGE2DMS = 577,
+ IMAGE2DMSARRAY = 578,
+ IIMAGE2DMSARRAY = 579,
+ UIMAGE2DMSARRAY = 580,
+ F16IMAGE1D = 581,
+ F16IMAGE2D = 582,
+ F16IMAGE3D = 583,
+ F16IMAGE2DRECT = 584,
+ F16IMAGECUBE = 585,
+ F16IMAGE1DARRAY = 586,
+ F16IMAGE2DARRAY = 587,
+ F16IMAGECUBEARRAY = 588,
+ F16IMAGEBUFFER = 589,
+ F16IMAGE2DMS = 590,
+ F16IMAGE2DMSARRAY = 591,
+ STRUCT = 592,
+ VOID = 593,
+ WHILE = 594,
+ IDENTIFIER = 595,
+ TYPE_NAME = 596,
+ FLOATCONSTANT = 597,
+ DOUBLECONSTANT = 598,
+ INT16CONSTANT = 599,
+ UINT16CONSTANT = 600,
+ INT32CONSTANT = 601,
+ UINT32CONSTANT = 602,
+ INTCONSTANT = 603,
+ UINTCONSTANT = 604,
+ INT64CONSTANT = 605,
+ UINT64CONSTANT = 606,
+ BOOLCONSTANT = 607,
+ FLOAT16CONSTANT = 608,
+ LEFT_OP = 609,
+ RIGHT_OP = 610,
+ INC_OP = 611,
+ DEC_OP = 612,
+ LE_OP = 613,
+ GE_OP = 614,
+ EQ_OP = 615,
+ NE_OP = 616,
+ AND_OP = 617,
+ OR_OP = 618,
+ XOR_OP = 619,
+ MUL_ASSIGN = 620,
+ DIV_ASSIGN = 621,
+ ADD_ASSIGN = 622,
+ MOD_ASSIGN = 623,
+ LEFT_ASSIGN = 624,
+ RIGHT_ASSIGN = 625,
+ AND_ASSIGN = 626,
+ XOR_ASSIGN = 627,
+ OR_ASSIGN = 628,
+ SUB_ASSIGN = 629,
+ LEFT_PAREN = 630,
+ RIGHT_PAREN = 631,
+ LEFT_BRACKET = 632,
+ RIGHT_BRACKET = 633,
+ LEFT_BRACE = 634,
+ RIGHT_BRACE = 635,
+ DOT = 636,
+ COMMA = 637,
+ COLON = 638,
+ EQUAL = 639,
+ SEMICOLON = 640,
+ BANG = 641,
+ DASH = 642,
+ TILDE = 643,
+ PLUS = 644,
+ STAR = 645,
+ SLASH = 646,
+ PERCENT = 647,
+ LEFT_ANGLE = 648,
+ RIGHT_ANGLE = 649,
+ VERTICAL_BAR = 650,
+ CARET = 651,
+ AMPERSAND = 652,
+ QUESTION = 653,
+ INVARIANT = 654,
+ PRECISE = 655,
+ HIGH_PRECISION = 656,
+ MEDIUM_PRECISION = 657,
+ LOW_PRECISION = 658,
+ PRECISION = 659,
+ PACKED = 660,
+ RESOURCE = 661,
+ SUPERP = 662
+ };
+#endif
+
+/* Value type. */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+
+union YYSTYPE
+{
+#line 71 "MachineIndependent/glslang.y" /* yacc.c:355 */
+
+ struct {
+ glslang::TSourceLoc loc;
+ union {
+ glslang::TString *string;
+ int i;
+ unsigned int u;
+ long long i64;
+ unsigned long long u64;
+ bool b;
+ double d;
+ };
+ glslang::TSymbol* symbol;
+ } lex;
+ struct {
+ glslang::TSourceLoc loc;
+ glslang::TOperator op;
+ union {
+ TIntermNode* intermNode;
+ glslang::TIntermNodePair nodePair;
+ glslang::TIntermTyped* intermTypedNode;
+ glslang::TAttributes* attributes;
+ };
+ union {
+ glslang::TPublicType type;
+ glslang::TFunction* function;
+ glslang::TParameter param;
+ glslang::TTypeLoc typeLine;
+ glslang::TTypeList* typeList;
+ glslang::TArraySizes* arraySizes;
+ glslang::TIdentifierList* identifierList;
+ };
+ glslang::TArraySizes* typeParameters;
+ } interm;
+
+#line 576 "MachineIndependent/glslang_tab.cpp" /* yacc.c:355 */
+};
+
+typedef union YYSTYPE YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+
+int yyparse (glslang::TParseContext* pParseContext);
+
+#endif /* !YY_YY_MACHINEINDEPENDENT_GLSLANG_TAB_CPP_H_INCLUDED */
+
+/* Copy the second part of user declarations. */
+#line 107 "MachineIndependent/glslang.y" /* yacc.c:358 */
+
+
+/* windows only pragma */
+#ifdef _MSC_VER
+ #pragma warning(disable : 4065)
+ #pragma warning(disable : 4127)
+ #pragma warning(disable : 4244)
+#endif
+
+#define parseContext (*pParseContext)
+#define yyerror(context, msg) context->parserError(msg)
+
+extern int yylex(YYSTYPE*, TParseContext&);
+
+
+#line 607 "MachineIndependent/glslang_tab.cpp" /* yacc.c:358 */
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#else
+typedef signed char yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(Msgid) dgettext ("bison-runtime", Msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(Msgid) Msgid
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE
+# if (defined __GNUC__ \
+ && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \
+ || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C
+# define YY_ATTRIBUTE(Spec) __attribute__(Spec)
+# else
+# define YY_ATTRIBUTE(Spec) /* empty */
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE_PURE
+# define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__))
+#endif
+
+#ifndef YY_ATTRIBUTE_UNUSED
+# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__))
+#endif
+
+#if !defined _Noreturn \
+ && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112)
+# if defined _MSC_VER && 1200 <= _MSC_VER
+# define _Noreturn __declspec (noreturn)
+# else
+# define _Noreturn YY_ATTRIBUTE ((__noreturn__))
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(E) ((void) (E))
+#else
+# define YYUSE(E) /* empty */
+#endif
+
+#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
+/* Suppress an incorrect diagnostic about yylval being uninitialized. */
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\
+ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END \
+ _Pragma ("GCC diagnostic pop")
+#else
+# define YY_INITIAL_VALUE(Value) Value
+#endif
+#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END
+#endif
+#ifndef YY_INITIAL_VALUE
+# define YY_INITIAL_VALUE(Value) /* Nothing. */
+#endif
+
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+ /* Use EXIT_SUCCESS as a witness for stdlib.h. */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's 'empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined EXIT_SUCCESS \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined EXIT_SUCCESS
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined EXIT_SUCCESS
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss_alloc;
+ YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
+ Stack = &yyptr->Stack_alloc; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (0)
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from SRC to DST. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(Dst, Src, Count) \
+ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))
+# else
+# define YYCOPY(Dst, Src, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (Dst)[yyi] = (Src)[yyi]; \
+ } \
+ while (0)
+# endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 384
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 9348
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 408
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 110
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 578
+/* YYNSTATES -- Number of states. */
+#define YYNSTATES 722
+
+/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned
+ by yylex, with out-of-bounds checking. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 662
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM
+ as returned by yylex, without out-of-bounds checking. */
+static const yytype_uint16 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
+ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
+ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
+ 115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137, 138, 139, 140, 141, 142, 143, 144,
+ 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
+ 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167, 168, 169, 170, 171, 172, 173, 174,
+ 175, 176, 177, 178, 179, 180, 181, 182, 183, 184,
+ 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
+ 195, 196, 197, 198, 199, 200, 201, 202, 203, 204,
+ 205, 206, 207, 208, 209, 210, 211, 212, 213, 214,
+ 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
+ 225, 226, 227, 228, 229, 230, 231, 232, 233, 234,
+ 235, 236, 237, 238, 239, 240, 241, 242, 243, 244,
+ 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
+ 255, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 302, 303, 304,
+ 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, 318, 319, 320, 321, 322, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, 334,
+ 335, 336, 337, 338, 339, 340, 341, 342, 343, 344,
+ 345, 346, 347, 348, 349, 350, 351, 352, 353, 354,
+ 355, 356, 357, 358, 359, 360, 361, 362, 363, 364,
+ 365, 366, 367, 368, 369, 370, 371, 372, 373, 374,
+ 375, 376, 377, 378, 379, 380, 381, 382, 383, 384,
+ 385, 386, 387, 388, 389, 390, 391, 392, 393, 394,
+ 395, 396, 397, 398, 399, 400, 401, 402, 403, 404,
+ 405, 406, 407
+};
+
+#if YYDEBUG
+ /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 302, 302, 308, 311, 315, 319, 322, 326, 330,
+ 334, 338, 342, 345, 349, 353, 356, 364, 367, 370,
+ 373, 376, 381, 389, 396, 403, 409, 413, 420, 423,
+ 429, 436, 446, 454, 459, 486, 494, 500, 504, 508,
+ 528, 529, 530, 531, 537, 538, 543, 548, 557, 558,
+ 563, 571, 572, 578, 587, 588, 593, 598, 603, 611,
+ 612, 621, 633, 634, 643, 644, 653, 654, 663, 664,
+ 672, 673, 681, 682, 690, 691, 691, 709, 710, 726,
+ 730, 734, 738, 743, 747, 751, 755, 759, 763, 767,
+ 774, 777, 788, 795, 800, 805, 813, 817, 821, 825,
+ 830, 835, 844, 844, 855, 859, 866, 873, 876, 883,
+ 891, 911, 934, 949, 974, 985, 995, 1005, 1015, 1024,
+ 1027, 1031, 1035, 1040, 1048, 1053, 1058, 1063, 1068, 1077,
+ 1088, 1115, 1124, 1131, 1138, 1149, 1158, 1168, 1180, 1189,
+ 1201, 1207, 1210, 1217, 1221, 1225, 1233, 1242, 1245, 1256,
+ 1259, 1262, 1266, 1270, 1274, 1278, 1284, 1288, 1300, 1314,
+ 1319, 1325, 1331, 1338, 1344, 1349, 1354, 1359, 1369, 1379,
+ 1389, 1399, 1408, 1420, 1424, 1429, 1434, 1439, 1444, 1449,
+ 1453, 1457, 1461, 1465, 1471, 1480, 1487, 1490, 1498, 1503,
+ 1513, 1518, 1526, 1530, 1540, 1543, 1549, 1555, 1562, 1572,
+ 1576, 1580, 1585, 1590, 1595, 1600, 1604, 1609, 1614, 1619,
+ 1624, 1629, 1634, 1639, 1644, 1649, 1653, 1658, 1663, 1668,
+ 1674, 1680, 1686, 1692, 1698, 1704, 1710, 1716, 1722, 1728,
+ 1734, 1740, 1745, 1750, 1755, 1760, 1765, 1770, 1776, 1782,
+ 1788, 1794, 1800, 1806, 1812, 1818, 1824, 1830, 1836, 1842,
+ 1848, 1854, 1860, 1866, 1872, 1878, 1884, 1890, 1896, 1902,
+ 1908, 1914, 1920, 1926, 1932, 1937, 1942, 1947, 1952, 1957,
+ 1962, 1967, 1972, 1977, 1982, 1987, 1992, 1998, 2004, 2010,
+ 2016, 2022, 2028, 2034, 2040, 2046, 2052, 2058, 2064, 2070,
+ 2076, 2082, 2088, 2094, 2100, 2106, 2112, 2118, 2124, 2130,
+ 2136, 2142, 2148, 2154, 2160, 2166, 2172, 2178, 2184, 2190,
+ 2196, 2202, 2208, 2214, 2220, 2226, 2232, 2238, 2244, 2250,
+ 2256, 2262, 2268, 2274, 2280, 2286, 2291, 2296, 2301, 2306,
+ 2311, 2316, 2321, 2326, 2331, 2336, 2341, 2346, 2351, 2356,
+ 2364, 2372, 2380, 2388, 2396, 2404, 2412, 2420, 2428, 2436,
+ 2444, 2452, 2460, 2465, 2470, 2475, 2480, 2485, 2490, 2495,
+ 2500, 2505, 2510, 2515, 2520, 2525, 2530, 2535, 2540, 2548,
+ 2556, 2561, 2566, 2571, 2579, 2584, 2589, 2594, 2602, 2607,
+ 2612, 2617, 2625, 2630, 2635, 2640, 2645, 2650, 2658, 2663,
+ 2671, 2676, 2684, 2689, 2697, 2702, 2710, 2715, 2723, 2728,
+ 2736, 2741, 2746, 2751, 2756, 2761, 2766, 2771, 2776, 2781,
+ 2786, 2791, 2796, 2801, 2806, 2811, 2819, 2824, 2829, 2834,
+ 2842, 2847, 2852, 2857, 2865, 2870, 2875, 2880, 2888, 2893,
+ 2898, 2903, 2911, 2916, 2921, 2926, 2934, 2939, 2944, 2949,
+ 2957, 2962, 2967, 2972, 2980, 2985, 2990, 2995, 3003, 3008,
+ 3013, 3018, 3026, 3031, 3036, 3041, 3049, 3054, 3059, 3064,
+ 3072, 3077, 3082, 3087, 3095, 3100, 3105, 3110, 3118, 3123,
+ 3128, 3133, 3141, 3146, 3151, 3157, 3163, 3169, 3175, 3184,
+ 3193, 3199, 3205, 3211, 3217, 3223, 3228, 3244, 3249, 3254,
+ 3262, 3262, 3273, 3273, 3283, 3286, 3299, 3321, 3348, 3352,
+ 3358, 3363, 3374, 3377, 3383, 3392, 3395, 3401, 3405, 3406,
+ 3412, 3413, 3414, 3415, 3416, 3417, 3418, 3422, 3423, 3427,
+ 3423, 3439, 3440, 3444, 3444, 3451, 3451, 3465, 3468, 3476,
+ 3484, 3495, 3496, 3500, 3503, 3509, 3516, 3520, 3528, 3532,
+ 3545, 3548, 3554, 3554, 3574, 3577, 3583, 3595, 3607, 3610,
+ 3616, 3616, 3631, 3631, 3647, 3647, 3668, 3671, 3677, 3680,
+ 3686, 3690, 3697, 3702, 3707, 3714, 3717, 3726, 3730, 3739,
+ 3742, 3745, 3753, 3753, 3775, 3781, 3784, 3789, 3792
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || 1
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "ATTRIBUTE", "VARYING", "FLOAT16_T",
+ "FLOAT", "FLOAT32_T", "DOUBLE", "FLOAT64_T", "CONST", "BOOL", "INT",
+ "UINT", "INT64_T", "UINT64_T", "INT32_T", "UINT32_T", "INT16_T",
+ "UINT16_T", "INT8_T", "UINT8_T", "BREAK", "CONTINUE", "DO", "ELSE",
+ "FOR", "IF", "DISCARD", "RETURN", "SWITCH", "CASE", "DEFAULT",
+ "SUBROUTINE", "BVEC2", "BVEC3", "BVEC4", "IVEC2", "IVEC3", "IVEC4",
+ "UVEC2", "UVEC3", "UVEC4", "I64VEC2", "I64VEC3", "I64VEC4", "U64VEC2",
+ "U64VEC3", "U64VEC4", "I32VEC2", "I32VEC3", "I32VEC4", "U32VEC2",
+ "U32VEC3", "U32VEC4", "I16VEC2", "I16VEC3", "I16VEC4", "U16VEC2",
+ "U16VEC3", "U16VEC4", "I8VEC2", "I8VEC3", "I8VEC4", "U8VEC2", "U8VEC3",
+ "U8VEC4", "VEC2", "VEC3", "VEC4", "MAT2", "MAT3", "MAT4", "CENTROID",
+ "IN", "OUT", "INOUT", "UNIFORM", "PATCH", "SAMPLE", "BUFFER", "SHARED",
+ "NONUNIFORM", "PAYLOADNV", "PAYLOADINNV", "HITATTRNV", "CALLDATANV",
+ "CALLDATAINNV", "COHERENT", "VOLATILE", "RESTRICT", "READONLY",
+ "WRITEONLY", "DEVICECOHERENT", "QUEUEFAMILYCOHERENT",
+ "WORKGROUPCOHERENT", "SUBGROUPCOHERENT", "NONPRIVATE", "DVEC2", "DVEC3",
+ "DVEC4", "DMAT2", "DMAT3", "DMAT4", "F16VEC2", "F16VEC3", "F16VEC4",
+ "F16MAT2", "F16MAT3", "F16MAT4", "F32VEC2", "F32VEC3", "F32VEC4",
+ "F32MAT2", "F32MAT3", "F32MAT4", "F64VEC2", "F64VEC3", "F64VEC4",
+ "F64MAT2", "F64MAT3", "F64MAT4", "NOPERSPECTIVE", "FLAT", "SMOOTH",
+ "LAYOUT", "EXPLICITINTERPAMD", "PERVERTEXNV", "PERPRIMITIVENV",
+ "PERVIEWNV", "PERTASKNV", "MAT2X2", "MAT2X3", "MAT2X4", "MAT3X2",
+ "MAT3X3", "MAT3X4", "MAT4X2", "MAT4X3", "MAT4X4", "DMAT2X2", "DMAT2X3",
+ "DMAT2X4", "DMAT3X2", "DMAT3X3", "DMAT3X4", "DMAT4X2", "DMAT4X3",
+ "DMAT4X4", "F16MAT2X2", "F16MAT2X3", "F16MAT2X4", "F16MAT3X2",
+ "F16MAT3X3", "F16MAT3X4", "F16MAT4X2", "F16MAT4X3", "F16MAT4X4",
+ "F32MAT2X2", "F32MAT2X3", "F32MAT2X4", "F32MAT3X2", "F32MAT3X3",
+ "F32MAT3X4", "F32MAT4X2", "F32MAT4X3", "F32MAT4X4", "F64MAT2X2",
+ "F64MAT2X3", "F64MAT2X4", "F64MAT3X2", "F64MAT3X3", "F64MAT3X4",
+ "F64MAT4X2", "F64MAT4X3", "F64MAT4X4", "ATOMIC_UINT", "ACCSTRUCTNV",
+ "FCOOPMATNV", "SAMPLER1D", "SAMPLER2D", "SAMPLER3D", "SAMPLERCUBE",
+ "SAMPLER1DSHADOW", "SAMPLER2DSHADOW", "SAMPLERCUBESHADOW",
+ "SAMPLER1DARRAY", "SAMPLER2DARRAY", "SAMPLER1DARRAYSHADOW",
+ "SAMPLER2DARRAYSHADOW", "ISAMPLER1D", "ISAMPLER2D", "ISAMPLER3D",
+ "ISAMPLERCUBE", "ISAMPLER1DARRAY", "ISAMPLER2DARRAY", "USAMPLER1D",
+ "USAMPLER2D", "USAMPLER3D", "USAMPLERCUBE", "USAMPLER1DARRAY",
+ "USAMPLER2DARRAY", "SAMPLER2DRECT", "SAMPLER2DRECTSHADOW",
+ "ISAMPLER2DRECT", "USAMPLER2DRECT", "SAMPLERBUFFER", "ISAMPLERBUFFER",
+ "USAMPLERBUFFER", "SAMPLERCUBEARRAY", "SAMPLERCUBEARRAYSHADOW",
+ "ISAMPLERCUBEARRAY", "USAMPLERCUBEARRAY", "SAMPLER2DMS", "ISAMPLER2DMS",
+ "USAMPLER2DMS", "SAMPLER2DMSARRAY", "ISAMPLER2DMSARRAY",
+ "USAMPLER2DMSARRAY", "SAMPLEREXTERNALOES", "SAMPLEREXTERNAL2DY2YEXT",
+ "F16SAMPLER1D", "F16SAMPLER2D", "F16SAMPLER3D", "F16SAMPLER2DRECT",
+ "F16SAMPLERCUBE", "F16SAMPLER1DARRAY", "F16SAMPLER2DARRAY",
+ "F16SAMPLERCUBEARRAY", "F16SAMPLERBUFFER", "F16SAMPLER2DMS",
+ "F16SAMPLER2DMSARRAY", "F16SAMPLER1DSHADOW", "F16SAMPLER2DSHADOW",
+ "F16SAMPLER1DARRAYSHADOW", "F16SAMPLER2DARRAYSHADOW",
+ "F16SAMPLER2DRECTSHADOW", "F16SAMPLERCUBESHADOW",
+ "F16SAMPLERCUBEARRAYSHADOW", "SAMPLER", "SAMPLERSHADOW", "TEXTURE1D",
+ "TEXTURE2D", "TEXTURE3D", "TEXTURECUBE", "TEXTURE1DARRAY",
+ "TEXTURE2DARRAY", "ITEXTURE1D", "ITEXTURE2D", "ITEXTURE3D",
+ "ITEXTURECUBE", "ITEXTURE1DARRAY", "ITEXTURE2DARRAY", "UTEXTURE1D",
+ "UTEXTURE2D", "UTEXTURE3D", "UTEXTURECUBE", "UTEXTURE1DARRAY",
+ "UTEXTURE2DARRAY", "TEXTURE2DRECT", "ITEXTURE2DRECT", "UTEXTURE2DRECT",
+ "TEXTUREBUFFER", "ITEXTUREBUFFER", "UTEXTUREBUFFER", "TEXTURECUBEARRAY",
+ "ITEXTURECUBEARRAY", "UTEXTURECUBEARRAY", "TEXTURE2DMS", "ITEXTURE2DMS",
+ "UTEXTURE2DMS", "TEXTURE2DMSARRAY", "ITEXTURE2DMSARRAY",
+ "UTEXTURE2DMSARRAY", "F16TEXTURE1D", "F16TEXTURE2D", "F16TEXTURE3D",
+ "F16TEXTURE2DRECT", "F16TEXTURECUBE", "F16TEXTURE1DARRAY",
+ "F16TEXTURE2DARRAY", "F16TEXTURECUBEARRAY", "F16TEXTUREBUFFER",
+ "F16TEXTURE2DMS", "F16TEXTURE2DMSARRAY", "SUBPASSINPUT",
+ "SUBPASSINPUTMS", "ISUBPASSINPUT", "ISUBPASSINPUTMS", "USUBPASSINPUT",
+ "USUBPASSINPUTMS", "F16SUBPASSINPUT", "F16SUBPASSINPUTMS", "IMAGE1D",
+ "IIMAGE1D", "UIMAGE1D", "IMAGE2D", "IIMAGE2D", "UIMAGE2D", "IMAGE3D",
+ "IIMAGE3D", "UIMAGE3D", "IMAGE2DRECT", "IIMAGE2DRECT", "UIMAGE2DRECT",
+ "IMAGECUBE", "IIMAGECUBE", "UIMAGECUBE", "IMAGEBUFFER", "IIMAGEBUFFER",
+ "UIMAGEBUFFER", "IMAGE1DARRAY", "IIMAGE1DARRAY", "UIMAGE1DARRAY",
+ "IMAGE2DARRAY", "IIMAGE2DARRAY", "UIMAGE2DARRAY", "IMAGECUBEARRAY",
+ "IIMAGECUBEARRAY", "UIMAGECUBEARRAY", "IMAGE2DMS", "IIMAGE2DMS",
+ "UIMAGE2DMS", "IMAGE2DMSARRAY", "IIMAGE2DMSARRAY", "UIMAGE2DMSARRAY",
+ "F16IMAGE1D", "F16IMAGE2D", "F16IMAGE3D", "F16IMAGE2DRECT",
+ "F16IMAGECUBE", "F16IMAGE1DARRAY", "F16IMAGE2DARRAY",
+ "F16IMAGECUBEARRAY", "F16IMAGEBUFFER", "F16IMAGE2DMS",
+ "F16IMAGE2DMSARRAY", "STRUCT", "VOID", "WHILE", "IDENTIFIER",
+ "TYPE_NAME", "FLOATCONSTANT", "DOUBLECONSTANT", "INT16CONSTANT",
+ "UINT16CONSTANT", "INT32CONSTANT", "UINT32CONSTANT", "INTCONSTANT",
+ "UINTCONSTANT", "INT64CONSTANT", "UINT64CONSTANT", "BOOLCONSTANT",
+ "FLOAT16CONSTANT", "LEFT_OP", "RIGHT_OP", "INC_OP", "DEC_OP", "LE_OP",
+ "GE_OP", "EQ_OP", "NE_OP", "AND_OP", "OR_OP", "XOR_OP", "MUL_ASSIGN",
+ "DIV_ASSIGN", "ADD_ASSIGN", "MOD_ASSIGN", "LEFT_ASSIGN", "RIGHT_ASSIGN",
+ "AND_ASSIGN", "XOR_ASSIGN", "OR_ASSIGN", "SUB_ASSIGN", "LEFT_PAREN",
+ "RIGHT_PAREN", "LEFT_BRACKET", "RIGHT_BRACKET", "LEFT_BRACE",
+ "RIGHT_BRACE", "DOT", "COMMA", "COLON", "EQUAL", "SEMICOLON", "BANG",
+ "DASH", "TILDE", "PLUS", "STAR", "SLASH", "PERCENT", "LEFT_ANGLE",
+ "RIGHT_ANGLE", "VERTICAL_BAR", "CARET", "AMPERSAND", "QUESTION",
+ "INVARIANT", "PRECISE", "HIGH_PRECISION", "MEDIUM_PRECISION",
+ "LOW_PRECISION", "PRECISION", "PACKED", "RESOURCE", "SUPERP", "$accept",
+ "variable_identifier", "primary_expression", "postfix_expression",
+ "integer_expression", "function_call", "function_call_or_method",
+ "function_call_generic", "function_call_header_no_parameters",
+ "function_call_header_with_parameters", "function_call_header",
+ "function_identifier", "unary_expression", "unary_operator",
+ "multiplicative_expression", "additive_expression", "shift_expression",
+ "relational_expression", "equality_expression", "and_expression",
+ "exclusive_or_expression", "inclusive_or_expression",
+ "logical_and_expression", "logical_xor_expression",
+ "logical_or_expression", "conditional_expression", "$@1",
+ "assignment_expression", "assignment_operator", "expression",
+ "constant_expression", "declaration", "block_structure", "$@2",
+ "identifier_list", "function_prototype", "function_declarator",
+ "function_header_with_parameters", "function_header",
+ "parameter_declarator", "parameter_declaration",
+ "parameter_type_specifier", "init_declarator_list", "single_declaration",
+ "fully_specified_type", "invariant_qualifier", "interpolation_qualifier",
+ "layout_qualifier", "layout_qualifier_id_list", "layout_qualifier_id",
+ "precise_qualifier", "type_qualifier", "single_type_qualifier",
+ "storage_qualifier", "non_uniform_qualifier", "type_name_list",
+ "type_specifier", "array_specifier", "type_parameter_specifier_opt",
+ "type_parameter_specifier", "type_parameter_specifier_list",
+ "type_specifier_nonarray", "precision_qualifier", "struct_specifier",
+ "$@3", "$@4", "struct_declaration_list", "struct_declaration",
+ "struct_declarator_list", "struct_declarator", "initializer",
+ "initializer_list", "declaration_statement", "statement",
+ "simple_statement", "compound_statement", "$@5", "$@6",
+ "statement_no_new_scope", "statement_scoped", "$@7", "$@8",
+ "compound_statement_no_new_scope", "statement_list",
+ "expression_statement", "selection_statement",
+ "selection_statement_nonattributed", "selection_rest_statement",
+ "condition", "switch_statement", "switch_statement_nonattributed", "$@9",
+ "switch_statement_list", "case_label", "iteration_statement",
+ "iteration_statement_nonattributed", "$@10", "$@11", "$@12",
+ "for_init_statement", "conditionopt", "for_rest_statement",
+ "jump_statement", "translation_unit", "external_declaration",
+ "function_definition", "$@13", "attribute", "attribute_list",
+ "single_attribute", YY_NULLPTR
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[NUM] -- (External) token number corresponding to the
+ (internal) symbol number NUM (which must be that of a token). */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 302, 303, 304,
+ 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, 318, 319, 320, 321, 322, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, 334,
+ 335, 336, 337, 338, 339, 340, 341, 342, 343, 344,
+ 345, 346, 347, 348, 349, 350, 351, 352, 353, 354,
+ 355, 356, 357, 358, 359, 360, 361, 362, 363, 364,
+ 365, 366, 367, 368, 369, 370, 371, 372, 373, 374,
+ 375, 376, 377, 378, 379, 380, 381, 382, 383, 384,
+ 385, 386, 387, 388, 389, 390, 391, 392, 393, 394,
+ 395, 396, 397, 398, 399, 400, 401, 402, 403, 404,
+ 405, 406, 407, 408, 409, 410, 411, 412, 413, 414,
+ 415, 416, 417, 418, 419, 420, 421, 422, 423, 424,
+ 425, 426, 427, 428, 429, 430, 431, 432, 433, 434,
+ 435, 436, 437, 438, 439, 440, 441, 442, 443, 444,
+ 445, 446, 447, 448, 449, 450, 451, 452, 453, 454,
+ 455, 456, 457, 458, 459, 460, 461, 462, 463, 464,
+ 465, 466, 467, 468, 469, 470, 471, 472, 473, 474,
+ 475, 476, 477, 478, 479, 480, 481, 482, 483, 484,
+ 485, 486, 487, 488, 489, 490, 491, 492, 493, 494,
+ 495, 496, 497, 498, 499, 500, 501, 502, 503, 504,
+ 505, 506, 507, 508, 509, 510, 511, 512, 513, 514,
+ 515, 516, 517, 518, 519, 520, 521, 522, 523, 524,
+ 525, 526, 527, 528, 529, 530, 531, 532, 533, 534,
+ 535, 536, 537, 538, 539, 540, 541, 542, 543, 544,
+ 545, 546, 547, 548, 549, 550, 551, 552, 553, 554,
+ 555, 556, 557, 558, 559, 560, 561, 562, 563, 564,
+ 565, 566, 567, 568, 569, 570, 571, 572, 573, 574,
+ 575, 576, 577, 578, 579, 580, 581, 582, 583, 584,
+ 585, 586, 587, 588, 589, 590, 591, 592, 593, 594,
+ 595, 596, 597, 598, 599, 600, 601, 602, 603, 604,
+ 605, 606, 607, 608, 609, 610, 611, 612, 613, 614,
+ 615, 616, 617, 618, 619, 620, 621, 622, 623, 624,
+ 625, 626, 627, 628, 629, 630, 631, 632, 633, 634,
+ 635, 636, 637, 638, 639, 640, 641, 642, 643, 644,
+ 645, 646, 647, 648, 649, 650, 651, 652, 653, 654,
+ 655, 656, 657, 658, 659, 660, 661, 662
+};
+# endif
+
+#define YYPACT_NINF -659
+
+#define yypact_value_is_default(Yystate) \
+ (!!((Yystate) == (-659)))
+
+#define YYTABLE_NINF -524
+
+#define yytable_value_is_error(Yytable_value) \
+ 0
+
+ /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+static const yytype_int16 yypact[] =
+{
+ 3535, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -331, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -324, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -319, -659, -659, -659, -659, -659,
+ -659, -659, -659, -256, -659, -314, -351, -309, -306, 5942,
+ -257, -659, -217, -659, -659, -659, -659, 4338, -659, -659,
+ -659, -659, -241, -659, -659, 721, -659, -659, -204, -71,
+ -219, -659, 9007, -349, -659, -659, -215, -659, 5942, -659,
+ -659, -659, 5942, -178, -172, -659, -337, -267, -659, -659,
+ -659, 8237, -207, -659, -659, -659, -659, -341, -659, -211,
+ -330, -659, -659, 5942, -210, 6697, -659, -322, 1123, -659,
+ -659, -659, -659, -207, -328, -659, 7082, -304, -659, -163,
+ -659, -252, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -659, -659, -659, 8237, 8237, 8237, -659, -659,
+ -659, -659, -659, -659, -303, -659, -659, -659, -196, -299,
+ 8622, -194, -659, 8237, -659, -659, -355, -195, -659, -157,
+ 8237, -659, -71, 5942, 5942, -155, 4739, -659, -659, -659,
+ -659, -242, -236, -249, -335, -206, -191, -187, -209, -149,
+ -150, -333, -162, 7467, -659, -170, -168, -659, -154, -153,
+ -167, 7852, -152, 8237, -159, -148, -151, -160, -659, -659,
+ -274, -659, -659, -251, -659, -351, -147, -144, -659, -659,
+ -659, -659, 1525, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -19, -195, 7082, -302, 7082, -659, -659, 7082,
+ 5942, -659, -115, -659, -659, -659, -292, -659, -659, 8237,
+ -108, -659, -659, 8237, -143, -659, -659, -659, 8237, -659,
+ -659, -659, -659, -659, 5140, -155, -207, -250, -659, -659,
+ -659, 8237, 8237, 8237, 8237, 8237, 8237, 8237, 8237, 8237,
+ 8237, 8237, 8237, 8237, 8237, 8237, 8237, 8237, 8237, 8237,
+ -659, -659, -659, -142, -659, -659, 1927, -659, 8237, -659,
+ -659, -245, 8237, -226, -659, -659, -106, -659, 1927, -659,
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ 8237, 8237, -659, -659, -659, -659, -659, -659, -659, 7082,
+ -659, -238, -659, 5541, -659, -659, -141, -140, -659, -659,
+ -659, -659, -244, -195, -155, -659, -659, -659, -659, -242,
+ -242, -236, -236, -249, -249, -249, -249, -335, -335, -206,
+ -191, -187, -209, -149, -150, 8237, -659, -104, 3133, -263,
+ -659, -260, -659, 3937, -136, -297, -659, 1927, -659, -659,
+ -659, -659, 6312, -659, -659, -659, -659, -224, -135, -659,
+ -659, 3937, -138, -659, -140, -97, 5942, -132, 8237, -133,
+ -106, -134, -659, -659, 8237, 8237, -659, -137, -129, 224,
+ -128, 2731, -659, -127, -131, 2329, -126, -659, -659, -659,
+ -659, -255, 8237, 2329, -138, -659, -659, 1927, 7082, -659,
+ -659, -659, -659, -130, -140, -659, -659, 1927, -123, -659,
+ -659, -659
+};
+
+ /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
+ Performed when YYTABLE does not specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint16 yydefact[] =
+{
+ 0, 157, 158, 202, 200, 203, 201, 204, 156, 215,
+ 205, 206, 213, 214, 211, 212, 209, 210, 207, 208,
+ 183, 231, 232, 233, 234, 235, 236, 249, 250, 251,
+ 246, 247, 248, 261, 262, 263, 243, 244, 245, 258,
+ 259, 260, 240, 241, 242, 255, 256, 257, 237, 238,
+ 239, 252, 253, 254, 216, 217, 218, 264, 265, 266,
+ 162, 160, 161, 159, 165, 163, 164, 166, 172, 185,
+ 168, 169, 167, 170, 171, 173, 179, 180, 181, 182,
+ 174, 175, 176, 177, 178, 219, 220, 221, 276, 277,
+ 278, 222, 223, 224, 288, 289, 290, 225, 226, 227,
+ 300, 301, 302, 228, 229, 230, 312, 313, 314, 134,
+ 133, 132, 0, 135, 136, 137, 138, 139, 267, 268,
+ 269, 270, 271, 272, 273, 274, 275, 279, 280, 281,
+ 282, 283, 284, 285, 286, 287, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 303, 304, 305, 306, 307,
+ 308, 309, 310, 311, 315, 316, 317, 318, 319, 320,
+ 321, 322, 323, 325, 324, 484, 326, 327, 328, 329,
+ 330, 331, 332, 333, 334, 335, 336, 352, 353, 354,
+ 355, 356, 357, 359, 360, 361, 362, 363, 364, 366,
+ 367, 370, 371, 372, 374, 375, 337, 338, 358, 365,
+ 376, 378, 379, 380, 382, 383, 474, 475, 339, 340,
+ 341, 368, 342, 346, 347, 350, 373, 377, 381, 343,
+ 344, 348, 349, 369, 345, 351, 384, 385, 386, 388,
+ 390, 392, 394, 396, 400, 401, 402, 403, 404, 405,
+ 407, 408, 409, 410, 411, 412, 414, 416, 417, 418,
+ 420, 421, 398, 406, 413, 422, 424, 425, 426, 428,
+ 429, 387, 389, 391, 415, 393, 395, 397, 399, 419,
+ 423, 427, 476, 477, 480, 481, 482, 483, 478, 479,
+ 430, 432, 433, 434, 436, 437, 438, 440, 441, 442,
+ 444, 445, 446, 448, 449, 450, 452, 453, 454, 456,
+ 457, 458, 460, 461, 462, 464, 465, 466, 468, 469,
+ 470, 472, 473, 431, 435, 439, 443, 447, 455, 459,
+ 463, 451, 467, 471, 0, 199, 486, 571, 131, 146,
+ 487, 488, 489, 0, 570, 0, 572, 0, 108, 107,
+ 0, 119, 124, 153, 152, 150, 154, 0, 147, 149,
+ 155, 129, 195, 151, 485, 0, 567, 569, 0, 0,
+ 0, 492, 0, 0, 96, 93, 0, 106, 0, 115,
+ 109, 117, 0, 118, 0, 94, 125, 0, 99, 148,
+ 130, 0, 188, 194, 1, 568, 186, 0, 145, 143,
+ 0, 141, 490, 0, 0, 0, 97, 0, 0, 573,
+ 110, 114, 116, 112, 120, 111, 0, 126, 102, 0,
+ 100, 0, 2, 12, 13, 10, 11, 4, 5, 6,
+ 7, 8, 9, 15, 14, 0, 0, 0, 42, 41,
+ 43, 40, 3, 17, 36, 19, 24, 25, 0, 0,
+ 29, 0, 197, 0, 35, 33, 0, 189, 184, 0,
+ 0, 140, 0, 0, 0, 0, 0, 494, 95, 190,
+ 44, 48, 51, 54, 59, 62, 64, 66, 68, 70,
+ 72, 74, 0, 0, 98, 0, 0, 552, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 518, 527, 531,
+ 44, 77, 90, 0, 507, 0, 155, 129, 510, 529,
+ 509, 508, 0, 511, 512, 533, 513, 540, 514, 515,
+ 548, 516, 0, 113, 0, 121, 0, 502, 128, 0,
+ 0, 104, 0, 101, 37, 38, 0, 21, 22, 0,
+ 0, 27, 26, 0, 199, 30, 32, 39, 0, 196,
+ 187, 92, 144, 142, 0, 0, 500, 0, 498, 493,
+ 495, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 75, 191, 192, 0, 563, 562, 0, 554, 0, 566,
+ 564, 0, 0, 0, 547, 550, 0, 517, 0, 80,
+ 81, 83, 82, 85, 86, 87, 88, 89, 84, 79,
+ 0, 0, 532, 528, 530, 534, 541, 549, 123, 0,
+ 505, 0, 127, 0, 105, 16, 0, 23, 20, 31,
+ 198, 491, 0, 501, 0, 496, 45, 46, 47, 50,
+ 49, 52, 53, 57, 58, 55, 56, 60, 61, 63,
+ 65, 67, 69, 71, 73, 0, 193, 0, 0, 0,
+ 565, 0, 546, 0, 577, 0, 575, 519, 78, 91,
+ 122, 503, 0, 103, 18, 497, 499, 0, 0, 557,
+ 556, 559, 525, 542, 538, 0, 0, 0, 0, 0,
+ 0, 0, 504, 506, 0, 0, 558, 0, 0, 537,
+ 0, 0, 535, 0, 0, 0, 0, 574, 576, 520,
+ 76, 0, 560, 0, 525, 524, 526, 544, 0, 522,
+ 551, 521, 578, 0, 561, 555, 536, 545, 0, 539,
+ 553, 543
+};
+
+ /* YYPGOTO[NTERM-NUM]. */
+static const yytype_int16 yypgoto[] =
+{
+ -659, -659, -659, -659, -659, -659, -659, -659, -659, -659,
+ -659, -659, -364, -659, -389, -385, -457, -384, -310, -307,
+ -305, -308, -301, -298, -659, -386, -659, -390, -659, -415,
+ -418, 1, -659, -659, -659, 2, -659, -659, -659, -110,
+ -105, -107, -659, -659, -628, -659, -659, -659, -659, -188,
+ -659, -336, -343, -659, 6, -659, 0, -334, -659, -659,
+ -659, -659, -67, -659, -659, -659, -431, -437, -277, -350,
+ -501, -659, -375, -488, -658, -414, -659, -659, -428, -426,
+ -659, -659, -87, -568, -368, -659, -231, -659, -388, -659,
+ -230, -659, -659, -659, -659, -228, -659, -659, -659, -659,
+ -659, -659, -659, -659, -70, -659, -659, -659, -659, -394
+};
+
+ /* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int16 yydefgoto[] =
+{
+ -1, 432, 433, 434, 616, 435, 436, 437, 438, 439,
+ 440, 441, 490, 443, 461, 462, 463, 464, 465, 466,
+ 467, 468, 469, 470, 471, 491, 645, 492, 600, 493,
+ 542, 494, 335, 520, 411, 495, 337, 338, 339, 369,
+ 370, 371, 340, 341, 342, 343, 344, 345, 390, 391,
+ 346, 347, 348, 349, 444, 387, 445, 397, 382, 383,
+ 446, 352, 353, 354, 453, 393, 456, 457, 547, 548,
+ 518, 611, 498, 499, 500, 501, 588, 681, 710, 689,
+ 690, 691, 711, 502, 503, 504, 505, 692, 677, 506,
+ 507, 693, 718, 508, 509, 510, 653, 576, 648, 671,
+ 687, 688, 511, 355, 356, 357, 366, 512, 655, 656
+};
+
+ /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule whose
+ number is the opposite. If YYTABLE_NINF, syntax error. */
+static const yytype_int16 yytable[] =
+{
+ 351, 334, 336, 372, 379, 477, 350, 478, 479, 472,
+ 388, 482, 526, 608, 604, 610, 517, 442, 612, 550,
+ 657, 360, 544, 558, 559, 675, 363, 538, 395, 379,
+ 569, 460, 372, 706, 365, 448, 396, 709, 405, 539,
+ 395, 449, 407, 675, 358, 709, 451, 406, 447, 395,
+ 535, 359, 452, 527, 528, 473, 514, 454, 560, 561,
+ 361, 524, 525, 474, 541, 570, 581, 367, 583, 513,
+ 515, 364, -34, 473, 529, 473, 368, 532, 530, 537,
+ 519, 679, 609, 533, 615, 680, 460, 573, 647, 613,
+ 601, 589, 590, 591, 592, 593, 594, 595, 596, 597,
+ 598, 633, 634, 635, 636, 556, 557, 550, 660, 460,
+ 599, 379, 408, 672, 617, 409, 673, 454, 410, 601,
+ 454, 713, 601, 376, 517, 374, 517, 601, 375, 517,
+ 522, 601, 624, 523, 602, 625, 386, 601, 624, 717,
+ 650, 665, 661, 619, 662, 330, 331, 332, 551, 552,
+ 553, 554, 381, 555, 562, 563, 601, 652, 601, 684,
+ 392, 683, 403, 649, 398, 629, 630, 651, 404, 604,
+ 395, 631, 632, 450, 620, 458, 550, 521, 637, 638,
+ 531, 536, 473, 540, 454, 546, 566, 626, 627, 628,
+ 460, 460, 460, 460, 460, 460, 460, 460, 460, 460,
+ 460, 460, 460, 460, 460, 460, 564, 719, 454, 565,
+ 658, 659, 623, 567, 568, 574, 571, 575, 579, 517,
+ 587, 577, 578, 582, 584, 614, 586, 585, -35, 604,
+ 667, -33, 618, -28, 654, 668, 646, 664, 674, 678,
+ 685, -523, 601, 694, 695, 697, 699, 703, 702, 704,
+ 712, 487, 707, 708, 639, 720, 674, 721, 640, 642,
+ 696, 641, 401, 400, 543, 402, 362, 643, 622, 389,
+ 701, 644, 517, 669, 666, 715, 705, 454, 716, 399,
+ 670, 605, 606, 686, 607, 385, 698, 714, 0, 0,
+ 0, 0, 541, 0, 700, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 460, 0, 0, 676, 517, 0,
+ 485, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 379, 0, 676, 0, 0, 0, 373,
+ 0, 0, 0, 0, 0, 350, 0, 380, 0, 0,
+ 0, 0, 0, 350, 0, 351, 334, 336, 0, 0,
+ 0, 350, 394, 0, 0, 0, 0, 0, 373, 0,
+ 0, 0, 373, 0, 350, 0, 0, 0, 350, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 455, 0, 0, 0, 0, 497, 350,
+ 0, 0, 0, 0, 496, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 455, 545, 0, 455, 0, 0, 350,
+ 350, 0, 350, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 497, 0, 0, 0, 0, 0, 496, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 455, 0, 0, 0, 0, 0, 350, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 455, 0, 0, 0, 0, 0,
+ 350, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 497, 0, 0, 0,
+ 0, 0, 496, 0, 0, 0, 0, 0, 497, 0,
+ 0, 0, 0, 0, 496, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 455, 0, 0, 0, 0, 0, 350,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 497, 0,
+ 0, 0, 0, 497, 496, 0, 0, 497, 0, 496,
+ 0, 0, 0, 496, 0, 0, 0, 0, 0, 0,
+ 0, 497, 0, 0, 0, 0, 380, 496, 0, 0,
+ 0, 0, 350, 0, 0, 0, 0, 0, 0, 0,
+ 0, 497, 0, 0, 0, 497, 0, 496, 0, 0,
+ 0, 496, 0, 497, 0, 0, 0, 497, 0, 496,
+ 0, 0, 0, 496, 0, 0, 0, 497, 0, 0,
+ 0, 384, 0, 496, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
+ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
+ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
+ 76, 77, 78, 79, 80, 81, 82, 83, 84, 85,
+ 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
+ 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
+ 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
+ 126, 127, 128, 129, 130, 131, 132, 133, 134, 135,
+ 136, 137, 138, 139, 140, 141, 142, 143, 144, 145,
+ 146, 147, 148, 149, 150, 151, 152, 153, 154, 155,
+ 156, 157, 158, 159, 160, 161, 162, 163, 164, 165,
+ 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
+ 176, 177, 178, 179, 180, 181, 182, 183, 184, 185,
+ 186, 187, 188, 189, 190, 191, 192, 193, 194, 195,
+ 196, 197, 198, 199, 200, 201, 202, 203, 204, 205,
+ 206, 207, 208, 209, 210, 211, 212, 213, 214, 215,
+ 216, 217, 218, 219, 220, 221, 222, 223, 224, 225,
+ 226, 227, 228, 229, 230, 231, 232, 233, 234, 235,
+ 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
+ 246, 247, 248, 249, 250, 251, 252, 253, 254, 255,
+ 256, 257, 258, 259, 260, 261, 262, 263, 264, 265,
+ 266, 267, 268, 269, 270, 271, 272, 273, 274, 275,
+ 276, 277, 278, 279, 280, 281, 282, 283, 284, 285,
+ 286, 287, 288, 289, 290, 291, 292, 293, 294, 295,
+ 296, 297, 298, 299, 300, 301, 302, 303, 304, 305,
+ 306, 307, 308, 309, 310, 311, 312, 313, 314, 315,
+ 316, 317, 318, 319, 320, 321, 322, 323, 324, 325,
+ 0, 0, 326, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 327, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 328, 329, 330, 331, 332, 333, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 475, 476, 477, 0, 478,
+ 479, 480, 481, 482, 483, 484, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
+ 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
+ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93,
+ 94, 95, 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
+ 114, 115, 116, 117, 118, 119, 120, 121, 122, 123,
+ 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
+ 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
+ 144, 145, 146, 147, 148, 149, 150, 151, 152, 153,
+ 154, 155, 156, 157, 158, 159, 160, 161, 162, 163,
+ 164, 165, 166, 167, 168, 169, 170, 171, 172, 173,
+ 174, 175, 176, 177, 178, 179, 180, 181, 182, 183,
+ 184, 185, 186, 187, 188, 189, 190, 191, 192, 193,
+ 194, 195, 196, 197, 198, 199, 200, 201, 202, 203,
+ 204, 205, 206, 207, 208, 209, 210, 211, 212, 213,
+ 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
+ 224, 225, 226, 227, 228, 229, 230, 231, 232, 233,
+ 234, 235, 236, 237, 238, 239, 240, 241, 242, 243,
+ 244, 245, 246, 247, 248, 249, 250, 251, 252, 253,
+ 254, 255, 256, 257, 258, 259, 260, 261, 262, 263,
+ 264, 265, 266, 267, 268, 269, 270, 271, 272, 273,
+ 274, 275, 276, 277, 278, 279, 280, 281, 282, 283,
+ 284, 285, 286, 287, 288, 289, 290, 291, 292, 293,
+ 294, 295, 296, 297, 298, 299, 300, 301, 302, 303,
+ 304, 305, 306, 307, 308, 309, 310, 311, 312, 313,
+ 314, 315, 316, 317, 318, 319, 320, 321, 322, 323,
+ 324, 325, 485, 412, 326, 413, 414, 415, 416, 417,
+ 418, 419, 420, 421, 422, 423, 424, 0, 0, 425,
+ 426, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 427, 0,
+ 486, 0, 487, 488, 0, 0, 0, 0, 489, 428,
+ 429, 430, 431, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 328, 329, 330, 331, 332, 333, 1, 2,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 475, 476, 477,
+ 0, 478, 479, 480, 481, 482, 483, 484, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
+ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
+ 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
+ 92, 93, 94, 95, 96, 97, 98, 99, 100, 101,
+ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
+ 122, 123, 124, 125, 126, 127, 128, 129, 130, 131,
+ 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
+ 142, 143, 144, 145, 146, 147, 148, 149, 150, 151,
+ 152, 153, 154, 155, 156, 157, 158, 159, 160, 161,
+ 162, 163, 164, 165, 166, 167, 168, 169, 170, 171,
+ 172, 173, 174, 175, 176, 177, 178, 179, 180, 181,
+ 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
+ 192, 193, 194, 195, 196, 197, 198, 199, 200, 201,
+ 202, 203, 204, 205, 206, 207, 208, 209, 210, 211,
+ 212, 213, 214, 215, 216, 217, 218, 219, 220, 221,
+ 222, 223, 224, 225, 226, 227, 228, 229, 230, 231,
+ 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
+ 242, 243, 244, 245, 246, 247, 248, 249, 250, 251,
+ 252, 253, 254, 255, 256, 257, 258, 259, 260, 261,
+ 262, 263, 264, 265, 266, 267, 268, 269, 270, 271,
+ 272, 273, 274, 275, 276, 277, 278, 279, 280, 281,
+ 282, 283, 284, 285, 286, 287, 288, 289, 290, 291,
+ 292, 293, 294, 295, 296, 297, 298, 299, 300, 301,
+ 302, 303, 304, 305, 306, 307, 308, 309, 310, 311,
+ 312, 313, 314, 315, 316, 317, 318, 319, 320, 321,
+ 322, 323, 324, 325, 485, 412, 326, 413, 414, 415,
+ 416, 417, 418, 419, 420, 421, 422, 423, 424, 0,
+ 0, 425, 426, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 427, 0, 486, 0, 487, 603, 0, 0, 0, 0,
+ 489, 428, 429, 430, 431, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 328, 329, 330, 331, 332, 333,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 475,
+ 476, 477, 0, 478, 479, 480, 481, 482, 483, 484,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
+ 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
+ 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
+ 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
+ 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
+ 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
+ 220, 221, 222, 223, 224, 225, 226, 227, 228, 229,
+ 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+ 240, 241, 242, 243, 244, 245, 246, 247, 248, 249,
+ 250, 251, 252, 253, 254, 255, 256, 257, 258, 259,
+ 260, 261, 262, 263, 264, 265, 266, 267, 268, 269,
+ 270, 271, 272, 273, 274, 275, 276, 277, 278, 279,
+ 280, 281, 282, 283, 284, 285, 286, 287, 288, 289,
+ 290, 291, 292, 293, 294, 295, 296, 297, 298, 299,
+ 300, 301, 302, 303, 304, 305, 306, 307, 308, 309,
+ 310, 311, 312, 313, 314, 315, 316, 317, 318, 319,
+ 320, 321, 322, 323, 324, 325, 485, 412, 326, 413,
+ 414, 415, 416, 417, 418, 419, 420, 421, 422, 423,
+ 424, 0, 0, 425, 426, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 427, 0, 486, 0, 487, 0, 0, 0,
+ 0, 0, 489, 428, 429, 430, 431, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 328, 329, 330, 331,
+ 332, 333, 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 475, 476, 477, 0, 478, 479, 480, 481, 482,
+ 483, 484, 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
+ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
+ 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
+ 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
+ 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135, 136, 137,
+ 138, 139, 140, 141, 142, 143, 144, 145, 146, 147,
+ 148, 149, 150, 151, 152, 153, 154, 155, 156, 157,
+ 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
+ 168, 169, 170, 171, 172, 173, 174, 175, 176, 177,
+ 178, 179, 180, 181, 182, 183, 184, 185, 186, 187,
+ 188, 189, 190, 191, 192, 193, 194, 195, 196, 197,
+ 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
+ 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
+ 218, 219, 220, 221, 222, 223, 224, 225, 226, 227,
+ 228, 229, 230, 231, 232, 233, 234, 235, 236, 237,
+ 238, 239, 240, 241, 242, 243, 244, 245, 246, 247,
+ 248, 249, 250, 251, 252, 253, 254, 255, 256, 257,
+ 258, 259, 260, 261, 262, 263, 264, 265, 266, 267,
+ 268, 269, 270, 271, 272, 273, 274, 275, 276, 277,
+ 278, 279, 280, 281, 282, 283, 284, 285, 286, 287,
+ 288, 289, 290, 291, 292, 293, 294, 295, 296, 297,
+ 298, 299, 300, 301, 302, 303, 304, 305, 306, 307,
+ 308, 309, 310, 311, 312, 313, 314, 315, 316, 317,
+ 318, 319, 320, 321, 322, 323, 324, 325, 485, 412,
+ 326, 413, 414, 415, 416, 417, 418, 419, 420, 421,
+ 422, 423, 424, 0, 0, 425, 426, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 427, 0, 486, 0, 398, 0,
+ 0, 0, 0, 0, 489, 428, 429, 430, 431, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 328, 329,
+ 330, 331, 332, 333, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 475, 476, 477, 0, 478, 479, 480,
+ 481, 482, 483, 484, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
+ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
+ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
+ 76, 77, 78, 79, 80, 81, 82, 83, 84, 85,
+ 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
+ 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
+ 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
+ 126, 127, 128, 129, 130, 131, 132, 133, 134, 135,
+ 136, 137, 138, 139, 140, 141, 142, 143, 144, 145,
+ 146, 147, 148, 149, 150, 151, 152, 153, 154, 155,
+ 156, 157, 158, 159, 160, 161, 162, 163, 164, 165,
+ 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
+ 176, 177, 178, 179, 180, 181, 182, 183, 184, 185,
+ 186, 187, 188, 189, 190, 191, 192, 193, 194, 195,
+ 196, 197, 198, 199, 200, 201, 202, 203, 204, 205,
+ 206, 207, 208, 209, 210, 211, 212, 213, 214, 215,
+ 216, 217, 218, 219, 220, 221, 222, 223, 224, 225,
+ 226, 227, 228, 229, 230, 231, 232, 233, 234, 235,
+ 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
+ 246, 247, 248, 249, 250, 251, 252, 253, 254, 255,
+ 256, 257, 258, 259, 260, 261, 262, 263, 264, 265,
+ 266, 267, 268, 269, 270, 271, 272, 273, 274, 275,
+ 276, 277, 278, 279, 280, 281, 282, 283, 284, 285,
+ 286, 287, 288, 289, 290, 291, 292, 293, 294, 295,
+ 296, 297, 298, 299, 300, 301, 302, 303, 304, 305,
+ 306, 307, 308, 309, 310, 311, 312, 313, 314, 315,
+ 316, 317, 318, 319, 320, 321, 322, 323, 324, 325,
+ 485, 412, 326, 413, 414, 415, 416, 417, 418, 419,
+ 420, 421, 422, 423, 424, 0, 0, 425, 426, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 427, 0, 486, 0,
+ 0, 0, 0, 0, 0, 0, 489, 428, 429, 430,
+ 431, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 328, 329, 330, 331, 332, 333, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
+ 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
+ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93,
+ 94, 95, 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
+ 114, 115, 116, 117, 118, 119, 120, 121, 122, 123,
+ 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
+ 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
+ 144, 145, 146, 147, 148, 149, 150, 151, 152, 153,
+ 154, 155, 156, 157, 158, 159, 160, 161, 162, 163,
+ 164, 165, 166, 167, 168, 169, 170, 171, 172, 173,
+ 174, 175, 176, 177, 178, 179, 180, 181, 182, 183,
+ 184, 185, 186, 187, 188, 189, 190, 191, 192, 193,
+ 194, 195, 196, 197, 198, 199, 200, 201, 202, 203,
+ 204, 205, 206, 207, 208, 209, 210, 211, 212, 213,
+ 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
+ 224, 225, 226, 227, 228, 229, 230, 231, 232, 233,
+ 234, 235, 236, 237, 238, 239, 240, 241, 242, 243,
+ 244, 245, 246, 247, 248, 249, 250, 251, 252, 253,
+ 254, 255, 256, 257, 258, 259, 260, 261, 262, 263,
+ 264, 265, 266, 267, 268, 269, 270, 271, 272, 273,
+ 274, 275, 276, 277, 278, 279, 280, 281, 282, 283,
+ 284, 285, 286, 287, 288, 289, 290, 291, 292, 293,
+ 294, 295, 296, 297, 298, 299, 300, 301, 302, 303,
+ 304, 305, 306, 307, 308, 309, 310, 311, 312, 313,
+ 314, 315, 316, 317, 318, 319, 320, 321, 322, 323,
+ 324, 325, 0, 412, 326, 413, 414, 415, 416, 417,
+ 418, 419, 420, 421, 422, 423, 424, 0, 0, 425,
+ 426, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 427, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 489, 428,
+ 429, 430, 431, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 328, 329, 330, 331, 332, 333, 1, 2,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
+ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
+ 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
+ 92, 93, 94, 95, 96, 97, 98, 99, 100, 101,
+ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
+ 122, 123, 124, 125, 126, 127, 128, 129, 130, 131,
+ 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
+ 142, 143, 144, 145, 146, 147, 148, 149, 150, 151,
+ 152, 153, 154, 155, 156, 157, 158, 159, 160, 161,
+ 162, 163, 164, 165, 166, 167, 168, 169, 170, 171,
+ 172, 173, 174, 175, 176, 177, 178, 179, 180, 181,
+ 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
+ 192, 193, 194, 195, 196, 197, 198, 199, 200, 201,
+ 202, 203, 204, 205, 206, 207, 208, 209, 210, 211,
+ 212, 213, 214, 215, 216, 217, 218, 219, 220, 221,
+ 222, 223, 224, 225, 226, 227, 228, 229, 230, 231,
+ 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
+ 242, 243, 244, 245, 246, 247, 248, 249, 250, 251,
+ 252, 253, 254, 255, 256, 257, 258, 259, 260, 261,
+ 262, 263, 264, 265, 266, 267, 268, 269, 270, 271,
+ 272, 273, 274, 275, 276, 277, 278, 279, 280, 281,
+ 282, 283, 284, 285, 286, 287, 288, 289, 290, 291,
+ 292, 293, 294, 295, 296, 297, 298, 299, 300, 301,
+ 302, 303, 304, 305, 306, 307, 308, 309, 310, 311,
+ 312, 313, 314, 315, 316, 317, 318, 319, 320, 321,
+ 322, 323, 324, 325, 0, 0, 326, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 327, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 328, 329, 330, 331, 332, 333,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
+ 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
+ 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
+ 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
+ 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
+ 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
+ 220, 221, 222, 223, 224, 225, 226, 227, 228, 229,
+ 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+ 240, 241, 242, 243, 244, 245, 246, 247, 248, 249,
+ 250, 251, 252, 253, 254, 255, 256, 257, 258, 259,
+ 260, 261, 262, 263, 264, 265, 266, 267, 268, 269,
+ 270, 271, 272, 273, 274, 275, 276, 277, 278, 279,
+ 280, 281, 282, 283, 284, 285, 286, 287, 288, 289,
+ 290, 291, 292, 293, 294, 295, 296, 297, 298, 299,
+ 300, 301, 302, 303, 304, 305, 306, 307, 308, 309,
+ 310, 311, 312, 313, 314, 315, 316, 317, 318, 319,
+ 320, 321, 322, 323, 324, 325, 0, 412, 326, 413,
+ 414, 415, 416, 417, 418, 419, 420, 421, 422, 423,
+ 424, 0, 0, 425, 426, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 427, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 428, 429, 430, 431, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 328, 329, 330, 331,
+ 332, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 20, 21, 22, 23, 24, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
+ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
+ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
+ 99, 100, 101, 102, 103, 104, 105, 106, 107, 108,
+ 109, 110, 111, 112, 113, 114, 115, 116, 117, 118,
+ 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
+ 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
+ 139, 140, 141, 142, 143, 144, 145, 146, 147, 148,
+ 149, 150, 151, 152, 153, 154, 155, 156, 157, 158,
+ 159, 160, 161, 162, 163, 164, 165, 166, 167, 168,
+ 169, 170, 171, 172, 173, 174, 175, 176, 177, 178,
+ 179, 180, 181, 182, 183, 184, 185, 186, 187, 188,
+ 189, 190, 191, 192, 193, 194, 195, 196, 197, 198,
+ 199, 200, 201, 202, 203, 204, 205, 206, 207, 208,
+ 209, 210, 211, 212, 213, 214, 215, 216, 217, 218,
+ 219, 220, 221, 222, 223, 224, 225, 226, 227, 228,
+ 229, 230, 231, 232, 233, 234, 235, 236, 237, 238,
+ 239, 240, 241, 242, 243, 244, 245, 246, 247, 248,
+ 249, 250, 251, 252, 253, 254, 255, 256, 257, 258,
+ 259, 260, 261, 262, 263, 264, 265, 266, 267, 268,
+ 269, 270, 271, 272, 273, 274, 275, 276, 277, 278,
+ 279, 280, 281, 282, 283, 284, 285, 286, 287, 288,
+ 289, 290, 291, 292, 293, 294, 295, 296, 297, 298,
+ 299, 300, 301, 302, 303, 304, 305, 306, 307, 308,
+ 309, 310, 311, 312, 313, 314, 315, 316, 317, 318,
+ 319, 320, 321, 322, 323, 324, 325, 0, 377, 326,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 378, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 328, 329, 330,
+ 331, 332, 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
+ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
+ 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
+ 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
+ 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135, 136, 137,
+ 138, 139, 140, 141, 142, 143, 144, 145, 146, 147,
+ 148, 149, 150, 151, 152, 153, 154, 155, 156, 157,
+ 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
+ 168, 169, 170, 171, 172, 173, 174, 175, 176, 177,
+ 178, 179, 180, 181, 182, 183, 184, 185, 186, 187,
+ 188, 189, 190, 191, 192, 193, 194, 195, 196, 197,
+ 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
+ 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
+ 218, 219, 220, 221, 222, 223, 224, 225, 226, 227,
+ 228, 229, 230, 231, 232, 233, 234, 235, 236, 237,
+ 238, 239, 240, 241, 242, 243, 244, 245, 246, 247,
+ 248, 249, 250, 251, 252, 253, 254, 255, 256, 257,
+ 258, 259, 260, 261, 262, 263, 264, 265, 266, 267,
+ 268, 269, 270, 271, 272, 273, 274, 275, 276, 277,
+ 278, 279, 280, 281, 282, 283, 284, 285, 286, 287,
+ 288, 289, 290, 291, 292, 293, 294, 295, 296, 297,
+ 298, 299, 300, 301, 302, 303, 304, 305, 306, 307,
+ 308, 309, 310, 311, 312, 313, 314, 315, 316, 317,
+ 318, 319, 320, 321, 322, 323, 324, 325, 0, 0,
+ 326, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 549,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 328, 329,
+ 330, 331, 332, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 20, 21, 22, 23, 24, 25, 26,
+ 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ 57, 58, 59, 60, 61, 62, 63, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
+ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
+ 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
+ 117, 118, 119, 120, 121, 122, 123, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, 134, 135, 136,
+ 137, 138, 139, 140, 141, 142, 143, 144, 145, 146,
+ 147, 148, 149, 150, 151, 152, 153, 154, 155, 156,
+ 157, 158, 159, 160, 161, 162, 163, 164, 165, 166,
+ 167, 168, 169, 170, 171, 172, 173, 174, 175, 176,
+ 177, 178, 179, 180, 181, 182, 183, 184, 185, 186,
+ 187, 188, 189, 190, 191, 192, 193, 194, 195, 196,
+ 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
+ 207, 208, 209, 210, 211, 212, 213, 214, 215, 216,
+ 217, 218, 219, 220, 221, 222, 223, 224, 225, 226,
+ 227, 228, 229, 230, 231, 232, 233, 234, 235, 236,
+ 237, 238, 239, 240, 241, 242, 243, 244, 245, 246,
+ 247, 248, 249, 250, 251, 252, 253, 254, 255, 256,
+ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266,
+ 267, 268, 269, 270, 271, 272, 273, 274, 275, 276,
+ 277, 278, 279, 280, 281, 282, 283, 284, 285, 286,
+ 287, 288, 289, 290, 291, 292, 293, 294, 295, 296,
+ 297, 298, 299, 300, 301, 302, 303, 304, 305, 306,
+ 307, 308, 309, 310, 311, 312, 313, 314, 315, 316,
+ 317, 318, 319, 320, 321, 322, 323, 324, 325, 0,
+ 0, 326, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 621, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 328,
+ 329, 330, 331, 332, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
+ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
+ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
+ 76, 77, 78, 79, 80, 81, 82, 83, 84, 85,
+ 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
+ 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
+ 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
+ 126, 127, 128, 129, 130, 131, 132, 133, 134, 135,
+ 136, 137, 138, 139, 140, 141, 142, 143, 144, 145,
+ 146, 147, 148, 149, 150, 151, 152, 153, 154, 155,
+ 156, 157, 158, 159, 160, 161, 162, 163, 164, 165,
+ 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
+ 176, 177, 178, 179, 180, 181, 182, 183, 184, 185,
+ 186, 187, 188, 189, 190, 191, 192, 193, 194, 195,
+ 196, 197, 198, 199, 200, 201, 202, 203, 204, 205,
+ 206, 207, 208, 209, 210, 211, 212, 213, 214, 215,
+ 216, 217, 218, 219, 220, 221, 222, 223, 224, 225,
+ 226, 227, 228, 229, 230, 231, 232, 233, 234, 235,
+ 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
+ 246, 247, 248, 249, 250, 251, 252, 253, 254, 255,
+ 256, 257, 258, 259, 260, 261, 262, 263, 264, 265,
+ 266, 267, 268, 269, 270, 271, 272, 273, 274, 275,
+ 276, 277, 278, 279, 280, 281, 282, 283, 284, 285,
+ 286, 287, 288, 289, 290, 291, 292, 293, 294, 295,
+ 296, 297, 298, 299, 300, 301, 302, 303, 304, 305,
+ 306, 307, 308, 309, 310, 311, 312, 313, 314, 315,
+ 316, 317, 318, 319, 320, 321, 322, 323, 324, 325,
+ 0, 0, 326, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 663, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 328, 329, 330, 331, 332, 1, 2, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
+ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
+ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
+ 115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137, 138, 139, 140, 141, 142, 143, 144,
+ 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
+ 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167, 168, 169, 170, 171, 172, 173, 174,
+ 175, 176, 177, 178, 179, 180, 181, 182, 183, 184,
+ 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
+ 195, 196, 197, 198, 199, 200, 201, 202, 203, 204,
+ 205, 206, 207, 208, 209, 210, 211, 212, 213, 214,
+ 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
+ 225, 226, 227, 228, 229, 230, 231, 232, 233, 234,
+ 235, 236, 237, 238, 239, 240, 241, 242, 243, 244,
+ 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
+ 255, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 302, 303, 304,
+ 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, 318, 319, 320, 321, 322, 323, 324,
+ 325, 0, 0, 326, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 3, 4, 5,
+ 6, 7, 0, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 0, 0, 0, 0, 0, 0,
+ 0, 328, 329, 330, 331, 332, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 69, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
+ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 118, 119, 120, 121, 122, 123, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137, 138, 139, 140, 141, 142, 143, 144,
+ 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
+ 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167, 168, 169, 170, 171, 172, 173, 174,
+ 175, 176, 177, 178, 179, 180, 181, 182, 183, 184,
+ 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
+ 195, 196, 197, 198, 199, 200, 201, 202, 203, 204,
+ 205, 206, 207, 208, 209, 210, 211, 212, 213, 214,
+ 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
+ 225, 226, 227, 228, 229, 230, 231, 232, 233, 234,
+ 235, 236, 237, 238, 239, 240, 241, 242, 243, 244,
+ 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
+ 255, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 302, 303, 304,
+ 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, 318, 319, 320, 321, 322, 323, 324,
+ 325, 0, 412, 326, 413, 414, 415, 416, 417, 418,
+ 419, 420, 421, 422, 423, 424, 0, 0, 425, 426,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 427, 0, 0,
+ 0, 516, 682, 0, 0, 0, 0, 0, 428, 429,
+ 430, 431, 3, 4, 5, 6, 7, 0, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 69,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
+ 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
+ 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
+ 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
+ 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
+ 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
+ 220, 221, 222, 223, 224, 225, 226, 227, 228, 229,
+ 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+ 240, 241, 242, 243, 244, 245, 246, 247, 248, 249,
+ 250, 251, 252, 253, 254, 255, 256, 257, 258, 259,
+ 260, 261, 262, 263, 264, 265, 266, 267, 268, 269,
+ 270, 271, 272, 273, 274, 275, 276, 277, 278, 279,
+ 280, 281, 282, 283, 284, 285, 286, 287, 288, 289,
+ 290, 291, 292, 293, 294, 295, 296, 297, 298, 299,
+ 300, 301, 302, 303, 304, 305, 306, 307, 308, 309,
+ 310, 311, 312, 313, 314, 315, 316, 317, 318, 319,
+ 320, 321, 322, 323, 324, 325, 0, 412, 326, 413,
+ 414, 415, 416, 417, 418, 419, 420, 421, 422, 423,
+ 424, 0, 0, 425, 426, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 427, 0, 0, 459, 0, 0, 0, 0,
+ 0, 0, 0, 428, 429, 430, 431, 3, 4, 5,
+ 6, 7, 0, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 69, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
+ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 118, 119, 120, 121, 122, 123, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137, 138, 139, 140, 141, 142, 143, 144,
+ 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
+ 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167, 168, 169, 170, 171, 172, 173, 174,
+ 175, 176, 177, 178, 179, 180, 181, 182, 183, 184,
+ 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
+ 195, 196, 197, 198, 199, 200, 201, 202, 203, 204,
+ 205, 206, 207, 208, 209, 210, 211, 212, 213, 214,
+ 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
+ 225, 226, 227, 228, 229, 230, 231, 232, 233, 234,
+ 235, 236, 237, 238, 239, 240, 241, 242, 243, 244,
+ 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
+ 255, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 302, 303, 304,
+ 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, 318, 319, 320, 321, 322, 323, 324,
+ 325, 0, 412, 326, 413, 414, 415, 416, 417, 418,
+ 419, 420, 421, 422, 423, 424, 0, 0, 425, 426,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 427, 0, 0,
+ 0, 516, 0, 0, 0, 0, 0, 0, 428, 429,
+ 430, 431, 3, 4, 5, 6, 7, 0, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 69,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
+ 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
+ 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
+ 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
+ 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
+ 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
+ 220, 221, 222, 223, 224, 225, 226, 227, 228, 229,
+ 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+ 240, 241, 242, 243, 244, 245, 246, 247, 248, 249,
+ 250, 251, 252, 253, 254, 255, 256, 257, 258, 259,
+ 260, 261, 262, 263, 264, 265, 266, 267, 268, 269,
+ 270, 271, 272, 273, 274, 275, 276, 277, 278, 279,
+ 280, 281, 282, 283, 284, 285, 286, 287, 288, 289,
+ 290, 291, 292, 293, 294, 295, 296, 297, 298, 299,
+ 300, 301, 302, 303, 304, 305, 306, 307, 308, 309,
+ 310, 311, 312, 313, 314, 315, 316, 317, 318, 319,
+ 320, 321, 322, 323, 324, 325, 0, 412, 326, 413,
+ 414, 415, 416, 417, 418, 419, 420, 421, 422, 423,
+ 424, 0, 0, 425, 426, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 427, 0, 0, 572, 0, 0, 0, 0,
+ 0, 0, 0, 428, 429, 430, 431, 3, 4, 5,
+ 6, 7, 0, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 69, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
+ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 118, 119, 120, 121, 122, 123, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137, 138, 139, 140, 141, 142, 143, 144,
+ 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
+ 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167, 168, 169, 170, 171, 172, 173, 174,
+ 175, 176, 177, 178, 179, 180, 181, 182, 183, 184,
+ 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
+ 195, 196, 197, 198, 199, 200, 201, 202, 203, 204,
+ 205, 206, 207, 208, 209, 210, 211, 212, 213, 214,
+ 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
+ 225, 226, 227, 228, 229, 230, 231, 232, 233, 234,
+ 235, 236, 237, 238, 239, 240, 241, 242, 243, 244,
+ 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
+ 255, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 302, 303, 304,
+ 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, 318, 319, 320, 321, 322, 323, 324,
+ 325, 0, 412, 326, 413, 414, 415, 416, 417, 418,
+ 419, 420, 421, 422, 423, 424, 0, 0, 425, 426,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 427, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 580, 428, 429,
+ 430, 431, 3, 4, 5, 6, 7, 0, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 69,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
+ 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
+ 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
+ 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
+ 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
+ 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
+ 220, 221, 222, 223, 224, 225, 226, 227, 228, 229,
+ 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+ 240, 241, 242, 243, 244, 245, 246, 247, 248, 249,
+ 250, 251, 252, 253, 254, 255, 256, 257, 258, 259,
+ 260, 261, 262, 263, 264, 265, 266, 267, 268, 269,
+ 270, 271, 272, 273, 274, 275, 276, 277, 278, 279,
+ 280, 281, 282, 283, 284, 285, 286, 287, 288, 289,
+ 290, 291, 292, 293, 294, 295, 296, 297, 298, 299,
+ 300, 301, 302, 303, 304, 305, 306, 307, 308, 309,
+ 310, 311, 312, 313, 314, 315, 316, 317, 318, 319,
+ 320, 321, 322, 323, 324, 325, 0, 412, 326, 413,
+ 414, 415, 416, 417, 418, 419, 420, 421, 422, 423,
+ 424, 0, 0, 425, 426, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 427, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 428, 429, 430, 431, 3, 4, 5,
+ 6, 7, 0, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 69, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
+ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 118, 119, 120, 121, 122, 123, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137, 138, 139, 140, 141, 142, 143, 144,
+ 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
+ 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167, 168, 169, 170, 171, 172, 173, 174,
+ 175, 176, 177, 178, 179, 180, 181, 182, 183, 184,
+ 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
+ 195, 196, 197, 198, 199, 200, 201, 202, 203, 204,
+ 205, 206, 207, 208, 209, 210, 211, 212, 213, 214,
+ 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
+ 225, 226, 227, 228, 229, 230, 231, 232, 233, 234,
+ 235, 236, 237, 238, 239, 240, 241, 242, 243, 244,
+ 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
+ 255, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 302, 303, 304,
+ 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, 318, 319, 320, 321, 322, 323, 324,
+ 534, 0, 412, 326, 413, 414, 415, 416, 417, 418,
+ 419, 420, 421, 422, 423, 424, 0, 0, 425, 426,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 427, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 428, 429,
+ 430, 431, 3, 4, 5, 6, 7, 0, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
+ 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
+ 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
+ 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
+ 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
+ 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
+ 220, 221, 222, 223, 224, 225, 226, 227, 228, 229,
+ 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+ 240, 241, 242, 243, 244, 245, 246, 247, 248, 249,
+ 250, 251, 252, 253, 254, 255, 256, 257, 258, 259,
+ 260, 261, 262, 263, 264, 265, 266, 267, 268, 269,
+ 270, 271, 272, 273, 274, 275, 276, 277, 278, 279,
+ 280, 281, 282, 283, 284, 285, 286, 287, 288, 289,
+ 290, 291, 292, 293, 294, 295, 296, 297, 298, 299,
+ 300, 301, 302, 303, 304, 305, 306, 307, 308, 309,
+ 310, 311, 312, 313, 314, 315, 316, 317, 318, 319,
+ 320, 321, 322, 323, 324, 325, 0, 0, 326
+};
+
+static const yytype_int16 yycheck[] =
+{
+ 0, 0, 0, 339, 347, 24, 0, 26, 27, 395,
+ 81, 30, 427, 514, 502, 516, 406, 381, 519, 456,
+ 588, 340, 453, 358, 359, 653, 340, 382, 377, 372,
+ 363, 395, 368, 691, 385, 376, 385, 695, 375, 394,
+ 377, 382, 376, 671, 375, 703, 376, 384, 382, 377,
+ 440, 375, 382, 356, 357, 377, 384, 393, 393, 394,
+ 379, 425, 426, 385, 450, 398, 481, 376, 483, 403,
+ 404, 385, 375, 377, 377, 377, 382, 376, 381, 443,
+ 384, 378, 384, 382, 376, 382, 450, 473, 576, 520,
+ 382, 365, 366, 367, 368, 369, 370, 371, 372, 373,
+ 374, 558, 559, 560, 561, 354, 355, 544, 609, 473,
+ 384, 454, 379, 376, 529, 382, 376, 453, 385, 382,
+ 456, 376, 382, 340, 514, 382, 516, 382, 385, 519,
+ 382, 382, 382, 385, 385, 385, 340, 382, 382, 707,
+ 385, 385, 380, 533, 382, 401, 402, 403, 390, 391,
+ 392, 387, 393, 389, 360, 361, 382, 383, 382, 383,
+ 379, 662, 340, 578, 379, 554, 555, 582, 340, 657,
+ 377, 556, 557, 384, 538, 385, 613, 340, 562, 563,
+ 376, 375, 377, 340, 520, 340, 395, 551, 552, 553,
+ 554, 555, 556, 557, 558, 559, 560, 561, 562, 563,
+ 564, 565, 566, 567, 568, 569, 397, 708, 544, 396,
+ 600, 601, 546, 362, 364, 385, 378, 385, 385, 609,
+ 380, 375, 375, 375, 383, 340, 377, 375, 375, 717,
+ 645, 375, 340, 376, 340, 339, 378, 378, 653, 375,
+ 375, 379, 382, 340, 376, 378, 380, 376, 385, 25,
+ 376, 379, 379, 384, 564, 385, 671, 380, 565, 567,
+ 678, 566, 372, 368, 452, 372, 333, 568, 545, 340,
+ 685, 569, 662, 648, 624, 703, 690, 613, 704, 366,
+ 648, 512, 512, 671, 512, 355, 680, 702, -1, -1,
+ -1, -1, 678, -1, 684, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 678, -1, -1, 653, 708, -1,
+ 339, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 676, -1, 671, -1, -1, -1, 339,
+ -1, -1, -1, -1, -1, 339, -1, 347, -1, -1,
+ -1, -1, -1, 347, -1, 355, 355, 355, -1, -1,
+ -1, 355, 362, -1, -1, -1, -1, -1, 368, -1,
+ -1, -1, 372, -1, 368, -1, -1, -1, 372, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 393, -1, -1, -1, -1, 398, 393,
+ -1, -1, -1, -1, 398, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 453, 454, -1, 456, -1, -1, 453,
+ 454, -1, 456, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 502, -1, -1, -1, -1, -1, 502, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 520, -1, -1, -1, -1, -1, 520, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 544, -1, -1, -1, -1, -1,
+ 544, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 576, -1, -1, -1,
+ -1, -1, 576, -1, -1, -1, -1, -1, 588, -1,
+ -1, -1, -1, -1, 588, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 613, -1, -1, -1, -1, -1, 613,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 648, -1,
+ -1, -1, -1, 653, 648, -1, -1, 657, -1, 653,
+ -1, -1, -1, 657, -1, -1, -1, -1, -1, -1,
+ -1, 671, -1, -1, -1, -1, 676, 671, -1, -1,
+ -1, -1, 676, -1, -1, -1, -1, -1, -1, -1,
+ -1, 691, -1, -1, -1, 695, -1, 691, -1, -1,
+ -1, 695, -1, 703, -1, -1, -1, 707, -1, 703,
+ -1, -1, -1, 707, -1, -1, -1, 717, -1, -1,
+ -1, 0, -1, 717, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
+ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
+ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
+ 99, 100, 101, 102, 103, 104, 105, 106, 107, 108,
+ 109, 110, 111, 112, 113, 114, 115, 116, 117, 118,
+ 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
+ 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
+ 139, 140, 141, 142, 143, 144, 145, 146, 147, 148,
+ 149, 150, 151, 152, 153, 154, 155, 156, 157, 158,
+ 159, 160, 161, 162, 163, 164, 165, 166, 167, 168,
+ 169, 170, 171, 172, 173, 174, 175, 176, 177, 178,
+ 179, 180, 181, 182, 183, 184, 185, 186, 187, 188,
+ 189, 190, 191, 192, 193, 194, 195, 196, 197, 198,
+ 199, 200, 201, 202, 203, 204, 205, 206, 207, 208,
+ 209, 210, 211, 212, 213, 214, 215, 216, 217, 218,
+ 219, 220, 221, 222, 223, 224, 225, 226, 227, 228,
+ 229, 230, 231, 232, 233, 234, 235, 236, 237, 238,
+ 239, 240, 241, 242, 243, 244, 245, 246, 247, 248,
+ 249, 250, 251, 252, 253, 254, 255, 256, 257, 258,
+ 259, 260, 261, 262, 263, 264, 265, 266, 267, 268,
+ 269, 270, 271, 272, 273, 274, 275, 276, 277, 278,
+ 279, 280, 281, 282, 283, 284, 285, 286, 287, 288,
+ 289, 290, 291, 292, 293, 294, 295, 296, 297, 298,
+ 299, 300, 301, 302, 303, 304, 305, 306, 307, 308,
+ 309, 310, 311, 312, 313, 314, 315, 316, 317, 318,
+ 319, 320, 321, 322, 323, 324, 325, 326, 327, 328,
+ 329, 330, 331, 332, 333, 334, 335, 336, 337, 338,
+ -1, -1, 341, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 385, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 399, 400, 401, 402, 403, 404, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, -1, 26,
+ 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ 57, 58, 59, 60, 61, 62, 63, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
+ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
+ 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
+ 117, 118, 119, 120, 121, 122, 123, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, 134, 135, 136,
+ 137, 138, 139, 140, 141, 142, 143, 144, 145, 146,
+ 147, 148, 149, 150, 151, 152, 153, 154, 155, 156,
+ 157, 158, 159, 160, 161, 162, 163, 164, 165, 166,
+ 167, 168, 169, 170, 171, 172, 173, 174, 175, 176,
+ 177, 178, 179, 180, 181, 182, 183, 184, 185, 186,
+ 187, 188, 189, 190, 191, 192, 193, 194, 195, 196,
+ 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
+ 207, 208, 209, 210, 211, 212, 213, 214, 215, 216,
+ 217, 218, 219, 220, 221, 222, 223, 224, 225, 226,
+ 227, 228, 229, 230, 231, 232, 233, 234, 235, 236,
+ 237, 238, 239, 240, 241, 242, 243, 244, 245, 246,
+ 247, 248, 249, 250, 251, 252, 253, 254, 255, 256,
+ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266,
+ 267, 268, 269, 270, 271, 272, 273, 274, 275, 276,
+ 277, 278, 279, 280, 281, 282, 283, 284, 285, 286,
+ 287, 288, 289, 290, 291, 292, 293, 294, 295, 296,
+ 297, 298, 299, 300, 301, 302, 303, 304, 305, 306,
+ 307, 308, 309, 310, 311, 312, 313, 314, 315, 316,
+ 317, 318, 319, 320, 321, 322, 323, 324, 325, 326,
+ 327, 328, 329, 330, 331, 332, 333, 334, 335, 336,
+ 337, 338, 339, 340, 341, 342, 343, 344, 345, 346,
+ 347, 348, 349, 350, 351, 352, 353, -1, -1, 356,
+ 357, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 375, -1,
+ 377, -1, 379, 380, -1, -1, -1, -1, 385, 386,
+ 387, 388, 389, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 399, 400, 401, 402, 403, 404, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
+ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
+ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
+ 115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137, 138, 139, 140, 141, 142, 143, 144,
+ 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
+ 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167, 168, 169, 170, 171, 172, 173, 174,
+ 175, 176, 177, 178, 179, 180, 181, 182, 183, 184,
+ 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
+ 195, 196, 197, 198, 199, 200, 201, 202, 203, 204,
+ 205, 206, 207, 208, 209, 210, 211, 212, 213, 214,
+ 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
+ 225, 226, 227, 228, 229, 230, 231, 232, 233, 234,
+ 235, 236, 237, 238, 239, 240, 241, 242, 243, 244,
+ 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
+ 255, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 302, 303, 304,
+ 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, 318, 319, 320, 321, 322, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, 334,
+ 335, 336, 337, 338, 339, 340, 341, 342, 343, 344,
+ 345, 346, 347, 348, 349, 350, 351, 352, 353, -1,
+ -1, 356, 357, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 375, -1, 377, -1, 379, 380, -1, -1, -1, -1,
+ 385, 386, 387, 388, 389, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 399, 400, 401, 402, 403, 404,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, -1, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
+ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
+ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
+ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120, 121, 122,
+ 123, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
+ 143, 144, 145, 146, 147, 148, 149, 150, 151, 152,
+ 153, 154, 155, 156, 157, 158, 159, 160, 161, 162,
+ 163, 164, 165, 166, 167, 168, 169, 170, 171, 172,
+ 173, 174, 175, 176, 177, 178, 179, 180, 181, 182,
+ 183, 184, 185, 186, 187, 188, 189, 190, 191, 192,
+ 193, 194, 195, 196, 197, 198, 199, 200, 201, 202,
+ 203, 204, 205, 206, 207, 208, 209, 210, 211, 212,
+ 213, 214, 215, 216, 217, 218, 219, 220, 221, 222,
+ 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
+ 233, 234, 235, 236, 237, 238, 239, 240, 241, 242,
+ 243, 244, 245, 246, 247, 248, 249, 250, 251, 252,
+ 253, 254, 255, 256, 257, 258, 259, 260, 261, 262,
+ 263, 264, 265, 266, 267, 268, 269, 270, 271, 272,
+ 273, 274, 275, 276, 277, 278, 279, 280, 281, 282,
+ 283, 284, 285, 286, 287, 288, 289, 290, 291, 292,
+ 293, 294, 295, 296, 297, 298, 299, 300, 301, 302,
+ 303, 304, 305, 306, 307, 308, 309, 310, 311, 312,
+ 313, 314, 315, 316, 317, 318, 319, 320, 321, 322,
+ 323, 324, 325, 326, 327, 328, 329, 330, 331, 332,
+ 333, 334, 335, 336, 337, 338, 339, 340, 341, 342,
+ 343, 344, 345, 346, 347, 348, 349, 350, 351, 352,
+ 353, -1, -1, 356, 357, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 375, -1, 377, -1, 379, -1, -1, -1,
+ -1, -1, 385, 386, 387, 388, 389, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 399, 400, 401, 402,
+ 403, 404, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, -1, 26, 27, 28, 29, 30,
+ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
+ 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
+ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
+ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
+ 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,
+ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
+ 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126, 127, 128, 129, 130,
+ 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
+ 141, 142, 143, 144, 145, 146, 147, 148, 149, 150,
+ 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
+ 161, 162, 163, 164, 165, 166, 167, 168, 169, 170,
+ 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
+ 181, 182, 183, 184, 185, 186, 187, 188, 189, 190,
+ 191, 192, 193, 194, 195, 196, 197, 198, 199, 200,
+ 201, 202, 203, 204, 205, 206, 207, 208, 209, 210,
+ 211, 212, 213, 214, 215, 216, 217, 218, 219, 220,
+ 221, 222, 223, 224, 225, 226, 227, 228, 229, 230,
+ 231, 232, 233, 234, 235, 236, 237, 238, 239, 240,
+ 241, 242, 243, 244, 245, 246, 247, 248, 249, 250,
+ 251, 252, 253, 254, 255, 256, 257, 258, 259, 260,
+ 261, 262, 263, 264, 265, 266, 267, 268, 269, 270,
+ 271, 272, 273, 274, 275, 276, 277, 278, 279, 280,
+ 281, 282, 283, 284, 285, 286, 287, 288, 289, 290,
+ 291, 292, 293, 294, 295, 296, 297, 298, 299, 300,
+ 301, 302, 303, 304, 305, 306, 307, 308, 309, 310,
+ 311, 312, 313, 314, 315, 316, 317, 318, 319, 320,
+ 321, 322, 323, 324, 325, 326, 327, 328, 329, 330,
+ 331, 332, 333, 334, 335, 336, 337, 338, 339, 340,
+ 341, 342, 343, 344, 345, 346, 347, 348, 349, 350,
+ 351, 352, 353, -1, -1, 356, 357, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 375, -1, 377, -1, 379, -1,
+ -1, -1, -1, -1, 385, 386, 387, 388, 389, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 399, 400,
+ 401, 402, 403, 404, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, -1, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
+ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
+ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
+ 99, 100, 101, 102, 103, 104, 105, 106, 107, 108,
+ 109, 110, 111, 112, 113, 114, 115, 116, 117, 118,
+ 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
+ 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
+ 139, 140, 141, 142, 143, 144, 145, 146, 147, 148,
+ 149, 150, 151, 152, 153, 154, 155, 156, 157, 158,
+ 159, 160, 161, 162, 163, 164, 165, 166, 167, 168,
+ 169, 170, 171, 172, 173, 174, 175, 176, 177, 178,
+ 179, 180, 181, 182, 183, 184, 185, 186, 187, 188,
+ 189, 190, 191, 192, 193, 194, 195, 196, 197, 198,
+ 199, 200, 201, 202, 203, 204, 205, 206, 207, 208,
+ 209, 210, 211, 212, 213, 214, 215, 216, 217, 218,
+ 219, 220, 221, 222, 223, 224, 225, 226, 227, 228,
+ 229, 230, 231, 232, 233, 234, 235, 236, 237, 238,
+ 239, 240, 241, 242, 243, 244, 245, 246, 247, 248,
+ 249, 250, 251, 252, 253, 254, 255, 256, 257, 258,
+ 259, 260, 261, 262, 263, 264, 265, 266, 267, 268,
+ 269, 270, 271, 272, 273, 274, 275, 276, 277, 278,
+ 279, 280, 281, 282, 283, 284, 285, 286, 287, 288,
+ 289, 290, 291, 292, 293, 294, 295, 296, 297, 298,
+ 299, 300, 301, 302, 303, 304, 305, 306, 307, 308,
+ 309, 310, 311, 312, 313, 314, 315, 316, 317, 318,
+ 319, 320, 321, 322, 323, 324, 325, 326, 327, 328,
+ 329, 330, 331, 332, 333, 334, 335, 336, 337, 338,
+ 339, 340, 341, 342, 343, 344, 345, 346, 347, 348,
+ 349, 350, 351, 352, 353, -1, -1, 356, 357, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 375, -1, 377, -1,
+ -1, -1, -1, -1, -1, -1, 385, 386, 387, 388,
+ 389, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 399, 400, 401, 402, 403, 404, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ 57, 58, 59, 60, 61, 62, 63, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
+ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
+ 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
+ 117, 118, 119, 120, 121, 122, 123, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, 134, 135, 136,
+ 137, 138, 139, 140, 141, 142, 143, 144, 145, 146,
+ 147, 148, 149, 150, 151, 152, 153, 154, 155, 156,
+ 157, 158, 159, 160, 161, 162, 163, 164, 165, 166,
+ 167, 168, 169, 170, 171, 172, 173, 174, 175, 176,
+ 177, 178, 179, 180, 181, 182, 183, 184, 185, 186,
+ 187, 188, 189, 190, 191, 192, 193, 194, 195, 196,
+ 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
+ 207, 208, 209, 210, 211, 212, 213, 214, 215, 216,
+ 217, 218, 219, 220, 221, 222, 223, 224, 225, 226,
+ 227, 228, 229, 230, 231, 232, 233, 234, 235, 236,
+ 237, 238, 239, 240, 241, 242, 243, 244, 245, 246,
+ 247, 248, 249, 250, 251, 252, 253, 254, 255, 256,
+ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266,
+ 267, 268, 269, 270, 271, 272, 273, 274, 275, 276,
+ 277, 278, 279, 280, 281, 282, 283, 284, 285, 286,
+ 287, 288, 289, 290, 291, 292, 293, 294, 295, 296,
+ 297, 298, 299, 300, 301, 302, 303, 304, 305, 306,
+ 307, 308, 309, 310, 311, 312, 313, 314, 315, 316,
+ 317, 318, 319, 320, 321, 322, 323, 324, 325, 326,
+ 327, 328, 329, 330, 331, 332, 333, 334, 335, 336,
+ 337, 338, -1, 340, 341, 342, 343, 344, 345, 346,
+ 347, 348, 349, 350, 351, 352, 353, -1, -1, 356,
+ 357, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 375, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 385, 386,
+ 387, 388, 389, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 399, 400, 401, 402, 403, 404, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
+ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
+ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
+ 115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137, 138, 139, 140, 141, 142, 143, 144,
+ 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
+ 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167, 168, 169, 170, 171, 172, 173, 174,
+ 175, 176, 177, 178, 179, 180, 181, 182, 183, 184,
+ 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
+ 195, 196, 197, 198, 199, 200, 201, 202, 203, 204,
+ 205, 206, 207, 208, 209, 210, 211, 212, 213, 214,
+ 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
+ 225, 226, 227, 228, 229, 230, 231, 232, 233, 234,
+ 235, 236, 237, 238, 239, 240, 241, 242, 243, 244,
+ 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
+ 255, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 302, 303, 304,
+ 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, 318, 319, 320, 321, 322, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, 334,
+ 335, 336, 337, 338, -1, -1, 341, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 385, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 399, 400, 401, 402, 403, 404,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
+ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
+ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
+ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120, 121, 122,
+ 123, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
+ 143, 144, 145, 146, 147, 148, 149, 150, 151, 152,
+ 153, 154, 155, 156, 157, 158, 159, 160, 161, 162,
+ 163, 164, 165, 166, 167, 168, 169, 170, 171, 172,
+ 173, 174, 175, 176, 177, 178, 179, 180, 181, 182,
+ 183, 184, 185, 186, 187, 188, 189, 190, 191, 192,
+ 193, 194, 195, 196, 197, 198, 199, 200, 201, 202,
+ 203, 204, 205, 206, 207, 208, 209, 210, 211, 212,
+ 213, 214, 215, 216, 217, 218, 219, 220, 221, 222,
+ 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
+ 233, 234, 235, 236, 237, 238, 239, 240, 241, 242,
+ 243, 244, 245, 246, 247, 248, 249, 250, 251, 252,
+ 253, 254, 255, 256, 257, 258, 259, 260, 261, 262,
+ 263, 264, 265, 266, 267, 268, 269, 270, 271, 272,
+ 273, 274, 275, 276, 277, 278, 279, 280, 281, 282,
+ 283, 284, 285, 286, 287, 288, 289, 290, 291, 292,
+ 293, 294, 295, 296, 297, 298, 299, 300, 301, 302,
+ 303, 304, 305, 306, 307, 308, 309, 310, 311, 312,
+ 313, 314, 315, 316, 317, 318, 319, 320, 321, 322,
+ 323, 324, 325, 326, 327, 328, 329, 330, 331, 332,
+ 333, 334, 335, 336, 337, 338, -1, 340, 341, 342,
+ 343, 344, 345, 346, 347, 348, 349, 350, 351, 352,
+ 353, -1, -1, 356, 357, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 375, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 386, 387, 388, 389, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 399, 400, 401, 402,
+ 403, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 33, 34, 35, 36, 37, 38, 39, 40, 41,
+ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
+ 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
+ 92, 93, 94, 95, 96, 97, 98, 99, 100, 101,
+ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
+ 122, 123, 124, 125, 126, 127, 128, 129, 130, 131,
+ 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
+ 142, 143, 144, 145, 146, 147, 148, 149, 150, 151,
+ 152, 153, 154, 155, 156, 157, 158, 159, 160, 161,
+ 162, 163, 164, 165, 166, 167, 168, 169, 170, 171,
+ 172, 173, 174, 175, 176, 177, 178, 179, 180, 181,
+ 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
+ 192, 193, 194, 195, 196, 197, 198, 199, 200, 201,
+ 202, 203, 204, 205, 206, 207, 208, 209, 210, 211,
+ 212, 213, 214, 215, 216, 217, 218, 219, 220, 221,
+ 222, 223, 224, 225, 226, 227, 228, 229, 230, 231,
+ 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
+ 242, 243, 244, 245, 246, 247, 248, 249, 250, 251,
+ 252, 253, 254, 255, 256, 257, 258, 259, 260, 261,
+ 262, 263, 264, 265, 266, 267, 268, 269, 270, 271,
+ 272, 273, 274, 275, 276, 277, 278, 279, 280, 281,
+ 282, 283, 284, 285, 286, 287, 288, 289, 290, 291,
+ 292, 293, 294, 295, 296, 297, 298, 299, 300, 301,
+ 302, 303, 304, 305, 306, 307, 308, 309, 310, 311,
+ 312, 313, 314, 315, 316, 317, 318, 319, 320, 321,
+ 322, 323, 324, 325, 326, 327, 328, 329, 330, 331,
+ 332, 333, 334, 335, 336, 337, 338, -1, 340, 341,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 385, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 399, 400, 401,
+ 402, 403, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
+ 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
+ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
+ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
+ 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,
+ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
+ 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126, 127, 128, 129, 130,
+ 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
+ 141, 142, 143, 144, 145, 146, 147, 148, 149, 150,
+ 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
+ 161, 162, 163, 164, 165, 166, 167, 168, 169, 170,
+ 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
+ 181, 182, 183, 184, 185, 186, 187, 188, 189, 190,
+ 191, 192, 193, 194, 195, 196, 197, 198, 199, 200,
+ 201, 202, 203, 204, 205, 206, 207, 208, 209, 210,
+ 211, 212, 213, 214, 215, 216, 217, 218, 219, 220,
+ 221, 222, 223, 224, 225, 226, 227, 228, 229, 230,
+ 231, 232, 233, 234, 235, 236, 237, 238, 239, 240,
+ 241, 242, 243, 244, 245, 246, 247, 248, 249, 250,
+ 251, 252, 253, 254, 255, 256, 257, 258, 259, 260,
+ 261, 262, 263, 264, 265, 266, 267, 268, 269, 270,
+ 271, 272, 273, 274, 275, 276, 277, 278, 279, 280,
+ 281, 282, 283, 284, 285, 286, 287, 288, 289, 290,
+ 291, 292, 293, 294, 295, 296, 297, 298, 299, 300,
+ 301, 302, 303, 304, 305, 306, 307, 308, 309, 310,
+ 311, 312, 313, 314, 315, 316, 317, 318, 319, 320,
+ 321, 322, 323, 324, 325, 326, 327, 328, 329, 330,
+ 331, 332, 333, 334, 335, 336, 337, 338, -1, -1,
+ 341, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 380,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 399, 400,
+ 401, 402, 403, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
+ 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
+ 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
+ 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
+ 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
+ 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
+ 220, 221, 222, 223, 224, 225, 226, 227, 228, 229,
+ 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+ 240, 241, 242, 243, 244, 245, 246, 247, 248, 249,
+ 250, 251, 252, 253, 254, 255, 256, 257, 258, 259,
+ 260, 261, 262, 263, 264, 265, 266, 267, 268, 269,
+ 270, 271, 272, 273, 274, 275, 276, 277, 278, 279,
+ 280, 281, 282, 283, 284, 285, 286, 287, 288, 289,
+ 290, 291, 292, 293, 294, 295, 296, 297, 298, 299,
+ 300, 301, 302, 303, 304, 305, 306, 307, 308, 309,
+ 310, 311, 312, 313, 314, 315, 316, 317, 318, 319,
+ 320, 321, 322, 323, 324, 325, 326, 327, 328, 329,
+ 330, 331, 332, 333, 334, 335, 336, 337, 338, -1,
+ -1, 341, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 380, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 399,
+ 400, 401, 402, 403, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
+ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
+ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
+ 99, 100, 101, 102, 103, 104, 105, 106, 107, 108,
+ 109, 110, 111, 112, 113, 114, 115, 116, 117, 118,
+ 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
+ 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
+ 139, 140, 141, 142, 143, 144, 145, 146, 147, 148,
+ 149, 150, 151, 152, 153, 154, 155, 156, 157, 158,
+ 159, 160, 161, 162, 163, 164, 165, 166, 167, 168,
+ 169, 170, 171, 172, 173, 174, 175, 176, 177, 178,
+ 179, 180, 181, 182, 183, 184, 185, 186, 187, 188,
+ 189, 190, 191, 192, 193, 194, 195, 196, 197, 198,
+ 199, 200, 201, 202, 203, 204, 205, 206, 207, 208,
+ 209, 210, 211, 212, 213, 214, 215, 216, 217, 218,
+ 219, 220, 221, 222, 223, 224, 225, 226, 227, 228,
+ 229, 230, 231, 232, 233, 234, 235, 236, 237, 238,
+ 239, 240, 241, 242, 243, 244, 245, 246, 247, 248,
+ 249, 250, 251, 252, 253, 254, 255, 256, 257, 258,
+ 259, 260, 261, 262, 263, 264, 265, 266, 267, 268,
+ 269, 270, 271, 272, 273, 274, 275, 276, 277, 278,
+ 279, 280, 281, 282, 283, 284, 285, 286, 287, 288,
+ 289, 290, 291, 292, 293, 294, 295, 296, 297, 298,
+ 299, 300, 301, 302, 303, 304, 305, 306, 307, 308,
+ 309, 310, 311, 312, 313, 314, 315, 316, 317, 318,
+ 319, 320, 321, 322, 323, 324, 325, 326, 327, 328,
+ 329, 330, 331, 332, 333, 334, 335, 336, 337, 338,
+ -1, -1, 341, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 380, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 399, 400, 401, 402, 403, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 33, 34, 35, 36, 37,
+ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
+ 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
+ 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
+ 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135, 136, 137,
+ 138, 139, 140, 141, 142, 143, 144, 145, 146, 147,
+ 148, 149, 150, 151, 152, 153, 154, 155, 156, 157,
+ 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
+ 168, 169, 170, 171, 172, 173, 174, 175, 176, 177,
+ 178, 179, 180, 181, 182, 183, 184, 185, 186, 187,
+ 188, 189, 190, 191, 192, 193, 194, 195, 196, 197,
+ 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
+ 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
+ 218, 219, 220, 221, 222, 223, 224, 225, 226, 227,
+ 228, 229, 230, 231, 232, 233, 234, 235, 236, 237,
+ 238, 239, 240, 241, 242, 243, 244, 245, 246, 247,
+ 248, 249, 250, 251, 252, 253, 254, 255, 256, 257,
+ 258, 259, 260, 261, 262, 263, 264, 265, 266, 267,
+ 268, 269, 270, 271, 272, 273, 274, 275, 276, 277,
+ 278, 279, 280, 281, 282, 283, 284, 285, 286, 287,
+ 288, 289, 290, 291, 292, 293, 294, 295, 296, 297,
+ 298, 299, 300, 301, 302, 303, 304, 305, 306, 307,
+ 308, 309, 310, 311, 312, 313, 314, 315, 316, 317,
+ 318, 319, 320, 321, 322, 323, 324, 325, 326, 327,
+ 328, 329, 330, 331, 332, 333, 334, 335, 336, 337,
+ 338, -1, -1, 341, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 5, 6, 7,
+ 8, 9, -1, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, -1, -1, -1, -1, -1, -1,
+ -1, 399, 400, 401, 402, 403, 34, 35, 36, 37,
+ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 82, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
+ 118, 119, 120, 121, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 131, 132, 133, 134, 135, 136, 137,
+ 138, 139, 140, 141, 142, 143, 144, 145, 146, 147,
+ 148, 149, 150, 151, 152, 153, 154, 155, 156, 157,
+ 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
+ 168, 169, 170, 171, 172, 173, 174, 175, 176, 177,
+ 178, 179, 180, 181, 182, 183, 184, 185, 186, 187,
+ 188, 189, 190, 191, 192, 193, 194, 195, 196, 197,
+ 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
+ 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
+ 218, 219, 220, 221, 222, 223, 224, 225, 226, 227,
+ 228, 229, 230, 231, 232, 233, 234, 235, 236, 237,
+ 238, 239, 240, 241, 242, 243, 244, 245, 246, 247,
+ 248, 249, 250, 251, 252, 253, 254, 255, 256, 257,
+ 258, 259, 260, 261, 262, 263, 264, 265, 266, 267,
+ 268, 269, 270, 271, 272, 273, 274, 275, 276, 277,
+ 278, 279, 280, 281, 282, 283, 284, 285, 286, 287,
+ 288, 289, 290, 291, 292, 293, 294, 295, 296, 297,
+ 298, 299, 300, 301, 302, 303, 304, 305, 306, 307,
+ 308, 309, 310, 311, 312, 313, 314, 315, 316, 317,
+ 318, 319, 320, 321, 322, 323, 324, 325, 326, 327,
+ 328, 329, 330, 331, 332, 333, 334, 335, 336, 337,
+ 338, -1, 340, 341, 342, 343, 344, 345, 346, 347,
+ 348, 349, 350, 351, 352, 353, -1, -1, 356, 357,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 375, -1, -1,
+ -1, 379, 380, -1, -1, -1, -1, -1, 386, 387,
+ 388, 389, 5, 6, 7, 8, 9, -1, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 82,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 98, 99, 100, 101, 102,
+ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120, 121, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 131, 132,
+ 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
+ 143, 144, 145, 146, 147, 148, 149, 150, 151, 152,
+ 153, 154, 155, 156, 157, 158, 159, 160, 161, 162,
+ 163, 164, 165, 166, 167, 168, 169, 170, 171, 172,
+ 173, 174, 175, 176, 177, 178, 179, 180, 181, 182,
+ 183, 184, 185, 186, 187, 188, 189, 190, 191, 192,
+ 193, 194, 195, 196, 197, 198, 199, 200, 201, 202,
+ 203, 204, 205, 206, 207, 208, 209, 210, 211, 212,
+ 213, 214, 215, 216, 217, 218, 219, 220, 221, 222,
+ 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
+ 233, 234, 235, 236, 237, 238, 239, 240, 241, 242,
+ 243, 244, 245, 246, 247, 248, 249, 250, 251, 252,
+ 253, 254, 255, 256, 257, 258, 259, 260, 261, 262,
+ 263, 264, 265, 266, 267, 268, 269, 270, 271, 272,
+ 273, 274, 275, 276, 277, 278, 279, 280, 281, 282,
+ 283, 284, 285, 286, 287, 288, 289, 290, 291, 292,
+ 293, 294, 295, 296, 297, 298, 299, 300, 301, 302,
+ 303, 304, 305, 306, 307, 308, 309, 310, 311, 312,
+ 313, 314, 315, 316, 317, 318, 319, 320, 321, 322,
+ 323, 324, 325, 326, 327, 328, 329, 330, 331, 332,
+ 333, 334, 335, 336, 337, 338, -1, 340, 341, 342,
+ 343, 344, 345, 346, 347, 348, 349, 350, 351, 352,
+ 353, -1, -1, 356, 357, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 375, -1, -1, 378, -1, -1, -1, -1,
+ -1, -1, -1, 386, 387, 388, 389, 5, 6, 7,
+ 8, 9, -1, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 34, 35, 36, 37,
+ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 82, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
+ 118, 119, 120, 121, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 131, 132, 133, 134, 135, 136, 137,
+ 138, 139, 140, 141, 142, 143, 144, 145, 146, 147,
+ 148, 149, 150, 151, 152, 153, 154, 155, 156, 157,
+ 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
+ 168, 169, 170, 171, 172, 173, 174, 175, 176, 177,
+ 178, 179, 180, 181, 182, 183, 184, 185, 186, 187,
+ 188, 189, 190, 191, 192, 193, 194, 195, 196, 197,
+ 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
+ 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
+ 218, 219, 220, 221, 222, 223, 224, 225, 226, 227,
+ 228, 229, 230, 231, 232, 233, 234, 235, 236, 237,
+ 238, 239, 240, 241, 242, 243, 244, 245, 246, 247,
+ 248, 249, 250, 251, 252, 253, 254, 255, 256, 257,
+ 258, 259, 260, 261, 262, 263, 264, 265, 266, 267,
+ 268, 269, 270, 271, 272, 273, 274, 275, 276, 277,
+ 278, 279, 280, 281, 282, 283, 284, 285, 286, 287,
+ 288, 289, 290, 291, 292, 293, 294, 295, 296, 297,
+ 298, 299, 300, 301, 302, 303, 304, 305, 306, 307,
+ 308, 309, 310, 311, 312, 313, 314, 315, 316, 317,
+ 318, 319, 320, 321, 322, 323, 324, 325, 326, 327,
+ 328, 329, 330, 331, 332, 333, 334, 335, 336, 337,
+ 338, -1, 340, 341, 342, 343, 344, 345, 346, 347,
+ 348, 349, 350, 351, 352, 353, -1, -1, 356, 357,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 375, -1, -1,
+ -1, 379, -1, -1, -1, -1, -1, -1, 386, 387,
+ 388, 389, 5, 6, 7, 8, 9, -1, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 82,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 98, 99, 100, 101, 102,
+ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120, 121, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 131, 132,
+ 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
+ 143, 144, 145, 146, 147, 148, 149, 150, 151, 152,
+ 153, 154, 155, 156, 157, 158, 159, 160, 161, 162,
+ 163, 164, 165, 166, 167, 168, 169, 170, 171, 172,
+ 173, 174, 175, 176, 177, 178, 179, 180, 181, 182,
+ 183, 184, 185, 186, 187, 188, 189, 190, 191, 192,
+ 193, 194, 195, 196, 197, 198, 199, 200, 201, 202,
+ 203, 204, 205, 206, 207, 208, 209, 210, 211, 212,
+ 213, 214, 215, 216, 217, 218, 219, 220, 221, 222,
+ 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
+ 233, 234, 235, 236, 237, 238, 239, 240, 241, 242,
+ 243, 244, 245, 246, 247, 248, 249, 250, 251, 252,
+ 253, 254, 255, 256, 257, 258, 259, 260, 261, 262,
+ 263, 264, 265, 266, 267, 268, 269, 270, 271, 272,
+ 273, 274, 275, 276, 277, 278, 279, 280, 281, 282,
+ 283, 284, 285, 286, 287, 288, 289, 290, 291, 292,
+ 293, 294, 295, 296, 297, 298, 299, 300, 301, 302,
+ 303, 304, 305, 306, 307, 308, 309, 310, 311, 312,
+ 313, 314, 315, 316, 317, 318, 319, 320, 321, 322,
+ 323, 324, 325, 326, 327, 328, 329, 330, 331, 332,
+ 333, 334, 335, 336, 337, 338, -1, 340, 341, 342,
+ 343, 344, 345, 346, 347, 348, 349, 350, 351, 352,
+ 353, -1, -1, 356, 357, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 375, -1, -1, 378, -1, -1, -1, -1,
+ -1, -1, -1, 386, 387, 388, 389, 5, 6, 7,
+ 8, 9, -1, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 34, 35, 36, 37,
+ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 82, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
+ 118, 119, 120, 121, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 131, 132, 133, 134, 135, 136, 137,
+ 138, 139, 140, 141, 142, 143, 144, 145, 146, 147,
+ 148, 149, 150, 151, 152, 153, 154, 155, 156, 157,
+ 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
+ 168, 169, 170, 171, 172, 173, 174, 175, 176, 177,
+ 178, 179, 180, 181, 182, 183, 184, 185, 186, 187,
+ 188, 189, 190, 191, 192, 193, 194, 195, 196, 197,
+ 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
+ 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
+ 218, 219, 220, 221, 222, 223, 224, 225, 226, 227,
+ 228, 229, 230, 231, 232, 233, 234, 235, 236, 237,
+ 238, 239, 240, 241, 242, 243, 244, 245, 246, 247,
+ 248, 249, 250, 251, 252, 253, 254, 255, 256, 257,
+ 258, 259, 260, 261, 262, 263, 264, 265, 266, 267,
+ 268, 269, 270, 271, 272, 273, 274, 275, 276, 277,
+ 278, 279, 280, 281, 282, 283, 284, 285, 286, 287,
+ 288, 289, 290, 291, 292, 293, 294, 295, 296, 297,
+ 298, 299, 300, 301, 302, 303, 304, 305, 306, 307,
+ 308, 309, 310, 311, 312, 313, 314, 315, 316, 317,
+ 318, 319, 320, 321, 322, 323, 324, 325, 326, 327,
+ 328, 329, 330, 331, 332, 333, 334, 335, 336, 337,
+ 338, -1, 340, 341, 342, 343, 344, 345, 346, 347,
+ 348, 349, 350, 351, 352, 353, -1, -1, 356, 357,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 375, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 385, 386, 387,
+ 388, 389, 5, 6, 7, 8, 9, -1, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 82,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 98, 99, 100, 101, 102,
+ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120, 121, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 131, 132,
+ 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
+ 143, 144, 145, 146, 147, 148, 149, 150, 151, 152,
+ 153, 154, 155, 156, 157, 158, 159, 160, 161, 162,
+ 163, 164, 165, 166, 167, 168, 169, 170, 171, 172,
+ 173, 174, 175, 176, 177, 178, 179, 180, 181, 182,
+ 183, 184, 185, 186, 187, 188, 189, 190, 191, 192,
+ 193, 194, 195, 196, 197, 198, 199, 200, 201, 202,
+ 203, 204, 205, 206, 207, 208, 209, 210, 211, 212,
+ 213, 214, 215, 216, 217, 218, 219, 220, 221, 222,
+ 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
+ 233, 234, 235, 236, 237, 238, 239, 240, 241, 242,
+ 243, 244, 245, 246, 247, 248, 249, 250, 251, 252,
+ 253, 254, 255, 256, 257, 258, 259, 260, 261, 262,
+ 263, 264, 265, 266, 267, 268, 269, 270, 271, 272,
+ 273, 274, 275, 276, 277, 278, 279, 280, 281, 282,
+ 283, 284, 285, 286, 287, 288, 289, 290, 291, 292,
+ 293, 294, 295, 296, 297, 298, 299, 300, 301, 302,
+ 303, 304, 305, 306, 307, 308, 309, 310, 311, 312,
+ 313, 314, 315, 316, 317, 318, 319, 320, 321, 322,
+ 323, 324, 325, 326, 327, 328, 329, 330, 331, 332,
+ 333, 334, 335, 336, 337, 338, -1, 340, 341, 342,
+ 343, 344, 345, 346, 347, 348, 349, 350, 351, 352,
+ 353, -1, -1, 356, 357, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 375, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 386, 387, 388, 389, 5, 6, 7,
+ 8, 9, -1, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 34, 35, 36, 37,
+ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 82, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
+ 118, 119, 120, 121, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 131, 132, 133, 134, 135, 136, 137,
+ 138, 139, 140, 141, 142, 143, 144, 145, 146, 147,
+ 148, 149, 150, 151, 152, 153, 154, 155, 156, 157,
+ 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
+ 168, 169, 170, 171, 172, 173, 174, 175, 176, 177,
+ 178, 179, 180, 181, 182, 183, 184, 185, 186, 187,
+ 188, 189, 190, 191, 192, 193, 194, 195, 196, 197,
+ 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
+ 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
+ 218, 219, 220, 221, 222, 223, 224, 225, 226, 227,
+ 228, 229, 230, 231, 232, 233, 234, 235, 236, 237,
+ 238, 239, 240, 241, 242, 243, 244, 245, 246, 247,
+ 248, 249, 250, 251, 252, 253, 254, 255, 256, 257,
+ 258, 259, 260, 261, 262, 263, 264, 265, 266, 267,
+ 268, 269, 270, 271, 272, 273, 274, 275, 276, 277,
+ 278, 279, 280, 281, 282, 283, 284, 285, 286, 287,
+ 288, 289, 290, 291, 292, 293, 294, 295, 296, 297,
+ 298, 299, 300, 301, 302, 303, 304, 305, 306, 307,
+ 308, 309, 310, 311, 312, 313, 314, 315, 316, 317,
+ 318, 319, 320, 321, 322, 323, 324, 325, 326, 327,
+ 328, 329, 330, 331, 332, 333, 334, 335, 336, 337,
+ 338, -1, 340, 341, 342, 343, 344, 345, 346, 347,
+ 348, 349, 350, 351, 352, 353, -1, -1, 356, 357,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 375, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 386, 387,
+ 388, 389, 5, 6, 7, 8, 9, -1, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 98, 99, 100, 101, 102,
+ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120, 121, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 131, 132,
+ 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
+ 143, 144, 145, 146, 147, 148, 149, 150, 151, 152,
+ 153, 154, 155, 156, 157, 158, 159, 160, 161, 162,
+ 163, 164, 165, 166, 167, 168, 169, 170, 171, 172,
+ 173, 174, 175, 176, 177, 178, 179, 180, 181, 182,
+ 183, 184, 185, 186, 187, 188, 189, 190, 191, 192,
+ 193, 194, 195, 196, 197, 198, 199, 200, 201, 202,
+ 203, 204, 205, 206, 207, 208, 209, 210, 211, 212,
+ 213, 214, 215, 216, 217, 218, 219, 220, 221, 222,
+ 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
+ 233, 234, 235, 236, 237, 238, 239, 240, 241, 242,
+ 243, 244, 245, 246, 247, 248, 249, 250, 251, 252,
+ 253, 254, 255, 256, 257, 258, 259, 260, 261, 262,
+ 263, 264, 265, 266, 267, 268, 269, 270, 271, 272,
+ 273, 274, 275, 276, 277, 278, 279, 280, 281, 282,
+ 283, 284, 285, 286, 287, 288, 289, 290, 291, 292,
+ 293, 294, 295, 296, 297, 298, 299, 300, 301, 302,
+ 303, 304, 305, 306, 307, 308, 309, 310, 311, 312,
+ 313, 314, 315, 316, 317, 318, 319, 320, 321, 322,
+ 323, 324, 325, 326, 327, 328, 329, 330, 331, 332,
+ 333, 334, 335, 336, 337, 338, -1, -1, 341
+};
+
+ /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint16 yystos[] =
+{
+ 0, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
+ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
+ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
+ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120, 121, 122,
+ 123, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
+ 143, 144, 145, 146, 147, 148, 149, 150, 151, 152,
+ 153, 154, 155, 156, 157, 158, 159, 160, 161, 162,
+ 163, 164, 165, 166, 167, 168, 169, 170, 171, 172,
+ 173, 174, 175, 176, 177, 178, 179, 180, 181, 182,
+ 183, 184, 185, 186, 187, 188, 189, 190, 191, 192,
+ 193, 194, 195, 196, 197, 198, 199, 200, 201, 202,
+ 203, 204, 205, 206, 207, 208, 209, 210, 211, 212,
+ 213, 214, 215, 216, 217, 218, 219, 220, 221, 222,
+ 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
+ 233, 234, 235, 236, 237, 238, 239, 240, 241, 242,
+ 243, 244, 245, 246, 247, 248, 249, 250, 251, 252,
+ 253, 254, 255, 256, 257, 258, 259, 260, 261, 262,
+ 263, 264, 265, 266, 267, 268, 269, 270, 271, 272,
+ 273, 274, 275, 276, 277, 278, 279, 280, 281, 282,
+ 283, 284, 285, 286, 287, 288, 289, 290, 291, 292,
+ 293, 294, 295, 296, 297, 298, 299, 300, 301, 302,
+ 303, 304, 305, 306, 307, 308, 309, 310, 311, 312,
+ 313, 314, 315, 316, 317, 318, 319, 320, 321, 322,
+ 323, 324, 325, 326, 327, 328, 329, 330, 331, 332,
+ 333, 334, 335, 336, 337, 338, 341, 385, 399, 400,
+ 401, 402, 403, 404, 439, 440, 443, 444, 445, 446,
+ 450, 451, 452, 453, 454, 455, 458, 459, 460, 461,
+ 462, 464, 469, 470, 471, 511, 512, 513, 375, 375,
+ 340, 379, 470, 340, 385, 385, 514, 376, 382, 447,
+ 448, 449, 459, 464, 382, 385, 340, 340, 385, 460,
+ 464, 393, 466, 467, 0, 512, 340, 463, 81, 340,
+ 456, 457, 379, 473, 464, 377, 385, 465, 379, 490,
+ 448, 447, 449, 340, 340, 375, 384, 465, 379, 382,
+ 385, 442, 340, 342, 343, 344, 345, 346, 347, 348,
+ 349, 350, 351, 352, 353, 356, 357, 375, 386, 387,
+ 388, 389, 409, 410, 411, 413, 414, 415, 416, 417,
+ 418, 419, 420, 421, 462, 464, 468, 465, 376, 382,
+ 384, 376, 382, 472, 459, 464, 474, 475, 385, 378,
+ 420, 422, 423, 424, 425, 426, 427, 428, 429, 430,
+ 431, 432, 433, 377, 385, 22, 23, 24, 26, 27,
+ 28, 29, 30, 31, 32, 339, 377, 379, 380, 385,
+ 420, 433, 435, 437, 439, 443, 462, 464, 480, 481,
+ 482, 483, 491, 492, 493, 494, 497, 498, 501, 502,
+ 503, 510, 515, 465, 384, 465, 379, 435, 478, 384,
+ 441, 340, 382, 385, 420, 420, 437, 356, 357, 377,
+ 381, 376, 376, 382, 338, 435, 375, 420, 382, 394,
+ 340, 433, 438, 457, 474, 464, 340, 476, 477, 380,
+ 475, 390, 391, 392, 387, 389, 354, 355, 358, 359,
+ 393, 394, 360, 361, 397, 396, 395, 362, 364, 363,
+ 398, 378, 378, 433, 385, 385, 505, 375, 375, 385,
+ 385, 437, 375, 437, 383, 375, 377, 380, 484, 365,
+ 366, 367, 368, 369, 370, 371, 372, 373, 374, 384,
+ 436, 382, 385, 380, 481, 494, 498, 503, 478, 384,
+ 478, 479, 478, 474, 340, 376, 412, 437, 340, 435,
+ 420, 380, 476, 465, 382, 385, 420, 420, 420, 422,
+ 422, 423, 423, 424, 424, 424, 424, 425, 425, 426,
+ 427, 428, 429, 430, 431, 434, 378, 481, 506, 437,
+ 385, 437, 383, 504, 340, 516, 517, 491, 435, 435,
+ 478, 380, 382, 380, 378, 385, 477, 437, 339, 480,
+ 492, 507, 376, 376, 437, 452, 459, 496, 375, 378,
+ 382, 485, 380, 478, 383, 375, 496, 508, 509, 487,
+ 488, 489, 495, 499, 340, 376, 438, 378, 517, 380,
+ 435, 437, 385, 376, 25, 483, 482, 379, 384, 482,
+ 486, 490, 376, 376, 437, 486, 487, 491, 500, 478,
+ 385, 380
+};
+
+ /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint16 yyr1[] =
+{
+ 0, 408, 409, 410, 410, 410, 410, 410, 410, 410,
+ 410, 410, 410, 410, 410, 410, 410, 411, 411, 411,
+ 411, 411, 411, 412, 413, 414, 415, 415, 416, 416,
+ 417, 417, 418, 419, 419, 419, 420, 420, 420, 420,
+ 421, 421, 421, 421, 422, 422, 422, 422, 423, 423,
+ 423, 424, 424, 424, 425, 425, 425, 425, 425, 426,
+ 426, 426, 427, 427, 428, 428, 429, 429, 430, 430,
+ 431, 431, 432, 432, 433, 434, 433, 435, 435, 436,
+ 436, 436, 436, 436, 436, 436, 436, 436, 436, 436,
+ 437, 437, 438, 439, 439, 439, 439, 439, 439, 439,
+ 439, 439, 441, 440, 442, 442, 443, 444, 444, 445,
+ 445, 446, 447, 447, 448, 448, 448, 448, 449, 450,
+ 450, 450, 450, 450, 451, 451, 451, 451, 451, 452,
+ 452, 453, 454, 454, 454, 454, 454, 454, 454, 454,
+ 455, 456, 456, 457, 457, 457, 458, 459, 459, 460,
+ 460, 460, 460, 460, 460, 460, 461, 461, 461, 461,
+ 461, 461, 461, 461, 461, 461, 461, 461, 461, 461,
+ 461, 461, 461, 461, 461, 461, 461, 461, 461, 461,
+ 461, 461, 461, 461, 461, 462, 463, 463, 464, 464,
+ 465, 465, 465, 465, 466, 466, 467, 468, 468, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 469, 469, 470, 470, 470,
+ 472, 471, 473, 471, 474, 474, 475, 475, 476, 476,
+ 477, 477, 478, 478, 478, 479, 479, 480, 481, 481,
+ 482, 482, 482, 482, 482, 482, 482, 483, 484, 485,
+ 483, 486, 486, 488, 487, 489, 487, 490, 490, 491,
+ 491, 492, 492, 493, 493, 494, 495, 495, 496, 496,
+ 497, 497, 499, 498, 500, 500, 501, 501, 502, 502,
+ 504, 503, 505, 503, 506, 503, 507, 507, 508, 508,
+ 509, 509, 510, 510, 510, 510, 510, 511, 511, 512,
+ 512, 512, 514, 513, 515, 516, 516, 517, 517
+};
+
+ /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 3, 1, 4, 1,
+ 3, 2, 2, 1, 1, 1, 2, 2, 2, 1,
+ 2, 3, 2, 1, 1, 1, 1, 2, 2, 2,
+ 1, 1, 1, 1, 1, 3, 3, 3, 1, 3,
+ 3, 1, 3, 3, 1, 3, 3, 3, 3, 1,
+ 3, 3, 1, 3, 1, 3, 1, 3, 1, 3,
+ 1, 3, 1, 3, 1, 0, 6, 1, 3, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 3, 1, 2, 2, 4, 2, 3, 4, 2,
+ 3, 4, 0, 6, 2, 3, 2, 1, 1, 2,
+ 3, 3, 2, 3, 2, 1, 2, 1, 1, 1,
+ 3, 4, 6, 5, 1, 2, 3, 5, 4, 1,
+ 2, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 4, 1, 3, 1, 3, 1, 1, 1, 2, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 4, 1, 1, 3, 2, 3,
+ 2, 3, 3, 4, 1, 0, 3, 1, 3, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 6, 0, 5, 1, 2, 3, 4, 1, 3,
+ 1, 2, 1, 3, 4, 1, 3, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 2, 0, 0,
+ 5, 1, 1, 0, 2, 0, 2, 2, 3, 1,
+ 2, 1, 2, 1, 2, 5, 3, 1, 1, 4,
+ 1, 2, 0, 8, 0, 1, 3, 2, 1, 2,
+ 0, 6, 0, 8, 0, 7, 1, 1, 1, 0,
+ 2, 3, 2, 2, 2, 3, 2, 1, 2, 1,
+ 1, 1, 0, 3, 5, 1, 3, 1, 4
+};
+
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ YYPOPSTACK (yylen); \
+ yystate = *yyssp; \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (pParseContext, YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (0)
+
+/* Error token number */
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (0)
+
+/* This macro is provided for backward compatibility. */
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value, pParseContext); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (0)
+
+
+/*----------------------------------------.
+| Print this symbol's value on YYOUTPUT. |
+`----------------------------------------*/
+
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, glslang::TParseContext* pParseContext)
+{
+ FILE *yyo = yyoutput;
+ YYUSE (yyo);
+ YYUSE (pParseContext);
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+ YYUSE (yytype);
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, glslang::TParseContext* pParseContext)
+{
+ YYFPRINTF (yyoutput, "%s %s (",
+ yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep, pParseContext);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+static void
+yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule, glslang::TParseContext* pParseContext)
+{
+ unsigned long int yylno = yyrline[yyrule];
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr,
+ yystos[yyssp[yyi + 1 - yynrhs]],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ , pParseContext);
+ YYFPRINTF (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyssp, yyvsp, Rule, pParseContext); \
+} while (0)
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+static YYSIZE_T
+yystrlen (const char *yystr)
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+ about the unexpected token YYTOKEN for the state stack whose top is
+ YYSSP.
+
+ Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is
+ not large enough to hold the message. In that case, also set
+ *YYMSG_ALLOC to the required number of bytes. Return 2 if the
+ required number of bytes is too large to store. */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+ yytype_int16 *yyssp, int yytoken)
+{
+ YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]);
+ YYSIZE_T yysize = yysize0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ /* Internationalized format string. */
+ const char *yyformat = YY_NULLPTR;
+ /* Arguments of yyformat. */
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ /* Number of reported tokens (one for the "unexpected", one per
+ "expected"). */
+ int yycount = 0;
+
+ /* There are many possibilities here to consider:
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yychar) is if
+ this state is a consistent state with a default action. Thus,
+ detecting the absence of a lookahead is sufficient to determine
+ that there is no unexpected or expected token to report. In that
+ case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is a
+ consistent state with a default action. There might have been a
+ previous inconsistent state, consistent state with a non-default
+ action, or user semantic action that manipulated yychar.
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+ if (yytoken != YYEMPTY)
+ {
+ int yyn = yypact[*yyssp];
+ yyarg[yycount++] = yytname[yytoken];
+ if (!yypact_value_is_default (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yyx;
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+ && !yytable_value_is_error (yytable[yyx + yyn]))
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ {
+ YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]);
+ if (! (yysize <= yysize1
+ && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+ }
+ }
+ }
+
+ switch (yycount)
+ {
+# define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ YYCASE_(0, YY_("syntax error"));
+ YYCASE_(1, YY_("syntax error, unexpected %s"));
+ YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+ }
+
+ {
+ YYSIZE_T yysize1 = yysize + yystrlen (yyformat);
+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+
+ if (*yymsg_alloc < yysize)
+ {
+ *yymsg_alloc = 2 * yysize;
+ if (! (yysize <= *yymsg_alloc
+ && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+ *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+ return 1;
+ }
+
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ {
+ char *yyp = *yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyformat) != '\0')
+ if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyformat += 2;
+ }
+ else
+ {
+ yyp++;
+ yyformat++;
+ }
+ }
+ return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, glslang::TParseContext* pParseContext)
+{
+ YYUSE (yyvaluep);
+ YYUSE (pParseContext);
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+ YYUSE (yytype);
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+}
+
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+int
+yyparse (glslang::TParseContext* pParseContext)
+{
+/* The lookahead symbol. */
+int yychar;
+
+
+/* The semantic value of the lookahead symbol. */
+/* Default value used for initialization, for pacifying older GCCs
+ or non-GCC compilers. */
+YY_INITIAL_VALUE (static YYSTYPE yyval_default;)
+YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default);
+
+ /* Number of syntax errors so far. */
+ int yynerrs;
+
+ int yystate;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+
+ /* The stacks and their tools:
+ 'yyss': related to states.
+ 'yyvs': related to semantic values.
+
+ Refer to the stacks through separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs;
+ YYSTYPE *yyvsp;
+
+ YYSIZE_T yystacksize;
+
+ int yyn;
+ int yyresult;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken = 0;
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ yyssp = yyss = yyssa;
+ yyvsp = yyvs = yyvsa;
+ yystacksize = YYINITDEPTH;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ lookahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to lookahead token. */
+ yyn = yypact[yystate];
+ if (yypact_value_is_default (yyn))
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = yylex (&yylval, parseContext);
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yytable_value_is_error (yyn))
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the lookahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+ *++yyvsp = yylval;
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ '$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 2:
+#line 302 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = parseContext.handleVariable((yyvsp[0].lex).loc, (yyvsp[0].lex).symbol, (yyvsp[0].lex).string);
+ }
+#line 4159 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 3:
+#line 308 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ }
+#line 4167 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 4:
+#line 311 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed literal");
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).i, (yyvsp[0].lex).loc, true);
+ }
+#line 4176 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 5:
+#line 315 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed literal");
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).u, (yyvsp[0].lex).loc, true);
+ }
+#line 4185 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 6:
+#line 319 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).i, (yyvsp[0].lex).loc, true);
+ }
+#line 4193 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 7:
+#line 322 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "unsigned literal");
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).u, (yyvsp[0].lex).loc, true);
+ }
+#line 4202 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 8:
+#line 326 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int64Check((yyvsp[0].lex).loc, "64-bit integer literal");
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).i64, (yyvsp[0].lex).loc, true);
+ }
+#line 4211 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 9:
+#line 330 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int64Check((yyvsp[0].lex).loc, "64-bit unsigned integer literal");
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).u64, (yyvsp[0].lex).loc, true);
+ }
+#line 4220 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 10:
+#line 334 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitInt16Check((yyvsp[0].lex).loc, "16-bit integer literal");
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((short)(yyvsp[0].lex).i, (yyvsp[0].lex).loc, true);
+ }
+#line 4229 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 11:
+#line 338 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitInt16Check((yyvsp[0].lex).loc, "16-bit unsigned integer literal");
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((unsigned short)(yyvsp[0].lex).u, (yyvsp[0].lex).loc, true);
+ }
+#line 4238 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 12:
+#line 342 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).d, EbtFloat, (yyvsp[0].lex).loc, true);
+ }
+#line 4246 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 13:
+#line 345 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.doubleCheck((yyvsp[0].lex).loc, "double literal");
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).d, EbtDouble, (yyvsp[0].lex).loc, true);
+ }
+#line 4255 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 14:
+#line 349 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.float16Check((yyvsp[0].lex).loc, "half float literal");
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).d, EbtFloat16, (yyvsp[0].lex).loc, true);
+ }
+#line 4264 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 15:
+#line 353 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).b, (yyvsp[0].lex).loc, true);
+ }
+#line 4272 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 16:
+#line 356 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = (yyvsp[-1].interm.intermTypedNode);
+ if ((yyval.interm.intermTypedNode)->getAsConstantUnion())
+ (yyval.interm.intermTypedNode)->getAsConstantUnion()->setExpression();
+ }
+#line 4282 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 17:
+#line 364 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ }
+#line 4290 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 18:
+#line 367 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = parseContext.handleBracketDereference((yyvsp[-2].lex).loc, (yyvsp[-3].interm.intermTypedNode), (yyvsp[-1].interm.intermTypedNode));
+ }
+#line 4298 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 19:
+#line 370 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ }
+#line 4306 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 20:
+#line 373 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = parseContext.handleDotDereference((yyvsp[0].lex).loc, (yyvsp[-2].interm.intermTypedNode), *(yyvsp[0].lex).string);
+ }
+#line 4314 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 21:
+#line 376 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.variableCheck((yyvsp[-1].interm.intermTypedNode));
+ parseContext.lValueErrorCheck((yyvsp[0].lex).loc, "++", (yyvsp[-1].interm.intermTypedNode));
+ (yyval.interm.intermTypedNode) = parseContext.handleUnaryMath((yyvsp[0].lex).loc, "++", EOpPostIncrement, (yyvsp[-1].interm.intermTypedNode));
+ }
+#line 4324 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 22:
+#line 381 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.variableCheck((yyvsp[-1].interm.intermTypedNode));
+ parseContext.lValueErrorCheck((yyvsp[0].lex).loc, "--", (yyvsp[-1].interm.intermTypedNode));
+ (yyval.interm.intermTypedNode) = parseContext.handleUnaryMath((yyvsp[0].lex).loc, "--", EOpPostDecrement, (yyvsp[-1].interm.intermTypedNode));
+ }
+#line 4334 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 23:
+#line 389 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.integerCheck((yyvsp[0].interm.intermTypedNode), "[]");
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ }
+#line 4343 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 24:
+#line 396 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = parseContext.handleFunctionCall((yyvsp[0].interm).loc, (yyvsp[0].interm).function, (yyvsp[0].interm).intermNode);
+ delete (yyvsp[0].interm).function;
+ }
+#line 4352 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 25:
+#line 403 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm) = (yyvsp[0].interm);
+ }
+#line 4360 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 26:
+#line 409 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm) = (yyvsp[-1].interm);
+ (yyval.interm).loc = (yyvsp[0].lex).loc;
+ }
+#line 4369 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 27:
+#line 413 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm) = (yyvsp[-1].interm);
+ (yyval.interm).loc = (yyvsp[0].lex).loc;
+ }
+#line 4378 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 28:
+#line 420 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm) = (yyvsp[-1].interm);
+ }
+#line 4386 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 29:
+#line 423 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm) = (yyvsp[0].interm);
+ }
+#line 4394 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 30:
+#line 429 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ TParameter param = { 0, new TType };
+ param.type->shallowCopy((yyvsp[0].interm.intermTypedNode)->getType());
+ (yyvsp[-1].interm).function->addParameter(param);
+ (yyval.interm).function = (yyvsp[-1].interm).function;
+ (yyval.interm).intermNode = (yyvsp[0].interm.intermTypedNode);
+ }
+#line 4406 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 31:
+#line 436 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ TParameter param = { 0, new TType };
+ param.type->shallowCopy((yyvsp[0].interm.intermTypedNode)->getType());
+ (yyvsp[-2].interm).function->addParameter(param);
+ (yyval.interm).function = (yyvsp[-2].interm).function;
+ (yyval.interm).intermNode = parseContext.intermediate.growAggregate((yyvsp[-2].interm).intermNode, (yyvsp[0].interm.intermTypedNode), (yyvsp[-1].lex).loc);
+ }
+#line 4418 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 32:
+#line 446 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm) = (yyvsp[-1].interm);
+ }
+#line 4426 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 33:
+#line 454 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ // Constructor
+ (yyval.interm).intermNode = 0;
+ (yyval.interm).function = parseContext.handleConstructorCall((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type));
+ }
+#line 4436 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 34:
+#line 459 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ //
+ // Should be a method or subroutine call, but we haven't recognized the arguments yet.
+ //
+ (yyval.interm).function = 0;
+ (yyval.interm).intermNode = 0;
+
+ TIntermMethod* method = (yyvsp[0].interm.intermTypedNode)->getAsMethodNode();
+ if (method) {
+ (yyval.interm).function = new TFunction(&method->getMethodName(), TType(EbtInt), EOpArrayLength);
+ (yyval.interm).intermNode = method->getObject();
+ } else {
+ TIntermSymbol* symbol = (yyvsp[0].interm.intermTypedNode)->getAsSymbolNode();
+ if (symbol) {
+ parseContext.reservedErrorCheck(symbol->getLoc(), symbol->getName());
+ TFunction *function = new TFunction(&symbol->getName(), TType(EbtVoid));
+ (yyval.interm).function = function;
+ } else
+ parseContext.error((yyvsp[0].interm.intermTypedNode)->getLoc(), "function call, method, or subroutine call expected", "", "");
+ }
+
+ if ((yyval.interm).function == 0) {
+ // error recover
+ TString* empty = NewPoolTString("");
+ (yyval.interm).function = new TFunction(empty, TType(EbtVoid), EOpNull);
+ }
+ }
+#line 4468 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 35:
+#line 486 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ // Constructor
+ (yyval.interm).intermNode = 0;
+ (yyval.interm).function = parseContext.handleConstructorCall((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type));
+ }
+#line 4478 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 36:
+#line 494 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.variableCheck((yyvsp[0].interm.intermTypedNode));
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ if (TIntermMethod* method = (yyvsp[0].interm.intermTypedNode)->getAsMethodNode())
+ parseContext.error((yyvsp[0].interm.intermTypedNode)->getLoc(), "incomplete method syntax", method->getMethodName().c_str(), "");
+ }
+#line 4489 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 37:
+#line 500 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.lValueErrorCheck((yyvsp[-1].lex).loc, "++", (yyvsp[0].interm.intermTypedNode));
+ (yyval.interm.intermTypedNode) = parseContext.handleUnaryMath((yyvsp[-1].lex).loc, "++", EOpPreIncrement, (yyvsp[0].interm.intermTypedNode));
+ }
+#line 4498 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 38:
+#line 504 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.lValueErrorCheck((yyvsp[-1].lex).loc, "--", (yyvsp[0].interm.intermTypedNode));
+ (yyval.interm.intermTypedNode) = parseContext.handleUnaryMath((yyvsp[-1].lex).loc, "--", EOpPreDecrement, (yyvsp[0].interm.intermTypedNode));
+ }
+#line 4507 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 39:
+#line 508 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ if ((yyvsp[-1].interm).op != EOpNull) {
+ char errorOp[2] = {0, 0};
+ switch((yyvsp[-1].interm).op) {
+ case EOpNegative: errorOp[0] = '-'; break;
+ case EOpLogicalNot: errorOp[0] = '!'; break;
+ case EOpBitwiseNot: errorOp[0] = '~'; break;
+ default: break; // some compilers want this
+ }
+ (yyval.interm.intermTypedNode) = parseContext.handleUnaryMath((yyvsp[-1].interm).loc, errorOp, (yyvsp[-1].interm).op, (yyvsp[0].interm.intermTypedNode));
+ } else {
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ if ((yyval.interm.intermTypedNode)->getAsConstantUnion())
+ (yyval.interm.intermTypedNode)->getAsConstantUnion()->setExpression();
+ }
+ }
+#line 4528 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 40:
+#line 528 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpNull; }
+#line 4534 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 41:
+#line 529 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpNegative; }
+#line 4540 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 42:
+#line 530 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpLogicalNot; }
+#line 4546 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 43:
+#line 531 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpBitwiseNot;
+ parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bitwise not"); }
+#line 4553 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 44:
+#line 537 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+#line 4559 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 45:
+#line 538 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "*", EOpMul, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode));
+ if ((yyval.interm.intermTypedNode) == 0)
+ (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode);
+ }
+#line 4569 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 46:
+#line 543 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "/", EOpDiv, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode));
+ if ((yyval.interm.intermTypedNode) == 0)
+ (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode);
+ }
+#line 4579 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 47:
+#line 548 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "%");
+ (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "%", EOpMod, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode));
+ if ((yyval.interm.intermTypedNode) == 0)
+ (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode);
+ }
+#line 4590 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 48:
+#line 557 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+#line 4596 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 49:
+#line 558 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "+", EOpAdd, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode));
+ if ((yyval.interm.intermTypedNode) == 0)
+ (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode);
+ }
+#line 4606 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 50:
+#line 563 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "-", EOpSub, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode));
+ if ((yyval.interm.intermTypedNode) == 0)
+ (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode);
+ }
+#line 4616 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 51:
+#line 571 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+#line 4622 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 52:
+#line 572 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "bit shift left");
+ (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "<<", EOpLeftShift, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode));
+ if ((yyval.interm.intermTypedNode) == 0)
+ (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode);
+ }
+#line 4633 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 53:
+#line 578 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "bit shift right");
+ (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, ">>", EOpRightShift, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode));
+ if ((yyval.interm.intermTypedNode) == 0)
+ (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode);
+ }
+#line 4644 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 54:
+#line 587 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+#line 4650 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 55:
+#line 588 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "<", EOpLessThan, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode));
+ if ((yyval.interm.intermTypedNode) == 0)
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc);
+ }
+#line 4660 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 56:
+#line 593 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, ">", EOpGreaterThan, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode));
+ if ((yyval.interm.intermTypedNode) == 0)
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc);
+ }
+#line 4670 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 57:
+#line 598 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "<=", EOpLessThanEqual, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode));
+ if ((yyval.interm.intermTypedNode) == 0)
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc);
+ }
+#line 4680 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 58:
+#line 603 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, ">=", EOpGreaterThanEqual, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode));
+ if ((yyval.interm.intermTypedNode) == 0)
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc);
+ }
+#line 4690 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 59:
+#line 611 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+#line 4696 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 60:
+#line 612 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.arrayObjectCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "array comparison");
+ parseContext.opaqueCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "==");
+ parseContext.specializationCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "==");
+ parseContext.referenceCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "==");
+ (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "==", EOpEqual, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode));
+ if ((yyval.interm.intermTypedNode) == 0)
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc);
+ }
+#line 4710 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 61:
+#line 621 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.arrayObjectCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "array comparison");
+ parseContext.opaqueCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "!=");
+ parseContext.specializationCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "!=");
+ parseContext.referenceCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "!=");
+ (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "!=", EOpNotEqual, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode));
+ if ((yyval.interm.intermTypedNode) == 0)
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc);
+ }
+#line 4724 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 62:
+#line 633 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+#line 4730 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 63:
+#line 634 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "bitwise and");
+ (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "&", EOpAnd, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode));
+ if ((yyval.interm.intermTypedNode) == 0)
+ (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode);
+ }
+#line 4741 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 64:
+#line 643 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+#line 4747 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 65:
+#line 644 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "bitwise exclusive or");
+ (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "^", EOpExclusiveOr, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode));
+ if ((yyval.interm.intermTypedNode) == 0)
+ (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode);
+ }
+#line 4758 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 66:
+#line 653 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+#line 4764 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 67:
+#line 654 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "bitwise inclusive or");
+ (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "|", EOpInclusiveOr, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode));
+ if ((yyval.interm.intermTypedNode) == 0)
+ (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode);
+ }
+#line 4775 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 68:
+#line 663 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+#line 4781 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 69:
+#line 664 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "&&", EOpLogicalAnd, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode));
+ if ((yyval.interm.intermTypedNode) == 0)
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc);
+ }
+#line 4791 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 70:
+#line 672 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+#line 4797 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 71:
+#line 673 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "^^", EOpLogicalXor, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode));
+ if ((yyval.interm.intermTypedNode) == 0)
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc);
+ }
+#line 4807 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 72:
+#line 681 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+#line 4813 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 73:
+#line 682 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "||", EOpLogicalOr, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode));
+ if ((yyval.interm.intermTypedNode) == 0)
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc);
+ }
+#line 4823 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 74:
+#line 690 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+#line 4829 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 75:
+#line 691 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ ++parseContext.controlFlowNestingLevel;
+ }
+#line 4837 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 76:
+#line 694 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ --parseContext.controlFlowNestingLevel;
+ parseContext.boolCheck((yyvsp[-4].lex).loc, (yyvsp[-5].interm.intermTypedNode));
+ parseContext.rValueErrorCheck((yyvsp[-4].lex).loc, "?", (yyvsp[-5].interm.intermTypedNode));
+ parseContext.rValueErrorCheck((yyvsp[-1].lex).loc, ":", (yyvsp[-2].interm.intermTypedNode));
+ parseContext.rValueErrorCheck((yyvsp[-1].lex).loc, ":", (yyvsp[0].interm.intermTypedNode));
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addSelection((yyvsp[-5].interm.intermTypedNode), (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yyvsp[-4].lex).loc);
+ if ((yyval.interm.intermTypedNode) == 0) {
+ parseContext.binaryOpError((yyvsp[-4].lex).loc, ":", (yyvsp[-2].interm.intermTypedNode)->getCompleteString(), (yyvsp[0].interm.intermTypedNode)->getCompleteString());
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ }
+ }
+#line 4854 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 77:
+#line 709 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+#line 4860 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 78:
+#line 710 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.arrayObjectCheck((yyvsp[-1].interm).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "array assignment");
+ parseContext.opaqueCheck((yyvsp[-1].interm).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "=");
+ parseContext.storage16BitAssignmentCheck((yyvsp[-1].interm).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "=");
+ parseContext.specializationCheck((yyvsp[-1].interm).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "=");
+ parseContext.lValueErrorCheck((yyvsp[-1].interm).loc, "assign", (yyvsp[-2].interm.intermTypedNode));
+ parseContext.rValueErrorCheck((yyvsp[-1].interm).loc, "assign", (yyvsp[0].interm.intermTypedNode));
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addAssign((yyvsp[-1].interm).op, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yyvsp[-1].interm).loc);
+ if ((yyval.interm.intermTypedNode) == 0) {
+ parseContext.assignError((yyvsp[-1].interm).loc, "assign", (yyvsp[-2].interm.intermTypedNode)->getCompleteString(), (yyvsp[0].interm.intermTypedNode)->getCompleteString());
+ (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode);
+ }
+ }
+#line 4878 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 79:
+#line 726 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm).loc = (yyvsp[0].lex).loc;
+ (yyval.interm).op = EOpAssign;
+ }
+#line 4887 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 80:
+#line 730 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm).loc = (yyvsp[0].lex).loc;
+ (yyval.interm).op = EOpMulAssign;
+ }
+#line 4896 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 81:
+#line 734 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm).loc = (yyvsp[0].lex).loc;
+ (yyval.interm).op = EOpDivAssign;
+ }
+#line 4905 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 82:
+#line 738 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "%=");
+ (yyval.interm).loc = (yyvsp[0].lex).loc;
+ (yyval.interm).op = EOpModAssign;
+ }
+#line 4915 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 83:
+#line 743 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm).loc = (yyvsp[0].lex).loc;
+ (yyval.interm).op = EOpAddAssign;
+ }
+#line 4924 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 84:
+#line 747 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm).loc = (yyvsp[0].lex).loc;
+ (yyval.interm).op = EOpSubAssign;
+ }
+#line 4933 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 85:
+#line 751 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bit-shift left assign");
+ (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpLeftShiftAssign;
+ }
+#line 4942 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 86:
+#line 755 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bit-shift right assign");
+ (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpRightShiftAssign;
+ }
+#line 4951 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 87:
+#line 759 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bitwise-and assign");
+ (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpAndAssign;
+ }
+#line 4960 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 88:
+#line 763 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bitwise-xor assign");
+ (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpExclusiveOrAssign;
+ }
+#line 4969 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 89:
+#line 767 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bitwise-or assign");
+ (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpInclusiveOrAssign;
+ }
+#line 4978 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 90:
+#line 774 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ }
+#line 4986 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 91:
+#line 777 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.samplerConstructorLocationCheck((yyvsp[-1].lex).loc, ",", (yyvsp[0].interm.intermTypedNode));
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.addComma((yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yyvsp[-1].lex).loc);
+ if ((yyval.interm.intermTypedNode) == 0) {
+ parseContext.binaryOpError((yyvsp[-1].lex).loc, ",", (yyvsp[-2].interm.intermTypedNode)->getCompleteString(), (yyvsp[0].interm.intermTypedNode)->getCompleteString());
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ }
+ }
+#line 4999 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 92:
+#line 788 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.constantValueCheck((yyvsp[0].interm.intermTypedNode), "");
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ }
+#line 5008 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 93:
+#line 795 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.handleFunctionDeclarator((yyvsp[-1].interm).loc, *(yyvsp[-1].interm).function, true /* prototype */);
+ (yyval.interm.intermNode) = 0;
+ // TODO: 4.0 functionality: subroutines: make the identifier a user type for this signature
+ }
+#line 5018 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 94:
+#line 800 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ if ((yyvsp[-1].interm).intermNode && (yyvsp[-1].interm).intermNode->getAsAggregate())
+ (yyvsp[-1].interm).intermNode->getAsAggregate()->setOperator(EOpSequence);
+ (yyval.interm.intermNode) = (yyvsp[-1].interm).intermNode;
+ }
+#line 5028 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 95:
+#line 805 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.profileRequires((yyvsp[-3].lex).loc, ENoProfile, 130, 0, "precision statement");
+
+ // lazy setting of the previous scope's defaults, has effect only the first time it is called in a particular scope
+ parseContext.symbolTable.setPreviousDefaultPrecisions(&parseContext.defaultPrecision[0]);
+ parseContext.setDefaultPrecision((yyvsp[-3].lex).loc, (yyvsp[-1].interm.type), (yyvsp[-2].interm.type).qualifier.precision);
+ (yyval.interm.intermNode) = 0;
+ }
+#line 5041 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 96:
+#line 813 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.declareBlock((yyvsp[-1].interm).loc, *(yyvsp[-1].interm).typeList);
+ (yyval.interm.intermNode) = 0;
+ }
+#line 5050 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 97:
+#line 817 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.declareBlock((yyvsp[-2].interm).loc, *(yyvsp[-2].interm).typeList, (yyvsp[-1].lex).string);
+ (yyval.interm.intermNode) = 0;
+ }
+#line 5059 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 98:
+#line 821 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.declareBlock((yyvsp[-3].interm).loc, *(yyvsp[-3].interm).typeList, (yyvsp[-2].lex).string, (yyvsp[-1].interm).arraySizes);
+ (yyval.interm.intermNode) = 0;
+ }
+#line 5068 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 99:
+#line 825 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.globalQualifierFixCheck((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).qualifier);
+ parseContext.updateStandaloneQualifierDefaults((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type));
+ (yyval.interm.intermNode) = 0;
+ }
+#line 5078 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 100:
+#line 830 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.checkNoShaderLayouts((yyvsp[-2].interm.type).loc, (yyvsp[-2].interm.type).shaderQualifiers);
+ parseContext.addQualifierToExisting((yyvsp[-2].interm.type).loc, (yyvsp[-2].interm.type).qualifier, *(yyvsp[-1].lex).string);
+ (yyval.interm.intermNode) = 0;
+ }
+#line 5088 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 101:
+#line 835 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.checkNoShaderLayouts((yyvsp[-3].interm.type).loc, (yyvsp[-3].interm.type).shaderQualifiers);
+ (yyvsp[-1].interm.identifierList)->push_back((yyvsp[-2].lex).string);
+ parseContext.addQualifierToExisting((yyvsp[-3].interm.type).loc, (yyvsp[-3].interm.type).qualifier, *(yyvsp[-1].interm.identifierList));
+ (yyval.interm.intermNode) = 0;
+ }
+#line 5099 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 102:
+#line 844 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { parseContext.nestedBlockCheck((yyvsp[-2].interm.type).loc); }
+#line 5105 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 103:
+#line 844 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ --parseContext.structNestingLevel;
+ parseContext.blockName = (yyvsp[-4].lex).string;
+ parseContext.globalQualifierFixCheck((yyvsp[-5].interm.type).loc, (yyvsp[-5].interm.type).qualifier);
+ parseContext.checkNoShaderLayouts((yyvsp[-5].interm.type).loc, (yyvsp[-5].interm.type).shaderQualifiers);
+ parseContext.currentBlockQualifier = (yyvsp[-5].interm.type).qualifier;
+ (yyval.interm).loc = (yyvsp[-5].interm.type).loc;
+ (yyval.interm).typeList = (yyvsp[-1].interm.typeList);
+ }
+#line 5119 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 104:
+#line 855 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.identifierList) = new TIdentifierList;
+ (yyval.interm.identifierList)->push_back((yyvsp[0].lex).string);
+ }
+#line 5128 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 105:
+#line 859 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.identifierList) = (yyvsp[-2].interm.identifierList);
+ (yyval.interm.identifierList)->push_back((yyvsp[0].lex).string);
+ }
+#line 5137 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 106:
+#line 866 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm).function = (yyvsp[-1].interm.function);
+ (yyval.interm).loc = (yyvsp[0].lex).loc;
+ }
+#line 5146 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 107:
+#line 873 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.function) = (yyvsp[0].interm.function);
+ }
+#line 5154 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 108:
+#line 876 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.function) = (yyvsp[0].interm.function);
+ }
+#line 5162 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 109:
+#line 883 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ // Add the parameter
+ (yyval.interm.function) = (yyvsp[-1].interm.function);
+ if ((yyvsp[0].interm).param.type->getBasicType() != EbtVoid)
+ (yyvsp[-1].interm.function)->addParameter((yyvsp[0].interm).param);
+ else
+ delete (yyvsp[0].interm).param.type;
+ }
+#line 5175 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 110:
+#line 891 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ //
+ // Only first parameter of one-parameter functions can be void
+ // The check for named parameters not being void is done in parameter_declarator
+ //
+ if ((yyvsp[0].interm).param.type->getBasicType() == EbtVoid) {
+ //
+ // This parameter > first is void
+ //
+ parseContext.error((yyvsp[-1].lex).loc, "cannot be an argument type except for '(void)'", "void", "");
+ delete (yyvsp[0].interm).param.type;
+ } else {
+ // Add the parameter
+ (yyval.interm.function) = (yyvsp[-2].interm.function);
+ (yyvsp[-2].interm.function)->addParameter((yyvsp[0].interm).param);
+ }
+ }
+#line 5197 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 111:
+#line 911 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ if ((yyvsp[-2].interm.type).qualifier.storage != EvqGlobal && (yyvsp[-2].interm.type).qualifier.storage != EvqTemporary) {
+ parseContext.error((yyvsp[-1].lex).loc, "no qualifiers allowed for function return",
+ GetStorageQualifierString((yyvsp[-2].interm.type).qualifier.storage), "");
+ }
+ if ((yyvsp[-2].interm.type).arraySizes)
+ parseContext.arraySizeRequiredCheck((yyvsp[-2].interm.type).loc, *(yyvsp[-2].interm.type).arraySizes);
+
+ // Add the function as a prototype after parsing it (we do not support recursion)
+ TFunction *function;
+ TType type((yyvsp[-2].interm.type));
+
+ // Potentially rename shader entry point function. No-op most of the time.
+ parseContext.renameShaderFunction((yyvsp[-1].lex).string);
+
+ // Make the function
+ function = new TFunction((yyvsp[-1].lex).string, type);
+ (yyval.interm.function) = function;
+ }
+#line 5221 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 112:
+#line 934 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ if ((yyvsp[-1].interm.type).arraySizes) {
+ parseContext.profileRequires((yyvsp[-1].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type");
+ parseContext.profileRequires((yyvsp[-1].interm.type).loc, EEsProfile, 300, 0, "arrayed type");
+ parseContext.arraySizeRequiredCheck((yyvsp[-1].interm.type).loc, *(yyvsp[-1].interm.type).arraySizes);
+ }
+ if ((yyvsp[-1].interm.type).basicType == EbtVoid) {
+ parseContext.error((yyvsp[0].lex).loc, "illegal use of type 'void'", (yyvsp[0].lex).string->c_str(), "");
+ }
+ parseContext.reservedErrorCheck((yyvsp[0].lex).loc, *(yyvsp[0].lex).string);
+
+ TParameter param = {(yyvsp[0].lex).string, new TType((yyvsp[-1].interm.type))};
+ (yyval.interm).loc = (yyvsp[0].lex).loc;
+ (yyval.interm).param = param;
+ }
+#line 5241 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 113:
+#line 949 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ if ((yyvsp[-2].interm.type).arraySizes) {
+ parseContext.profileRequires((yyvsp[-2].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type");
+ parseContext.profileRequires((yyvsp[-2].interm.type).loc, EEsProfile, 300, 0, "arrayed type");
+ parseContext.arraySizeRequiredCheck((yyvsp[-2].interm.type).loc, *(yyvsp[-2].interm.type).arraySizes);
+ }
+ TType* type = new TType((yyvsp[-2].interm.type));
+ type->transferArraySizes((yyvsp[0].interm).arraySizes);
+ type->copyArrayInnerSizes((yyvsp[-2].interm.type).arraySizes);
+
+ parseContext.arrayOfArrayVersionCheck((yyvsp[-1].lex).loc, type->getArraySizes());
+ parseContext.arraySizeRequiredCheck((yyvsp[0].interm).loc, *(yyvsp[0].interm).arraySizes);
+ parseContext.reservedErrorCheck((yyvsp[-1].lex).loc, *(yyvsp[-1].lex).string);
+
+ TParameter param = { (yyvsp[-1].lex).string, type };
+
+ (yyval.interm).loc = (yyvsp[-1].lex).loc;
+ (yyval.interm).param = param;
+ }
+#line 5265 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 114:
+#line 974 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm) = (yyvsp[0].interm);
+ if ((yyvsp[-1].interm.type).qualifier.precision != EpqNone)
+ (yyval.interm).param.type->getQualifier().precision = (yyvsp[-1].interm.type).qualifier.precision;
+ parseContext.precisionQualifierCheck((yyval.interm).loc, (yyval.interm).param.type->getBasicType(), (yyval.interm).param.type->getQualifier());
+
+ parseContext.checkNoShaderLayouts((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).shaderQualifiers);
+ parseContext.parameterTypeCheck((yyvsp[0].interm).loc, (yyvsp[-1].interm.type).qualifier.storage, *(yyval.interm).param.type);
+ parseContext.paramCheckFix((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).qualifier, *(yyval.interm).param.type);
+
+ }
+#line 5281 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 115:
+#line 985 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm) = (yyvsp[0].interm);
+
+ parseContext.parameterTypeCheck((yyvsp[0].interm).loc, EvqIn, *(yyvsp[0].interm).param.type);
+ parseContext.paramCheckFixStorage((yyvsp[0].interm).loc, EvqTemporary, *(yyval.interm).param.type);
+ parseContext.precisionQualifierCheck((yyval.interm).loc, (yyval.interm).param.type->getBasicType(), (yyval.interm).param.type->getQualifier());
+ }
+#line 5293 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 116:
+#line 995 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm) = (yyvsp[0].interm);
+ if ((yyvsp[-1].interm.type).qualifier.precision != EpqNone)
+ (yyval.interm).param.type->getQualifier().precision = (yyvsp[-1].interm.type).qualifier.precision;
+ parseContext.precisionQualifierCheck((yyvsp[-1].interm.type).loc, (yyval.interm).param.type->getBasicType(), (yyval.interm).param.type->getQualifier());
+
+ parseContext.checkNoShaderLayouts((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).shaderQualifiers);
+ parseContext.parameterTypeCheck((yyvsp[0].interm).loc, (yyvsp[-1].interm.type).qualifier.storage, *(yyval.interm).param.type);
+ parseContext.paramCheckFix((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).qualifier, *(yyval.interm).param.type);
+ }
+#line 5308 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 117:
+#line 1005 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm) = (yyvsp[0].interm);
+
+ parseContext.parameterTypeCheck((yyvsp[0].interm).loc, EvqIn, *(yyvsp[0].interm).param.type);
+ parseContext.paramCheckFixStorage((yyvsp[0].interm).loc, EvqTemporary, *(yyval.interm).param.type);
+ parseContext.precisionQualifierCheck((yyval.interm).loc, (yyval.interm).param.type->getBasicType(), (yyval.interm).param.type->getQualifier());
+ }
+#line 5320 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 118:
+#line 1015 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ TParameter param = { 0, new TType((yyvsp[0].interm.type)) };
+ (yyval.interm).param = param;
+ if ((yyvsp[0].interm.type).arraySizes)
+ parseContext.arraySizeRequiredCheck((yyvsp[0].interm.type).loc, *(yyvsp[0].interm.type).arraySizes);
+ }
+#line 5331 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 119:
+#line 1024 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm) = (yyvsp[0].interm);
+ }
+#line 5339 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 120:
+#line 1027 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm) = (yyvsp[-2].interm);
+ parseContext.declareVariable((yyvsp[0].lex).loc, *(yyvsp[0].lex).string, (yyvsp[-2].interm).type);
+ }
+#line 5348 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 121:
+#line 1031 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm) = (yyvsp[-3].interm);
+ parseContext.declareVariable((yyvsp[-1].lex).loc, *(yyvsp[-1].lex).string, (yyvsp[-3].interm).type, (yyvsp[0].interm).arraySizes);
+ }
+#line 5357 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 122:
+#line 1035 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm).type = (yyvsp[-5].interm).type;
+ TIntermNode* initNode = parseContext.declareVariable((yyvsp[-3].lex).loc, *(yyvsp[-3].lex).string, (yyvsp[-5].interm).type, (yyvsp[-2].interm).arraySizes, (yyvsp[0].interm.intermTypedNode));
+ (yyval.interm).intermNode = parseContext.intermediate.growAggregate((yyvsp[-5].interm).intermNode, initNode, (yyvsp[-1].lex).loc);
+ }
+#line 5367 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 123:
+#line 1040 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm).type = (yyvsp[-4].interm).type;
+ TIntermNode* initNode = parseContext.declareVariable((yyvsp[-2].lex).loc, *(yyvsp[-2].lex).string, (yyvsp[-4].interm).type, 0, (yyvsp[0].interm.intermTypedNode));
+ (yyval.interm).intermNode = parseContext.intermediate.growAggregate((yyvsp[-4].interm).intermNode, initNode, (yyvsp[-1].lex).loc);
+ }
+#line 5377 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 124:
+#line 1048 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm).type = (yyvsp[0].interm.type);
+ (yyval.interm).intermNode = 0;
+ parseContext.declareTypeDefaults((yyval.interm).loc, (yyval.interm).type);
+ }
+#line 5387 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 125:
+#line 1053 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm).type = (yyvsp[-1].interm.type);
+ (yyval.interm).intermNode = 0;
+ parseContext.declareVariable((yyvsp[0].lex).loc, *(yyvsp[0].lex).string, (yyvsp[-1].interm.type));
+ }
+#line 5397 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 126:
+#line 1058 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm).type = (yyvsp[-2].interm.type);
+ (yyval.interm).intermNode = 0;
+ parseContext.declareVariable((yyvsp[-1].lex).loc, *(yyvsp[-1].lex).string, (yyvsp[-2].interm.type), (yyvsp[0].interm).arraySizes);
+ }
+#line 5407 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 127:
+#line 1063 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm).type = (yyvsp[-4].interm.type);
+ TIntermNode* initNode = parseContext.declareVariable((yyvsp[-3].lex).loc, *(yyvsp[-3].lex).string, (yyvsp[-4].interm.type), (yyvsp[-2].interm).arraySizes, (yyvsp[0].interm.intermTypedNode));
+ (yyval.interm).intermNode = parseContext.intermediate.growAggregate(0, initNode, (yyvsp[-1].lex).loc);
+ }
+#line 5417 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 128:
+#line 1068 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm).type = (yyvsp[-3].interm.type);
+ TIntermNode* initNode = parseContext.declareVariable((yyvsp[-2].lex).loc, *(yyvsp[-2].lex).string, (yyvsp[-3].interm.type), 0, (yyvsp[0].interm.intermTypedNode));
+ (yyval.interm).intermNode = parseContext.intermediate.growAggregate(0, initNode, (yyvsp[-1].lex).loc);
+ }
+#line 5427 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 129:
+#line 1077 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type) = (yyvsp[0].interm.type);
+
+ parseContext.globalQualifierTypeCheck((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type).qualifier, (yyval.interm.type));
+ if ((yyvsp[0].interm.type).arraySizes) {
+ parseContext.profileRequires((yyvsp[0].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type");
+ parseContext.profileRequires((yyvsp[0].interm.type).loc, EEsProfile, 300, 0, "arrayed type");
+ }
+
+ parseContext.precisionQualifierCheck((yyval.interm.type).loc, (yyval.interm.type).basicType, (yyval.interm.type).qualifier);
+ }
+#line 5443 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 130:
+#line 1088 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.globalQualifierFixCheck((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).qualifier);
+ parseContext.globalQualifierTypeCheck((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).qualifier, (yyvsp[0].interm.type));
+
+ if ((yyvsp[0].interm.type).arraySizes) {
+ parseContext.profileRequires((yyvsp[0].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type");
+ parseContext.profileRequires((yyvsp[0].interm.type).loc, EEsProfile, 300, 0, "arrayed type");
+ }
+
+ if ((yyvsp[0].interm.type).arraySizes && parseContext.arrayQualifierError((yyvsp[0].interm.type).loc, (yyvsp[-1].interm.type).qualifier))
+ (yyvsp[0].interm.type).arraySizes = nullptr;
+
+ parseContext.checkNoShaderLayouts((yyvsp[0].interm.type).loc, (yyvsp[-1].interm.type).shaderQualifiers);
+ (yyvsp[0].interm.type).shaderQualifiers.merge((yyvsp[-1].interm.type).shaderQualifiers);
+ parseContext.mergeQualifiers((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type).qualifier, (yyvsp[-1].interm.type).qualifier, true);
+ parseContext.precisionQualifierCheck((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type).basicType, (yyvsp[0].interm.type).qualifier);
+
+ (yyval.interm.type) = (yyvsp[0].interm.type);
+
+ if (! (yyval.interm.type).qualifier.isInterpolation() &&
+ ((parseContext.language == EShLangVertex && (yyval.interm.type).qualifier.storage == EvqVaryingOut) ||
+ (parseContext.language == EShLangFragment && (yyval.interm.type).qualifier.storage == EvqVaryingIn)))
+ (yyval.interm.type).qualifier.smooth = true;
+ }
+#line 5472 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 131:
+#line 1115 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.globalCheck((yyvsp[0].lex).loc, "invariant");
+ parseContext.profileRequires((yyval.interm.type).loc, ENoProfile, 120, 0, "invariant");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.invariant = true;
+ }
+#line 5483 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 132:
+#line 1124 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.globalCheck((yyvsp[0].lex).loc, "smooth");
+ parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "smooth");
+ parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 300, 0, "smooth");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.smooth = true;
+ }
+#line 5495 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 133:
+#line 1131 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.globalCheck((yyvsp[0].lex).loc, "flat");
+ parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "flat");
+ parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 300, 0, "flat");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.flat = true;
+ }
+#line 5507 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 134:
+#line 1138 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.globalCheck((yyvsp[0].lex).loc, "noperspective");
+#ifdef NV_EXTENSIONS
+ parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 0, E_GL_NV_shader_noperspective_interpolation, "noperspective");
+#else
+ parseContext.requireProfile((yyvsp[0].lex).loc, ~EEsProfile, "noperspective");
+#endif
+ parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "noperspective");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.nopersp = true;
+ }
+#line 5523 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 135:
+#line 1149 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.globalCheck((yyvsp[0].lex).loc, "__explicitInterpAMD");
+ parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 450, E_GL_AMD_shader_explicit_vertex_parameter, "explicit interpolation");
+ parseContext.profileRequires((yyvsp[0].lex).loc, ECompatibilityProfile, 450, E_GL_AMD_shader_explicit_vertex_parameter, "explicit interpolation");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.explicitInterp = true;
+#endif
+ }
+#line 5537 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 136:
+#line 1158 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef NV_EXTENSIONS
+ parseContext.globalCheck((yyvsp[0].lex).loc, "pervertexNV");
+ parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric");
+ parseContext.profileRequires((yyvsp[0].lex).loc, ECompatibilityProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric");
+ parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.pervertexNV = true;
+#endif
+ }
+#line 5552 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 137:
+#line 1168 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef NV_EXTENSIONS
+ // No need for profile version or extension check. Shader stage already checks both.
+ parseContext.globalCheck((yyvsp[0].lex).loc, "perprimitiveNV");
+ parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangFragmentMask | EShLangMeshNVMask), "perprimitiveNV");
+ // Fragment shader stage doesn't check for extension. So we explicitly add below extension check.
+ if (parseContext.language == EShLangFragment)
+ parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_NV_mesh_shader, "perprimitiveNV");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.perPrimitiveNV = true;
+#endif
+ }
+#line 5569 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 138:
+#line 1180 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef NV_EXTENSIONS
+ // No need for profile version or extension check. Shader stage already checks both.
+ parseContext.globalCheck((yyvsp[0].lex).loc, "perviewNV");
+ parseContext.requireStage((yyvsp[0].lex).loc, EShLangMeshNV, "perviewNV");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.perViewNV = true;
+#endif
+ }
+#line 5583 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 139:
+#line 1189 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef NV_EXTENSIONS
+ // No need for profile version or extension check. Shader stage already checks both.
+ parseContext.globalCheck((yyvsp[0].lex).loc, "taskNV");
+ parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangTaskNVMask | EShLangMeshNVMask), "taskNV");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.perTaskNV = true;
+#endif
+ }
+#line 5597 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 140:
+#line 1201 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type) = (yyvsp[-1].interm.type);
+ }
+#line 5605 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 141:
+#line 1207 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type) = (yyvsp[0].interm.type);
+ }
+#line 5613 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 142:
+#line 1210 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type) = (yyvsp[-2].interm.type);
+ (yyval.interm.type).shaderQualifiers.merge((yyvsp[0].interm.type).shaderQualifiers);
+ parseContext.mergeObjectLayoutQualifiers((yyval.interm.type).qualifier, (yyvsp[0].interm.type).qualifier, false);
+ }
+#line 5623 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 143:
+#line 1217 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ parseContext.setLayoutQualifier((yyvsp[0].lex).loc, (yyval.interm.type), *(yyvsp[0].lex).string);
+ }
+#line 5632 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 144:
+#line 1221 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[-2].lex).loc);
+ parseContext.setLayoutQualifier((yyvsp[-2].lex).loc, (yyval.interm.type), *(yyvsp[-2].lex).string, (yyvsp[0].interm.intermTypedNode));
+ }
+#line 5641 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 145:
+#line 1225 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { // because "shared" is both an identifier and a keyword
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ TString strShared("shared");
+ parseContext.setLayoutQualifier((yyvsp[0].lex).loc, (yyval.interm.type), strShared);
+ }
+#line 5651 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 146:
+#line 1233 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.profileRequires((yyval.interm.type).loc, ECoreProfile | ECompatibilityProfile, 400, E_GL_ARB_gpu_shader5, "precise");
+ parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, "precise");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.noContraction = true;
+ }
+#line 5662 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 147:
+#line 1242 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type) = (yyvsp[0].interm.type);
+ }
+#line 5670 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 148:
+#line 1245 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type) = (yyvsp[-1].interm.type);
+ if ((yyval.interm.type).basicType == EbtVoid)
+ (yyval.interm.type).basicType = (yyvsp[0].interm.type).basicType;
+
+ (yyval.interm.type).shaderQualifiers.merge((yyvsp[0].interm.type).shaderQualifiers);
+ parseContext.mergeQualifiers((yyval.interm.type).loc, (yyval.interm.type).qualifier, (yyvsp[0].interm.type).qualifier, false);
+ }
+#line 5683 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 149:
+#line 1256 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type) = (yyvsp[0].interm.type);
+ }
+#line 5691 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 150:
+#line 1259 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type) = (yyvsp[0].interm.type);
+ }
+#line 5699 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 151:
+#line 1262 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.checkPrecisionQualifier((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type).qualifier.precision);
+ (yyval.interm.type) = (yyvsp[0].interm.type);
+ }
+#line 5708 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 152:
+#line 1266 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ // allow inheritance of storage qualifier from block declaration
+ (yyval.interm.type) = (yyvsp[0].interm.type);
+ }
+#line 5717 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 153:
+#line 1270 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ // allow inheritance of storage qualifier from block declaration
+ (yyval.interm.type) = (yyvsp[0].interm.type);
+ }
+#line 5726 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 154:
+#line 1274 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ // allow inheritance of storage qualifier from block declaration
+ (yyval.interm.type) = (yyvsp[0].interm.type);
+ }
+#line 5735 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 155:
+#line 1278 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type) = (yyvsp[0].interm.type);
+ }
+#line 5743 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 156:
+#line 1284 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.storage = EvqConst; // will later turn into EvqConstReadOnly, if the initializer is not constant
+ }
+#line 5752 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 157:
+#line 1288 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.requireStage((yyvsp[0].lex).loc, EShLangVertex, "attribute");
+ parseContext.checkDeprecated((yyvsp[0].lex).loc, ECoreProfile, 130, "attribute");
+ parseContext.checkDeprecated((yyvsp[0].lex).loc, ENoProfile, 130, "attribute");
+ parseContext.requireNotRemoved((yyvsp[0].lex).loc, ECoreProfile, 420, "attribute");
+ parseContext.requireNotRemoved((yyvsp[0].lex).loc, EEsProfile, 300, "attribute");
+
+ parseContext.globalCheck((yyvsp[0].lex).loc, "attribute");
+
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.storage = EvqVaryingIn;
+ }
+#line 5769 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 158:
+#line 1300 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.checkDeprecated((yyvsp[0].lex).loc, ENoProfile, 130, "varying");
+ parseContext.checkDeprecated((yyvsp[0].lex).loc, ECoreProfile, 130, "varying");
+ parseContext.requireNotRemoved((yyvsp[0].lex).loc, ECoreProfile, 420, "varying");
+ parseContext.requireNotRemoved((yyvsp[0].lex).loc, EEsProfile, 300, "varying");
+
+ parseContext.globalCheck((yyvsp[0].lex).loc, "varying");
+
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ if (parseContext.language == EShLangVertex)
+ (yyval.interm.type).qualifier.storage = EvqVaryingOut;
+ else
+ (yyval.interm.type).qualifier.storage = EvqVaryingIn;
+ }
+#line 5788 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 159:
+#line 1314 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.globalCheck((yyvsp[0].lex).loc, "inout");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.storage = EvqInOut;
+ }
+#line 5798 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 160:
+#line 1319 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.globalCheck((yyvsp[0].lex).loc, "in");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ // whether this is a parameter "in" or a pipeline "in" will get sorted out a bit later
+ (yyval.interm.type).qualifier.storage = EvqIn;
+ }
+#line 5809 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 161:
+#line 1325 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.globalCheck((yyvsp[0].lex).loc, "out");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ // whether this is a parameter "out" or a pipeline "out" will get sorted out a bit later
+ (yyval.interm.type).qualifier.storage = EvqOut;
+ }
+#line 5820 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 162:
+#line 1331 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 120, 0, "centroid");
+ parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 300, 0, "centroid");
+ parseContext.globalCheck((yyvsp[0].lex).loc, "centroid");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.centroid = true;
+ }
+#line 5832 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 163:
+#line 1338 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.globalCheck((yyvsp[0].lex).loc, "patch");
+ parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangTessControlMask | EShLangTessEvaluationMask), "patch");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.patch = true;
+ }
+#line 5843 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 164:
+#line 1344 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.globalCheck((yyvsp[0].lex).loc, "sample");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.sample = true;
+ }
+#line 5853 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 165:
+#line 1349 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.globalCheck((yyvsp[0].lex).loc, "uniform");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.storage = EvqUniform;
+ }
+#line 5863 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 166:
+#line 1354 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.globalCheck((yyvsp[0].lex).loc, "buffer");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.storage = EvqBuffer;
+ }
+#line 5873 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 167:
+#line 1359 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef NV_EXTENSIONS
+ parseContext.globalCheck((yyvsp[0].lex).loc, "hitAttributeNV");
+ parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangIntersectNVMask | EShLangClosestHitNVMask
+ | EShLangAnyHitNVMask), "hitAttributeNV");
+ parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "hitAttributeNV");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.storage = EvqHitAttrNV;
+#endif
+ }
+#line 5888 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 168:
+#line 1369 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef NV_EXTENSIONS
+ parseContext.globalCheck((yyvsp[0].lex).loc, "rayPayloadNV");
+ parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangRayGenNVMask | EShLangClosestHitNVMask |
+ EShLangAnyHitNVMask | EShLangMissNVMask), "rayPayloadNV");
+ parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "rayPayloadNV");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.storage = EvqPayloadNV;
+#endif
+ }
+#line 5903 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 169:
+#line 1379 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef NV_EXTENSIONS
+ parseContext.globalCheck((yyvsp[0].lex).loc, "rayPayloadInNV");
+ parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangClosestHitNVMask |
+ EShLangAnyHitNVMask | EShLangMissNVMask), "rayPayloadInNV");
+ parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "rayPayloadInNV");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.storage = EvqPayloadInNV;
+#endif
+ }
+#line 5918 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 170:
+#line 1389 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef NV_EXTENSIONS
+ parseContext.globalCheck((yyvsp[0].lex).loc, "callableDataNV");
+ parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangRayGenNVMask |
+ EShLangClosestHitNVMask | EShLangMissNVMask | EShLangCallableNVMask), "callableDataNV");
+ parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "callableDataNV");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.storage = EvqCallableDataNV;
+#endif
+ }
+#line 5933 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 171:
+#line 1399 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef NV_EXTENSIONS
+ parseContext.globalCheck((yyvsp[0].lex).loc, "callableDataInNV");
+ parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangCallableNVMask), "callableDataInNV");
+ parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "callableDataInNV");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.storage = EvqCallableDataInNV;
+#endif
+ }
+#line 5947 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 172:
+#line 1408 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.globalCheck((yyvsp[0].lex).loc, "shared");
+ parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, 430, E_GL_ARB_compute_shader, "shared");
+ parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 310, 0, "shared");
+#ifdef NV_EXTENSIONS
+ parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangComputeMask | EShLangMeshNVMask | EShLangTaskNVMask), "shared");
+#else
+ parseContext.requireStage((yyvsp[0].lex).loc, EShLangCompute, "shared");
+#endif
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.storage = EvqShared;
+ }
+#line 5964 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 173:
+#line 1420 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.coherent = true;
+ }
+#line 5973 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 174:
+#line 1424 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_KHR_memory_scope_semantics, "devicecoherent");
+ (yyval.interm.type).qualifier.devicecoherent = true;
+ }
+#line 5983 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 175:
+#line 1429 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_KHR_memory_scope_semantics, "queuefamilycoherent");
+ (yyval.interm.type).qualifier.queuefamilycoherent = true;
+ }
+#line 5993 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 176:
+#line 1434 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_KHR_memory_scope_semantics, "workgroupcoherent");
+ (yyval.interm.type).qualifier.workgroupcoherent = true;
+ }
+#line 6003 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 177:
+#line 1439 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_KHR_memory_scope_semantics, "subgroupcoherent");
+ (yyval.interm.type).qualifier.subgroupcoherent = true;
+ }
+#line 6013 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 178:
+#line 1444 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_KHR_memory_scope_semantics, "nonprivate");
+ (yyval.interm.type).qualifier.nonprivate = true;
+ }
+#line 6023 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 179:
+#line 1449 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.volatil = true;
+ }
+#line 6032 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 180:
+#line 1453 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.restrict = true;
+ }
+#line 6041 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 181:
+#line 1457 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.readonly = true;
+ }
+#line 6050 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 182:
+#line 1461 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.writeonly = true;
+ }
+#line 6059 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 183:
+#line 1465 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.spvRemoved((yyvsp[0].lex).loc, "subroutine");
+ parseContext.globalCheck((yyvsp[0].lex).loc, "subroutine");
+ parseContext.unimplemented((yyvsp[0].lex).loc, "subroutine");
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ }
+#line 6070 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 184:
+#line 1471 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.spvRemoved((yyvsp[-3].lex).loc, "subroutine");
+ parseContext.globalCheck((yyvsp[-3].lex).loc, "subroutine");
+ parseContext.unimplemented((yyvsp[-3].lex).loc, "subroutine");
+ (yyval.interm.type).init((yyvsp[-3].lex).loc);
+ }
+#line 6081 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 185:
+#line 1480 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc);
+ (yyval.interm.type).qualifier.nonUniform = true;
+ }
+#line 6090 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 186:
+#line 1487 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ // TODO
+ }
+#line 6098 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 187:
+#line 1490 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ // TODO: 4.0 semantics: subroutines
+ // 1) make sure each identifier is a type declared earlier with SUBROUTINE
+ // 2) save all of the identifiers for future comparison with the declared function
+ }
+#line 6108 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 188:
+#line 1498 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type) = (yyvsp[-1].interm.type);
+ (yyval.interm.type).qualifier.precision = parseContext.getDefaultPrecision((yyval.interm.type));
+ (yyval.interm.type).typeParameters = (yyvsp[0].interm.typeParameters);
+ }
+#line 6118 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 189:
+#line 1503 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.arrayOfArrayVersionCheck((yyvsp[0].interm).loc, (yyvsp[0].interm).arraySizes);
+ (yyval.interm.type) = (yyvsp[-2].interm.type);
+ (yyval.interm.type).qualifier.precision = parseContext.getDefaultPrecision((yyval.interm.type));
+ (yyval.interm.type).typeParameters = (yyvsp[-1].interm.typeParameters);
+ (yyval.interm.type).arraySizes = (yyvsp[0].interm).arraySizes;
+ }
+#line 6130 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 190:
+#line 1513 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm).loc = (yyvsp[-1].lex).loc;
+ (yyval.interm).arraySizes = new TArraySizes;
+ (yyval.interm).arraySizes->addInnerSize();
+ }
+#line 6140 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 191:
+#line 1518 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm).loc = (yyvsp[-2].lex).loc;
+ (yyval.interm).arraySizes = new TArraySizes;
+
+ TArraySize size;
+ parseContext.arraySizeCheck((yyvsp[-1].interm.intermTypedNode)->getLoc(), (yyvsp[-1].interm.intermTypedNode), size, "array size");
+ (yyval.interm).arraySizes->addInnerSize(size);
+ }
+#line 6153 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 192:
+#line 1526 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm) = (yyvsp[-2].interm);
+ (yyval.interm).arraySizes->addInnerSize();
+ }
+#line 6162 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 193:
+#line 1530 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm) = (yyvsp[-3].interm);
+
+ TArraySize size;
+ parseContext.arraySizeCheck((yyvsp[-1].interm.intermTypedNode)->getLoc(), (yyvsp[-1].interm.intermTypedNode), size, "array size");
+ (yyval.interm).arraySizes->addInnerSize(size);
+ }
+#line 6174 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 194:
+#line 1540 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.typeParameters) = (yyvsp[0].interm.typeParameters);
+ }
+#line 6182 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 195:
+#line 1543 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.typeParameters) = 0;
+ }
+#line 6190 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 196:
+#line 1549 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.typeParameters) = (yyvsp[-1].interm.typeParameters);
+ }
+#line 6198 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 197:
+#line 1555 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.typeParameters) = new TArraySizes;
+
+ TArraySize size;
+ parseContext.arraySizeCheck((yyvsp[0].interm.intermTypedNode)->getLoc(), (yyvsp[0].interm.intermTypedNode), size, "type parameter");
+ (yyval.interm.typeParameters)->addInnerSize(size);
+ }
+#line 6210 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 198:
+#line 1562 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.typeParameters) = (yyvsp[-2].interm.typeParameters);
+
+ TArraySize size;
+ parseContext.arraySizeCheck((yyvsp[0].interm.intermTypedNode)->getLoc(), (yyvsp[0].interm.intermTypedNode), size, "type parameter");
+ (yyval.interm.typeParameters)->addInnerSize(size);
+ }
+#line 6222 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 199:
+#line 1572 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtVoid;
+ }
+#line 6231 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 200:
+#line 1576 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ }
+#line 6240 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 201:
+#line 1580 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.doubleCheck((yyvsp[0].lex).loc, "double");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ }
+#line 6250 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 202:
+#line 1585 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.float16ScalarVectorCheck((yyvsp[0].lex).loc, "float16_t", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat16;
+ }
+#line 6260 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 203:
+#line 1590 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ }
+#line 6270 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 204:
+#line 1595 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ }
+#line 6280 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 205:
+#line 1600 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtInt;
+ }
+#line 6289 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 206:
+#line 1604 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "unsigned integer");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtUint;
+ }
+#line 6299 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 207:
+#line 1609 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit signed integer", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtInt8;
+ }
+#line 6309 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 208:
+#line 1614 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtUint8;
+ }
+#line 6319 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 209:
+#line 1619 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit signed integer", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtInt16;
+ }
+#line 6329 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 210:
+#line 1624 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtUint16;
+ }
+#line 6339 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 211:
+#line 1629 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed integer", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtInt;
+ }
+#line 6349 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 212:
+#line 1634 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtUint;
+ }
+#line 6359 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 213:
+#line 1639 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int64Check((yyvsp[0].lex).loc, "64-bit integer", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtInt64;
+ }
+#line 6369 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 214:
+#line 1644 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int64Check((yyvsp[0].lex).loc, "64-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtUint64;
+ }
+#line 6379 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 215:
+#line 1649 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtBool;
+ }
+#line 6388 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 216:
+#line 1653 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setVector(2);
+ }
+#line 6398 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 217:
+#line 1658 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setVector(3);
+ }
+#line 6408 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 218:
+#line 1663 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setVector(4);
+ }
+#line 6418 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 219:
+#line 1668 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.doubleCheck((yyvsp[0].lex).loc, "double vector");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setVector(2);
+ }
+#line 6429 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 220:
+#line 1674 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.doubleCheck((yyvsp[0].lex).loc, "double vector");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setVector(3);
+ }
+#line 6440 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 221:
+#line 1680 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.doubleCheck((yyvsp[0].lex).loc, "double vector");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setVector(4);
+ }
+#line 6451 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 222:
+#line 1686 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.float16ScalarVectorCheck((yyvsp[0].lex).loc, "half float vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat16;
+ (yyval.interm.type).setVector(2);
+ }
+#line 6462 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 223:
+#line 1692 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.float16ScalarVectorCheck((yyvsp[0].lex).loc, "half float vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat16;
+ (yyval.interm.type).setVector(3);
+ }
+#line 6473 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 224:
+#line 1698 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.float16ScalarVectorCheck((yyvsp[0].lex).loc, "half float vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat16;
+ (yyval.interm.type).setVector(4);
+ }
+#line 6484 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 225:
+#line 1704 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setVector(2);
+ }
+#line 6495 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 226:
+#line 1710 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setVector(3);
+ }
+#line 6506 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 227:
+#line 1716 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setVector(4);
+ }
+#line 6517 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 228:
+#line 1722 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setVector(2);
+ }
+#line 6528 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 229:
+#line 1728 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setVector(3);
+ }
+#line 6539 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 230:
+#line 1734 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setVector(4);
+ }
+#line 6550 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 231:
+#line 1740 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtBool;
+ (yyval.interm.type).setVector(2);
+ }
+#line 6560 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 232:
+#line 1745 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtBool;
+ (yyval.interm.type).setVector(3);
+ }
+#line 6570 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 233:
+#line 1750 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtBool;
+ (yyval.interm.type).setVector(4);
+ }
+#line 6580 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 234:
+#line 1755 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtInt;
+ (yyval.interm.type).setVector(2);
+ }
+#line 6590 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 235:
+#line 1760 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtInt;
+ (yyval.interm.type).setVector(3);
+ }
+#line 6600 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 236:
+#line 1765 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtInt;
+ (yyval.interm.type).setVector(4);
+ }
+#line 6610 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 237:
+#line 1770 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtInt8;
+ (yyval.interm.type).setVector(2);
+ }
+#line 6621 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 238:
+#line 1776 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtInt8;
+ (yyval.interm.type).setVector(3);
+ }
+#line 6632 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 239:
+#line 1782 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtInt8;
+ (yyval.interm.type).setVector(4);
+ }
+#line 6643 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 240:
+#line 1788 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtInt16;
+ (yyval.interm.type).setVector(2);
+ }
+#line 6654 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 241:
+#line 1794 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtInt16;
+ (yyval.interm.type).setVector(3);
+ }
+#line 6665 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 242:
+#line 1800 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtInt16;
+ (yyval.interm.type).setVector(4);
+ }
+#line 6676 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 243:
+#line 1806 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtInt;
+ (yyval.interm.type).setVector(2);
+ }
+#line 6687 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 244:
+#line 1812 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtInt;
+ (yyval.interm.type).setVector(3);
+ }
+#line 6698 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 245:
+#line 1818 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtInt;
+ (yyval.interm.type).setVector(4);
+ }
+#line 6709 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 246:
+#line 1824 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int64Check((yyvsp[0].lex).loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtInt64;
+ (yyval.interm.type).setVector(2);
+ }
+#line 6720 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 247:
+#line 1830 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int64Check((yyvsp[0].lex).loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtInt64;
+ (yyval.interm.type).setVector(3);
+ }
+#line 6731 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 248:
+#line 1836 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int64Check((yyvsp[0].lex).loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtInt64;
+ (yyval.interm.type).setVector(4);
+ }
+#line 6742 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 249:
+#line 1842 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "unsigned integer vector");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtUint;
+ (yyval.interm.type).setVector(2);
+ }
+#line 6753 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 250:
+#line 1848 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "unsigned integer vector");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtUint;
+ (yyval.interm.type).setVector(3);
+ }
+#line 6764 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 251:
+#line 1854 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "unsigned integer vector");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtUint;
+ (yyval.interm.type).setVector(4);
+ }
+#line 6775 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 252:
+#line 1860 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtUint8;
+ (yyval.interm.type).setVector(2);
+ }
+#line 6786 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 253:
+#line 1866 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtUint8;
+ (yyval.interm.type).setVector(3);
+ }
+#line 6797 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 254:
+#line 1872 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtUint8;
+ (yyval.interm.type).setVector(4);
+ }
+#line 6808 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 255:
+#line 1878 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtUint16;
+ (yyval.interm.type).setVector(2);
+ }
+#line 6819 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 256:
+#line 1884 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtUint16;
+ (yyval.interm.type).setVector(3);
+ }
+#line 6830 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 257:
+#line 1890 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtUint16;
+ (yyval.interm.type).setVector(4);
+ }
+#line 6841 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 258:
+#line 1896 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtUint;
+ (yyval.interm.type).setVector(2);
+ }
+#line 6852 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 259:
+#line 1902 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtUint;
+ (yyval.interm.type).setVector(3);
+ }
+#line 6863 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 260:
+#line 1908 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtUint;
+ (yyval.interm.type).setVector(4);
+ }
+#line 6874 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 261:
+#line 1914 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int64Check((yyvsp[0].lex).loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtUint64;
+ (yyval.interm.type).setVector(2);
+ }
+#line 6885 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 262:
+#line 1920 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int64Check((yyvsp[0].lex).loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtUint64;
+ (yyval.interm.type).setVector(3);
+ }
+#line 6896 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 263:
+#line 1926 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.int64Check((yyvsp[0].lex).loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtUint64;
+ (yyval.interm.type).setVector(4);
+ }
+#line 6907 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 264:
+#line 1932 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(2, 2);
+ }
+#line 6917 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 265:
+#line 1937 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(3, 3);
+ }
+#line 6927 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 266:
+#line 1942 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(4, 4);
+ }
+#line 6937 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 267:
+#line 1947 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(2, 2);
+ }
+#line 6947 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 268:
+#line 1952 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(2, 3);
+ }
+#line 6957 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 269:
+#line 1957 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(2, 4);
+ }
+#line 6967 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 270:
+#line 1962 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(3, 2);
+ }
+#line 6977 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 271:
+#line 1967 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(3, 3);
+ }
+#line 6987 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 272:
+#line 1972 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(3, 4);
+ }
+#line 6997 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 273:
+#line 1977 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(4, 2);
+ }
+#line 7007 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 274:
+#line 1982 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(4, 3);
+ }
+#line 7017 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 275:
+#line 1987 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(4, 4);
+ }
+#line 7027 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 276:
+#line 1992 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(2, 2);
+ }
+#line 7038 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 277:
+#line 1998 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(3, 3);
+ }
+#line 7049 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 278:
+#line 2004 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(4, 4);
+ }
+#line 7060 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 279:
+#line 2010 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(2, 2);
+ }
+#line 7071 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 280:
+#line 2016 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(2, 3);
+ }
+#line 7082 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 281:
+#line 2022 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(2, 4);
+ }
+#line 7093 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 282:
+#line 2028 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(3, 2);
+ }
+#line 7104 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 283:
+#line 2034 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(3, 3);
+ }
+#line 7115 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 284:
+#line 2040 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(3, 4);
+ }
+#line 7126 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 285:
+#line 2046 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(4, 2);
+ }
+#line 7137 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 286:
+#line 2052 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(4, 3);
+ }
+#line 7148 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 287:
+#line 2058 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(4, 4);
+ }
+#line 7159 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 288:
+#line 2064 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat16;
+ (yyval.interm.type).setMatrix(2, 2);
+ }
+#line 7170 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 289:
+#line 2070 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat16;
+ (yyval.interm.type).setMatrix(3, 3);
+ }
+#line 7181 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 290:
+#line 2076 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat16;
+ (yyval.interm.type).setMatrix(4, 4);
+ }
+#line 7192 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 291:
+#line 2082 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat16;
+ (yyval.interm.type).setMatrix(2, 2);
+ }
+#line 7203 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 292:
+#line 2088 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat16;
+ (yyval.interm.type).setMatrix(2, 3);
+ }
+#line 7214 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 293:
+#line 2094 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat16;
+ (yyval.interm.type).setMatrix(2, 4);
+ }
+#line 7225 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 294:
+#line 2100 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat16;
+ (yyval.interm.type).setMatrix(3, 2);
+ }
+#line 7236 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 295:
+#line 2106 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat16;
+ (yyval.interm.type).setMatrix(3, 3);
+ }
+#line 7247 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 296:
+#line 2112 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat16;
+ (yyval.interm.type).setMatrix(3, 4);
+ }
+#line 7258 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 297:
+#line 2118 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat16;
+ (yyval.interm.type).setMatrix(4, 2);
+ }
+#line 7269 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 298:
+#line 2124 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat16;
+ (yyval.interm.type).setMatrix(4, 3);
+ }
+#line 7280 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 299:
+#line 2130 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat16;
+ (yyval.interm.type).setMatrix(4, 4);
+ }
+#line 7291 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 300:
+#line 2136 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(2, 2);
+ }
+#line 7302 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 301:
+#line 2142 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(3, 3);
+ }
+#line 7313 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 302:
+#line 2148 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(4, 4);
+ }
+#line 7324 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 303:
+#line 2154 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(2, 2);
+ }
+#line 7335 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 304:
+#line 2160 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(2, 3);
+ }
+#line 7346 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 305:
+#line 2166 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(2, 4);
+ }
+#line 7357 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 306:
+#line 2172 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(3, 2);
+ }
+#line 7368 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 307:
+#line 2178 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(3, 3);
+ }
+#line 7379 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 308:
+#line 2184 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(3, 4);
+ }
+#line 7390 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 309:
+#line 2190 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(4, 2);
+ }
+#line 7401 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 310:
+#line 2196 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(4, 3);
+ }
+#line 7412 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 311:
+#line 2202 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).setMatrix(4, 4);
+ }
+#line 7423 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 312:
+#line 2208 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(2, 2);
+ }
+#line 7434 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 313:
+#line 2214 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(3, 3);
+ }
+#line 7445 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 314:
+#line 2220 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(4, 4);
+ }
+#line 7456 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 315:
+#line 2226 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(2, 2);
+ }
+#line 7467 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 316:
+#line 2232 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(2, 3);
+ }
+#line 7478 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 317:
+#line 2238 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(2, 4);
+ }
+#line 7489 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 318:
+#line 2244 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(3, 2);
+ }
+#line 7500 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 319:
+#line 2250 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(3, 3);
+ }
+#line 7511 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 320:
+#line 2256 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(3, 4);
+ }
+#line 7522 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 321:
+#line 2262 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(4, 2);
+ }
+#line 7533 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 322:
+#line 2268 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(4, 3);
+ }
+#line 7544 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 323:
+#line 2274 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtDouble;
+ (yyval.interm.type).setMatrix(4, 4);
+ }
+#line 7555 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 324:
+#line 2280 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef NV_EXTENSIONS
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtAccStructNV;
+#endif
+ }
+#line 7566 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 325:
+#line 2286 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.vulkanRemoved((yyvsp[0].lex).loc, "atomic counter types");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtAtomicUint;
+ }
+#line 7576 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 326:
+#line 2291 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat, Esd1D);
+ }
+#line 7586 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 327:
+#line 2296 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat, Esd2D);
+ }
+#line 7596 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 328:
+#line 2301 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat, Esd3D);
+ }
+#line 7606 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 329:
+#line 2306 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat, EsdCube);
+ }
+#line 7616 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 330:
+#line 2311 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat, Esd1D, false, true);
+ }
+#line 7626 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 331:
+#line 2316 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat, Esd2D, false, true);
+ }
+#line 7636 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 332:
+#line 2321 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat, EsdCube, false, true);
+ }
+#line 7646 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 333:
+#line 2326 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat, Esd1D, true);
+ }
+#line 7656 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 334:
+#line 2331 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat, Esd2D, true);
+ }
+#line 7666 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 335:
+#line 2336 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat, Esd1D, true, true);
+ }
+#line 7676 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 336:
+#line 2341 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat, Esd2D, true, true);
+ }
+#line 7686 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 337:
+#line 2346 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat, EsdCube, true);
+ }
+#line 7696 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 338:
+#line 2351 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat, EsdCube, true, true);
+ }
+#line 7706 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 339:
+#line 2356 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat16, Esd1D);
+#endif
+ }
+#line 7719 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 340:
+#line 2364 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat16, Esd2D);
+#endif
+ }
+#line 7732 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 341:
+#line 2372 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat16, Esd3D);
+#endif
+ }
+#line 7745 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 342:
+#line 2380 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat16, EsdCube);
+#endif
+ }
+#line 7758 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 343:
+#line 2388 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat16, Esd1D, false, true);
+#endif
+ }
+#line 7771 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 344:
+#line 2396 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat16, Esd2D, false, true);
+#endif
+ }
+#line 7784 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 345:
+#line 2404 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat16, EsdCube, false, true);
+#endif
+ }
+#line 7797 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 346:
+#line 2412 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat16, Esd1D, true);
+#endif
+ }
+#line 7810 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 347:
+#line 2420 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat16, Esd2D, true);
+#endif
+ }
+#line 7823 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 348:
+#line 2428 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat16, Esd1D, true, true);
+#endif
+ }
+#line 7836 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 349:
+#line 2436 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat16, Esd2D, true, true);
+#endif
+ }
+#line 7849 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 350:
+#line 2444 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat16, EsdCube, true);
+#endif
+ }
+#line 7862 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 351:
+#line 2452 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat16, EsdCube, true, true);
+#endif
+ }
+#line 7875 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 352:
+#line 2460 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtInt, Esd1D);
+ }
+#line 7885 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 353:
+#line 2465 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtInt, Esd2D);
+ }
+#line 7895 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 354:
+#line 2470 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtInt, Esd3D);
+ }
+#line 7905 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 355:
+#line 2475 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtInt, EsdCube);
+ }
+#line 7915 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 356:
+#line 2480 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtInt, Esd1D, true);
+ }
+#line 7925 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 357:
+#line 2485 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtInt, Esd2D, true);
+ }
+#line 7935 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 358:
+#line 2490 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtInt, EsdCube, true);
+ }
+#line 7945 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 359:
+#line 2495 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtUint, Esd1D);
+ }
+#line 7955 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 360:
+#line 2500 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtUint, Esd2D);
+ }
+#line 7965 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 361:
+#line 2505 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtUint, Esd3D);
+ }
+#line 7975 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 362:
+#line 2510 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtUint, EsdCube);
+ }
+#line 7985 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 363:
+#line 2515 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtUint, Esd1D, true);
+ }
+#line 7995 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 364:
+#line 2520 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtUint, Esd2D, true);
+ }
+#line 8005 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 365:
+#line 2525 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtUint, EsdCube, true);
+ }
+#line 8015 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 366:
+#line 2530 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat, EsdRect);
+ }
+#line 8025 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 367:
+#line 2535 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat, EsdRect, false, true);
+ }
+#line 8035 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 368:
+#line 2540 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat16, EsdRect);
+#endif
+ }
+#line 8048 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 369:
+#line 2548 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat16, EsdRect, false, true);
+#endif
+ }
+#line 8061 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 370:
+#line 2556 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtInt, EsdRect);
+ }
+#line 8071 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 371:
+#line 2561 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtUint, EsdRect);
+ }
+#line 8081 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 372:
+#line 2566 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat, EsdBuffer);
+ }
+#line 8091 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 373:
+#line 2571 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat16, EsdBuffer);
+#endif
+ }
+#line 8104 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 374:
+#line 2579 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtInt, EsdBuffer);
+ }
+#line 8114 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 375:
+#line 2584 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtUint, EsdBuffer);
+ }
+#line 8124 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 376:
+#line 2589 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat, Esd2D, false, false, true);
+ }
+#line 8134 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 377:
+#line 2594 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat16, Esd2D, false, false, true);
+#endif
+ }
+#line 8147 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 378:
+#line 2602 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtInt, Esd2D, false, false, true);
+ }
+#line 8157 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 379:
+#line 2607 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtUint, Esd2D, false, false, true);
+ }
+#line 8167 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 380:
+#line 2612 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat, Esd2D, true, false, true);
+ }
+#line 8177 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 381:
+#line 2617 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat16, Esd2D, true, false, true);
+#endif
+ }
+#line 8190 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 382:
+#line 2625 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtInt, Esd2D, true, false, true);
+ }
+#line 8200 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 383:
+#line 2630 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtUint, Esd2D, true, false, true);
+ }
+#line 8210 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 384:
+#line 2635 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setPureSampler(false);
+ }
+#line 8220 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 385:
+#line 2640 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setPureSampler(true);
+ }
+#line 8230 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 386:
+#line 2645 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat, Esd1D);
+ }
+#line 8240 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 387:
+#line 2650 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd1D);
+#endif
+ }
+#line 8253 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 388:
+#line 2658 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat, Esd2D);
+ }
+#line 8263 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 389:
+#line 2663 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd2D);
+#endif
+ }
+#line 8276 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 390:
+#line 2671 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat, Esd3D);
+ }
+#line 8286 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 391:
+#line 2676 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd3D);
+#endif
+ }
+#line 8299 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 392:
+#line 2684 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat, EsdCube);
+ }
+#line 8309 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 393:
+#line 2689 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat16, EsdCube);
+#endif
+ }
+#line 8322 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 394:
+#line 2697 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat, Esd1D, true);
+ }
+#line 8332 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 395:
+#line 2702 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd1D, true);
+#endif
+ }
+#line 8345 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 396:
+#line 2710 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat, Esd2D, true);
+ }
+#line 8355 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 397:
+#line 2715 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd2D, true);
+#endif
+ }
+#line 8368 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 398:
+#line 2723 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat, EsdCube, true);
+ }
+#line 8378 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 399:
+#line 2728 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat16, EsdCube, true);
+#endif
+ }
+#line 8391 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 400:
+#line 2736 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtInt, Esd1D);
+ }
+#line 8401 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 401:
+#line 2741 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtInt, Esd2D);
+ }
+#line 8411 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 402:
+#line 2746 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtInt, Esd3D);
+ }
+#line 8421 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 403:
+#line 2751 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtInt, EsdCube);
+ }
+#line 8431 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 404:
+#line 2756 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtInt, Esd1D, true);
+ }
+#line 8441 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 405:
+#line 2761 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtInt, Esd2D, true);
+ }
+#line 8451 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 406:
+#line 2766 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtInt, EsdCube, true);
+ }
+#line 8461 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 407:
+#line 2771 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtUint, Esd1D);
+ }
+#line 8471 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 408:
+#line 2776 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtUint, Esd2D);
+ }
+#line 8481 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 409:
+#line 2781 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtUint, Esd3D);
+ }
+#line 8491 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 410:
+#line 2786 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtUint, EsdCube);
+ }
+#line 8501 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 411:
+#line 2791 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtUint, Esd1D, true);
+ }
+#line 8511 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 412:
+#line 2796 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtUint, Esd2D, true);
+ }
+#line 8521 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 413:
+#line 2801 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtUint, EsdCube, true);
+ }
+#line 8531 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 414:
+#line 2806 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat, EsdRect);
+ }
+#line 8541 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 415:
+#line 2811 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat16, EsdRect);
+#endif
+ }
+#line 8554 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 416:
+#line 2819 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtInt, EsdRect);
+ }
+#line 8564 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 417:
+#line 2824 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtUint, EsdRect);
+ }
+#line 8574 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 418:
+#line 2829 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat, EsdBuffer);
+ }
+#line 8584 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 419:
+#line 2834 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat16, EsdBuffer);
+#endif
+ }
+#line 8597 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 420:
+#line 2842 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtInt, EsdBuffer);
+ }
+#line 8607 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 421:
+#line 2847 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtUint, EsdBuffer);
+ }
+#line 8617 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 422:
+#line 2852 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat, Esd2D, false, false, true);
+ }
+#line 8627 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 423:
+#line 2857 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd2D, false, false, true);
+#endif
+ }
+#line 8640 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 424:
+#line 2865 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtInt, Esd2D, false, false, true);
+ }
+#line 8650 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 425:
+#line 2870 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtUint, Esd2D, false, false, true);
+ }
+#line 8660 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 426:
+#line 2875 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat, Esd2D, true, false, true);
+ }
+#line 8670 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 427:
+#line 2880 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd2D, true, false, true);
+#endif
+ }
+#line 8683 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 428:
+#line 2888 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtInt, Esd2D, true, false, true);
+ }
+#line 8693 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 429:
+#line 2893 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setTexture(EbtUint, Esd2D, true, false, true);
+ }
+#line 8703 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 430:
+#line 2898 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat, Esd1D);
+ }
+#line 8713 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 431:
+#line 2903 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat16, Esd1D);
+#endif
+ }
+#line 8726 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 432:
+#line 2911 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtInt, Esd1D);
+ }
+#line 8736 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 433:
+#line 2916 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtUint, Esd1D);
+ }
+#line 8746 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 434:
+#line 2921 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat, Esd2D);
+ }
+#line 8756 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 435:
+#line 2926 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat16, Esd2D);
+#endif
+ }
+#line 8769 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 436:
+#line 2934 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtInt, Esd2D);
+ }
+#line 8779 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 437:
+#line 2939 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtUint, Esd2D);
+ }
+#line 8789 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 438:
+#line 2944 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat, Esd3D);
+ }
+#line 8799 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 439:
+#line 2949 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat16, Esd3D);
+#endif
+ }
+#line 8812 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 440:
+#line 2957 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtInt, Esd3D);
+ }
+#line 8822 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 441:
+#line 2962 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtUint, Esd3D);
+ }
+#line 8832 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 442:
+#line 2967 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat, EsdRect);
+ }
+#line 8842 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 443:
+#line 2972 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat16, EsdRect);
+#endif
+ }
+#line 8855 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 444:
+#line 2980 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtInt, EsdRect);
+ }
+#line 8865 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 445:
+#line 2985 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtUint, EsdRect);
+ }
+#line 8875 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 446:
+#line 2990 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat, EsdCube);
+ }
+#line 8885 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 447:
+#line 2995 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat16, EsdCube);
+#endif
+ }
+#line 8898 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 448:
+#line 3003 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtInt, EsdCube);
+ }
+#line 8908 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 449:
+#line 3008 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtUint, EsdCube);
+ }
+#line 8918 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 450:
+#line 3013 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat, EsdBuffer);
+ }
+#line 8928 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 451:
+#line 3018 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat16, EsdBuffer);
+#endif
+ }
+#line 8941 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 452:
+#line 3026 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtInt, EsdBuffer);
+ }
+#line 8951 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 453:
+#line 3031 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtUint, EsdBuffer);
+ }
+#line 8961 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 454:
+#line 3036 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat, Esd1D, true);
+ }
+#line 8971 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 455:
+#line 3041 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat16, Esd1D, true);
+#endif
+ }
+#line 8984 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 456:
+#line 3049 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtInt, Esd1D, true);
+ }
+#line 8994 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 457:
+#line 3054 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtUint, Esd1D, true);
+ }
+#line 9004 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 458:
+#line 3059 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat, Esd2D, true);
+ }
+#line 9014 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 459:
+#line 3064 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat16, Esd2D, true);
+#endif
+ }
+#line 9027 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 460:
+#line 3072 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtInt, Esd2D, true);
+ }
+#line 9037 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 461:
+#line 3077 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtUint, Esd2D, true);
+ }
+#line 9047 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 462:
+#line 3082 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat, EsdCube, true);
+ }
+#line 9057 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 463:
+#line 3087 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat16, EsdCube, true);
+#endif
+ }
+#line 9070 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 464:
+#line 3095 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtInt, EsdCube, true);
+ }
+#line 9080 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 465:
+#line 3100 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtUint, EsdCube, true);
+ }
+#line 9090 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 466:
+#line 3105 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat, Esd2D, false, false, true);
+ }
+#line 9100 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 467:
+#line 3110 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat16, Esd2D, false, false, true);
+#endif
+ }
+#line 9113 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 468:
+#line 3118 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtInt, Esd2D, false, false, true);
+ }
+#line 9123 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 469:
+#line 3123 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtUint, Esd2D, false, false, true);
+ }
+#line 9133 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 470:
+#line 3128 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat, Esd2D, true, false, true);
+ }
+#line 9143 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 471:
+#line 3133 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtFloat16, Esd2D, true, false, true);
+#endif
+ }
+#line 9156 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 472:
+#line 3141 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtInt, Esd2D, true, false, true);
+ }
+#line 9166 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 473:
+#line 3146 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setImage(EbtUint, Esd2D, true, false, true);
+ }
+#line 9176 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 474:
+#line 3151 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { // GL_OES_EGL_image_external
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat, Esd2D);
+ (yyval.interm.type).sampler.external = true;
+ }
+#line 9187 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 475:
+#line 3157 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { // GL_EXT_YUV_target
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.set(EbtFloat, Esd2D);
+ (yyval.interm.type).sampler.yuv = true;
+ }
+#line 9198 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 476:
+#line 3163 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setSubpass(EbtFloat);
+ }
+#line 9209 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 477:
+#line 3169 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setSubpass(EbtFloat, true);
+ }
+#line 9220 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 478:
+#line 3175 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float subpass input", parseContext.symbolTable.atBuiltInLevel());
+ parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setSubpass(EbtFloat16);
+#endif
+ }
+#line 9234 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 479:
+#line 3184 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+#ifdef AMD_EXTENSIONS
+ parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float subpass input", parseContext.symbolTable.atBuiltInLevel());
+ parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setSubpass(EbtFloat16, true);
+#endif
+ }
+#line 9248 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 480:
+#line 3193 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setSubpass(EbtInt);
+ }
+#line 9259 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 481:
+#line 3199 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setSubpass(EbtInt, true);
+ }
+#line 9270 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 482:
+#line 3205 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setSubpass(EbtUint);
+ }
+#line 9281 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 483:
+#line 3211 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtSampler;
+ (yyval.interm.type).sampler.setSubpass(EbtUint, true);
+ }
+#line 9292 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 484:
+#line 3217 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.fcoopmatCheck((yyvsp[0].lex).loc, "fcoopmatNV", parseContext.symbolTable.atBuiltInLevel());
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtFloat;
+ (yyval.interm.type).coopmat = true;
+ }
+#line 9303 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 485:
+#line 3223 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.type) = (yyvsp[0].interm.type);
+ (yyval.interm.type).qualifier.storage = parseContext.symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;
+ parseContext.structTypeCheck((yyval.interm.type).loc, (yyval.interm.type));
+ }
+#line 9313 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 486:
+#line 3228 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ //
+ // This is for user defined type names. The lexical phase looked up the
+ // type.
+ //
+ if (const TVariable* variable = ((yyvsp[0].lex).symbol)->getAsVariable()) {
+ const TType& structure = variable->getType();
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ (yyval.interm.type).basicType = EbtStruct;
+ (yyval.interm.type).userDef = &structure;
+ } else
+ parseContext.error((yyvsp[0].lex).loc, "expected type name", (yyvsp[0].lex).string->c_str(), "");
+ }
+#line 9331 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 487:
+#line 3244 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "highp precision qualifier");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ parseContext.handlePrecisionQualifier((yyvsp[0].lex).loc, (yyval.interm.type).qualifier, EpqHigh);
+ }
+#line 9341 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 488:
+#line 3249 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "mediump precision qualifier");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ parseContext.handlePrecisionQualifier((yyvsp[0].lex).loc, (yyval.interm.type).qualifier, EpqMedium);
+ }
+#line 9351 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 489:
+#line 3254 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "lowp precision qualifier");
+ (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel());
+ parseContext.handlePrecisionQualifier((yyvsp[0].lex).loc, (yyval.interm.type).qualifier, EpqLow);
+ }
+#line 9361 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 490:
+#line 3262 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { parseContext.nestedStructCheck((yyvsp[-2].lex).loc); }
+#line 9367 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 491:
+#line 3262 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ TType* structure = new TType((yyvsp[-1].interm.typeList), *(yyvsp[-4].lex).string);
+ parseContext.structArrayCheck((yyvsp[-4].lex).loc, *structure);
+ TVariable* userTypeDef = new TVariable((yyvsp[-4].lex).string, *structure, true);
+ if (! parseContext.symbolTable.insert(*userTypeDef))
+ parseContext.error((yyvsp[-4].lex).loc, "redefinition", (yyvsp[-4].lex).string->c_str(), "struct");
+ (yyval.interm.type).init((yyvsp[-5].lex).loc);
+ (yyval.interm.type).basicType = EbtStruct;
+ (yyval.interm.type).userDef = structure;
+ --parseContext.structNestingLevel;
+ }
+#line 9383 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 492:
+#line 3273 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { parseContext.nestedStructCheck((yyvsp[-1].lex).loc); }
+#line 9389 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 493:
+#line 3273 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ TType* structure = new TType((yyvsp[-1].interm.typeList), TString(""));
+ (yyval.interm.type).init((yyvsp[-4].lex).loc);
+ (yyval.interm.type).basicType = EbtStruct;
+ (yyval.interm.type).userDef = structure;
+ --parseContext.structNestingLevel;
+ }
+#line 9401 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 494:
+#line 3283 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.typeList) = (yyvsp[0].interm.typeList);
+ }
+#line 9409 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 495:
+#line 3286 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.typeList) = (yyvsp[-1].interm.typeList);
+ for (unsigned int i = 0; i < (yyvsp[0].interm.typeList)->size(); ++i) {
+ for (unsigned int j = 0; j < (yyval.interm.typeList)->size(); ++j) {
+ if ((*(yyval.interm.typeList))[j].type->getFieldName() == (*(yyvsp[0].interm.typeList))[i].type->getFieldName())
+ parseContext.error((*(yyvsp[0].interm.typeList))[i].loc, "duplicate member name:", "", (*(yyvsp[0].interm.typeList))[i].type->getFieldName().c_str());
+ }
+ (yyval.interm.typeList)->push_back((*(yyvsp[0].interm.typeList))[i]);
+ }
+ }
+#line 9424 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 496:
+#line 3299 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ if ((yyvsp[-2].interm.type).arraySizes) {
+ parseContext.profileRequires((yyvsp[-2].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type");
+ parseContext.profileRequires((yyvsp[-2].interm.type).loc, EEsProfile, 300, 0, "arrayed type");
+ if (parseContext.profile == EEsProfile)
+ parseContext.arraySizeRequiredCheck((yyvsp[-2].interm.type).loc, *(yyvsp[-2].interm.type).arraySizes);
+ }
+
+ (yyval.interm.typeList) = (yyvsp[-1].interm.typeList);
+
+ parseContext.voidErrorCheck((yyvsp[-2].interm.type).loc, (*(yyvsp[-1].interm.typeList))[0].type->getFieldName(), (yyvsp[-2].interm.type).basicType);
+ parseContext.precisionQualifierCheck((yyvsp[-2].interm.type).loc, (yyvsp[-2].interm.type).basicType, (yyvsp[-2].interm.type).qualifier);
+
+ for (unsigned int i = 0; i < (yyval.interm.typeList)->size(); ++i) {
+ TType type((yyvsp[-2].interm.type));
+ type.setFieldName((*(yyval.interm.typeList))[i].type->getFieldName());
+ type.transferArraySizes((*(yyval.interm.typeList))[i].type->getArraySizes());
+ type.copyArrayInnerSizes((yyvsp[-2].interm.type).arraySizes);
+ parseContext.arrayOfArrayVersionCheck((*(yyval.interm.typeList))[i].loc, type.getArraySizes());
+ (*(yyval.interm.typeList))[i].type->shallowCopy(type);
+ }
+ }
+#line 9451 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 497:
+#line 3321 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ if ((yyvsp[-2].interm.type).arraySizes) {
+ parseContext.profileRequires((yyvsp[-2].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type");
+ parseContext.profileRequires((yyvsp[-2].interm.type).loc, EEsProfile, 300, 0, "arrayed type");
+ if (parseContext.profile == EEsProfile)
+ parseContext.arraySizeRequiredCheck((yyvsp[-2].interm.type).loc, *(yyvsp[-2].interm.type).arraySizes);
+ }
+
+ (yyval.interm.typeList) = (yyvsp[-1].interm.typeList);
+
+ parseContext.memberQualifierCheck((yyvsp[-3].interm.type));
+ parseContext.voidErrorCheck((yyvsp[-2].interm.type).loc, (*(yyvsp[-1].interm.typeList))[0].type->getFieldName(), (yyvsp[-2].interm.type).basicType);
+ parseContext.mergeQualifiers((yyvsp[-2].interm.type).loc, (yyvsp[-2].interm.type).qualifier, (yyvsp[-3].interm.type).qualifier, true);
+ parseContext.precisionQualifierCheck((yyvsp[-2].interm.type).loc, (yyvsp[-2].interm.type).basicType, (yyvsp[-2].interm.type).qualifier);
+
+ for (unsigned int i = 0; i < (yyval.interm.typeList)->size(); ++i) {
+ TType type((yyvsp[-2].interm.type));
+ type.setFieldName((*(yyval.interm.typeList))[i].type->getFieldName());
+ type.transferArraySizes((*(yyval.interm.typeList))[i].type->getArraySizes());
+ type.copyArrayInnerSizes((yyvsp[-2].interm.type).arraySizes);
+ parseContext.arrayOfArrayVersionCheck((*(yyval.interm.typeList))[i].loc, type.getArraySizes());
+ (*(yyval.interm.typeList))[i].type->shallowCopy(type);
+ }
+ }
+#line 9480 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 498:
+#line 3348 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.typeList) = new TTypeList;
+ (yyval.interm.typeList)->push_back((yyvsp[0].interm.typeLine));
+ }
+#line 9489 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 499:
+#line 3352 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.typeList)->push_back((yyvsp[0].interm.typeLine));
+ }
+#line 9497 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 500:
+#line 3358 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.typeLine).type = new TType(EbtVoid);
+ (yyval.interm.typeLine).loc = (yyvsp[0].lex).loc;
+ (yyval.interm.typeLine).type->setFieldName(*(yyvsp[0].lex).string);
+ }
+#line 9507 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 501:
+#line 3363 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.arrayOfArrayVersionCheck((yyvsp[-1].lex).loc, (yyvsp[0].interm).arraySizes);
+
+ (yyval.interm.typeLine).type = new TType(EbtVoid);
+ (yyval.interm.typeLine).loc = (yyvsp[-1].lex).loc;
+ (yyval.interm.typeLine).type->setFieldName(*(yyvsp[-1].lex).string);
+ (yyval.interm.typeLine).type->transferArraySizes((yyvsp[0].interm).arraySizes);
+ }
+#line 9520 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 502:
+#line 3374 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ }
+#line 9528 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 503:
+#line 3377 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ const char* initFeature = "{ } style initializers";
+ parseContext.requireProfile((yyvsp[-2].lex).loc, ~EEsProfile, initFeature);
+ parseContext.profileRequires((yyvsp[-2].lex).loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature);
+ (yyval.interm.intermTypedNode) = (yyvsp[-1].interm.intermTypedNode);
+ }
+#line 9539 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 504:
+#line 3383 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ const char* initFeature = "{ } style initializers";
+ parseContext.requireProfile((yyvsp[-3].lex).loc, ~EEsProfile, initFeature);
+ parseContext.profileRequires((yyvsp[-3].lex).loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature);
+ (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode);
+ }
+#line 9550 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 505:
+#line 3392 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.growAggregate(0, (yyvsp[0].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)->getLoc());
+ }
+#line 9558 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 506:
+#line 3395 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = parseContext.intermediate.growAggregate((yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode));
+ }
+#line 9566 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 507:
+#line 3401 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); }
+#line 9572 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 508:
+#line 3405 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); }
+#line 9578 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 509:
+#line 3406 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); }
+#line 9584 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 510:
+#line 3412 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); }
+#line 9590 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 511:
+#line 3413 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); }
+#line 9596 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 512:
+#line 3414 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); }
+#line 9602 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 513:
+#line 3415 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); }
+#line 9608 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 514:
+#line 3416 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); }
+#line 9614 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 515:
+#line 3417 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); }
+#line 9620 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 516:
+#line 3418 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); }
+#line 9626 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 517:
+#line 3422 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermNode) = 0; }
+#line 9632 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 518:
+#line 3423 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.symbolTable.push();
+ ++parseContext.statementNestingLevel;
+ }
+#line 9641 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 519:
+#line 3427 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]);
+ --parseContext.statementNestingLevel;
+ }
+#line 9650 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 520:
+#line 3431 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ if ((yyvsp[-2].interm.intermNode) && (yyvsp[-2].interm.intermNode)->getAsAggregate())
+ (yyvsp[-2].interm.intermNode)->getAsAggregate()->setOperator(EOpSequence);
+ (yyval.interm.intermNode) = (yyvsp[-2].interm.intermNode);
+ }
+#line 9660 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 521:
+#line 3439 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); }
+#line 9666 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 522:
+#line 3440 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); }
+#line 9672 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 523:
+#line 3444 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ ++parseContext.controlFlowNestingLevel;
+ }
+#line 9680 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 524:
+#line 3447 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ --parseContext.controlFlowNestingLevel;
+ (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode);
+ }
+#line 9689 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 525:
+#line 3451 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.symbolTable.push();
+ ++parseContext.statementNestingLevel;
+ ++parseContext.controlFlowNestingLevel;
+ }
+#line 9699 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 526:
+#line 3456 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]);
+ --parseContext.statementNestingLevel;
+ --parseContext.controlFlowNestingLevel;
+ (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode);
+ }
+#line 9710 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 527:
+#line 3465 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermNode) = 0;
+ }
+#line 9718 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 528:
+#line 3468 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ if ((yyvsp[-1].interm.intermNode) && (yyvsp[-1].interm.intermNode)->getAsAggregate())
+ (yyvsp[-1].interm.intermNode)->getAsAggregate()->setOperator(EOpSequence);
+ (yyval.interm.intermNode) = (yyvsp[-1].interm.intermNode);
+ }
+#line 9728 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 529:
+#line 3476 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermNode) = parseContext.intermediate.makeAggregate((yyvsp[0].interm.intermNode));
+ if ((yyvsp[0].interm.intermNode) && (yyvsp[0].interm.intermNode)->getAsBranchNode() && ((yyvsp[0].interm.intermNode)->getAsBranchNode()->getFlowOp() == EOpCase ||
+ (yyvsp[0].interm.intermNode)->getAsBranchNode()->getFlowOp() == EOpDefault)) {
+ parseContext.wrapupSwitchSubsequence(0, (yyvsp[0].interm.intermNode));
+ (yyval.interm.intermNode) = 0; // start a fresh subsequence for what's after this case
+ }
+ }
+#line 9741 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 530:
+#line 3484 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ if ((yyvsp[0].interm.intermNode) && (yyvsp[0].interm.intermNode)->getAsBranchNode() && ((yyvsp[0].interm.intermNode)->getAsBranchNode()->getFlowOp() == EOpCase ||
+ (yyvsp[0].interm.intermNode)->getAsBranchNode()->getFlowOp() == EOpDefault)) {
+ parseContext.wrapupSwitchSubsequence((yyvsp[-1].interm.intermNode) ? (yyvsp[-1].interm.intermNode)->getAsAggregate() : 0, (yyvsp[0].interm.intermNode));
+ (yyval.interm.intermNode) = 0; // start a fresh subsequence for what's after this case
+ } else
+ (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyvsp[-1].interm.intermNode), (yyvsp[0].interm.intermNode));
+ }
+#line 9754 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 531:
+#line 3495 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermNode) = 0; }
+#line 9760 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 532:
+#line 3496 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ { (yyval.interm.intermNode) = static_cast<TIntermNode*>((yyvsp[-1].interm.intermTypedNode)); }
+#line 9766 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 533:
+#line 3500 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode);
+ }
+#line 9774 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 534:
+#line 3503 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.handleSelectionAttributes(*(yyvsp[-1].interm.attributes), (yyvsp[0].interm.intermNode));
+ (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode);
+ }
+#line 9783 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 535:
+#line 3509 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.boolCheck((yyvsp[-4].lex).loc, (yyvsp[-2].interm.intermTypedNode));
+ (yyval.interm.intermNode) = parseContext.intermediate.addSelection((yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.nodePair), (yyvsp[-4].lex).loc);
+ }
+#line 9792 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 536:
+#line 3516 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.nodePair).node1 = (yyvsp[-2].interm.intermNode);
+ (yyval.interm.nodePair).node2 = (yyvsp[0].interm.intermNode);
+ }
+#line 9801 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 537:
+#line 3520 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.nodePair).node1 = (yyvsp[0].interm.intermNode);
+ (yyval.interm.nodePair).node2 = 0;
+ }
+#line 9810 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 538:
+#line 3528 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ parseContext.boolCheck((yyvsp[0].interm.intermTypedNode)->getLoc(), (yyvsp[0].interm.intermTypedNode));
+ }
+#line 9819 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 539:
+#line 3532 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.boolCheck((yyvsp[-2].lex).loc, (yyvsp[-3].interm.type));
+
+ TType type((yyvsp[-3].interm.type));
+ TIntermNode* initNode = parseContext.declareVariable((yyvsp[-2].lex).loc, *(yyvsp[-2].lex).string, (yyvsp[-3].interm.type), 0, (yyvsp[0].interm.intermTypedNode));
+ if (initNode)
+ (yyval.interm.intermTypedNode) = initNode->getAsTyped();
+ else
+ (yyval.interm.intermTypedNode) = 0;
+ }
+#line 9834 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 540:
+#line 3545 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode);
+ }
+#line 9842 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 541:
+#line 3548 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.handleSwitchAttributes(*(yyvsp[-1].interm.attributes), (yyvsp[0].interm.intermNode));
+ (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode);
+ }
+#line 9851 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 542:
+#line 3554 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ // start new switch sequence on the switch stack
+ ++parseContext.controlFlowNestingLevel;
+ ++parseContext.statementNestingLevel;
+ parseContext.switchSequenceStack.push_back(new TIntermSequence);
+ parseContext.switchLevel.push_back(parseContext.statementNestingLevel);
+ parseContext.symbolTable.push();
+ }
+#line 9864 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 543:
+#line 3562 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermNode) = parseContext.addSwitch((yyvsp[-7].lex).loc, (yyvsp[-5].interm.intermTypedNode), (yyvsp[-1].interm.intermNode) ? (yyvsp[-1].interm.intermNode)->getAsAggregate() : 0);
+ delete parseContext.switchSequenceStack.back();
+ parseContext.switchSequenceStack.pop_back();
+ parseContext.switchLevel.pop_back();
+ parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]);
+ --parseContext.statementNestingLevel;
+ --parseContext.controlFlowNestingLevel;
+ }
+#line 9878 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 544:
+#line 3574 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermNode) = 0;
+ }
+#line 9886 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 545:
+#line 3577 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode);
+ }
+#line 9894 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 546:
+#line 3583 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermNode) = 0;
+ if (parseContext.switchLevel.size() == 0)
+ parseContext.error((yyvsp[-2].lex).loc, "cannot appear outside switch statement", "case", "");
+ else if (parseContext.switchLevel.back() != parseContext.statementNestingLevel)
+ parseContext.error((yyvsp[-2].lex).loc, "cannot be nested inside control flow", "case", "");
+ else {
+ parseContext.constantValueCheck((yyvsp[-1].interm.intermTypedNode), "case");
+ parseContext.integerCheck((yyvsp[-1].interm.intermTypedNode), "case");
+ (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpCase, (yyvsp[-1].interm.intermTypedNode), (yyvsp[-2].lex).loc);
+ }
+ }
+#line 9911 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 547:
+#line 3595 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermNode) = 0;
+ if (parseContext.switchLevel.size() == 0)
+ parseContext.error((yyvsp[-1].lex).loc, "cannot appear outside switch statement", "default", "");
+ else if (parseContext.switchLevel.back() != parseContext.statementNestingLevel)
+ parseContext.error((yyvsp[-1].lex).loc, "cannot be nested inside control flow", "default", "");
+ else
+ (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpDefault, (yyvsp[-1].lex).loc);
+ }
+#line 9925 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 548:
+#line 3607 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode);
+ }
+#line 9933 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 549:
+#line 3610 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.handleLoopAttributes(*(yyvsp[-1].interm.attributes), (yyvsp[0].interm.intermNode));
+ (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode);
+ }
+#line 9942 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 550:
+#line 3616 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ if (! parseContext.limits.whileLoops)
+ parseContext.error((yyvsp[-1].lex).loc, "while loops not available", "limitation", "");
+ parseContext.symbolTable.push();
+ ++parseContext.loopNestingLevel;
+ ++parseContext.statementNestingLevel;
+ ++parseContext.controlFlowNestingLevel;
+ }
+#line 9955 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 551:
+#line 3624 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]);
+ (yyval.interm.intermNode) = parseContext.intermediate.addLoop((yyvsp[0].interm.intermNode), (yyvsp[-2].interm.intermTypedNode), 0, true, (yyvsp[-5].lex).loc);
+ --parseContext.loopNestingLevel;
+ --parseContext.statementNestingLevel;
+ --parseContext.controlFlowNestingLevel;
+ }
+#line 9967 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 552:
+#line 3631 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ ++parseContext.loopNestingLevel;
+ ++parseContext.statementNestingLevel;
+ ++parseContext.controlFlowNestingLevel;
+ }
+#line 9977 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 553:
+#line 3636 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ if (! parseContext.limits.whileLoops)
+ parseContext.error((yyvsp[-7].lex).loc, "do-while loops not available", "limitation", "");
+
+ parseContext.boolCheck((yyvsp[0].lex).loc, (yyvsp[-2].interm.intermTypedNode));
+
+ (yyval.interm.intermNode) = parseContext.intermediate.addLoop((yyvsp[-5].interm.intermNode), (yyvsp[-2].interm.intermTypedNode), 0, false, (yyvsp[-4].lex).loc);
+ --parseContext.loopNestingLevel;
+ --parseContext.statementNestingLevel;
+ --parseContext.controlFlowNestingLevel;
+ }
+#line 9993 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 554:
+#line 3647 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.symbolTable.push();
+ ++parseContext.loopNestingLevel;
+ ++parseContext.statementNestingLevel;
+ ++parseContext.controlFlowNestingLevel;
+ }
+#line 10004 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 555:
+#line 3653 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]);
+ (yyval.interm.intermNode) = parseContext.intermediate.makeAggregate((yyvsp[-3].interm.intermNode), (yyvsp[-5].lex).loc);
+ TIntermLoop* forLoop = parseContext.intermediate.addLoop((yyvsp[0].interm.intermNode), reinterpret_cast<TIntermTyped*>((yyvsp[-2].interm.nodePair).node1), reinterpret_cast<TIntermTyped*>((yyvsp[-2].interm.nodePair).node2), true, (yyvsp[-6].lex).loc);
+ if (! parseContext.limits.nonInductiveForLoops)
+ parseContext.inductiveLoopCheck((yyvsp[-6].lex).loc, (yyvsp[-3].interm.intermNode), forLoop);
+ (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyval.interm.intermNode), forLoop, (yyvsp[-6].lex).loc);
+ (yyval.interm.intermNode)->getAsAggregate()->setOperator(EOpSequence);
+ --parseContext.loopNestingLevel;
+ --parseContext.statementNestingLevel;
+ --parseContext.controlFlowNestingLevel;
+ }
+#line 10021 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 556:
+#line 3668 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode);
+ }
+#line 10029 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 557:
+#line 3671 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode);
+ }
+#line 10037 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 558:
+#line 3677 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ }
+#line 10045 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 559:
+#line 3680 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermTypedNode) = 0;
+ }
+#line 10053 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 560:
+#line 3686 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.nodePair).node1 = (yyvsp[-1].interm.intermTypedNode);
+ (yyval.interm.nodePair).node2 = 0;
+ }
+#line 10062 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 561:
+#line 3690 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.nodePair).node1 = (yyvsp[-2].interm.intermTypedNode);
+ (yyval.interm.nodePair).node2 = (yyvsp[0].interm.intermTypedNode);
+ }
+#line 10071 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 562:
+#line 3697 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ if (parseContext.loopNestingLevel <= 0)
+ parseContext.error((yyvsp[-1].lex).loc, "continue statement only allowed in loops", "", "");
+ (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpContinue, (yyvsp[-1].lex).loc);
+ }
+#line 10081 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 563:
+#line 3702 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ if (parseContext.loopNestingLevel + parseContext.switchSequenceStack.size() <= 0)
+ parseContext.error((yyvsp[-1].lex).loc, "break statement only allowed in switch and loops", "", "");
+ (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpBreak, (yyvsp[-1].lex).loc);
+ }
+#line 10091 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 564:
+#line 3707 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpReturn, (yyvsp[-1].lex).loc);
+ if (parseContext.currentFunctionType->getBasicType() != EbtVoid)
+ parseContext.error((yyvsp[-1].lex).loc, "non-void function must return a value", "return", "");
+ if (parseContext.inMain)
+ parseContext.postEntryPointReturn = true;
+ }
+#line 10103 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 565:
+#line 3714 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermNode) = parseContext.handleReturnValue((yyvsp[-2].lex).loc, (yyvsp[-1].interm.intermTypedNode));
+ }
+#line 10111 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 566:
+#line 3717 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.requireStage((yyvsp[-1].lex).loc, EShLangFragment, "discard");
+ (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpKill, (yyvsp[-1].lex).loc);
+ }
+#line 10120 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 567:
+#line 3726 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode);
+ parseContext.intermediate.setTreeRoot((yyval.interm.intermNode));
+ }
+#line 10129 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 568:
+#line 3730 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ if ((yyvsp[0].interm.intermNode) != nullptr) {
+ (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyvsp[-1].interm.intermNode), (yyvsp[0].interm.intermNode));
+ parseContext.intermediate.setTreeRoot((yyval.interm.intermNode));
+ }
+ }
+#line 10140 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 569:
+#line 3739 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode);
+ }
+#line 10148 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 570:
+#line 3742 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode);
+ }
+#line 10156 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 571:
+#line 3745 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ parseContext.requireProfile((yyvsp[0].lex).loc, ~EEsProfile, "extraneous semicolon");
+ parseContext.profileRequires((yyvsp[0].lex).loc, ~EEsProfile, 460, nullptr, "extraneous semicolon");
+ (yyval.interm.intermNode) = nullptr;
+ }
+#line 10166 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 572:
+#line 3753 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyvsp[0].interm).function = parseContext.handleFunctionDeclarator((yyvsp[0].interm).loc, *(yyvsp[0].interm).function, false /* not prototype */);
+ (yyvsp[0].interm).intermNode = parseContext.handleFunctionDefinition((yyvsp[0].interm).loc, *(yyvsp[0].interm).function);
+ }
+#line 10175 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 573:
+#line 3757 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ // May be best done as post process phase on intermediate code
+ if (parseContext.currentFunctionType->getBasicType() != EbtVoid && ! parseContext.functionReturnsValue)
+ parseContext.error((yyvsp[-2].interm).loc, "function does not return a value:", "", (yyvsp[-2].interm).function->getName().c_str());
+ parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]);
+ (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyvsp[-2].interm).intermNode, (yyvsp[0].interm.intermNode));
+ parseContext.intermediate.setAggregateOperator((yyval.interm.intermNode), EOpFunction, (yyvsp[-2].interm).function->getType(), (yyvsp[-2].interm).loc);
+ (yyval.interm.intermNode)->getAsAggregate()->setName((yyvsp[-2].interm).function->getMangledName().c_str());
+
+ // store the pragma information for debug and optimize and other vendor specific
+ // information. This information can be queried from the parse tree
+ (yyval.interm.intermNode)->getAsAggregate()->setOptimize(parseContext.contextPragma.optimize);
+ (yyval.interm.intermNode)->getAsAggregate()->setDebug(parseContext.contextPragma.debug);
+ (yyval.interm.intermNode)->getAsAggregate()->setPragmaTable(parseContext.contextPragma.pragmaTable);
+ }
+#line 10195 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 574:
+#line 3775 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.attributes) = (yyvsp[-2].interm.attributes);
+ parseContext.requireExtensions((yyvsp[-4].lex).loc, 1, &E_GL_EXT_control_flow_attributes, "attribute");
+ }
+#line 10204 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 575:
+#line 3781 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.attributes) = (yyvsp[0].interm.attributes);
+ }
+#line 10212 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 576:
+#line 3784 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.attributes) = parseContext.mergeAttributes((yyvsp[-2].interm.attributes), (yyvsp[0].interm.attributes));
+ }
+#line 10220 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 577:
+#line 3789 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.attributes) = parseContext.makeAttributes(*(yyvsp[0].lex).string);
+ }
+#line 10228 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+ case 578:
+#line 3792 "MachineIndependent/glslang.y" /* yacc.c:1646 */
+ {
+ (yyval.interm.attributes) = parseContext.makeAttributes(*(yyvsp[-3].lex).string, (yyvsp[-1].interm.intermTypedNode));
+ }
+#line 10236 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ break;
+
+
+#line 10240 "MachineIndependent/glslang_tab.cpp" /* yacc.c:1646 */
+ default: break;
+ }
+ /* User semantic actions sometimes alter yychar, and that requires
+ that yytoken be updated with the new translation. We take the
+ approach of translating immediately before every use of yytoken.
+ One alternative is translating here after every semantic action,
+ but that translation would be missed if the semantic action invokes
+ YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+ if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an
+ incorrect destructor might then be invoked immediately. In the
+ case of YYERROR or YYBACKUP, subsequent parser actions might lead
+ to an incorrect destructor call or verbose syntax error message
+ before the lookahead is translated. */
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+ /* Now 'shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*--------------------------------------.
+| yyerrlab -- here on detecting error. |
+`--------------------------------------*/
+yyerrlab:
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (pParseContext, YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+ yyssp, yytoken)
+ {
+ char const *yymsgp = YY_("syntax error");
+ int yysyntax_error_status;
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ if (yysyntax_error_status == 0)
+ yymsgp = yymsg;
+ else if (yysyntax_error_status == 1)
+ {
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+ if (!yymsg)
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ yysyntax_error_status = 2;
+ }
+ else
+ {
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ yymsgp = yymsg;
+ }
+ }
+ yyerror (pParseContext, yymsgp);
+ if (yysyntax_error_status == 2)
+ goto yyexhaustedlab;
+ }
+# undef YYSYNTAX_ERROR
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval, pParseContext);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule whose action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (!yypact_value_is_default (yyn))
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp, pParseContext);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+ *++yyvsp = yylval;
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#if !defined yyoverflow || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (pParseContext, YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEMPTY)
+ {
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = YYTRANSLATE (yychar);
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval, pParseContext);
+ }
+ /* Do not reclaim the symbols of the rule whose action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp, pParseContext);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ return yyresult;
+}
+#line 3796 "MachineIndependent/glslang.y" /* yacc.c:1906 */
+
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/glslang_tab.cpp.h b/src/3rdparty/glslang/glslang/MachineIndependent/glslang_tab.cpp.h
new file mode 100644
index 0000000..a467db6
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/glslang_tab.cpp.h
@@ -0,0 +1,509 @@
+/* A Bison parser, made by GNU Bison 3.0.4. */
+
+/* Bison interface for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+#ifndef YY_YY_MACHINEINDEPENDENT_GLSLANG_TAB_CPP_H_INCLUDED
+# define YY_YY_MACHINEINDEPENDENT_GLSLANG_TAB_CPP_H_INCLUDED
+/* Debug traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 1
+#endif
+#if YYDEBUG
+extern int yydebug;
+#endif
+
+/* Token type. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ enum yytokentype
+ {
+ ATTRIBUTE = 258,
+ VARYING = 259,
+ FLOAT16_T = 260,
+ FLOAT = 261,
+ FLOAT32_T = 262,
+ DOUBLE = 263,
+ FLOAT64_T = 264,
+ CONST = 265,
+ BOOL = 266,
+ INT = 267,
+ UINT = 268,
+ INT64_T = 269,
+ UINT64_T = 270,
+ INT32_T = 271,
+ UINT32_T = 272,
+ INT16_T = 273,
+ UINT16_T = 274,
+ INT8_T = 275,
+ UINT8_T = 276,
+ BREAK = 277,
+ CONTINUE = 278,
+ DO = 279,
+ ELSE = 280,
+ FOR = 281,
+ IF = 282,
+ DISCARD = 283,
+ RETURN = 284,
+ SWITCH = 285,
+ CASE = 286,
+ DEFAULT = 287,
+ SUBROUTINE = 288,
+ BVEC2 = 289,
+ BVEC3 = 290,
+ BVEC4 = 291,
+ IVEC2 = 292,
+ IVEC3 = 293,
+ IVEC4 = 294,
+ UVEC2 = 295,
+ UVEC3 = 296,
+ UVEC4 = 297,
+ I64VEC2 = 298,
+ I64VEC3 = 299,
+ I64VEC4 = 300,
+ U64VEC2 = 301,
+ U64VEC3 = 302,
+ U64VEC4 = 303,
+ I32VEC2 = 304,
+ I32VEC3 = 305,
+ I32VEC4 = 306,
+ U32VEC2 = 307,
+ U32VEC3 = 308,
+ U32VEC4 = 309,
+ I16VEC2 = 310,
+ I16VEC3 = 311,
+ I16VEC4 = 312,
+ U16VEC2 = 313,
+ U16VEC3 = 314,
+ U16VEC4 = 315,
+ I8VEC2 = 316,
+ I8VEC3 = 317,
+ I8VEC4 = 318,
+ U8VEC2 = 319,
+ U8VEC3 = 320,
+ U8VEC4 = 321,
+ VEC2 = 322,
+ VEC3 = 323,
+ VEC4 = 324,
+ MAT2 = 325,
+ MAT3 = 326,
+ MAT4 = 327,
+ CENTROID = 328,
+ IN = 329,
+ OUT = 330,
+ INOUT = 331,
+ UNIFORM = 332,
+ PATCH = 333,
+ SAMPLE = 334,
+ BUFFER = 335,
+ SHARED = 336,
+ NONUNIFORM = 337,
+ PAYLOADNV = 338,
+ PAYLOADINNV = 339,
+ HITATTRNV = 340,
+ CALLDATANV = 341,
+ CALLDATAINNV = 342,
+ COHERENT = 343,
+ VOLATILE = 344,
+ RESTRICT = 345,
+ READONLY = 346,
+ WRITEONLY = 347,
+ DEVICECOHERENT = 348,
+ QUEUEFAMILYCOHERENT = 349,
+ WORKGROUPCOHERENT = 350,
+ SUBGROUPCOHERENT = 351,
+ NONPRIVATE = 352,
+ DVEC2 = 353,
+ DVEC3 = 354,
+ DVEC4 = 355,
+ DMAT2 = 356,
+ DMAT3 = 357,
+ DMAT4 = 358,
+ F16VEC2 = 359,
+ F16VEC3 = 360,
+ F16VEC4 = 361,
+ F16MAT2 = 362,
+ F16MAT3 = 363,
+ F16MAT4 = 364,
+ F32VEC2 = 365,
+ F32VEC3 = 366,
+ F32VEC4 = 367,
+ F32MAT2 = 368,
+ F32MAT3 = 369,
+ F32MAT4 = 370,
+ F64VEC2 = 371,
+ F64VEC3 = 372,
+ F64VEC4 = 373,
+ F64MAT2 = 374,
+ F64MAT3 = 375,
+ F64MAT4 = 376,
+ NOPERSPECTIVE = 377,
+ FLAT = 378,
+ SMOOTH = 379,
+ LAYOUT = 380,
+ EXPLICITINTERPAMD = 381,
+ PERVERTEXNV = 382,
+ PERPRIMITIVENV = 383,
+ PERVIEWNV = 384,
+ PERTASKNV = 385,
+ MAT2X2 = 386,
+ MAT2X3 = 387,
+ MAT2X4 = 388,
+ MAT3X2 = 389,
+ MAT3X3 = 390,
+ MAT3X4 = 391,
+ MAT4X2 = 392,
+ MAT4X3 = 393,
+ MAT4X4 = 394,
+ DMAT2X2 = 395,
+ DMAT2X3 = 396,
+ DMAT2X4 = 397,
+ DMAT3X2 = 398,
+ DMAT3X3 = 399,
+ DMAT3X4 = 400,
+ DMAT4X2 = 401,
+ DMAT4X3 = 402,
+ DMAT4X4 = 403,
+ F16MAT2X2 = 404,
+ F16MAT2X3 = 405,
+ F16MAT2X4 = 406,
+ F16MAT3X2 = 407,
+ F16MAT3X3 = 408,
+ F16MAT3X4 = 409,
+ F16MAT4X2 = 410,
+ F16MAT4X3 = 411,
+ F16MAT4X4 = 412,
+ F32MAT2X2 = 413,
+ F32MAT2X3 = 414,
+ F32MAT2X4 = 415,
+ F32MAT3X2 = 416,
+ F32MAT3X3 = 417,
+ F32MAT3X4 = 418,
+ F32MAT4X2 = 419,
+ F32MAT4X3 = 420,
+ F32MAT4X4 = 421,
+ F64MAT2X2 = 422,
+ F64MAT2X3 = 423,
+ F64MAT2X4 = 424,
+ F64MAT3X2 = 425,
+ F64MAT3X3 = 426,
+ F64MAT3X4 = 427,
+ F64MAT4X2 = 428,
+ F64MAT4X3 = 429,
+ F64MAT4X4 = 430,
+ ATOMIC_UINT = 431,
+ ACCSTRUCTNV = 432,
+ FCOOPMATNV = 433,
+ SAMPLER1D = 434,
+ SAMPLER2D = 435,
+ SAMPLER3D = 436,
+ SAMPLERCUBE = 437,
+ SAMPLER1DSHADOW = 438,
+ SAMPLER2DSHADOW = 439,
+ SAMPLERCUBESHADOW = 440,
+ SAMPLER1DARRAY = 441,
+ SAMPLER2DARRAY = 442,
+ SAMPLER1DARRAYSHADOW = 443,
+ SAMPLER2DARRAYSHADOW = 444,
+ ISAMPLER1D = 445,
+ ISAMPLER2D = 446,
+ ISAMPLER3D = 447,
+ ISAMPLERCUBE = 448,
+ ISAMPLER1DARRAY = 449,
+ ISAMPLER2DARRAY = 450,
+ USAMPLER1D = 451,
+ USAMPLER2D = 452,
+ USAMPLER3D = 453,
+ USAMPLERCUBE = 454,
+ USAMPLER1DARRAY = 455,
+ USAMPLER2DARRAY = 456,
+ SAMPLER2DRECT = 457,
+ SAMPLER2DRECTSHADOW = 458,
+ ISAMPLER2DRECT = 459,
+ USAMPLER2DRECT = 460,
+ SAMPLERBUFFER = 461,
+ ISAMPLERBUFFER = 462,
+ USAMPLERBUFFER = 463,
+ SAMPLERCUBEARRAY = 464,
+ SAMPLERCUBEARRAYSHADOW = 465,
+ ISAMPLERCUBEARRAY = 466,
+ USAMPLERCUBEARRAY = 467,
+ SAMPLER2DMS = 468,
+ ISAMPLER2DMS = 469,
+ USAMPLER2DMS = 470,
+ SAMPLER2DMSARRAY = 471,
+ ISAMPLER2DMSARRAY = 472,
+ USAMPLER2DMSARRAY = 473,
+ SAMPLEREXTERNALOES = 474,
+ SAMPLEREXTERNAL2DY2YEXT = 475,
+ F16SAMPLER1D = 476,
+ F16SAMPLER2D = 477,
+ F16SAMPLER3D = 478,
+ F16SAMPLER2DRECT = 479,
+ F16SAMPLERCUBE = 480,
+ F16SAMPLER1DARRAY = 481,
+ F16SAMPLER2DARRAY = 482,
+ F16SAMPLERCUBEARRAY = 483,
+ F16SAMPLERBUFFER = 484,
+ F16SAMPLER2DMS = 485,
+ F16SAMPLER2DMSARRAY = 486,
+ F16SAMPLER1DSHADOW = 487,
+ F16SAMPLER2DSHADOW = 488,
+ F16SAMPLER1DARRAYSHADOW = 489,
+ F16SAMPLER2DARRAYSHADOW = 490,
+ F16SAMPLER2DRECTSHADOW = 491,
+ F16SAMPLERCUBESHADOW = 492,
+ F16SAMPLERCUBEARRAYSHADOW = 493,
+ SAMPLER = 494,
+ SAMPLERSHADOW = 495,
+ TEXTURE1D = 496,
+ TEXTURE2D = 497,
+ TEXTURE3D = 498,
+ TEXTURECUBE = 499,
+ TEXTURE1DARRAY = 500,
+ TEXTURE2DARRAY = 501,
+ ITEXTURE1D = 502,
+ ITEXTURE2D = 503,
+ ITEXTURE3D = 504,
+ ITEXTURECUBE = 505,
+ ITEXTURE1DARRAY = 506,
+ ITEXTURE2DARRAY = 507,
+ UTEXTURE1D = 508,
+ UTEXTURE2D = 509,
+ UTEXTURE3D = 510,
+ UTEXTURECUBE = 511,
+ UTEXTURE1DARRAY = 512,
+ UTEXTURE2DARRAY = 513,
+ TEXTURE2DRECT = 514,
+ ITEXTURE2DRECT = 515,
+ UTEXTURE2DRECT = 516,
+ TEXTUREBUFFER = 517,
+ ITEXTUREBUFFER = 518,
+ UTEXTUREBUFFER = 519,
+ TEXTURECUBEARRAY = 520,
+ ITEXTURECUBEARRAY = 521,
+ UTEXTURECUBEARRAY = 522,
+ TEXTURE2DMS = 523,
+ ITEXTURE2DMS = 524,
+ UTEXTURE2DMS = 525,
+ TEXTURE2DMSARRAY = 526,
+ ITEXTURE2DMSARRAY = 527,
+ UTEXTURE2DMSARRAY = 528,
+ F16TEXTURE1D = 529,
+ F16TEXTURE2D = 530,
+ F16TEXTURE3D = 531,
+ F16TEXTURE2DRECT = 532,
+ F16TEXTURECUBE = 533,
+ F16TEXTURE1DARRAY = 534,
+ F16TEXTURE2DARRAY = 535,
+ F16TEXTURECUBEARRAY = 536,
+ F16TEXTUREBUFFER = 537,
+ F16TEXTURE2DMS = 538,
+ F16TEXTURE2DMSARRAY = 539,
+ SUBPASSINPUT = 540,
+ SUBPASSINPUTMS = 541,
+ ISUBPASSINPUT = 542,
+ ISUBPASSINPUTMS = 543,
+ USUBPASSINPUT = 544,
+ USUBPASSINPUTMS = 545,
+ F16SUBPASSINPUT = 546,
+ F16SUBPASSINPUTMS = 547,
+ IMAGE1D = 548,
+ IIMAGE1D = 549,
+ UIMAGE1D = 550,
+ IMAGE2D = 551,
+ IIMAGE2D = 552,
+ UIMAGE2D = 553,
+ IMAGE3D = 554,
+ IIMAGE3D = 555,
+ UIMAGE3D = 556,
+ IMAGE2DRECT = 557,
+ IIMAGE2DRECT = 558,
+ UIMAGE2DRECT = 559,
+ IMAGECUBE = 560,
+ IIMAGECUBE = 561,
+ UIMAGECUBE = 562,
+ IMAGEBUFFER = 563,
+ IIMAGEBUFFER = 564,
+ UIMAGEBUFFER = 565,
+ IMAGE1DARRAY = 566,
+ IIMAGE1DARRAY = 567,
+ UIMAGE1DARRAY = 568,
+ IMAGE2DARRAY = 569,
+ IIMAGE2DARRAY = 570,
+ UIMAGE2DARRAY = 571,
+ IMAGECUBEARRAY = 572,
+ IIMAGECUBEARRAY = 573,
+ UIMAGECUBEARRAY = 574,
+ IMAGE2DMS = 575,
+ IIMAGE2DMS = 576,
+ UIMAGE2DMS = 577,
+ IMAGE2DMSARRAY = 578,
+ IIMAGE2DMSARRAY = 579,
+ UIMAGE2DMSARRAY = 580,
+ F16IMAGE1D = 581,
+ F16IMAGE2D = 582,
+ F16IMAGE3D = 583,
+ F16IMAGE2DRECT = 584,
+ F16IMAGECUBE = 585,
+ F16IMAGE1DARRAY = 586,
+ F16IMAGE2DARRAY = 587,
+ F16IMAGECUBEARRAY = 588,
+ F16IMAGEBUFFER = 589,
+ F16IMAGE2DMS = 590,
+ F16IMAGE2DMSARRAY = 591,
+ STRUCT = 592,
+ VOID = 593,
+ WHILE = 594,
+ IDENTIFIER = 595,
+ TYPE_NAME = 596,
+ FLOATCONSTANT = 597,
+ DOUBLECONSTANT = 598,
+ INT16CONSTANT = 599,
+ UINT16CONSTANT = 600,
+ INT32CONSTANT = 601,
+ UINT32CONSTANT = 602,
+ INTCONSTANT = 603,
+ UINTCONSTANT = 604,
+ INT64CONSTANT = 605,
+ UINT64CONSTANT = 606,
+ BOOLCONSTANT = 607,
+ FLOAT16CONSTANT = 608,
+ LEFT_OP = 609,
+ RIGHT_OP = 610,
+ INC_OP = 611,
+ DEC_OP = 612,
+ LE_OP = 613,
+ GE_OP = 614,
+ EQ_OP = 615,
+ NE_OP = 616,
+ AND_OP = 617,
+ OR_OP = 618,
+ XOR_OP = 619,
+ MUL_ASSIGN = 620,
+ DIV_ASSIGN = 621,
+ ADD_ASSIGN = 622,
+ MOD_ASSIGN = 623,
+ LEFT_ASSIGN = 624,
+ RIGHT_ASSIGN = 625,
+ AND_ASSIGN = 626,
+ XOR_ASSIGN = 627,
+ OR_ASSIGN = 628,
+ SUB_ASSIGN = 629,
+ LEFT_PAREN = 630,
+ RIGHT_PAREN = 631,
+ LEFT_BRACKET = 632,
+ RIGHT_BRACKET = 633,
+ LEFT_BRACE = 634,
+ RIGHT_BRACE = 635,
+ DOT = 636,
+ COMMA = 637,
+ COLON = 638,
+ EQUAL = 639,
+ SEMICOLON = 640,
+ BANG = 641,
+ DASH = 642,
+ TILDE = 643,
+ PLUS = 644,
+ STAR = 645,
+ SLASH = 646,
+ PERCENT = 647,
+ LEFT_ANGLE = 648,
+ RIGHT_ANGLE = 649,
+ VERTICAL_BAR = 650,
+ CARET = 651,
+ AMPERSAND = 652,
+ QUESTION = 653,
+ INVARIANT = 654,
+ PRECISE = 655,
+ HIGH_PRECISION = 656,
+ MEDIUM_PRECISION = 657,
+ LOW_PRECISION = 658,
+ PRECISION = 659,
+ PACKED = 660,
+ RESOURCE = 661,
+ SUPERP = 662
+ };
+#endif
+
+/* Value type. */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+
+union YYSTYPE
+{
+#line 71 "MachineIndependent/glslang.y" /* yacc.c:1909 */
+
+ struct {
+ glslang::TSourceLoc loc;
+ union {
+ glslang::TString *string;
+ int i;
+ unsigned int u;
+ long long i64;
+ unsigned long long u64;
+ bool b;
+ double d;
+ };
+ glslang::TSymbol* symbol;
+ } lex;
+ struct {
+ glslang::TSourceLoc loc;
+ glslang::TOperator op;
+ union {
+ TIntermNode* intermNode;
+ glslang::TIntermNodePair nodePair;
+ glslang::TIntermTyped* intermTypedNode;
+ glslang::TAttributes* attributes;
+ };
+ union {
+ glslang::TPublicType type;
+ glslang::TFunction* function;
+ glslang::TParameter param;
+ glslang::TTypeLoc typeLine;
+ glslang::TTypeList* typeList;
+ glslang::TArraySizes* arraySizes;
+ glslang::TIdentifierList* identifierList;
+ };
+ glslang::TArraySizes* typeParameters;
+ } interm;
+
+#line 498 "MachineIndependent/glslang_tab.cpp.h" /* yacc.c:1909 */
+};
+
+typedef union YYSTYPE YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+
+int yyparse (glslang::TParseContext* pParseContext);
+
+#endif /* !YY_YY_MACHINEINDEPENDENT_GLSLANG_TAB_CPP_H_INCLUDED */
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/intermOut.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/intermOut.cpp
new file mode 100644
index 0000000..a2c3627
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/intermOut.cpp
@@ -0,0 +1,1518 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2012-2016 LunarG, Inc.
+// Copyright (C) 2017 ARM Limited.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "localintermediate.h"
+#include "../Include/InfoSink.h"
+
+#ifdef _MSC_VER
+#include <cfloat>
+#else
+#include <cmath>
+#endif
+#include <cstdint>
+
+namespace {
+
+bool IsInfinity(double x) {
+#ifdef _MSC_VER
+ switch (_fpclass(x)) {
+ case _FPCLASS_NINF:
+ case _FPCLASS_PINF:
+ return true;
+ default:
+ return false;
+ }
+#else
+ return std::isinf(x);
+#endif
+}
+
+bool IsNan(double x) {
+#ifdef _MSC_VER
+ switch (_fpclass(x)) {
+ case _FPCLASS_SNAN:
+ case _FPCLASS_QNAN:
+ return true;
+ default:
+ return false;
+ }
+#else
+ return std::isnan(x);
+#endif
+}
+
+}
+
+namespace glslang {
+
+//
+// Two purposes:
+// 1. Show an example of how to iterate tree. Functions can
+// also directly call Traverse() on children themselves to
+// have finer grained control over the process than shown here.
+// See the last function for how to get started.
+// 2. Print out a text based description of the tree.
+//
+
+//
+// Use this class to carry along data from node to node in
+// the traversal
+//
+class TOutputTraverser : public TIntermTraverser {
+public:
+ TOutputTraverser(TInfoSink& i) : infoSink(i), extraOutput(NoExtraOutput) { }
+
+ enum EExtraOutput {
+ NoExtraOutput,
+ BinaryDoubleOutput
+ };
+ void setDoubleOutput(EExtraOutput extra) { extraOutput = extra; }
+
+ virtual bool visitBinary(TVisit, TIntermBinary* node);
+ virtual bool visitUnary(TVisit, TIntermUnary* node);
+ virtual bool visitAggregate(TVisit, TIntermAggregate* node);
+ virtual bool visitSelection(TVisit, TIntermSelection* node);
+ virtual void visitConstantUnion(TIntermConstantUnion* node);
+ virtual void visitSymbol(TIntermSymbol* node);
+ virtual bool visitLoop(TVisit, TIntermLoop* node);
+ virtual bool visitBranch(TVisit, TIntermBranch* node);
+ virtual bool visitSwitch(TVisit, TIntermSwitch* node);
+
+ TInfoSink& infoSink;
+protected:
+ TOutputTraverser(TOutputTraverser&);
+ TOutputTraverser& operator=(TOutputTraverser&);
+
+ EExtraOutput extraOutput;
+};
+
+//
+// Helper functions for printing, not part of traversing.
+//
+
+static void OutputTreeText(TInfoSink& infoSink, const TIntermNode* node, const int depth)
+{
+ int i;
+
+ infoSink.debug << node->getLoc().string << ":";
+ if (node->getLoc().line)
+ infoSink.debug << node->getLoc().line;
+ else
+ infoSink.debug << "? ";
+
+ for (i = 0; i < depth; ++i)
+ infoSink.debug << " ";
+}
+
+//
+// The rest of the file are the traversal functions. The last one
+// is the one that starts the traversal.
+//
+// Return true from interior nodes to have the external traversal
+// continue on to children. If you process children yourself,
+// return false.
+//
+
+bool TOutputTraverser::visitBinary(TVisit /* visit */, TIntermBinary* node)
+{
+ TInfoSink& out = infoSink;
+
+ OutputTreeText(out, node, depth);
+
+ switch (node->getOp()) {
+ case EOpAssign: out.debug << "move second child to first child"; break;
+ case EOpAddAssign: out.debug << "add second child into first child"; break;
+ case EOpSubAssign: out.debug << "subtract second child into first child"; break;
+ case EOpMulAssign: out.debug << "multiply second child into first child"; break;
+ case EOpVectorTimesMatrixAssign: out.debug << "matrix mult second child into first child"; break;
+ case EOpVectorTimesScalarAssign: out.debug << "vector scale second child into first child"; break;
+ case EOpMatrixTimesScalarAssign: out.debug << "matrix scale second child into first child"; break;
+ case EOpMatrixTimesMatrixAssign: out.debug << "matrix mult second child into first child"; break;
+ case EOpDivAssign: out.debug << "divide second child into first child"; break;
+ case EOpModAssign: out.debug << "mod second child into first child"; break;
+ case EOpAndAssign: out.debug << "and second child into first child"; break;
+ case EOpInclusiveOrAssign: out.debug << "or second child into first child"; break;
+ case EOpExclusiveOrAssign: out.debug << "exclusive or second child into first child"; break;
+ case EOpLeftShiftAssign: out.debug << "left shift second child into first child"; break;
+ case EOpRightShiftAssign: out.debug << "right shift second child into first child"; break;
+
+ case EOpIndexDirect: out.debug << "direct index"; break;
+ case EOpIndexIndirect: out.debug << "indirect index"; break;
+ case EOpIndexDirectStruct:
+ {
+ bool reference = node->getLeft()->getType().getBasicType() == EbtReference;
+ const TTypeList *members = reference ? node->getLeft()->getType().getReferentType()->getStruct() : node->getLeft()->getType().getStruct();
+ out.debug << (*members)[node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst()].type->getFieldName();
+ out.debug << ": direct index for structure"; break;
+ }
+ case EOpVectorSwizzle: out.debug << "vector swizzle"; break;
+ case EOpMatrixSwizzle: out.debug << "matrix swizzle"; break;
+
+ case EOpAdd: out.debug << "add"; break;
+ case EOpSub: out.debug << "subtract"; break;
+ case EOpMul: out.debug << "component-wise multiply"; break;
+ case EOpDiv: out.debug << "divide"; break;
+ case EOpMod: out.debug << "mod"; break;
+ case EOpRightShift: out.debug << "right-shift"; break;
+ case EOpLeftShift: out.debug << "left-shift"; break;
+ case EOpAnd: out.debug << "bitwise and"; break;
+ case EOpInclusiveOr: out.debug << "inclusive-or"; break;
+ case EOpExclusiveOr: out.debug << "exclusive-or"; break;
+ case EOpEqual: out.debug << "Compare Equal"; break;
+ case EOpNotEqual: out.debug << "Compare Not Equal"; break;
+ case EOpLessThan: out.debug << "Compare Less Than"; break;
+ case EOpGreaterThan: out.debug << "Compare Greater Than"; break;
+ case EOpLessThanEqual: out.debug << "Compare Less Than or Equal"; break;
+ case EOpGreaterThanEqual: out.debug << "Compare Greater Than or Equal"; break;
+ case EOpVectorEqual: out.debug << "Equal"; break;
+ case EOpVectorNotEqual: out.debug << "NotEqual"; break;
+
+ case EOpVectorTimesScalar: out.debug << "vector-scale"; break;
+ case EOpVectorTimesMatrix: out.debug << "vector-times-matrix"; break;
+ case EOpMatrixTimesVector: out.debug << "matrix-times-vector"; break;
+ case EOpMatrixTimesScalar: out.debug << "matrix-scale"; break;
+ case EOpMatrixTimesMatrix: out.debug << "matrix-multiply"; break;
+
+ case EOpLogicalOr: out.debug << "logical-or"; break;
+ case EOpLogicalXor: out.debug << "logical-xor"; break;
+ case EOpLogicalAnd: out.debug << "logical-and"; break;
+
+ default: out.debug << "<unknown op>";
+ }
+
+ out.debug << " (" << node->getCompleteString() << ")";
+
+ out.debug << "\n";
+
+ return true;
+}
+
+bool TOutputTraverser::visitUnary(TVisit /* visit */, TIntermUnary* node)
+{
+ TInfoSink& out = infoSink;
+
+ OutputTreeText(out, node, depth);
+
+ switch (node->getOp()) {
+ case EOpNegative: out.debug << "Negate value"; break;
+ case EOpVectorLogicalNot:
+ case EOpLogicalNot: out.debug << "Negate conditional"; break;
+ case EOpBitwiseNot: out.debug << "Bitwise not"; break;
+
+ case EOpPostIncrement: out.debug << "Post-Increment"; break;
+ case EOpPostDecrement: out.debug << "Post-Decrement"; break;
+ case EOpPreIncrement: out.debug << "Pre-Increment"; break;
+ case EOpPreDecrement: out.debug << "Pre-Decrement"; break;
+
+ // * -> bool
+ case EOpConvInt8ToBool: out.debug << "Convert int8_t to bool"; break;
+ case EOpConvUint8ToBool: out.debug << "Convert uint8_t to bool"; break;
+ case EOpConvInt16ToBool: out.debug << "Convert int16_t to bool"; break;
+ case EOpConvUint16ToBool: out.debug << "Convert uint16_t to bool";break;
+ case EOpConvIntToBool: out.debug << "Convert int to bool"; break;
+ case EOpConvUintToBool: out.debug << "Convert uint to bool"; break;
+ case EOpConvInt64ToBool: out.debug << "Convert int64 to bool"; break;
+ case EOpConvUint64ToBool: out.debug << "Convert uint64 to bool"; break;
+ case EOpConvFloat16ToBool: out.debug << "Convert float16_t to bool"; break;
+ case EOpConvFloatToBool: out.debug << "Convert float to bool"; break;
+ case EOpConvDoubleToBool: out.debug << "Convert double to bool"; break;
+
+ // bool -> *
+ case EOpConvBoolToInt8: out.debug << "Convert bool to int8_t"; break;
+ case EOpConvBoolToUint8: out.debug << "Convert bool to uint8_t"; break;
+ case EOpConvBoolToInt16: out.debug << "Convert bool to in16t_t"; break;
+ case EOpConvBoolToUint16: out.debug << "Convert bool to uint16_t";break;
+ case EOpConvBoolToInt: out.debug << "Convert bool to int" ; break;
+ case EOpConvBoolToUint: out.debug << "Convert bool to uint"; break;
+ case EOpConvBoolToInt64: out.debug << "Convert bool to int64"; break;
+ case EOpConvBoolToUint64: out.debug << "Convert bool to uint64";break;
+ case EOpConvBoolToFloat16: out.debug << "Convert bool to float16_t"; break;
+ case EOpConvBoolToFloat: out.debug << "Convert bool to float"; break;
+ case EOpConvBoolToDouble: out.debug << "Convert bool to double"; break;
+
+ // int8_t -> (u)int*
+ case EOpConvInt8ToInt16: out.debug << "Convert int8_t to int16_t";break;
+ case EOpConvInt8ToInt: out.debug << "Convert int8_t to int"; break;
+ case EOpConvInt8ToInt64: out.debug << "Convert int8_t to int64"; break;
+ case EOpConvInt8ToUint8: out.debug << "Convert int8_t to uint8_t";break;
+ case EOpConvInt8ToUint16: out.debug << "Convert int8_t to uint16_t";break;
+ case EOpConvInt8ToUint: out.debug << "Convert int8_t to uint"; break;
+ case EOpConvInt8ToUint64: out.debug << "Convert int8_t to uint64"; break;
+
+ // uint8_t -> (u)int*
+ case EOpConvUint8ToInt8: out.debug << "Convert uint8_t to int8_t";break;
+ case EOpConvUint8ToInt16: out.debug << "Convert uint8_t to int16_t";break;
+ case EOpConvUint8ToInt: out.debug << "Convert uint8_t to int"; break;
+ case EOpConvUint8ToInt64: out.debug << "Convert uint8_t to int64"; break;
+ case EOpConvUint8ToUint16: out.debug << "Convert uint8_t to uint16_t";break;
+ case EOpConvUint8ToUint: out.debug << "Convert uint8_t to uint"; break;
+ case EOpConvUint8ToUint64: out.debug << "Convert uint8_t to uint64"; break;
+
+ // int8_t -> float*
+ case EOpConvInt8ToFloat16: out.debug << "Convert int8_t to float16_t";break;
+ case EOpConvInt8ToFloat: out.debug << "Convert int8_t to float"; break;
+ case EOpConvInt8ToDouble: out.debug << "Convert int8_t to double"; break;
+
+ // uint8_t -> float*
+ case EOpConvUint8ToFloat16: out.debug << "Convert uint8_t to float16_t";break;
+ case EOpConvUint8ToFloat: out.debug << "Convert uint8_t to float"; break;
+ case EOpConvUint8ToDouble: out.debug << "Convert uint8_t to double"; break;
+
+ // int16_t -> (u)int*
+ case EOpConvInt16ToInt8: out.debug << "Convert int16_t to int8_t";break;
+ case EOpConvInt16ToInt: out.debug << "Convert int16_t to int"; break;
+ case EOpConvInt16ToInt64: out.debug << "Convert int16_t to int64"; break;
+ case EOpConvInt16ToUint8: out.debug << "Convert int16_t to uint8_t";break;
+ case EOpConvInt16ToUint16: out.debug << "Convert int16_t to uint16_t";break;
+ case EOpConvInt16ToUint: out.debug << "Convert int16_t to uint"; break;
+ case EOpConvInt16ToUint64: out.debug << "Convert int16_t to uint64"; break;
+
+ // int16_t -> float*
+ case EOpConvInt16ToFloat16: out.debug << "Convert int16_t to float16_t";break;
+ case EOpConvInt16ToFloat: out.debug << "Convert int16_t to float"; break;
+ case EOpConvInt16ToDouble: out.debug << "Convert int16_t to double"; break;
+
+ // uint16_t -> (u)int*
+ case EOpConvUint16ToInt8: out.debug << "Convert uint16_t to int8_t";break;
+ case EOpConvUint16ToInt16: out.debug << "Convert uint16_t to int16_t";break;
+ case EOpConvUint16ToInt: out.debug << "Convert uint16_t to int"; break;
+ case EOpConvUint16ToInt64: out.debug << "Convert uint16_t to int64"; break;
+ case EOpConvUint16ToUint8: out.debug << "Convert uint16_t to uint8_t";break;
+ case EOpConvUint16ToUint: out.debug << "Convert uint16_t to uint"; break;
+ case EOpConvUint16ToUint64: out.debug << "Convert uint16_t to uint64"; break;
+
+ // uint16_t -> float*
+ case EOpConvUint16ToFloat16: out.debug << "Convert uint16_t to float16_t";break;
+ case EOpConvUint16ToFloat: out.debug << "Convert uint16_t to float"; break;
+ case EOpConvUint16ToDouble: out.debug << "Convert uint16_t to double"; break;
+
+ // int32_t -> (u)int*
+ case EOpConvIntToInt8: out.debug << "Convert int to int8_t";break;
+ case EOpConvIntToInt16: out.debug << "Convert int to int16_t";break;
+ case EOpConvIntToInt64: out.debug << "Convert int to int64"; break;
+ case EOpConvIntToUint8: out.debug << "Convert int to uint8_t";break;
+ case EOpConvIntToUint16: out.debug << "Convert int to uint16_t";break;
+ case EOpConvIntToUint: out.debug << "Convert int to uint"; break;
+ case EOpConvIntToUint64: out.debug << "Convert int to uint64"; break;
+
+ // int32_t -> float*
+ case EOpConvIntToFloat16: out.debug << "Convert int to float16_t";break;
+ case EOpConvIntToFloat: out.debug << "Convert int to float"; break;
+ case EOpConvIntToDouble: out.debug << "Convert int to double"; break;
+
+ // uint32_t -> (u)int*
+ case EOpConvUintToInt8: out.debug << "Convert uint to int8_t";break;
+ case EOpConvUintToInt16: out.debug << "Convert uint to int16_t";break;
+ case EOpConvUintToInt: out.debug << "Convert uint to int";break;
+ case EOpConvUintToInt64: out.debug << "Convert uint to int64"; break;
+ case EOpConvUintToUint8: out.debug << "Convert uint to uint8_t";break;
+ case EOpConvUintToUint16: out.debug << "Convert uint to uint16_t";break;
+ case EOpConvUintToUint64: out.debug << "Convert uint to uint64"; break;
+
+ // uint32_t -> float*
+ case EOpConvUintToFloat16: out.debug << "Convert uint to float16_t";break;
+ case EOpConvUintToFloat: out.debug << "Convert uint to float"; break;
+ case EOpConvUintToDouble: out.debug << "Convert uint to double"; break;
+
+ // int64 -> (u)int*
+ case EOpConvInt64ToInt8: out.debug << "Convert int64 to int8_t"; break;
+ case EOpConvInt64ToInt16: out.debug << "Convert int64 to int16_t"; break;
+ case EOpConvInt64ToInt: out.debug << "Convert int64 to int"; break;
+ case EOpConvInt64ToUint8: out.debug << "Convert int64 to uint8_t";break;
+ case EOpConvInt64ToUint16: out.debug << "Convert int64 to uint16_t";break;
+ case EOpConvInt64ToUint: out.debug << "Convert int64 to uint"; break;
+ case EOpConvInt64ToUint64: out.debug << "Convert int64 to uint64"; break;
+
+ // int64 -> float*
+ case EOpConvInt64ToFloat16: out.debug << "Convert int64 to float16_t";break;
+ case EOpConvInt64ToFloat: out.debug << "Convert int64 to float"; break;
+ case EOpConvInt64ToDouble: out.debug << "Convert int64 to double"; break;
+
+ // uint64 -> (u)int*
+ case EOpConvUint64ToInt8: out.debug << "Convert uint64 to int8_t";break;
+ case EOpConvUint64ToInt16: out.debug << "Convert uint64 to int16_t";break;
+ case EOpConvUint64ToInt: out.debug << "Convert uint64 to int"; break;
+ case EOpConvUint64ToInt64: out.debug << "Convert uint64 to int64"; break;
+ case EOpConvUint64ToUint8: out.debug << "Convert uint64 to uint8_t";break;
+ case EOpConvUint64ToUint16: out.debug << "Convert uint64 to uint16"; break;
+ case EOpConvUint64ToUint: out.debug << "Convert uint64 to uint"; break;
+
+ // uint64 -> float*
+ case EOpConvUint64ToFloat16: out.debug << "Convert uint64 to float16_t";break;
+ case EOpConvUint64ToFloat: out.debug << "Convert uint64 to float"; break;
+ case EOpConvUint64ToDouble: out.debug << "Convert uint64 to double"; break;
+
+ // float16_t -> int*
+ case EOpConvFloat16ToInt8: out.debug << "Convert float16_t to int8_t"; break;
+ case EOpConvFloat16ToInt16: out.debug << "Convert float16_t to int16_t"; break;
+ case EOpConvFloat16ToInt: out.debug << "Convert float16_t to int"; break;
+ case EOpConvFloat16ToInt64: out.debug << "Convert float16_t to int64"; break;
+
+ // float16_t -> uint*
+ case EOpConvFloat16ToUint8: out.debug << "Convert float16_t to uint8_t"; break;
+ case EOpConvFloat16ToUint16: out.debug << "Convert float16_t to uint16_t"; break;
+ case EOpConvFloat16ToUint: out.debug << "Convert float16_t to uint"; break;
+ case EOpConvFloat16ToUint64: out.debug << "Convert float16_t to uint64"; break;
+
+ // float16_t -> float*
+ case EOpConvFloat16ToFloat: out.debug << "Convert float16_t to float"; break;
+ case EOpConvFloat16ToDouble: out.debug << "Convert float16_t to double"; break;
+
+ // float32 -> float*
+ case EOpConvFloatToFloat16: out.debug << "Convert float to float16_t"; break;
+ case EOpConvFloatToDouble: out.debug << "Convert float to double"; break;
+
+ // float32_t -> int*
+ case EOpConvFloatToInt8: out.debug << "Convert float to int8_t"; break;
+ case EOpConvFloatToInt16: out.debug << "Convert float to int16_t"; break;
+ case EOpConvFloatToInt: out.debug << "Convert float to int"; break;
+ case EOpConvFloatToInt64: out.debug << "Convert float to int64"; break;
+
+ // float32_t -> uint*
+ case EOpConvFloatToUint8: out.debug << "Convert float to uint8_t"; break;
+ case EOpConvFloatToUint16: out.debug << "Convert float to uint16_t"; break;
+ case EOpConvFloatToUint: out.debug << "Convert float to uint"; break;
+ case EOpConvFloatToUint64: out.debug << "Convert float to uint64"; break;
+
+ // double -> float*
+ case EOpConvDoubleToFloat16: out.debug << "Convert double to float16_t"; break;
+ case EOpConvDoubleToFloat: out.debug << "Convert double to float"; break;
+
+ // double -> int*
+ case EOpConvDoubleToInt8: out.debug << "Convert double to int8_t"; break;
+ case EOpConvDoubleToInt16: out.debug << "Convert double to int16_t"; break;
+ case EOpConvDoubleToInt: out.debug << "Convert double to int"; break;
+ case EOpConvDoubleToInt64: out.debug << "Convert double to int64"; break;
+
+ // float32_t -> uint*
+ case EOpConvDoubleToUint8: out.debug << "Convert double to uint8_t"; break;
+ case EOpConvDoubleToUint16: out.debug << "Convert double to uint16_t"; break;
+ case EOpConvDoubleToUint: out.debug << "Convert double to uint"; break;
+ case EOpConvDoubleToUint64: out.debug << "Convert double to uint64"; break;
+
+ case EOpConvUint64ToPtr: out.debug << "Convert uint64_t to pointer"; break;
+ case EOpConvPtrToUint64: out.debug << "Convert pointer to uint64_t"; break;
+
+ case EOpRadians: out.debug << "radians"; break;
+ case EOpDegrees: out.debug << "degrees"; break;
+ case EOpSin: out.debug << "sine"; break;
+ case EOpCos: out.debug << "cosine"; break;
+ case EOpTan: out.debug << "tangent"; break;
+ case EOpAsin: out.debug << "arc sine"; break;
+ case EOpAcos: out.debug << "arc cosine"; break;
+ case EOpAtan: out.debug << "arc tangent"; break;
+ case EOpSinh: out.debug << "hyp. sine"; break;
+ case EOpCosh: out.debug << "hyp. cosine"; break;
+ case EOpTanh: out.debug << "hyp. tangent"; break;
+ case EOpAsinh: out.debug << "arc hyp. sine"; break;
+ case EOpAcosh: out.debug << "arc hyp. cosine"; break;
+ case EOpAtanh: out.debug << "arc hyp. tangent"; break;
+
+ case EOpExp: out.debug << "exp"; break;
+ case EOpLog: out.debug << "log"; break;
+ case EOpExp2: out.debug << "exp2"; break;
+ case EOpLog2: out.debug << "log2"; break;
+ case EOpSqrt: out.debug << "sqrt"; break;
+ case EOpInverseSqrt: out.debug << "inverse sqrt"; break;
+
+ case EOpAbs: out.debug << "Absolute value"; break;
+ case EOpSign: out.debug << "Sign"; break;
+ case EOpFloor: out.debug << "Floor"; break;
+ case EOpTrunc: out.debug << "trunc"; break;
+ case EOpRound: out.debug << "round"; break;
+ case EOpRoundEven: out.debug << "roundEven"; break;
+ case EOpCeil: out.debug << "Ceiling"; break;
+ case EOpFract: out.debug << "Fraction"; break;
+
+ case EOpIsNan: out.debug << "isnan"; break;
+ case EOpIsInf: out.debug << "isinf"; break;
+
+ case EOpFloatBitsToInt: out.debug << "floatBitsToInt"; break;
+ case EOpFloatBitsToUint:out.debug << "floatBitsToUint"; break;
+ case EOpIntBitsToFloat: out.debug << "intBitsToFloat"; break;
+ case EOpUintBitsToFloat:out.debug << "uintBitsToFloat"; break;
+ case EOpDoubleBitsToInt64: out.debug << "doubleBitsToInt64"; break;
+ case EOpDoubleBitsToUint64: out.debug << "doubleBitsToUint64"; break;
+ case EOpInt64BitsToDouble: out.debug << "int64BitsToDouble"; break;
+ case EOpUint64BitsToDouble: out.debug << "uint64BitsToDouble"; break;
+ case EOpFloat16BitsToInt16: out.debug << "float16BitsToInt16"; break;
+ case EOpFloat16BitsToUint16: out.debug << "float16BitsToUint16"; break;
+ case EOpInt16BitsToFloat16: out.debug << "int16BitsToFloat16"; break;
+ case EOpUint16BitsToFloat16: out.debug << "uint16BitsToFloat16"; break;
+
+ case EOpPackSnorm2x16: out.debug << "packSnorm2x16"; break;
+ case EOpUnpackSnorm2x16:out.debug << "unpackSnorm2x16"; break;
+ case EOpPackUnorm2x16: out.debug << "packUnorm2x16"; break;
+ case EOpUnpackUnorm2x16:out.debug << "unpackUnorm2x16"; break;
+ case EOpPackHalf2x16: out.debug << "packHalf2x16"; break;
+ case EOpUnpackHalf2x16: out.debug << "unpackHalf2x16"; break;
+ case EOpPack16: out.debug << "pack16"; break;
+ case EOpPack32: out.debug << "pack32"; break;
+ case EOpPack64: out.debug << "pack64"; break;
+ case EOpUnpack32: out.debug << "unpack32"; break;
+ case EOpUnpack16: out.debug << "unpack16"; break;
+ case EOpUnpack8: out.debug << "unpack8"; break;
+
+ case EOpPackSnorm4x8: out.debug << "PackSnorm4x8"; break;
+ case EOpUnpackSnorm4x8: out.debug << "UnpackSnorm4x8"; break;
+ case EOpPackUnorm4x8: out.debug << "PackUnorm4x8"; break;
+ case EOpUnpackUnorm4x8: out.debug << "UnpackUnorm4x8"; break;
+ case EOpPackDouble2x32: out.debug << "PackDouble2x32"; break;
+ case EOpUnpackDouble2x32: out.debug << "UnpackDouble2x32"; break;
+
+ case EOpPackInt2x32: out.debug << "packInt2x32"; break;
+ case EOpUnpackInt2x32: out.debug << "unpackInt2x32"; break;
+ case EOpPackUint2x32: out.debug << "packUint2x32"; break;
+ case EOpUnpackUint2x32: out.debug << "unpackUint2x32"; break;
+
+ case EOpPackInt2x16: out.debug << "packInt2x16"; break;
+ case EOpUnpackInt2x16: out.debug << "unpackInt2x16"; break;
+ case EOpPackUint2x16: out.debug << "packUint2x16"; break;
+ case EOpUnpackUint2x16: out.debug << "unpackUint2x16"; break;
+
+ case EOpPackInt4x16: out.debug << "packInt4x16"; break;
+ case EOpUnpackInt4x16: out.debug << "unpackInt4x16"; break;
+ case EOpPackUint4x16: out.debug << "packUint4x16"; break;
+ case EOpUnpackUint4x16: out.debug << "unpackUint4x16"; break;
+ case EOpPackFloat2x16: out.debug << "packFloat2x16"; break;
+ case EOpUnpackFloat2x16: out.debug << "unpackFloat2x16"; break;
+
+ case EOpLength: out.debug << "length"; break;
+ case EOpNormalize: out.debug << "normalize"; break;
+ case EOpDPdx: out.debug << "dPdx"; break;
+ case EOpDPdy: out.debug << "dPdy"; break;
+ case EOpFwidth: out.debug << "fwidth"; break;
+ case EOpDPdxFine: out.debug << "dPdxFine"; break;
+ case EOpDPdyFine: out.debug << "dPdyFine"; break;
+ case EOpFwidthFine: out.debug << "fwidthFine"; break;
+ case EOpDPdxCoarse: out.debug << "dPdxCoarse"; break;
+ case EOpDPdyCoarse: out.debug << "dPdyCoarse"; break;
+ case EOpFwidthCoarse: out.debug << "fwidthCoarse"; break;
+
+ case EOpInterpolateAtCentroid: out.debug << "interpolateAtCentroid"; break;
+
+ case EOpDeterminant: out.debug << "determinant"; break;
+ case EOpMatrixInverse: out.debug << "inverse"; break;
+ case EOpTranspose: out.debug << "transpose"; break;
+
+ case EOpAny: out.debug << "any"; break;
+ case EOpAll: out.debug << "all"; break;
+
+ case EOpArrayLength: out.debug << "array length"; break;
+
+ case EOpEmitStreamVertex: out.debug << "EmitStreamVertex"; break;
+ case EOpEndStreamPrimitive: out.debug << "EndStreamPrimitive"; break;
+
+ case EOpAtomicCounterIncrement: out.debug << "AtomicCounterIncrement";break;
+ case EOpAtomicCounterDecrement: out.debug << "AtomicCounterDecrement";break;
+ case EOpAtomicCounter: out.debug << "AtomicCounter"; break;
+
+ case EOpTextureQuerySize: out.debug << "textureSize"; break;
+ case EOpTextureQueryLod: out.debug << "textureQueryLod"; break;
+ case EOpTextureQueryLevels: out.debug << "textureQueryLevels"; break;
+ case EOpTextureQuerySamples: out.debug << "textureSamples"; break;
+ case EOpImageQuerySize: out.debug << "imageQuerySize"; break;
+ case EOpImageQuerySamples: out.debug << "imageQuerySamples"; break;
+ case EOpImageLoad: out.debug << "imageLoad"; break;
+
+ case EOpBitFieldReverse: out.debug << "bitFieldReverse"; break;
+ case EOpBitCount: out.debug << "bitCount"; break;
+ case EOpFindLSB: out.debug << "findLSB"; break;
+ case EOpFindMSB: out.debug << "findMSB"; break;
+
+ case EOpNoise: out.debug << "noise"; break;
+
+ case EOpBallot: out.debug << "ballot"; break;
+ case EOpReadFirstInvocation: out.debug << "readFirstInvocation"; break;
+
+ case EOpAnyInvocation: out.debug << "anyInvocation"; break;
+ case EOpAllInvocations: out.debug << "allInvocations"; break;
+ case EOpAllInvocationsEqual: out.debug << "allInvocationsEqual"; break;
+
+ case EOpSubgroupElect: out.debug << "subgroupElect"; break;
+ case EOpSubgroupAll: out.debug << "subgroupAll"; break;
+ case EOpSubgroupAny: out.debug << "subgroupAny"; break;
+ case EOpSubgroupAllEqual: out.debug << "subgroupAllEqual"; break;
+ case EOpSubgroupBroadcast: out.debug << "subgroupBroadcast"; break;
+ case EOpSubgroupBroadcastFirst: out.debug << "subgroupBroadcastFirst"; break;
+ case EOpSubgroupBallot: out.debug << "subgroupBallot"; break;
+ case EOpSubgroupInverseBallot: out.debug << "subgroupInverseBallot"; break;
+ case EOpSubgroupBallotBitExtract: out.debug << "subgroupBallotBitExtract"; break;
+ case EOpSubgroupBallotBitCount: out.debug << "subgroupBallotBitCount"; break;
+ case EOpSubgroupBallotInclusiveBitCount: out.debug << "subgroupBallotInclusiveBitCount"; break;
+ case EOpSubgroupBallotExclusiveBitCount: out.debug << "subgroupBallotExclusiveBitCount"; break;
+ case EOpSubgroupBallotFindLSB: out.debug << "subgroupBallotFindLSB"; break;
+ case EOpSubgroupBallotFindMSB: out.debug << "subgroupBallotFindMSB"; break;
+ case EOpSubgroupShuffle: out.debug << "subgroupShuffle"; break;
+ case EOpSubgroupShuffleXor: out.debug << "subgroupShuffleXor"; break;
+ case EOpSubgroupShuffleUp: out.debug << "subgroupShuffleUp"; break;
+ case EOpSubgroupShuffleDown: out.debug << "subgroupShuffleDown"; break;
+ case EOpSubgroupAdd: out.debug << "subgroupAdd"; break;
+ case EOpSubgroupMul: out.debug << "subgroupMul"; break;
+ case EOpSubgroupMin: out.debug << "subgroupMin"; break;
+ case EOpSubgroupMax: out.debug << "subgroupMax"; break;
+ case EOpSubgroupAnd: out.debug << "subgroupAnd"; break;
+ case EOpSubgroupOr: out.debug << "subgroupOr"; break;
+ case EOpSubgroupXor: out.debug << "subgroupXor"; break;
+ case EOpSubgroupInclusiveAdd: out.debug << "subgroupInclusiveAdd"; break;
+ case EOpSubgroupInclusiveMul: out.debug << "subgroupInclusiveMul"; break;
+ case EOpSubgroupInclusiveMin: out.debug << "subgroupInclusiveMin"; break;
+ case EOpSubgroupInclusiveMax: out.debug << "subgroupInclusiveMax"; break;
+ case EOpSubgroupInclusiveAnd: out.debug << "subgroupInclusiveAnd"; break;
+ case EOpSubgroupInclusiveOr: out.debug << "subgroupInclusiveOr"; break;
+ case EOpSubgroupInclusiveXor: out.debug << "subgroupInclusiveXor"; break;
+ case EOpSubgroupExclusiveAdd: out.debug << "subgroupExclusiveAdd"; break;
+ case EOpSubgroupExclusiveMul: out.debug << "subgroupExclusiveMul"; break;
+ case EOpSubgroupExclusiveMin: out.debug << "subgroupExclusiveMin"; break;
+ case EOpSubgroupExclusiveMax: out.debug << "subgroupExclusiveMax"; break;
+ case EOpSubgroupExclusiveAnd: out.debug << "subgroupExclusiveAnd"; break;
+ case EOpSubgroupExclusiveOr: out.debug << "subgroupExclusiveOr"; break;
+ case EOpSubgroupExclusiveXor: out.debug << "subgroupExclusiveXor"; break;
+ case EOpSubgroupClusteredAdd: out.debug << "subgroupClusteredAdd"; break;
+ case EOpSubgroupClusteredMul: out.debug << "subgroupClusteredMul"; break;
+ case EOpSubgroupClusteredMin: out.debug << "subgroupClusteredMin"; break;
+ case EOpSubgroupClusteredMax: out.debug << "subgroupClusteredMax"; break;
+ case EOpSubgroupClusteredAnd: out.debug << "subgroupClusteredAnd"; break;
+ case EOpSubgroupClusteredOr: out.debug << "subgroupClusteredOr"; break;
+ case EOpSubgroupClusteredXor: out.debug << "subgroupClusteredXor"; break;
+ case EOpSubgroupQuadBroadcast: out.debug << "subgroupQuadBroadcast"; break;
+ case EOpSubgroupQuadSwapHorizontal: out.debug << "subgroupQuadSwapHorizontal"; break;
+ case EOpSubgroupQuadSwapVertical: out.debug << "subgroupQuadSwapVertical"; break;
+ case EOpSubgroupQuadSwapDiagonal: out.debug << "subgroupQuadSwapDiagonal"; break;
+
+#ifdef NV_EXTENSIONS
+ case EOpSubgroupPartition: out.debug << "subgroupPartitionNV"; break;
+ case EOpSubgroupPartitionedAdd: out.debug << "subgroupPartitionedAddNV"; break;
+ case EOpSubgroupPartitionedMul: out.debug << "subgroupPartitionedMulNV"; break;
+ case EOpSubgroupPartitionedMin: out.debug << "subgroupPartitionedMinNV"; break;
+ case EOpSubgroupPartitionedMax: out.debug << "subgroupPartitionedMaxNV"; break;
+ case EOpSubgroupPartitionedAnd: out.debug << "subgroupPartitionedAndNV"; break;
+ case EOpSubgroupPartitionedOr: out.debug << "subgroupPartitionedOrNV"; break;
+ case EOpSubgroupPartitionedXor: out.debug << "subgroupPartitionedXorNV"; break;
+ case EOpSubgroupPartitionedInclusiveAdd: out.debug << "subgroupPartitionedInclusiveAddNV"; break;
+ case EOpSubgroupPartitionedInclusiveMul: out.debug << "subgroupPartitionedInclusiveMulNV"; break;
+ case EOpSubgroupPartitionedInclusiveMin: out.debug << "subgroupPartitionedInclusiveMinNV"; break;
+ case EOpSubgroupPartitionedInclusiveMax: out.debug << "subgroupPartitionedInclusiveMaxNV"; break;
+ case EOpSubgroupPartitionedInclusiveAnd: out.debug << "subgroupPartitionedInclusiveAndNV"; break;
+ case EOpSubgroupPartitionedInclusiveOr: out.debug << "subgroupPartitionedInclusiveOrNV"; break;
+ case EOpSubgroupPartitionedInclusiveXor: out.debug << "subgroupPartitionedInclusiveXorNV"; break;
+ case EOpSubgroupPartitionedExclusiveAdd: out.debug << "subgroupPartitionedExclusiveAddNV"; break;
+ case EOpSubgroupPartitionedExclusiveMul: out.debug << "subgroupPartitionedExclusiveMulNV"; break;
+ case EOpSubgroupPartitionedExclusiveMin: out.debug << "subgroupPartitionedExclusiveMinNV"; break;
+ case EOpSubgroupPartitionedExclusiveMax: out.debug << "subgroupPartitionedExclusiveMaxNV"; break;
+ case EOpSubgroupPartitionedExclusiveAnd: out.debug << "subgroupPartitionedExclusiveAndNV"; break;
+ case EOpSubgroupPartitionedExclusiveOr: out.debug << "subgroupPartitionedExclusiveOrNV"; break;
+ case EOpSubgroupPartitionedExclusiveXor: out.debug << "subgroupPartitionedExclusiveXorNV"; break;
+#endif
+
+ case EOpClip: out.debug << "clip"; break;
+ case EOpIsFinite: out.debug << "isfinite"; break;
+ case EOpLog10: out.debug << "log10"; break;
+ case EOpRcp: out.debug << "rcp"; break;
+ case EOpSaturate: out.debug << "saturate"; break;
+
+ case EOpSparseTexelsResident: out.debug << "sparseTexelsResident"; break;
+
+#ifdef AMD_EXTENSIONS
+ case EOpMinInvocations: out.debug << "minInvocations"; break;
+ case EOpMaxInvocations: out.debug << "maxInvocations"; break;
+ case EOpAddInvocations: out.debug << "addInvocations"; break;
+ case EOpMinInvocationsNonUniform: out.debug << "minInvocationsNonUniform"; break;
+ case EOpMaxInvocationsNonUniform: out.debug << "maxInvocationsNonUniform"; break;
+ case EOpAddInvocationsNonUniform: out.debug << "addInvocationsNonUniform"; break;
+
+ case EOpMinInvocationsInclusiveScan: out.debug << "minInvocationsInclusiveScan"; break;
+ case EOpMaxInvocationsInclusiveScan: out.debug << "maxInvocationsInclusiveScan"; break;
+ case EOpAddInvocationsInclusiveScan: out.debug << "addInvocationsInclusiveScan"; break;
+ case EOpMinInvocationsInclusiveScanNonUniform: out.debug << "minInvocationsInclusiveScanNonUniform"; break;
+ case EOpMaxInvocationsInclusiveScanNonUniform: out.debug << "maxInvocationsInclusiveScanNonUniform"; break;
+ case EOpAddInvocationsInclusiveScanNonUniform: out.debug << "addInvocationsInclusiveScanNonUniform"; break;
+
+ case EOpMinInvocationsExclusiveScan: out.debug << "minInvocationsExclusiveScan"; break;
+ case EOpMaxInvocationsExclusiveScan: out.debug << "maxInvocationsExclusiveScan"; break;
+ case EOpAddInvocationsExclusiveScan: out.debug << "addInvocationsExclusiveScan"; break;
+ case EOpMinInvocationsExclusiveScanNonUniform: out.debug << "minInvocationsExclusiveScanNonUniform"; break;
+ case EOpMaxInvocationsExclusiveScanNonUniform: out.debug << "maxInvocationsExclusiveScanNonUniform"; break;
+ case EOpAddInvocationsExclusiveScanNonUniform: out.debug << "addInvocationsExclusiveScanNonUniform"; break;
+
+ case EOpMbcnt: out.debug << "mbcnt"; break;
+
+ case EOpFragmentMaskFetch: out.debug << "fragmentMaskFetchAMD"; break;
+ case EOpFragmentFetch: out.debug << "fragmentFetchAMD"; break;
+
+ case EOpCubeFaceIndex: out.debug << "cubeFaceIndex"; break;
+ case EOpCubeFaceCoord: out.debug << "cubeFaceCoord"; break;
+#endif
+
+ case EOpSubpassLoad: out.debug << "subpassLoad"; break;
+ case EOpSubpassLoadMS: out.debug << "subpassLoadMS"; break;
+
+ case EOpConstructReference: out.debug << "Construct reference type"; break;
+
+ default: out.debug.message(EPrefixError, "Bad unary op");
+ }
+
+ out.debug << " (" << node->getCompleteString() << ")";
+
+ out.debug << "\n";
+
+ return true;
+}
+
+bool TOutputTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node)
+{
+ TInfoSink& out = infoSink;
+
+ if (node->getOp() == EOpNull) {
+ out.debug.message(EPrefixError, "node is still EOpNull!");
+ return true;
+ }
+
+ OutputTreeText(out, node, depth);
+
+ switch (node->getOp()) {
+ case EOpSequence: out.debug << "Sequence\n"; return true;
+ case EOpLinkerObjects: out.debug << "Linker Objects\n"; return true;
+ case EOpComma: out.debug << "Comma"; break;
+ case EOpFunction: out.debug << "Function Definition: " << node->getName(); break;
+ case EOpFunctionCall: out.debug << "Function Call: " << node->getName(); break;
+ case EOpParameters: out.debug << "Function Parameters: "; break;
+
+ case EOpConstructFloat: out.debug << "Construct float"; break;
+ case EOpConstructDouble:out.debug << "Construct double"; break;
+
+ case EOpConstructVec2: out.debug << "Construct vec2"; break;
+ case EOpConstructVec3: out.debug << "Construct vec3"; break;
+ case EOpConstructVec4: out.debug << "Construct vec4"; break;
+ case EOpConstructDVec2: out.debug << "Construct dvec2"; break;
+ case EOpConstructDVec3: out.debug << "Construct dvec3"; break;
+ case EOpConstructDVec4: out.debug << "Construct dvec4"; break;
+ case EOpConstructBool: out.debug << "Construct bool"; break;
+ case EOpConstructBVec2: out.debug << "Construct bvec2"; break;
+ case EOpConstructBVec3: out.debug << "Construct bvec3"; break;
+ case EOpConstructBVec4: out.debug << "Construct bvec4"; break;
+ case EOpConstructInt8: out.debug << "Construct int8_t"; break;
+ case EOpConstructI8Vec2: out.debug << "Construct i8vec2"; break;
+ case EOpConstructI8Vec3: out.debug << "Construct i8vec3"; break;
+ case EOpConstructI8Vec4: out.debug << "Construct i8vec4"; break;
+ case EOpConstructInt: out.debug << "Construct int"; break;
+ case EOpConstructIVec2: out.debug << "Construct ivec2"; break;
+ case EOpConstructIVec3: out.debug << "Construct ivec3"; break;
+ case EOpConstructIVec4: out.debug << "Construct ivec4"; break;
+ case EOpConstructUint8: out.debug << "Construct uint8_t"; break;
+ case EOpConstructU8Vec2: out.debug << "Construct u8vec2"; break;
+ case EOpConstructU8Vec3: out.debug << "Construct u8vec3"; break;
+ case EOpConstructU8Vec4: out.debug << "Construct u8vec4"; break;
+ case EOpConstructUint: out.debug << "Construct uint"; break;
+ case EOpConstructUVec2: out.debug << "Construct uvec2"; break;
+ case EOpConstructUVec3: out.debug << "Construct uvec3"; break;
+ case EOpConstructUVec4: out.debug << "Construct uvec4"; break;
+ case EOpConstructInt64: out.debug << "Construct int64"; break;
+ case EOpConstructI64Vec2: out.debug << "Construct i64vec2"; break;
+ case EOpConstructI64Vec3: out.debug << "Construct i64vec3"; break;
+ case EOpConstructI64Vec4: out.debug << "Construct i64vec4"; break;
+ case EOpConstructUint64: out.debug << "Construct uint64"; break;
+ case EOpConstructU64Vec2: out.debug << "Construct u64vec2"; break;
+ case EOpConstructU64Vec3: out.debug << "Construct u64vec3"; break;
+ case EOpConstructU64Vec4: out.debug << "Construct u64vec4"; break;
+ case EOpConstructInt16: out.debug << "Construct int16_t"; break;
+ case EOpConstructI16Vec2: out.debug << "Construct i16vec2"; break;
+ case EOpConstructI16Vec3: out.debug << "Construct i16vec3"; break;
+ case EOpConstructI16Vec4: out.debug << "Construct i16vec4"; break;
+ case EOpConstructUint16: out.debug << "Construct uint16_t"; break;
+ case EOpConstructU16Vec2: out.debug << "Construct u16vec2"; break;
+ case EOpConstructU16Vec3: out.debug << "Construct u16vec3"; break;
+ case EOpConstructU16Vec4: out.debug << "Construct u16vec4"; break;
+ case EOpConstructMat2x2: out.debug << "Construct mat2"; break;
+ case EOpConstructMat2x3: out.debug << "Construct mat2x3"; break;
+ case EOpConstructMat2x4: out.debug << "Construct mat2x4"; break;
+ case EOpConstructMat3x2: out.debug << "Construct mat3x2"; break;
+ case EOpConstructMat3x3: out.debug << "Construct mat3"; break;
+ case EOpConstructMat3x4: out.debug << "Construct mat3x4"; break;
+ case EOpConstructMat4x2: out.debug << "Construct mat4x2"; break;
+ case EOpConstructMat4x3: out.debug << "Construct mat4x3"; break;
+ case EOpConstructMat4x4: out.debug << "Construct mat4"; break;
+ case EOpConstructDMat2x2: out.debug << "Construct dmat2"; break;
+ case EOpConstructDMat2x3: out.debug << "Construct dmat2x3"; break;
+ case EOpConstructDMat2x4: out.debug << "Construct dmat2x4"; break;
+ case EOpConstructDMat3x2: out.debug << "Construct dmat3x2"; break;
+ case EOpConstructDMat3x3: out.debug << "Construct dmat3"; break;
+ case EOpConstructDMat3x4: out.debug << "Construct dmat3x4"; break;
+ case EOpConstructDMat4x2: out.debug << "Construct dmat4x2"; break;
+ case EOpConstructDMat4x3: out.debug << "Construct dmat4x3"; break;
+ case EOpConstructDMat4x4: out.debug << "Construct dmat4"; break;
+ case EOpConstructIMat2x2: out.debug << "Construct imat2"; break;
+ case EOpConstructIMat2x3: out.debug << "Construct imat2x3"; break;
+ case EOpConstructIMat2x4: out.debug << "Construct imat2x4"; break;
+ case EOpConstructIMat3x2: out.debug << "Construct imat3x2"; break;
+ case EOpConstructIMat3x3: out.debug << "Construct imat3"; break;
+ case EOpConstructIMat3x4: out.debug << "Construct imat3x4"; break;
+ case EOpConstructIMat4x2: out.debug << "Construct imat4x2"; break;
+ case EOpConstructIMat4x3: out.debug << "Construct imat4x3"; break;
+ case EOpConstructIMat4x4: out.debug << "Construct imat4"; break;
+ case EOpConstructUMat2x2: out.debug << "Construct umat2"; break;
+ case EOpConstructUMat2x3: out.debug << "Construct umat2x3"; break;
+ case EOpConstructUMat2x4: out.debug << "Construct umat2x4"; break;
+ case EOpConstructUMat3x2: out.debug << "Construct umat3x2"; break;
+ case EOpConstructUMat3x3: out.debug << "Construct umat3"; break;
+ case EOpConstructUMat3x4: out.debug << "Construct umat3x4"; break;
+ case EOpConstructUMat4x2: out.debug << "Construct umat4x2"; break;
+ case EOpConstructUMat4x3: out.debug << "Construct umat4x3"; break;
+ case EOpConstructUMat4x4: out.debug << "Construct umat4"; break;
+ case EOpConstructBMat2x2: out.debug << "Construct bmat2"; break;
+ case EOpConstructBMat2x3: out.debug << "Construct bmat2x3"; break;
+ case EOpConstructBMat2x4: out.debug << "Construct bmat2x4"; break;
+ case EOpConstructBMat3x2: out.debug << "Construct bmat3x2"; break;
+ case EOpConstructBMat3x3: out.debug << "Construct bmat3"; break;
+ case EOpConstructBMat3x4: out.debug << "Construct bmat3x4"; break;
+ case EOpConstructBMat4x2: out.debug << "Construct bmat4x2"; break;
+ case EOpConstructBMat4x3: out.debug << "Construct bmat4x3"; break;
+ case EOpConstructBMat4x4: out.debug << "Construct bmat4"; break;
+ case EOpConstructFloat16: out.debug << "Construct float16_t"; break;
+ case EOpConstructF16Vec2: out.debug << "Construct f16vec2"; break;
+ case EOpConstructF16Vec3: out.debug << "Construct f16vec3"; break;
+ case EOpConstructF16Vec4: out.debug << "Construct f16vec4"; break;
+ case EOpConstructF16Mat2x2: out.debug << "Construct f16mat2"; break;
+ case EOpConstructF16Mat2x3: out.debug << "Construct f16mat2x3"; break;
+ case EOpConstructF16Mat2x4: out.debug << "Construct f16mat2x4"; break;
+ case EOpConstructF16Mat3x2: out.debug << "Construct f16mat3x2"; break;
+ case EOpConstructF16Mat3x3: out.debug << "Construct f16mat3"; break;
+ case EOpConstructF16Mat3x4: out.debug << "Construct f16mat3x4"; break;
+ case EOpConstructF16Mat4x2: out.debug << "Construct f16mat4x2"; break;
+ case EOpConstructF16Mat4x3: out.debug << "Construct f16mat4x3"; break;
+ case EOpConstructF16Mat4x4: out.debug << "Construct f16mat4"; break;
+ case EOpConstructStruct: out.debug << "Construct structure"; break;
+ case EOpConstructTextureSampler: out.debug << "Construct combined texture-sampler"; break;
+ case EOpConstructReference: out.debug << "Construct reference"; break;
+ case EOpConstructCooperativeMatrix: out.debug << "Construct cooperative matrix"; break;
+
+ case EOpLessThan: out.debug << "Compare Less Than"; break;
+ case EOpGreaterThan: out.debug << "Compare Greater Than"; break;
+ case EOpLessThanEqual: out.debug << "Compare Less Than or Equal"; break;
+ case EOpGreaterThanEqual: out.debug << "Compare Greater Than or Equal"; break;
+ case EOpVectorEqual: out.debug << "Equal"; break;
+ case EOpVectorNotEqual: out.debug << "NotEqual"; break;
+
+ case EOpMod: out.debug << "mod"; break;
+ case EOpModf: out.debug << "modf"; break;
+ case EOpPow: out.debug << "pow"; break;
+
+ case EOpAtan: out.debug << "arc tangent"; break;
+
+ case EOpMin: out.debug << "min"; break;
+ case EOpMax: out.debug << "max"; break;
+ case EOpClamp: out.debug << "clamp"; break;
+ case EOpMix: out.debug << "mix"; break;
+ case EOpStep: out.debug << "step"; break;
+ case EOpSmoothStep: out.debug << "smoothstep"; break;
+
+ case EOpDistance: out.debug << "distance"; break;
+ case EOpDot: out.debug << "dot-product"; break;
+ case EOpCross: out.debug << "cross-product"; break;
+ case EOpFaceForward: out.debug << "face-forward"; break;
+ case EOpReflect: out.debug << "reflect"; break;
+ case EOpRefract: out.debug << "refract"; break;
+ case EOpMul: out.debug << "component-wise multiply"; break;
+ case EOpOuterProduct: out.debug << "outer product"; break;
+
+ case EOpEmitVertex: out.debug << "EmitVertex"; break;
+ case EOpEndPrimitive: out.debug << "EndPrimitive"; break;
+
+ case EOpBarrier: out.debug << "Barrier"; break;
+ case EOpMemoryBarrier: out.debug << "MemoryBarrier"; break;
+ case EOpMemoryBarrierAtomicCounter: out.debug << "MemoryBarrierAtomicCounter"; break;
+ case EOpMemoryBarrierBuffer: out.debug << "MemoryBarrierBuffer"; break;
+ case EOpMemoryBarrierImage: out.debug << "MemoryBarrierImage"; break;
+ case EOpMemoryBarrierShared: out.debug << "MemoryBarrierShared"; break;
+ case EOpGroupMemoryBarrier: out.debug << "GroupMemoryBarrier"; break;
+
+ case EOpReadInvocation: out.debug << "readInvocation"; break;
+
+#ifdef AMD_EXTENSIONS
+ case EOpSwizzleInvocations: out.debug << "swizzleInvocations"; break;
+ case EOpSwizzleInvocationsMasked: out.debug << "swizzleInvocationsMasked"; break;
+ case EOpWriteInvocation: out.debug << "writeInvocation"; break;
+
+ case EOpMin3: out.debug << "min3"; break;
+ case EOpMax3: out.debug << "max3"; break;
+ case EOpMid3: out.debug << "mid3"; break;
+
+ case EOpTime: out.debug << "time"; break;
+#endif
+
+ case EOpAtomicAdd: out.debug << "AtomicAdd"; break;
+ case EOpAtomicMin: out.debug << "AtomicMin"; break;
+ case EOpAtomicMax: out.debug << "AtomicMax"; break;
+ case EOpAtomicAnd: out.debug << "AtomicAnd"; break;
+ case EOpAtomicOr: out.debug << "AtomicOr"; break;
+ case EOpAtomicXor: out.debug << "AtomicXor"; break;
+ case EOpAtomicExchange: out.debug << "AtomicExchange"; break;
+ case EOpAtomicCompSwap: out.debug << "AtomicCompSwap"; break;
+ case EOpAtomicLoad: out.debug << "AtomicLoad"; break;
+ case EOpAtomicStore: out.debug << "AtomicStore"; break;
+
+ case EOpAtomicCounterAdd: out.debug << "AtomicCounterAdd"; break;
+ case EOpAtomicCounterSubtract: out.debug << "AtomicCounterSubtract"; break;
+ case EOpAtomicCounterMin: out.debug << "AtomicCounterMin"; break;
+ case EOpAtomicCounterMax: out.debug << "AtomicCounterMax"; break;
+ case EOpAtomicCounterAnd: out.debug << "AtomicCounterAnd"; break;
+ case EOpAtomicCounterOr: out.debug << "AtomicCounterOr"; break;
+ case EOpAtomicCounterXor: out.debug << "AtomicCounterXor"; break;
+ case EOpAtomicCounterExchange: out.debug << "AtomicCounterExchange"; break;
+ case EOpAtomicCounterCompSwap: out.debug << "AtomicCounterCompSwap"; break;
+
+ case EOpImageQuerySize: out.debug << "imageQuerySize"; break;
+ case EOpImageQuerySamples: out.debug << "imageQuerySamples"; break;
+ case EOpImageLoad: out.debug << "imageLoad"; break;
+ case EOpImageStore: out.debug << "imageStore"; break;
+ case EOpImageAtomicAdd: out.debug << "imageAtomicAdd"; break;
+ case EOpImageAtomicMin: out.debug << "imageAtomicMin"; break;
+ case EOpImageAtomicMax: out.debug << "imageAtomicMax"; break;
+ case EOpImageAtomicAnd: out.debug << "imageAtomicAnd"; break;
+ case EOpImageAtomicOr: out.debug << "imageAtomicOr"; break;
+ case EOpImageAtomicXor: out.debug << "imageAtomicXor"; break;
+ case EOpImageAtomicExchange: out.debug << "imageAtomicExchange"; break;
+ case EOpImageAtomicCompSwap: out.debug << "imageAtomicCompSwap"; break;
+ case EOpImageAtomicLoad: out.debug << "imageAtomicLoad"; break;
+ case EOpImageAtomicStore: out.debug << "imageAtomicStore"; break;
+#ifdef AMD_EXTENSIONS
+ case EOpImageLoadLod: out.debug << "imageLoadLod"; break;
+ case EOpImageStoreLod: out.debug << "imageStoreLod"; break;
+#endif
+
+ case EOpTextureQuerySize: out.debug << "textureSize"; break;
+ case EOpTextureQueryLod: out.debug << "textureQueryLod"; break;
+ case EOpTextureQueryLevels: out.debug << "textureQueryLevels"; break;
+ case EOpTextureQuerySamples: out.debug << "textureSamples"; break;
+ case EOpTexture: out.debug << "texture"; break;
+ case EOpTextureProj: out.debug << "textureProj"; break;
+ case EOpTextureLod: out.debug << "textureLod"; break;
+ case EOpTextureOffset: out.debug << "textureOffset"; break;
+ case EOpTextureFetch: out.debug << "textureFetch"; break;
+ case EOpTextureFetchOffset: out.debug << "textureFetchOffset"; break;
+ case EOpTextureProjOffset: out.debug << "textureProjOffset"; break;
+ case EOpTextureLodOffset: out.debug << "textureLodOffset"; break;
+ case EOpTextureProjLod: out.debug << "textureProjLod"; break;
+ case EOpTextureProjLodOffset: out.debug << "textureProjLodOffset"; break;
+ case EOpTextureGrad: out.debug << "textureGrad"; break;
+ case EOpTextureGradOffset: out.debug << "textureGradOffset"; break;
+ case EOpTextureProjGrad: out.debug << "textureProjGrad"; break;
+ case EOpTextureProjGradOffset: out.debug << "textureProjGradOffset"; break;
+ case EOpTextureGather: out.debug << "textureGather"; break;
+ case EOpTextureGatherOffset: out.debug << "textureGatherOffset"; break;
+ case EOpTextureGatherOffsets: out.debug << "textureGatherOffsets"; break;
+ case EOpTextureClamp: out.debug << "textureClamp"; break;
+ case EOpTextureOffsetClamp: out.debug << "textureOffsetClamp"; break;
+ case EOpTextureGradClamp: out.debug << "textureGradClamp"; break;
+ case EOpTextureGradOffsetClamp: out.debug << "textureGradOffsetClamp"; break;
+#ifdef AMD_EXTENSIONS
+ case EOpTextureGatherLod: out.debug << "textureGatherLod"; break;
+ case EOpTextureGatherLodOffset: out.debug << "textureGatherLodOffset"; break;
+ case EOpTextureGatherLodOffsets: out.debug << "textureGatherLodOffsets"; break;
+#endif
+
+ case EOpSparseTexture: out.debug << "sparseTexture"; break;
+ case EOpSparseTextureOffset: out.debug << "sparseTextureOffset"; break;
+ case EOpSparseTextureLod: out.debug << "sparseTextureLod"; break;
+ case EOpSparseTextureLodOffset: out.debug << "sparseTextureLodOffset"; break;
+ case EOpSparseTextureFetch: out.debug << "sparseTexelFetch"; break;
+ case EOpSparseTextureFetchOffset: out.debug << "sparseTexelFetchOffset"; break;
+ case EOpSparseTextureGrad: out.debug << "sparseTextureGrad"; break;
+ case EOpSparseTextureGradOffset: out.debug << "sparseTextureGradOffset"; break;
+ case EOpSparseTextureGather: out.debug << "sparseTextureGather"; break;
+ case EOpSparseTextureGatherOffset: out.debug << "sparseTextureGatherOffset"; break;
+ case EOpSparseTextureGatherOffsets: out.debug << "sparseTextureGatherOffsets"; break;
+ case EOpSparseImageLoad: out.debug << "sparseImageLoad"; break;
+ case EOpSparseTextureClamp: out.debug << "sparseTextureClamp"; break;
+ case EOpSparseTextureOffsetClamp: out.debug << "sparseTextureOffsetClamp"; break;
+ case EOpSparseTextureGradClamp: out.debug << "sparseTextureGradClamp"; break;
+ case EOpSparseTextureGradOffsetClamp: out.debug << "sparseTextureGradOffsetClam"; break;
+#ifdef AMD_EXTENSIONS
+ case EOpSparseTextureGatherLod: out.debug << "sparseTextureGatherLod"; break;
+ case EOpSparseTextureGatherLodOffset: out.debug << "sparseTextureGatherLodOffset"; break;
+ case EOpSparseTextureGatherLodOffsets: out.debug << "sparseTextureGatherLodOffsets"; break;
+ case EOpSparseImageLoadLod: out.debug << "sparseImageLoadLod"; break;
+#endif
+#ifdef NV_EXTENSIONS
+ case EOpImageSampleFootprintNV: out.debug << "imageSampleFootprintNV"; break;
+ case EOpImageSampleFootprintClampNV: out.debug << "imageSampleFootprintClampNV"; break;
+ case EOpImageSampleFootprintLodNV: out.debug << "imageSampleFootprintLodNV"; break;
+ case EOpImageSampleFootprintGradNV: out.debug << "imageSampleFootprintGradNV"; break;
+ case EOpImageSampleFootprintGradClampNV: out.debug << "mageSampleFootprintGradClampNV"; break;
+#endif
+ case EOpAddCarry: out.debug << "addCarry"; break;
+ case EOpSubBorrow: out.debug << "subBorrow"; break;
+ case EOpUMulExtended: out.debug << "uMulExtended"; break;
+ case EOpIMulExtended: out.debug << "iMulExtended"; break;
+ case EOpBitfieldExtract: out.debug << "bitfieldExtract"; break;
+ case EOpBitfieldInsert: out.debug << "bitfieldInsert"; break;
+
+ case EOpFma: out.debug << "fma"; break;
+ case EOpFrexp: out.debug << "frexp"; break;
+ case EOpLdexp: out.debug << "ldexp"; break;
+
+ case EOpInterpolateAtSample: out.debug << "interpolateAtSample"; break;
+ case EOpInterpolateAtOffset: out.debug << "interpolateAtOffset"; break;
+#ifdef AMD_EXTENSIONS
+ case EOpInterpolateAtVertex: out.debug << "interpolateAtVertex"; break;
+#endif
+
+ case EOpSinCos: out.debug << "sincos"; break;
+ case EOpGenMul: out.debug << "mul"; break;
+
+ case EOpAllMemoryBarrierWithGroupSync: out.debug << "AllMemoryBarrierWithGroupSync"; break;
+ case EOpDeviceMemoryBarrier: out.debug << "DeviceMemoryBarrier"; break;
+ case EOpDeviceMemoryBarrierWithGroupSync: out.debug << "DeviceMemoryBarrierWithGroupSync"; break;
+ case EOpWorkgroupMemoryBarrier: out.debug << "WorkgroupMemoryBarrier"; break;
+ case EOpWorkgroupMemoryBarrierWithGroupSync: out.debug << "WorkgroupMemoryBarrierWithGroupSync"; break;
+
+ case EOpSubgroupBarrier: out.debug << "subgroupBarrier"; break;
+ case EOpSubgroupMemoryBarrier: out.debug << "subgroupMemoryBarrier"; break;
+ case EOpSubgroupMemoryBarrierBuffer: out.debug << "subgroupMemoryBarrierBuffer"; break;
+ case EOpSubgroupMemoryBarrierImage: out.debug << "subgroupMemoryBarrierImage"; break;
+ case EOpSubgroupMemoryBarrierShared: out.debug << "subgroupMemoryBarrierShared"; break;
+ case EOpSubgroupElect: out.debug << "subgroupElect"; break;
+ case EOpSubgroupAll: out.debug << "subgroupAll"; break;
+ case EOpSubgroupAny: out.debug << "subgroupAny"; break;
+ case EOpSubgroupAllEqual: out.debug << "subgroupAllEqual"; break;
+ case EOpSubgroupBroadcast: out.debug << "subgroupBroadcast"; break;
+ case EOpSubgroupBroadcastFirst: out.debug << "subgroupBroadcastFirst"; break;
+ case EOpSubgroupBallot: out.debug << "subgroupBallot"; break;
+ case EOpSubgroupInverseBallot: out.debug << "subgroupInverseBallot"; break;
+ case EOpSubgroupBallotBitExtract: out.debug << "subgroupBallotBitExtract"; break;
+ case EOpSubgroupBallotBitCount: out.debug << "subgroupBallotBitCount"; break;
+ case EOpSubgroupBallotInclusiveBitCount: out.debug << "subgroupBallotInclusiveBitCount"; break;
+ case EOpSubgroupBallotExclusiveBitCount: out.debug << "subgroupBallotExclusiveBitCount"; break;
+ case EOpSubgroupBallotFindLSB: out.debug << "subgroupBallotFindLSB"; break;
+ case EOpSubgroupBallotFindMSB: out.debug << "subgroupBallotFindMSB"; break;
+ case EOpSubgroupShuffle: out.debug << "subgroupShuffle"; break;
+ case EOpSubgroupShuffleXor: out.debug << "subgroupShuffleXor"; break;
+ case EOpSubgroupShuffleUp: out.debug << "subgroupShuffleUp"; break;
+ case EOpSubgroupShuffleDown: out.debug << "subgroupShuffleDown"; break;
+ case EOpSubgroupAdd: out.debug << "subgroupAdd"; break;
+ case EOpSubgroupMul: out.debug << "subgroupMul"; break;
+ case EOpSubgroupMin: out.debug << "subgroupMin"; break;
+ case EOpSubgroupMax: out.debug << "subgroupMax"; break;
+ case EOpSubgroupAnd: out.debug << "subgroupAnd"; break;
+ case EOpSubgroupOr: out.debug << "subgroupOr"; break;
+ case EOpSubgroupXor: out.debug << "subgroupXor"; break;
+ case EOpSubgroupInclusiveAdd: out.debug << "subgroupInclusiveAdd"; break;
+ case EOpSubgroupInclusiveMul: out.debug << "subgroupInclusiveMul"; break;
+ case EOpSubgroupInclusiveMin: out.debug << "subgroupInclusiveMin"; break;
+ case EOpSubgroupInclusiveMax: out.debug << "subgroupInclusiveMax"; break;
+ case EOpSubgroupInclusiveAnd: out.debug << "subgroupInclusiveAnd"; break;
+ case EOpSubgroupInclusiveOr: out.debug << "subgroupInclusiveOr"; break;
+ case EOpSubgroupInclusiveXor: out.debug << "subgroupInclusiveXor"; break;
+ case EOpSubgroupExclusiveAdd: out.debug << "subgroupExclusiveAdd"; break;
+ case EOpSubgroupExclusiveMul: out.debug << "subgroupExclusiveMul"; break;
+ case EOpSubgroupExclusiveMin: out.debug << "subgroupExclusiveMin"; break;
+ case EOpSubgroupExclusiveMax: out.debug << "subgroupExclusiveMax"; break;
+ case EOpSubgroupExclusiveAnd: out.debug << "subgroupExclusiveAnd"; break;
+ case EOpSubgroupExclusiveOr: out.debug << "subgroupExclusiveOr"; break;
+ case EOpSubgroupExclusiveXor: out.debug << "subgroupExclusiveXor"; break;
+ case EOpSubgroupClusteredAdd: out.debug << "subgroupClusteredAdd"; break;
+ case EOpSubgroupClusteredMul: out.debug << "subgroupClusteredMul"; break;
+ case EOpSubgroupClusteredMin: out.debug << "subgroupClusteredMin"; break;
+ case EOpSubgroupClusteredMax: out.debug << "subgroupClusteredMax"; break;
+ case EOpSubgroupClusteredAnd: out.debug << "subgroupClusteredAnd"; break;
+ case EOpSubgroupClusteredOr: out.debug << "subgroupClusteredOr"; break;
+ case EOpSubgroupClusteredXor: out.debug << "subgroupClusteredXor"; break;
+ case EOpSubgroupQuadBroadcast: out.debug << "subgroupQuadBroadcast"; break;
+ case EOpSubgroupQuadSwapHorizontal: out.debug << "subgroupQuadSwapHorizontal"; break;
+ case EOpSubgroupQuadSwapVertical: out.debug << "subgroupQuadSwapVertical"; break;
+ case EOpSubgroupQuadSwapDiagonal: out.debug << "subgroupQuadSwapDiagonal"; break;
+
+ case EOpSubpassLoad: out.debug << "subpassLoad"; break;
+ case EOpSubpassLoadMS: out.debug << "subpassLoadMS"; break;
+
+#ifdef NV_EXTENSIONS
+ case EOpTraceNV: out.debug << "traceNV"; break;
+ case EOpReportIntersectionNV: out.debug << "reportIntersectionNV"; break;
+ case EOpIgnoreIntersectionNV: out.debug << "ignoreIntersectionNV"; break;
+ case EOpTerminateRayNV: out.debug << "terminateRayNV"; break;
+ case EOpExecuteCallableNV: out.debug << "executeCallableNV"; break;
+ case EOpWritePackedPrimitiveIndices4x8NV: out.debug << "writePackedPrimitiveIndices4x8NV"; break;
+#endif
+
+ case EOpCooperativeMatrixLoad: out.debug << "Load cooperative matrix"; break;
+ case EOpCooperativeMatrixStore: out.debug << "Store cooperative matrix"; break;
+ case EOpCooperativeMatrixMulAdd: out.debug << "MulAdd cooperative matrices"; break;
+
+ default: out.debug.message(EPrefixError, "Bad aggregation op");
+ }
+
+ if (node->getOp() != EOpSequence && node->getOp() != EOpParameters)
+ out.debug << " (" << node->getCompleteString() << ")";
+
+ out.debug << "\n";
+
+ return true;
+}
+
+bool TOutputTraverser::visitSelection(TVisit /* visit */, TIntermSelection* node)
+{
+ TInfoSink& out = infoSink;
+
+ OutputTreeText(out, node, depth);
+
+ out.debug << "Test condition and select";
+ out.debug << " (" << node->getCompleteString() << ")";
+
+ if (node->getShortCircuit() == false)
+ out.debug << ": no shortcircuit";
+ if (node->getFlatten())
+ out.debug << ": Flatten";
+ if (node->getDontFlatten())
+ out.debug << ": DontFlatten";
+ out.debug << "\n";
+
+ ++depth;
+
+ OutputTreeText(out, node, depth);
+ out.debug << "Condition\n";
+ node->getCondition()->traverse(this);
+
+ OutputTreeText(out, node, depth);
+ if (node->getTrueBlock()) {
+ out.debug << "true case\n";
+ node->getTrueBlock()->traverse(this);
+ } else
+ out.debug << "true case is null\n";
+
+ if (node->getFalseBlock()) {
+ OutputTreeText(out, node, depth);
+ out.debug << "false case\n";
+ node->getFalseBlock()->traverse(this);
+ }
+
+ --depth;
+
+ return false;
+}
+
+// Print infinities and NaNs, and numbers in a portable way.
+// Goals:
+// - portable (across IEEE 754 platforms)
+// - shows all possible IEEE values
+// - shows simple numbers in a simple way, e.g., no leading/trailing 0s
+// - shows all digits, no premature rounding
+static void OutputDouble(TInfoSink& out, double value, TOutputTraverser::EExtraOutput extra)
+{
+ if (IsInfinity(value)) {
+ if (value < 0)
+ out.debug << "-1.#INF";
+ else
+ out.debug << "+1.#INF";
+ } else if (IsNan(value))
+ out.debug << "1.#IND";
+ else {
+ const int maxSize = 340;
+ char buf[maxSize];
+ const char* format = "%f";
+ if (fabs(value) > 0.0 && (fabs(value) < 1e-5 || fabs(value) > 1e12))
+ format = "%-.13e";
+ int len = snprintf(buf, maxSize, format, value);
+ assert(len < maxSize);
+
+ // remove a leading zero in the 100s slot in exponent; it is not portable
+ // pattern: XX...XXXe+0XX or XX...XXXe-0XX
+ if (len > 5) {
+ if (buf[len-5] == 'e' && (buf[len-4] == '+' || buf[len-4] == '-') && buf[len-3] == '0') {
+ buf[len-3] = buf[len-2];
+ buf[len-2] = buf[len-1];
+ buf[len-1] = '\0';
+ }
+ }
+
+ out.debug << buf;
+
+ switch (extra) {
+ case TOutputTraverser::BinaryDoubleOutput:
+ {
+ uint64_t b;
+ static_assert(sizeof(b) == sizeof(value), "sizeof(uint64_t) != sizeof(double)");
+ memcpy(&b, &value, sizeof(b));
+
+ out.debug << " : ";
+ for (size_t i = 0; i < 8 * sizeof(value); ++i, ++b) {
+ out.debug << ((b & 0x8000000000000000) != 0 ? "1" : "0");
+ b <<= 1;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+static void OutputConstantUnion(TInfoSink& out, const TIntermTyped* node, const TConstUnionArray& constUnion,
+ TOutputTraverser::EExtraOutput extra, int depth)
+{
+ int size = node->getType().computeNumComponents();
+
+ for (int i = 0; i < size; i++) {
+ OutputTreeText(out, node, depth);
+ switch (constUnion[i].getType()) {
+ case EbtBool:
+ if (constUnion[i].getBConst())
+ out.debug << "true";
+ else
+ out.debug << "false";
+
+ out.debug << " (" << "const bool" << ")";
+
+ out.debug << "\n";
+ break;
+ case EbtFloat:
+ case EbtDouble:
+ case EbtFloat16:
+ OutputDouble(out, constUnion[i].getDConst(), extra);
+ out.debug << "\n";
+ break;
+ case EbtInt8:
+ {
+ const int maxSize = 300;
+ char buf[maxSize];
+ snprintf(buf, maxSize, "%d (%s)", constUnion[i].getI8Const(), "const int8_t");
+
+ out.debug << buf << "\n";
+ }
+ break;
+ case EbtUint8:
+ {
+ const int maxSize = 300;
+ char buf[maxSize];
+ snprintf(buf, maxSize, "%u (%s)", constUnion[i].getU8Const(), "const uint8_t");
+
+ out.debug << buf << "\n";
+ }
+ break;
+ case EbtInt16:
+ {
+ const int maxSize = 300;
+ char buf[maxSize];
+ snprintf(buf, maxSize, "%d (%s)", constUnion[i].getI16Const(), "const int16_t");
+
+ out.debug << buf << "\n";
+ }
+ break;
+ case EbtUint16:
+ {
+ const int maxSize = 300;
+ char buf[maxSize];
+ snprintf(buf, maxSize, "%u (%s)", constUnion[i].getU16Const(), "const uint16_t");
+
+ out.debug << buf << "\n";
+ }
+ break;
+ case EbtInt:
+ {
+ const int maxSize = 300;
+ char buf[maxSize];
+ snprintf(buf, maxSize, "%d (%s)", constUnion[i].getIConst(), "const int");
+
+ out.debug << buf << "\n";
+ }
+ break;
+ case EbtUint:
+ {
+ const int maxSize = 300;
+ char buf[maxSize];
+ snprintf(buf, maxSize, "%u (%s)", constUnion[i].getUConst(), "const uint");
+
+ out.debug << buf << "\n";
+ }
+ break;
+ case EbtInt64:
+ {
+ const int maxSize = 300;
+ char buf[maxSize];
+ snprintf(buf, maxSize, "%lld (%s)", constUnion[i].getI64Const(), "const int64_t");
+
+ out.debug << buf << "\n";
+ }
+ break;
+ case EbtUint64:
+ {
+ const int maxSize = 300;
+ char buf[maxSize];
+ snprintf(buf, maxSize, "%llu (%s)", constUnion[i].getU64Const(), "const uint64_t");
+
+ out.debug << buf << "\n";
+ }
+ break;
+ default:
+ out.info.message(EPrefixInternalError, "Unknown constant", node->getLoc());
+ break;
+ }
+ }
+}
+
+void TOutputTraverser::visitConstantUnion(TIntermConstantUnion* node)
+{
+ OutputTreeText(infoSink, node, depth);
+ infoSink.debug << "Constant:\n";
+
+ OutputConstantUnion(infoSink, node, node->getConstArray(), extraOutput, depth + 1);
+}
+
+void TOutputTraverser::visitSymbol(TIntermSymbol* node)
+{
+ OutputTreeText(infoSink, node, depth);
+
+ infoSink.debug << "'" << node->getName() << "' (" << node->getCompleteString() << ")\n";
+
+ if (! node->getConstArray().empty())
+ OutputConstantUnion(infoSink, node, node->getConstArray(), extraOutput, depth + 1);
+ else if (node->getConstSubtree()) {
+ incrementDepth(node);
+ node->getConstSubtree()->traverse(this);
+ decrementDepth();
+ }
+}
+
+bool TOutputTraverser::visitLoop(TVisit /* visit */, TIntermLoop* node)
+{
+ TInfoSink& out = infoSink;
+
+ OutputTreeText(out, node, depth);
+
+ out.debug << "Loop with condition ";
+ if (! node->testFirst())
+ out.debug << "not ";
+ out.debug << "tested first";
+
+ if (node->getUnroll())
+ out.debug << ": Unroll";
+ if (node->getDontUnroll())
+ out.debug << ": DontUnroll";
+ if (node->getLoopDependency()) {
+ out.debug << ": Dependency ";
+ out.debug << node->getLoopDependency();
+ }
+ out.debug << "\n";
+
+ ++depth;
+
+ OutputTreeText(infoSink, node, depth);
+ if (node->getTest()) {
+ out.debug << "Loop Condition\n";
+ node->getTest()->traverse(this);
+ } else
+ out.debug << "No loop condition\n";
+
+ OutputTreeText(infoSink, node, depth);
+ if (node->getBody()) {
+ out.debug << "Loop Body\n";
+ node->getBody()->traverse(this);
+ } else
+ out.debug << "No loop body\n";
+
+ if (node->getTerminal()) {
+ OutputTreeText(infoSink, node, depth);
+ out.debug << "Loop Terminal Expression\n";
+ node->getTerminal()->traverse(this);
+ }
+
+ --depth;
+
+ return false;
+}
+
+bool TOutputTraverser::visitBranch(TVisit /* visit*/, TIntermBranch* node)
+{
+ TInfoSink& out = infoSink;
+
+ OutputTreeText(out, node, depth);
+
+ switch (node->getFlowOp()) {
+ case EOpKill: out.debug << "Branch: Kill"; break;
+ case EOpBreak: out.debug << "Branch: Break"; break;
+ case EOpContinue: out.debug << "Branch: Continue"; break;
+ case EOpReturn: out.debug << "Branch: Return"; break;
+ case EOpCase: out.debug << "case: "; break;
+ case EOpDefault: out.debug << "default: "; break;
+ default: out.debug << "Branch: Unknown Branch"; break;
+ }
+
+ if (node->getExpression()) {
+ out.debug << " with expression\n";
+ ++depth;
+ node->getExpression()->traverse(this);
+ --depth;
+ } else
+ out.debug << "\n";
+
+ return false;
+}
+
+bool TOutputTraverser::visitSwitch(TVisit /* visit */, TIntermSwitch* node)
+{
+ TInfoSink& out = infoSink;
+
+ OutputTreeText(out, node, depth);
+ out.debug << "switch";
+
+ if (node->getFlatten())
+ out.debug << ": Flatten";
+ if (node->getDontFlatten())
+ out.debug << ": DontFlatten";
+ out.debug << "\n";
+
+ OutputTreeText(out, node, depth);
+ out.debug << "condition\n";
+ ++depth;
+ node->getCondition()->traverse(this);
+
+ --depth;
+ OutputTreeText(out, node, depth);
+ out.debug << "body\n";
+ ++depth;
+ node->getBody()->traverse(this);
+
+ --depth;
+
+ return false;
+}
+
+//
+// This function is the one to call externally to start the traversal.
+// Individual functions can be initialized to 0 to skip processing of that
+// type of node. It's children will still be processed.
+//
+void TIntermediate::output(TInfoSink& infoSink, bool tree)
+{
+ infoSink.debug << "Shader version: " << version << "\n";
+ if (requestedExtensions.size() > 0) {
+ for (auto extIt = requestedExtensions.begin(); extIt != requestedExtensions.end(); ++extIt)
+ infoSink.debug << "Requested " << *extIt << "\n";
+ }
+
+ if (xfbMode)
+ infoSink.debug << "in xfb mode\n";
+
+ switch (language) {
+ case EShLangVertex:
+ break;
+
+ case EShLangTessControl:
+ infoSink.debug << "vertices = " << vertices << "\n";
+
+ if (inputPrimitive != ElgNone)
+ infoSink.debug << "input primitive = " << TQualifier::getGeometryString(inputPrimitive) << "\n";
+ if (vertexSpacing != EvsNone)
+ infoSink.debug << "vertex spacing = " << TQualifier::getVertexSpacingString(vertexSpacing) << "\n";
+ if (vertexOrder != EvoNone)
+ infoSink.debug << "triangle order = " << TQualifier::getVertexOrderString(vertexOrder) << "\n";
+ break;
+
+ case EShLangTessEvaluation:
+ infoSink.debug << "input primitive = " << TQualifier::getGeometryString(inputPrimitive) << "\n";
+ infoSink.debug << "vertex spacing = " << TQualifier::getVertexSpacingString(vertexSpacing) << "\n";
+ infoSink.debug << "triangle order = " << TQualifier::getVertexOrderString(vertexOrder) << "\n";
+ if (pointMode)
+ infoSink.debug << "using point mode\n";
+ break;
+
+ case EShLangGeometry:
+ infoSink.debug << "invocations = " << invocations << "\n";
+ infoSink.debug << "max_vertices = " << vertices << "\n";
+ infoSink.debug << "input primitive = " << TQualifier::getGeometryString(inputPrimitive) << "\n";
+ infoSink.debug << "output primitive = " << TQualifier::getGeometryString(outputPrimitive) << "\n";
+ break;
+
+ case EShLangFragment:
+ if (pixelCenterInteger)
+ infoSink.debug << "gl_FragCoord pixel center is integer\n";
+ if (originUpperLeft)
+ infoSink.debug << "gl_FragCoord origin is upper left\n";
+ if (earlyFragmentTests)
+ infoSink.debug << "using early_fragment_tests\n";
+ if (postDepthCoverage)
+ infoSink.debug << "using post_depth_coverage\n";
+ if (depthLayout != EldNone)
+ infoSink.debug << "using " << TQualifier::getLayoutDepthString(depthLayout) << "\n";
+ if (blendEquations != 0) {
+ infoSink.debug << "using";
+ // blendEquations is a mask, decode it
+ for (TBlendEquationShift be = (TBlendEquationShift)0; be < EBlendCount; be = (TBlendEquationShift)(be + 1)) {
+ if (blendEquations & (1 << be))
+ infoSink.debug << " " << TQualifier::getBlendEquationString(be);
+ }
+ infoSink.debug << "\n";
+ }
+ break;
+
+#ifdef NV_EXTENSIONS
+ case EShLangMeshNV:
+ infoSink.debug << "max_vertices = " << vertices << "\n";
+ infoSink.debug << "max_primitives = " << primitives << "\n";
+ infoSink.debug << "output primitive = " << TQualifier::getGeometryString(outputPrimitive) << "\n";
+ // Fall through
+
+ case EShLangTaskNV:
+ // Fall through
+#endif
+ case EShLangCompute:
+ infoSink.debug << "local_size = (" << localSize[0] << ", " << localSize[1] << ", " << localSize[2] << ")\n";
+ {
+ if (localSizeSpecId[0] != TQualifier::layoutNotSet ||
+ localSizeSpecId[1] != TQualifier::layoutNotSet ||
+ localSizeSpecId[2] != TQualifier::layoutNotSet) {
+ infoSink.debug << "local_size ids = (" <<
+ localSizeSpecId[0] << ", " <<
+ localSizeSpecId[1] << ", " <<
+ localSizeSpecId[2] << ")\n";
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (treeRoot == 0 || ! tree)
+ return;
+
+ TOutputTraverser it(infoSink);
+ if (getBinaryDoubleOutput())
+ it.setDoubleOutput(TOutputTraverser::BinaryDoubleOutput);
+ treeRoot->traverse(&it);
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/iomapper.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/iomapper.cpp
new file mode 100644
index 0000000..46c7558
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/iomapper.cpp
@@ -0,0 +1,818 @@
+//
+// Copyright (C) 2016-2017 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "../Include/Common.h"
+#include "../Include/InfoSink.h"
+#include "iomapper.h"
+#include "LiveTraverser.h"
+#include "localintermediate.h"
+
+#include "gl_types.h"
+
+#include <unordered_set>
+#include <unordered_map>
+
+//
+// Map IO bindings.
+//
+// High-level algorithm for one stage:
+//
+// 1. Traverse all code (live+dead) to find the explicitly provided bindings.
+//
+// 2. Traverse (just) the live code to determine which non-provided bindings
+// require auto-numbering. We do not auto-number dead ones.
+//
+// 3. Traverse all the code to apply the bindings:
+// a. explicitly given bindings are offset according to their type
+// b. implicit live bindings are auto-numbered into the holes, using
+// any open binding slot.
+// c. implicit dead bindings are left un-bound.
+//
+
+
+namespace glslang {
+
+struct TVarEntryInfo
+{
+ int id;
+ TIntermSymbol* symbol;
+ bool live;
+ int newBinding;
+ int newSet;
+ int newLocation;
+ int newComponent;
+ int newIndex;
+
+ struct TOrderById
+ {
+ inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r)
+ {
+ return l.id < r.id;
+ }
+ };
+
+ struct TOrderByPriority
+ {
+ // ordering:
+ // 1) has both binding and set
+ // 2) has binding but no set
+ // 3) has no binding but set
+ // 4) has no binding and no set
+ inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r)
+ {
+ const TQualifier& lq = l.symbol->getQualifier();
+ const TQualifier& rq = r.symbol->getQualifier();
+
+ // simple rules:
+ // has binding gives 2 points
+ // has set gives 1 point
+ // who has the most points is more important.
+ int lPoints = (lq.hasBinding() ? 2 : 0) + (lq.hasSet() ? 1 : 0);
+ int rPoints = (rq.hasBinding() ? 2 : 0) + (rq.hasSet() ? 1 : 0);
+
+ if (lPoints == rPoints)
+ return l.id < r.id;
+ return lPoints > rPoints;
+ }
+ };
+};
+
+
+
+typedef std::vector<TVarEntryInfo> TVarLiveMap;
+
+class TVarGatherTraverser : public TLiveTraverser
+{
+public:
+ TVarGatherTraverser(const TIntermediate& i, bool traverseDeadCode, TVarLiveMap& inList, TVarLiveMap& outList, TVarLiveMap& uniformList)
+ : TLiveTraverser(i, traverseDeadCode, true, true, false)
+ , inputList(inList)
+ , outputList(outList)
+ , uniformList(uniformList)
+ {
+ }
+
+
+ virtual void visitSymbol(TIntermSymbol* base)
+ {
+ TVarLiveMap* target = nullptr;
+ if (base->getQualifier().storage == EvqVaryingIn)
+ target = &inputList;
+ else if (base->getQualifier().storage == EvqVaryingOut)
+ target = &outputList;
+ else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().layoutPushConstant)
+ target = &uniformList;
+
+ if (target) {
+ TVarEntryInfo ent = { base->getId(), base, !traverseAll };
+ TVarLiveMap::iterator at = std::lower_bound(target->begin(), target->end(), ent, TVarEntryInfo::TOrderById());
+ if (at != target->end() && at->id == ent.id)
+ at->live = at->live || !traverseAll; // update live state
+ else
+ target->insert(at, ent);
+ }
+ }
+
+private:
+ TVarLiveMap& inputList;
+ TVarLiveMap& outputList;
+ TVarLiveMap& uniformList;
+};
+
+class TVarSetTraverser : public TLiveTraverser
+{
+public:
+ TVarSetTraverser(const TIntermediate& i, const TVarLiveMap& inList, const TVarLiveMap& outList, const TVarLiveMap& uniformList)
+ : TLiveTraverser(i, true, true, true, false)
+ , inputList(inList)
+ , outputList(outList)
+ , uniformList(uniformList)
+ {
+ }
+
+
+ virtual void visitSymbol(TIntermSymbol* base)
+ {
+ const TVarLiveMap* source;
+ if (base->getQualifier().storage == EvqVaryingIn)
+ source = &inputList;
+ else if (base->getQualifier().storage == EvqVaryingOut)
+ source = &outputList;
+ else if (base->getQualifier().isUniformOrBuffer())
+ source = &uniformList;
+ else
+ return;
+
+ TVarEntryInfo ent = { base->getId() };
+ TVarLiveMap::const_iterator at = std::lower_bound(source->begin(), source->end(), ent, TVarEntryInfo::TOrderById());
+ if (at == source->end())
+ return;
+
+ if (at->id != ent.id)
+ return;
+
+ if (at->newBinding != -1)
+ base->getWritableType().getQualifier().layoutBinding = at->newBinding;
+ if (at->newSet != -1)
+ base->getWritableType().getQualifier().layoutSet = at->newSet;
+ if (at->newLocation != -1)
+ base->getWritableType().getQualifier().layoutLocation = at->newLocation;
+ if (at->newComponent != -1)
+ base->getWritableType().getQualifier().layoutComponent = at->newComponent;
+ if (at->newIndex != -1)
+ base->getWritableType().getQualifier().layoutIndex = at->newIndex;
+ }
+
+ private:
+ const TVarLiveMap& inputList;
+ const TVarLiveMap& outputList;
+ const TVarLiveMap& uniformList;
+};
+
+struct TNotifyUniformAdaptor
+{
+ EShLanguage stage;
+ TIoMapResolver& resolver;
+ inline TNotifyUniformAdaptor(EShLanguage s, TIoMapResolver& r)
+ : stage(s)
+ , resolver(r)
+ {
+ }
+ inline void operator()(TVarEntryInfo& ent)
+ {
+ resolver.notifyBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), ent.live);
+ }
+private:
+ TNotifyUniformAdaptor& operator=(TNotifyUniformAdaptor&);
+};
+
+struct TNotifyInOutAdaptor
+{
+ EShLanguage stage;
+ TIoMapResolver& resolver;
+ inline TNotifyInOutAdaptor(EShLanguage s, TIoMapResolver& r)
+ : stage(s)
+ , resolver(r)
+ {
+ }
+ inline void operator()(TVarEntryInfo& ent)
+ {
+ resolver.notifyInOut(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), ent.live);
+ }
+private:
+ TNotifyInOutAdaptor& operator=(TNotifyInOutAdaptor&);
+};
+
+struct TResolverUniformAdaptor
+{
+ TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e, TIntermediate& interm)
+ : stage(s)
+ , resolver(r)
+ , infoSink(i)
+ , error(e)
+ , intermediate(interm)
+ {
+ }
+
+ inline void operator()(TVarEntryInfo& ent)
+ {
+ ent.newLocation = -1;
+ ent.newComponent = -1;
+ ent.newBinding = -1;
+ ent.newSet = -1;
+ ent.newIndex = -1;
+ const bool isValid = resolver.validateBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(),
+ ent.live);
+ if (isValid) {
+ ent.newBinding = resolver.resolveBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(),
+ ent.live);
+ ent.newSet = resolver.resolveSet(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), ent.live);
+ ent.newLocation = resolver.resolveUniformLocation(stage, ent.symbol->getName().c_str(),
+ ent.symbol->getType(), ent.live);
+
+ if (ent.newBinding != -1) {
+ if (ent.newBinding >= int(TQualifier::layoutBindingEnd)) {
+ TString err = "mapped binding out of range: " + ent.symbol->getName();
+
+ infoSink.info.message(EPrefixInternalError, err.c_str());
+ error = true;
+ }
+ }
+ if (ent.newSet != -1) {
+ if (ent.newSet >= int(TQualifier::layoutSetEnd)) {
+ TString err = "mapped set out of range: " + ent.symbol->getName();
+
+ infoSink.info.message(EPrefixInternalError, err.c_str());
+ error = true;
+ }
+ }
+ } else {
+ TString errorMsg = "Invalid binding: " + ent.symbol->getName();
+ infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
+ error = true;
+ }
+ }
+
+ EShLanguage stage;
+ TIoMapResolver& resolver;
+ TInfoSink& infoSink;
+ bool& error;
+ TIntermediate& intermediate;
+
+private:
+ TResolverUniformAdaptor& operator=(TResolverUniformAdaptor&);
+};
+
+struct TResolverInOutAdaptor
+{
+ TResolverInOutAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e, TIntermediate& interm)
+ : stage(s)
+ , resolver(r)
+ , infoSink(i)
+ , error(e)
+ , intermediate(interm)
+ {
+ }
+
+ inline void operator()(TVarEntryInfo& ent)
+ {
+ ent.newLocation = -1;
+ ent.newComponent = -1;
+ ent.newBinding = -1;
+ ent.newSet = -1;
+ ent.newIndex = -1;
+ const bool isValid = resolver.validateInOut(stage,
+ ent.symbol->getName().c_str(),
+ ent.symbol->getType(),
+ ent.live);
+ if (isValid) {
+ ent.newLocation = resolver.resolveInOutLocation(stage,
+ ent.symbol->getName().c_str(),
+ ent.symbol->getType(),
+ ent.live);
+ ent.newComponent = resolver.resolveInOutComponent(stage,
+ ent.symbol->getName().c_str(),
+ ent.symbol->getType(),
+ ent.live);
+ ent.newIndex = resolver.resolveInOutIndex(stage,
+ ent.symbol->getName().c_str(),
+ ent.symbol->getType(),
+ ent.live);
+ } else {
+ TString errorMsg;
+ if (ent.symbol->getType().getQualifier().semanticName != nullptr) {
+ errorMsg = "Invalid shader In/Out variable semantic: ";
+ errorMsg += ent.symbol->getType().getQualifier().semanticName;
+ } else {
+ errorMsg = "Invalid shader In/Out variable: ";
+ errorMsg += ent.symbol->getName();
+ }
+ infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
+ error = true;
+ }
+ }
+
+ EShLanguage stage;
+ TIoMapResolver& resolver;
+ TInfoSink& infoSink;
+ bool& error;
+ TIntermediate& intermediate;
+
+private:
+ TResolverInOutAdaptor& operator=(TResolverInOutAdaptor&);
+};
+
+// Base class for shared TIoMapResolver services, used by several derivations.
+struct TDefaultIoResolverBase : public glslang::TIoMapResolver
+{
+ TDefaultIoResolverBase(const TIntermediate &intermediate) :
+ intermediate(intermediate),
+ nextUniformLocation(intermediate.getUniformLocationBase()),
+ nextInputLocation(0),
+ nextOutputLocation(0)
+ { }
+
+ int getBaseBinding(TResourceType res, unsigned int set) const {
+ return selectBaseBinding(intermediate.getShiftBinding(res),
+ intermediate.getShiftBindingForSet(res, set));
+ }
+
+ const std::vector<std::string>& getResourceSetBinding() const { return intermediate.getResourceSetBinding(); }
+
+ bool doAutoBindingMapping() const { return intermediate.getAutoMapBindings(); }
+ bool doAutoLocationMapping() const { return intermediate.getAutoMapLocations(); }
+
+ typedef std::vector<int> TSlotSet;
+ typedef std::unordered_map<int, TSlotSet> TSlotSetMap;
+ TSlotSetMap slots;
+
+ TSlotSet::iterator findSlot(int set, int slot)
+ {
+ return std::lower_bound(slots[set].begin(), slots[set].end(), slot);
+ }
+
+ bool checkEmpty(int set, int slot)
+ {
+ TSlotSet::iterator at = findSlot(set, slot);
+ return !(at != slots[set].end() && *at == slot);
+ }
+
+ int reserveSlot(int set, int slot, int size = 1)
+ {
+ TSlotSet::iterator at = findSlot(set, slot);
+
+ // tolerate aliasing, by not double-recording aliases
+ // (policy about appropriateness of the alias is higher up)
+ for (int i = 0; i < size; i++) {
+ if (at == slots[set].end() || *at != slot + i)
+ at = slots[set].insert(at, slot + i);
+ ++at;
+ }
+
+ return slot;
+ }
+
+ int getFreeSlot(int set, int base, int size = 1)
+ {
+ TSlotSet::iterator at = findSlot(set, base);
+ if (at == slots[set].end())
+ return reserveSlot(set, base, size);
+
+ // look for a big enough gap
+ for (; at != slots[set].end(); ++at) {
+ if (*at - base >= size)
+ break;
+ base = *at + 1;
+ }
+ return reserveSlot(set, base, size);
+ }
+
+ virtual bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool /*is_live*/) override = 0;
+
+ virtual int resolveBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool is_live) override = 0;
+
+ int resolveSet(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool /*is_live*/) override
+ {
+ if (type.getQualifier().hasSet())
+ return type.getQualifier().layoutSet;
+
+ // If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN)
+ if (getResourceSetBinding().size() == 1)
+ return atoi(getResourceSetBinding()[0].c_str());
+
+ return 0;
+ }
+ int resolveUniformLocation(EShLanguage /*stage*/, const char* name, const glslang::TType& type, bool /*is_live*/) override
+ {
+ // kick out of not doing this
+ if (!doAutoLocationMapping())
+ return -1;
+
+ // no locations added if already present, a built-in variable, a block, or an opaque
+ if (type.getQualifier().hasLocation() || type.isBuiltIn() ||
+ type.getBasicType() == EbtBlock ||
+ type.getBasicType() == EbtAtomicUint ||
+ (type.containsOpaque() && intermediate.getSpv().openGl == 0))
+ return -1;
+
+ // no locations on blocks of built-in variables
+ if (type.isStruct()) {
+ if (type.getStruct()->size() < 1)
+ return -1;
+ if ((*type.getStruct())[0].type->isBuiltIn())
+ return -1;
+ }
+
+ int location = intermediate.getUniformLocationOverride(name);
+ if (location != -1)
+ return location;
+
+ location = nextUniformLocation;
+
+ nextUniformLocation += TIntermediate::computeTypeUniformLocationSize(type);
+
+ return location;
+ }
+ bool validateInOut(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
+ {
+ return true;
+ }
+ int resolveInOutLocation(EShLanguage stage, const char* /*name*/, const TType& type, bool /*is_live*/) override
+ {
+ // kick out of not doing this
+ if (!doAutoLocationMapping())
+ return -1;
+
+ // no locations added if already present, or a built-in variable
+ if (type.getQualifier().hasLocation() || type.isBuiltIn())
+ return -1;
+
+ // no locations on blocks of built-in variables
+ if (type.isStruct()) {
+ if (type.getStruct()->size() < 1)
+ return -1;
+ if ((*type.getStruct())[0].type->isBuiltIn())
+ return -1;
+ }
+
+ // point to the right input or output location counter
+ int& nextLocation = type.getQualifier().isPipeInput() ? nextInputLocation : nextOutputLocation;
+
+ // Placeholder. This does not do proper cross-stage lining up, nor
+ // work with mixed location/no-location declarations.
+ int location = nextLocation;
+ int typeLocationSize;
+ // Don’t take into account the outer-most array if the stage’s
+ // interface is automatically an array.
+ if (type.getQualifier().isArrayedIo(stage)) {
+ TType elementType(type, 0);
+ typeLocationSize = TIntermediate::computeTypeLocationSize(elementType, stage);
+ } else {
+ typeLocationSize = TIntermediate::computeTypeLocationSize(type, stage);
+ }
+ nextLocation += typeLocationSize;
+
+ return location;
+ }
+ int resolveInOutComponent(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
+ {
+ return -1;
+ }
+ int resolveInOutIndex(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
+ {
+ return -1;
+ }
+
+ void notifyBinding(EShLanguage, const char* /*name*/, const TType&, bool /*is_live*/) override {}
+ void notifyInOut(EShLanguage, const char* /*name*/, const TType&, bool /*is_live*/) override {}
+ void endNotifications(EShLanguage) override {}
+ void beginNotifications(EShLanguage) override {}
+ void beginResolve(EShLanguage) override {}
+ void endResolve(EShLanguage) override {}
+
+protected:
+ TDefaultIoResolverBase(TDefaultIoResolverBase&);
+ TDefaultIoResolverBase& operator=(TDefaultIoResolverBase&);
+
+ const TIntermediate &intermediate;
+ int nextUniformLocation;
+ int nextInputLocation;
+ int nextOutputLocation;
+
+ // Return descriptor set specific base if there is one, and the generic base otherwise.
+ int selectBaseBinding(int base, int descriptorSetBase) const {
+ return descriptorSetBase != -1 ? descriptorSetBase : base;
+ }
+
+ static int getLayoutSet(const glslang::TType& type) {
+ if (type.getQualifier().hasSet())
+ return type.getQualifier().layoutSet;
+ else
+ return 0;
+ }
+
+ static bool isSamplerType(const glslang::TType& type) {
+ return type.getBasicType() == glslang::EbtSampler && type.getSampler().isPureSampler();
+ }
+
+ static bool isTextureType(const glslang::TType& type) {
+ return (type.getBasicType() == glslang::EbtSampler &&
+ (type.getSampler().isTexture() || type.getSampler().isSubpass()));
+ }
+
+ static bool isUboType(const glslang::TType& type) {
+ return type.getQualifier().storage == EvqUniform;
+ }
+};
+
+/*
+ * Basic implementation of glslang::TIoMapResolver that replaces the
+ * previous offset behavior.
+ * It does the same, uses the offsets for the corresponding uniform
+ * types. Also respects the EOptionAutoMapBindings flag and binds
+ * them if needed.
+ */
+/*
+ * Default resolver
+ */
+struct TDefaultIoResolver : public TDefaultIoResolverBase
+{
+ TDefaultIoResolver(const TIntermediate &intermediate) : TDefaultIoResolverBase(intermediate) { }
+
+ bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& /*type*/, bool /*is_live*/) override
+ {
+ return true;
+ }
+
+ int resolveBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool is_live) override
+ {
+ const int set = getLayoutSet(type);
+ // On OpenGL arrays of opaque types take a seperate binding for each element
+ int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
+
+ if (type.getQualifier().hasBinding()) {
+ if (isImageType(type))
+ return reserveSlot(set, getBaseBinding(EResImage, set) + type.getQualifier().layoutBinding, numBindings);
+
+ if (isTextureType(type))
+ return reserveSlot(set, getBaseBinding(EResTexture, set) + type.getQualifier().layoutBinding, numBindings);
+
+ if (isSsboType(type))
+ return reserveSlot(set, getBaseBinding(EResSsbo, set) + type.getQualifier().layoutBinding, numBindings);
+
+ if (isSamplerType(type))
+ return reserveSlot(set, getBaseBinding(EResSampler, set) + type.getQualifier().layoutBinding, numBindings);
+
+ if (isUboType(type))
+ return reserveSlot(set, getBaseBinding(EResUbo, set) + type.getQualifier().layoutBinding, numBindings);
+ } else if (is_live && doAutoBindingMapping()) {
+ // find free slot, the caller did make sure it passes all vars with binding
+ // first and now all are passed that do not have a binding and needs one
+
+ if (isImageType(type))
+ return getFreeSlot(set, getBaseBinding(EResImage, set), numBindings);
+
+ if (isTextureType(type))
+ return getFreeSlot(set, getBaseBinding(EResTexture, set), numBindings);
+
+ if (isSsboType(type))
+ return getFreeSlot(set, getBaseBinding(EResSsbo, set), numBindings);
+
+ if (isSamplerType(type))
+ return getFreeSlot(set, getBaseBinding(EResSampler, set), numBindings);
+
+ if (isUboType(type))
+ return getFreeSlot(set, getBaseBinding(EResUbo, set), numBindings);
+ }
+
+ return -1;
+ }
+
+protected:
+ static bool isImageType(const glslang::TType& type) {
+ return type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage();
+ }
+
+ static bool isSsboType(const glslang::TType& type) {
+ return type.getQualifier().storage == EvqBuffer;
+ }
+};
+
+/********************************************************************************
+The following IO resolver maps types in HLSL register space, as follows:
+
+t - for shader resource views (SRV)
+ TEXTURE1D
+ TEXTURE1DARRAY
+ TEXTURE2D
+ TEXTURE2DARRAY
+ TEXTURE3D
+ TEXTURECUBE
+ TEXTURECUBEARRAY
+ TEXTURE2DMS
+ TEXTURE2DMSARRAY
+ STRUCTUREDBUFFER
+ BYTEADDRESSBUFFER
+ BUFFER
+ TBUFFER
+
+s - for samplers
+ SAMPLER
+ SAMPLER1D
+ SAMPLER2D
+ SAMPLER3D
+ SAMPLERCUBE
+ SAMPLERSTATE
+ SAMPLERCOMPARISONSTATE
+
+u - for unordered access views (UAV)
+ RWBYTEADDRESSBUFFER
+ RWSTRUCTUREDBUFFER
+ APPENDSTRUCTUREDBUFFER
+ CONSUMESTRUCTUREDBUFFER
+ RWBUFFER
+ RWTEXTURE1D
+ RWTEXTURE1DARRAY
+ RWTEXTURE2D
+ RWTEXTURE2DARRAY
+ RWTEXTURE3D
+
+b - for constant buffer views (CBV)
+ CBUFFER
+ CONSTANTBUFFER
+ ********************************************************************************/
+struct TDefaultHlslIoResolver : public TDefaultIoResolverBase
+{
+ TDefaultHlslIoResolver(const TIntermediate &intermediate) : TDefaultIoResolverBase(intermediate) { }
+
+ bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& /*type*/, bool /*is_live*/) override
+ {
+ return true;
+ }
+
+ int resolveBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool is_live) override
+ {
+ const int set = getLayoutSet(type);
+
+ if (type.getQualifier().hasBinding()) {
+ if (isUavType(type))
+ return reserveSlot(set, getBaseBinding(EResUav, set) + type.getQualifier().layoutBinding);
+
+ if (isSrvType(type))
+ return reserveSlot(set, getBaseBinding(EResTexture, set) + type.getQualifier().layoutBinding);
+
+ if (isSamplerType(type))
+ return reserveSlot(set, getBaseBinding(EResSampler, set) + type.getQualifier().layoutBinding);
+
+ if (isUboType(type))
+ return reserveSlot(set, getBaseBinding(EResUbo, set) + type.getQualifier().layoutBinding);
+ } else if (is_live && doAutoBindingMapping()) {
+ // find free slot, the caller did make sure it passes all vars with binding
+ // first and now all are passed that do not have a binding and needs one
+
+ if (isUavType(type))
+ return getFreeSlot(set, getBaseBinding(EResUav, set));
+
+ if (isSrvType(type))
+ return getFreeSlot(set, getBaseBinding(EResTexture, set));
+
+ if (isSamplerType(type))
+ return getFreeSlot(set, getBaseBinding(EResSampler, set));
+
+ if (isUboType(type))
+ return getFreeSlot(set, getBaseBinding(EResUbo, set));
+ }
+
+ return -1;
+ }
+
+protected:
+ // Return true if this is a SRV (shader resource view) type:
+ static bool isSrvType(const glslang::TType& type) {
+ return isTextureType(type) || type.getQualifier().storage == EvqBuffer;
+ }
+
+ // Return true if this is a UAV (unordered access view) type:
+ static bool isUavType(const glslang::TType& type) {
+ if (type.getQualifier().readonly)
+ return false;
+
+ return (type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage()) ||
+ (type.getQualifier().storage == EvqBuffer);
+ }
+};
+
+
+// Map I/O variables to provided offsets, and make bindings for
+// unbound but live variables.
+//
+// Returns false if the input is too malformed to do this.
+bool TIoMapper::addStage(EShLanguage stage, TIntermediate &intermediate, TInfoSink &infoSink, TIoMapResolver *resolver)
+{
+ bool somethingToDo = !intermediate.getResourceSetBinding().empty() ||
+ intermediate.getAutoMapBindings() ||
+ intermediate.getAutoMapLocations();
+
+ for (int res = 0; res < EResCount; ++res) {
+ somethingToDo = somethingToDo ||
+ (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
+ intermediate.hasShiftBindingForSet(TResourceType(res));
+ }
+
+ if (!somethingToDo && resolver == nullptr)
+ return true;
+
+ if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive())
+ return false;
+
+ TIntermNode* root = intermediate.getTreeRoot();
+ if (root == nullptr)
+ return false;
+
+ // if no resolver is provided, use the default resolver with the given shifts and auto map settings
+ TDefaultIoResolver defaultResolver(intermediate);
+ TDefaultHlslIoResolver defaultHlslResolver(intermediate);
+
+ if (resolver == nullptr) {
+ // TODO: use a passed in IO mapper for this
+ if (intermediate.usingHlslIoMapping())
+ resolver = &defaultHlslResolver;
+ else
+ resolver = &defaultResolver;
+ }
+
+ TVarLiveMap inVarMap, outVarMap, uniformVarMap;
+ TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap);
+ TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap);
+
+ root->traverse(&iter_binding_all);
+ iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
+
+ while (!iter_binding_live.functions.empty()) {
+ TIntermNode* function = iter_binding_live.functions.back();
+ iter_binding_live.functions.pop_back();
+ function->traverse(&iter_binding_live);
+ }
+
+ // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info.
+ std::sort(uniformVarMap.begin(), uniformVarMap.end(), TVarEntryInfo::TOrderByPriority());
+
+ bool hadError = false;
+ TNotifyInOutAdaptor inOutNotify(stage, *resolver);
+ TNotifyUniformAdaptor uniformNotify(stage, *resolver);
+ TResolverUniformAdaptor uniformResolve(stage, *resolver, infoSink, hadError, intermediate);
+ TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError, intermediate);
+ resolver->beginNotifications(stage);
+ std::for_each(inVarMap.begin(), inVarMap.end(), inOutNotify);
+ std::for_each(outVarMap.begin(), outVarMap.end(), inOutNotify);
+ std::for_each(uniformVarMap.begin(), uniformVarMap.end(), uniformNotify);
+ resolver->endNotifications(stage);
+ resolver->beginResolve(stage);
+ std::for_each(inVarMap.begin(), inVarMap.end(), inOutResolve);
+ std::for_each(outVarMap.begin(), outVarMap.end(), inOutResolve);
+ std::for_each(uniformVarMap.begin(), uniformVarMap.end(), uniformResolve);
+ resolver->endResolve(stage);
+
+ if (!hadError) {
+ // sort by id again, so we can use lower bound to find entries
+ std::sort(uniformVarMap.begin(), uniformVarMap.end(), TVarEntryInfo::TOrderById());
+ TVarSetTraverser iter_iomap(intermediate, inVarMap, outVarMap, uniformVarMap);
+ root->traverse(&iter_iomap);
+ }
+
+ return !hadError;
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/iomapper.h b/src/3rdparty/glslang/glslang/MachineIndependent/iomapper.h
new file mode 100644
index 0000000..5e0d439
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/iomapper.h
@@ -0,0 +1,63 @@
+//
+// Copyright (C) 2016 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef _IOMAPPER_INCLUDED
+#define _IOMAPPER_INCLUDED
+
+#include "../Public/ShaderLang.h"
+
+//
+// A reflection database and its interface, consistent with the OpenGL API reflection queries.
+//
+
+class TInfoSink;
+
+namespace glslang {
+
+class TIntermediate;
+
+// I/O mapper
+class TIoMapper {
+public:
+ TIoMapper() {}
+ virtual ~TIoMapper() {}
+
+ // grow the reflection stage by stage
+ bool addStage(EShLanguage, TIntermediate&, TInfoSink&, TIoMapResolver*);
+};
+
+} // end namespace glslang
+
+#endif // _IOMAPPER_INCLUDED
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/limits.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/limits.cpp
new file mode 100644
index 0000000..64d191b
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/limits.cpp
@@ -0,0 +1,198 @@
+//
+// Copyright (C) 2013 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+//
+// Do sub tree walks for
+// 1) inductive loop bodies to see if the inductive variable is modified
+// 2) array-index expressions to see if they are "constant-index-expression"
+//
+// These are per Appendix A of ES 2.0:
+//
+// "Within the body of the loop, the loop index is not statically assigned to nor is it used as the
+// argument to a function out or inout parameter."
+//
+// "The following are constant-index-expressions:
+// - Constant expressions
+// - Loop indices as defined in section 4
+// - Expressions composed of both of the above"
+//
+// N.B.: assuming the last rule excludes function calls
+//
+
+#include "ParseHelper.h"
+
+namespace glslang {
+
+//
+// The inductive loop-body traverser.
+//
+// Just look at things that might modify the loop index.
+//
+
+class TInductiveTraverser : public TIntermTraverser {
+public:
+ TInductiveTraverser(int id, TSymbolTable& st)
+ : loopId(id), symbolTable(st), bad(false) { }
+
+ virtual bool visitBinary(TVisit, TIntermBinary* node);
+ virtual bool visitUnary(TVisit, TIntermUnary* node);
+ virtual bool visitAggregate(TVisit, TIntermAggregate* node);
+
+ int loopId; // unique ID of the symbol that's the loop inductive variable
+ TSymbolTable& symbolTable;
+ bool bad;
+ TSourceLoc badLoc;
+
+protected:
+ TInductiveTraverser(TInductiveTraverser&);
+ TInductiveTraverser& operator=(TInductiveTraverser&);
+};
+
+// check binary operations for those modifying the loop index
+bool TInductiveTraverser::visitBinary(TVisit /* visit */, TIntermBinary* node)
+{
+ if (node->modifiesState() && node->getLeft()->getAsSymbolNode() &&
+ node->getLeft()->getAsSymbolNode()->getId() == loopId) {
+ bad = true;
+ badLoc = node->getLoc();
+ }
+
+ return true;
+}
+
+// check unary operations for those modifying the loop index
+bool TInductiveTraverser::visitUnary(TVisit /* visit */, TIntermUnary* node)
+{
+ if (node->modifiesState() && node->getOperand()->getAsSymbolNode() &&
+ node->getOperand()->getAsSymbolNode()->getId() == loopId) {
+ bad = true;
+ badLoc = node->getLoc();
+ }
+
+ return true;
+}
+
+// check function calls for arguments modifying the loop index
+bool TInductiveTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node)
+{
+ if (node->getOp() == EOpFunctionCall) {
+ // see if an out or inout argument is the loop index
+ const TIntermSequence& args = node->getSequence();
+ for (int i = 0; i < (int)args.size(); ++i) {
+ if (args[i]->getAsSymbolNode() && args[i]->getAsSymbolNode()->getId() == loopId) {
+ TSymbol* function = symbolTable.find(node->getName());
+ const TType* type = (*function->getAsFunction())[i].type;
+ if (type->getQualifier().storage == EvqOut ||
+ type->getQualifier().storage == EvqInOut) {
+ bad = true;
+ badLoc = node->getLoc();
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+//
+// External function to call for loop check.
+//
+void TParseContext::inductiveLoopBodyCheck(TIntermNode* body, int loopId, TSymbolTable& symbolTable)
+{
+ TInductiveTraverser it(loopId, symbolTable);
+
+ if (body == nullptr)
+ return;
+
+ body->traverse(&it);
+
+ if (it.bad)
+ error(it.badLoc, "inductive loop index modified", "limitations", "");
+}
+
+//
+// The "constant-index-expression" tranverser.
+//
+// Just look at things that can form an index.
+//
+
+class TIndexTraverser : public TIntermTraverser {
+public:
+ TIndexTraverser(const TIdSetType& ids) : inductiveLoopIds(ids), bad(false) { }
+ virtual void visitSymbol(TIntermSymbol* symbol);
+ virtual bool visitAggregate(TVisit, TIntermAggregate* node);
+ const TIdSetType& inductiveLoopIds;
+ bool bad;
+ TSourceLoc badLoc;
+
+protected:
+ TIndexTraverser(TIndexTraverser&);
+ TIndexTraverser& operator=(TIndexTraverser&);
+};
+
+// make sure symbols are inductive-loop indexes
+void TIndexTraverser::visitSymbol(TIntermSymbol* symbol)
+{
+ if (inductiveLoopIds.find(symbol->getId()) == inductiveLoopIds.end()) {
+ bad = true;
+ badLoc = symbol->getLoc();
+ }
+}
+
+// check for function calls, assuming they are bad; spec. doesn't really say
+bool TIndexTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node)
+{
+ if (node->getOp() == EOpFunctionCall) {
+ bad = true;
+ badLoc = node->getLoc();
+ }
+
+ return true;
+}
+
+//
+// External function to call for loop check.
+//
+void TParseContext::constantIndexExpressionCheck(TIntermNode* index)
+{
+ TIndexTraverser it(inductiveLoopIds);
+
+ index->traverse(&it);
+
+ if (it.bad)
+ error(it.badLoc, "Non-constant-index-expression", "limitations", "");
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/linkValidate.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/linkValidate.cpp
new file mode 100644
index 0000000..0cf2d36
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/linkValidate.cpp
@@ -0,0 +1,1686 @@
+//
+// Copyright (C) 2013 LunarG, Inc.
+// Copyright (C) 2017 ARM Limited.
+// Copyright (C) 2015-2018 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+//
+// Do link-time merging and validation of intermediate representations.
+//
+// Basic model is that during compilation, each compilation unit (shader) is
+// compiled into one TIntermediate instance. Then, at link time, multiple
+// units for the same stage can be merged together, which can generate errors.
+// Then, after all merging, a single instance of TIntermediate represents
+// the whole stage. A final error check can be done on the resulting stage,
+// even if no merging was done (i.e., the stage was only one compilation unit).
+//
+
+#include "localintermediate.h"
+#include "../Include/InfoSink.h"
+
+namespace glslang {
+
+//
+// Link-time error emitter.
+//
+void TIntermediate::error(TInfoSink& infoSink, const char* message)
+{
+ infoSink.info.prefix(EPrefixError);
+ infoSink.info << "Linking " << StageName(language) << " stage: " << message << "\n";
+
+ ++numErrors;
+}
+
+// Link-time warning.
+void TIntermediate::warn(TInfoSink& infoSink, const char* message)
+{
+ infoSink.info.prefix(EPrefixWarning);
+ infoSink.info << "Linking " << StageName(language) << " stage: " << message << "\n";
+}
+
+// TODO: 4.4 offset/align: "Two blocks linked together in the same program with the same block
+// name must have the exact same set of members qualified with offset and their integral-constant
+// expression values must be the same, or a link-time error results."
+
+//
+// Merge the information from 'unit' into 'this'
+//
+void TIntermediate::merge(TInfoSink& infoSink, TIntermediate& unit)
+{
+ mergeCallGraphs(infoSink, unit);
+ mergeModes(infoSink, unit);
+ mergeTrees(infoSink, unit);
+}
+
+void TIntermediate::mergeCallGraphs(TInfoSink& infoSink, TIntermediate& unit)
+{
+ if (unit.getNumEntryPoints() > 0) {
+ if (getNumEntryPoints() > 0)
+ error(infoSink, "can't handle multiple entry points per stage");
+ else {
+ entryPointName = unit.getEntryPointName();
+ entryPointMangledName = unit.getEntryPointMangledName();
+ }
+ }
+ numEntryPoints += unit.getNumEntryPoints();
+
+ callGraph.insert(callGraph.end(), unit.callGraph.begin(), unit.callGraph.end());
+}
+
+#define MERGE_MAX(member) member = std::max(member, unit.member)
+#define MERGE_TRUE(member) if (unit.member) member = unit.member;
+
+void TIntermediate::mergeModes(TInfoSink& infoSink, TIntermediate& unit)
+{
+ if (language != unit.language)
+ error(infoSink, "stages must match when linking into a single stage");
+
+ if (source == EShSourceNone)
+ source = unit.source;
+ if (source != unit.source)
+ error(infoSink, "can't link compilation units from different source languages");
+
+ if (treeRoot == nullptr) {
+ profile = unit.profile;
+ version = unit.version;
+ requestedExtensions = unit.requestedExtensions;
+ } else {
+ if ((profile == EEsProfile) != (unit.profile == EEsProfile))
+ error(infoSink, "Cannot cross link ES and desktop profiles");
+ else if (unit.profile == ECompatibilityProfile)
+ profile = ECompatibilityProfile;
+ version = std::max(version, unit.version);
+ requestedExtensions.insert(unit.requestedExtensions.begin(), unit.requestedExtensions.end());
+ }
+
+ MERGE_MAX(spvVersion.spv);
+ MERGE_MAX(spvVersion.vulkanGlsl);
+ MERGE_MAX(spvVersion.vulkan);
+ MERGE_MAX(spvVersion.openGl);
+
+ numErrors += unit.getNumErrors();
+ numPushConstants += unit.numPushConstants;
+
+ if (unit.invocations != TQualifier::layoutNotSet) {
+ if (invocations == TQualifier::layoutNotSet)
+ invocations = unit.invocations;
+ else if (invocations != unit.invocations)
+ error(infoSink, "number of invocations must match between compilation units");
+ }
+
+ if (vertices == TQualifier::layoutNotSet)
+ vertices = unit.vertices;
+ else if (vertices != unit.vertices) {
+ if (language == EShLangGeometry
+#ifdef NV_EXTENSIONS
+ || language == EShLangMeshNV
+#endif
+ )
+ error(infoSink, "Contradictory layout max_vertices values");
+ else if (language == EShLangTessControl)
+ error(infoSink, "Contradictory layout vertices values");
+ else
+ assert(0);
+ }
+#ifdef NV_EXTENSIONS
+ if (primitives == TQualifier::layoutNotSet)
+ primitives = unit.primitives;
+ else if (primitives != unit.primitives) {
+ if (language == EShLangMeshNV)
+ error(infoSink, "Contradictory layout max_primitives values");
+ else
+ assert(0);
+ }
+#endif
+
+ if (inputPrimitive == ElgNone)
+ inputPrimitive = unit.inputPrimitive;
+ else if (inputPrimitive != unit.inputPrimitive)
+ error(infoSink, "Contradictory input layout primitives");
+
+ if (outputPrimitive == ElgNone)
+ outputPrimitive = unit.outputPrimitive;
+ else if (outputPrimitive != unit.outputPrimitive)
+ error(infoSink, "Contradictory output layout primitives");
+
+ if (originUpperLeft != unit.originUpperLeft || pixelCenterInteger != unit.pixelCenterInteger)
+ error(infoSink, "gl_FragCoord redeclarations must match across shaders");
+
+ if (vertexSpacing == EvsNone)
+ vertexSpacing = unit.vertexSpacing;
+ else if (vertexSpacing != unit.vertexSpacing)
+ error(infoSink, "Contradictory input vertex spacing");
+
+ if (vertexOrder == EvoNone)
+ vertexOrder = unit.vertexOrder;
+ else if (vertexOrder != unit.vertexOrder)
+ error(infoSink, "Contradictory triangle ordering");
+
+ MERGE_TRUE(pointMode);
+
+ for (int i = 0; i < 3; ++i) {
+ if (localSize[i] > 1)
+ localSize[i] = unit.localSize[i];
+ else if (localSize[i] != unit.localSize[i])
+ error(infoSink, "Contradictory local size");
+
+ if (localSizeSpecId[i] != TQualifier::layoutNotSet)
+ localSizeSpecId[i] = unit.localSizeSpecId[i];
+ else if (localSizeSpecId[i] != unit.localSizeSpecId[i])
+ error(infoSink, "Contradictory local size specialization ids");
+ }
+
+ MERGE_TRUE(earlyFragmentTests);
+ MERGE_TRUE(postDepthCoverage);
+
+ if (depthLayout == EldNone)
+ depthLayout = unit.depthLayout;
+ else if (depthLayout != unit.depthLayout)
+ error(infoSink, "Contradictory depth layouts");
+
+ MERGE_TRUE(depthReplacing);
+ MERGE_TRUE(hlslFunctionality1);
+
+ blendEquations |= unit.blendEquations;
+
+ MERGE_TRUE(xfbMode);
+
+ for (size_t b = 0; b < xfbBuffers.size(); ++b) {
+ if (xfbBuffers[b].stride == TQualifier::layoutXfbStrideEnd)
+ xfbBuffers[b].stride = unit.xfbBuffers[b].stride;
+ else if (xfbBuffers[b].stride != unit.xfbBuffers[b].stride)
+ error(infoSink, "Contradictory xfb_stride");
+ xfbBuffers[b].implicitStride = std::max(xfbBuffers[b].implicitStride, unit.xfbBuffers[b].implicitStride);
+ if (unit.xfbBuffers[b].contains64BitType)
+ xfbBuffers[b].contains64BitType = true;
+#ifdef AMD_EXTENSIONS
+ if (unit.xfbBuffers[b].contains32BitType)
+ xfbBuffers[b].contains32BitType = true;
+ if (unit.xfbBuffers[b].contains16BitType)
+ xfbBuffers[b].contains16BitType = true;
+#endif
+ // TODO: 4.4 link: enhanced layouts: compare ranges
+ }
+
+ MERGE_TRUE(multiStream);
+
+#ifdef NV_EXTENSIONS
+ MERGE_TRUE(layoutOverrideCoverage);
+ MERGE_TRUE(geoPassthroughEXT);
+#endif
+
+ for (unsigned int i = 0; i < unit.shiftBinding.size(); ++i) {
+ if (unit.shiftBinding[i] > 0)
+ setShiftBinding((TResourceType)i, unit.shiftBinding[i]);
+ }
+
+ for (unsigned int i = 0; i < unit.shiftBindingForSet.size(); ++i) {
+ for (auto it = unit.shiftBindingForSet[i].begin(); it != unit.shiftBindingForSet[i].end(); ++it)
+ setShiftBindingForSet((TResourceType)i, it->second, it->first);
+ }
+
+ resourceSetBinding.insert(resourceSetBinding.end(), unit.resourceSetBinding.begin(), unit.resourceSetBinding.end());
+
+ MERGE_TRUE(autoMapBindings);
+ MERGE_TRUE(autoMapLocations);
+ MERGE_TRUE(invertY);
+ MERGE_TRUE(flattenUniformArrays);
+ MERGE_TRUE(useUnknownFormat);
+ MERGE_TRUE(hlslOffsets);
+ MERGE_TRUE(useStorageBuffer);
+ MERGE_TRUE(hlslIoMapping);
+
+ // TODO: sourceFile
+ // TODO: sourceText
+ // TODO: processes
+
+ MERGE_TRUE(needToLegalize);
+ MERGE_TRUE(binaryDoubleOutput);
+ MERGE_TRUE(usePhysicalStorageBuffer);
+}
+
+//
+// Merge the 'unit' AST into 'this' AST.
+// That includes rationalizing the unique IDs, which were set up independently,
+// and might have overlaps that are not the same symbol, or might have different
+// IDs for what should be the same shared symbol.
+//
+void TIntermediate::mergeTrees(TInfoSink& infoSink, TIntermediate& unit)
+{
+ if (unit.treeRoot == nullptr)
+ return;
+
+ if (treeRoot == nullptr) {
+ treeRoot = unit.treeRoot;
+ return;
+ }
+
+ // Getting this far means we have two existing trees to merge...
+#ifdef NV_EXTENSIONS
+ numShaderRecordNVBlocks += unit.numShaderRecordNVBlocks;
+#endif
+
+#ifdef NV_EXTENSIONS
+ numTaskNVBlocks += unit.numTaskNVBlocks;
+#endif
+
+ // Get the top-level globals of each unit
+ TIntermSequence& globals = treeRoot->getAsAggregate()->getSequence();
+ TIntermSequence& unitGlobals = unit.treeRoot->getAsAggregate()->getSequence();
+
+ // Get the linker-object lists
+ TIntermSequence& linkerObjects = findLinkerObjects()->getSequence();
+ const TIntermSequence& unitLinkerObjects = unit.findLinkerObjects()->getSequence();
+
+ // Map by global name to unique ID to rationalize the same object having
+ // differing IDs in different trees.
+ TMap<TString, int> idMap;
+ int maxId;
+ seedIdMap(idMap, maxId);
+ remapIds(idMap, maxId + 1, unit);
+
+ mergeBodies(infoSink, globals, unitGlobals);
+ mergeLinkerObjects(infoSink, linkerObjects, unitLinkerObjects);
+ ioAccessed.insert(unit.ioAccessed.begin(), unit.ioAccessed.end());
+}
+
+// Traverser that seeds an ID map with all built-ins, and tracks the
+// maximum ID used.
+// (It would be nice to put this in a function, but that causes warnings
+// on having no bodies for the copy-constructor/operator=.)
+class TBuiltInIdTraverser : public TIntermTraverser {
+public:
+ TBuiltInIdTraverser(TMap<TString, int>& idMap) : idMap(idMap), maxId(0) { }
+ // If it's a built in, add it to the map.
+ // Track the max ID.
+ virtual void visitSymbol(TIntermSymbol* symbol)
+ {
+ const TQualifier& qualifier = symbol->getType().getQualifier();
+ if (qualifier.builtIn != EbvNone)
+ idMap[symbol->getName()] = symbol->getId();
+ maxId = std::max(maxId, symbol->getId());
+ }
+ int getMaxId() const { return maxId; }
+protected:
+ TBuiltInIdTraverser(TBuiltInIdTraverser&);
+ TBuiltInIdTraverser& operator=(TBuiltInIdTraverser&);
+ TMap<TString, int>& idMap;
+ int maxId;
+};
+
+// Traverser that seeds an ID map with non-builtins.
+// (It would be nice to put this in a function, but that causes warnings
+// on having no bodies for the copy-constructor/operator=.)
+class TUserIdTraverser : public TIntermTraverser {
+public:
+ TUserIdTraverser(TMap<TString, int>& idMap) : idMap(idMap) { }
+ // If its a non-built-in global, add it to the map.
+ virtual void visitSymbol(TIntermSymbol* symbol)
+ {
+ const TQualifier& qualifier = symbol->getType().getQualifier();
+ if (qualifier.builtIn == EbvNone)
+ idMap[symbol->getName()] = symbol->getId();
+ }
+
+protected:
+ TUserIdTraverser(TUserIdTraverser&);
+ TUserIdTraverser& operator=(TUserIdTraverser&);
+ TMap<TString, int>& idMap; // over biggest id
+};
+
+// Initialize the the ID map with what we know of 'this' AST.
+void TIntermediate::seedIdMap(TMap<TString, int>& idMap, int& maxId)
+{
+ // all built-ins everywhere need to align on IDs and contribute to the max ID
+ TBuiltInIdTraverser builtInIdTraverser(idMap);
+ treeRoot->traverse(&builtInIdTraverser);
+ maxId = builtInIdTraverser.getMaxId();
+
+ // user variables in the linker object list need to align on ids
+ TUserIdTraverser userIdTraverser(idMap);
+ findLinkerObjects()->traverse(&userIdTraverser);
+}
+
+// Traverser to map an AST ID to what was known from the seeding AST.
+// (It would be nice to put this in a function, but that causes warnings
+// on having no bodies for the copy-constructor/operator=.)
+class TRemapIdTraverser : public TIntermTraverser {
+public:
+ TRemapIdTraverser(const TMap<TString, int>& idMap, int idShift) : idMap(idMap), idShift(idShift) { }
+ // Do the mapping:
+ // - if the same symbol, adopt the 'this' ID
+ // - otherwise, ensure a unique ID by shifting to a new space
+ virtual void visitSymbol(TIntermSymbol* symbol)
+ {
+ const TQualifier& qualifier = symbol->getType().getQualifier();
+ bool remapped = false;
+ if (qualifier.isLinkable() || qualifier.builtIn != EbvNone) {
+ auto it = idMap.find(symbol->getName());
+ if (it != idMap.end()) {
+ symbol->changeId(it->second);
+ remapped = true;
+ }
+ }
+ if (!remapped)
+ symbol->changeId(symbol->getId() + idShift);
+ }
+protected:
+ TRemapIdTraverser(TRemapIdTraverser&);
+ TRemapIdTraverser& operator=(TRemapIdTraverser&);
+ const TMap<TString, int>& idMap;
+ int idShift;
+};
+
+void TIntermediate::remapIds(const TMap<TString, int>& idMap, int idShift, TIntermediate& unit)
+{
+ // Remap all IDs to either share or be unique, as dictated by the idMap and idShift.
+ TRemapIdTraverser idTraverser(idMap, idShift);
+ unit.getTreeRoot()->traverse(&idTraverser);
+}
+
+//
+// Merge the function bodies and global-level initializers from unitGlobals into globals.
+// Will error check duplication of function bodies for the same signature.
+//
+void TIntermediate::mergeBodies(TInfoSink& infoSink, TIntermSequence& globals, const TIntermSequence& unitGlobals)
+{
+ // TODO: link-time performance: Processing in alphabetical order will be faster
+
+ // Error check the global objects, not including the linker objects
+ for (unsigned int child = 0; child < globals.size() - 1; ++child) {
+ for (unsigned int unitChild = 0; unitChild < unitGlobals.size() - 1; ++unitChild) {
+ TIntermAggregate* body = globals[child]->getAsAggregate();
+ TIntermAggregate* unitBody = unitGlobals[unitChild]->getAsAggregate();
+ if (body && unitBody && body->getOp() == EOpFunction && unitBody->getOp() == EOpFunction && body->getName() == unitBody->getName()) {
+ error(infoSink, "Multiple function bodies in multiple compilation units for the same signature in the same stage:");
+ infoSink.info << " " << globals[child]->getAsAggregate()->getName() << "\n";
+ }
+ }
+ }
+
+ // Merge the global objects, just in front of the linker objects
+ globals.insert(globals.end() - 1, unitGlobals.begin(), unitGlobals.end() - 1);
+}
+
+//
+// Merge the linker objects from unitLinkerObjects into linkerObjects.
+// Duplication is expected and filtered out, but contradictions are an error.
+//
+void TIntermediate::mergeLinkerObjects(TInfoSink& infoSink, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects)
+{
+ // Error check and merge the linker objects (duplicates should not be created)
+ std::size_t initialNumLinkerObjects = linkerObjects.size();
+ for (unsigned int unitLinkObj = 0; unitLinkObj < unitLinkerObjects.size(); ++unitLinkObj) {
+ bool merge = true;
+ for (std::size_t linkObj = 0; linkObj < initialNumLinkerObjects; ++linkObj) {
+ TIntermSymbol* symbol = linkerObjects[linkObj]->getAsSymbolNode();
+ TIntermSymbol* unitSymbol = unitLinkerObjects[unitLinkObj]->getAsSymbolNode();
+ assert(symbol && unitSymbol);
+ if (symbol->getName() == unitSymbol->getName()) {
+ // filter out copy
+ merge = false;
+
+ // but if one has an initializer and the other does not, update
+ // the initializer
+ if (symbol->getConstArray().empty() && ! unitSymbol->getConstArray().empty())
+ symbol->setConstArray(unitSymbol->getConstArray());
+
+ // Similarly for binding
+ if (! symbol->getQualifier().hasBinding() && unitSymbol->getQualifier().hasBinding())
+ symbol->getQualifier().layoutBinding = unitSymbol->getQualifier().layoutBinding;
+
+ // Update implicit array sizes
+ mergeImplicitArraySizes(symbol->getWritableType(), unitSymbol->getType());
+
+ // Check for consistent types/qualification/initializers etc.
+ mergeErrorCheck(infoSink, *symbol, *unitSymbol, false);
+ }
+ }
+ if (merge)
+ linkerObjects.push_back(unitLinkerObjects[unitLinkObj]);
+ }
+}
+
+// TODO 4.5 link functionality: cull distance array size checking
+
+// Recursively merge the implicit array sizes through the objects' respective type trees.
+void TIntermediate::mergeImplicitArraySizes(TType& type, const TType& unitType)
+{
+ if (type.isUnsizedArray()) {
+ if (unitType.isUnsizedArray()) {
+ type.updateImplicitArraySize(unitType.getImplicitArraySize());
+ if (unitType.isArrayVariablyIndexed())
+ type.setArrayVariablyIndexed();
+ } else if (unitType.isSizedArray())
+ type.changeOuterArraySize(unitType.getOuterArraySize());
+ }
+
+ // Type mismatches are caught and reported after this, just be careful for now.
+ if (! type.isStruct() || ! unitType.isStruct() || type.getStruct()->size() != unitType.getStruct()->size())
+ return;
+
+ for (int i = 0; i < (int)type.getStruct()->size(); ++i)
+ mergeImplicitArraySizes(*(*type.getStruct())[i].type, *(*unitType.getStruct())[i].type);
+}
+
+//
+// Compare two global objects from two compilation units and see if they match
+// well enough. Rules can be different for intra- vs. cross-stage matching.
+//
+// This function only does one of intra- or cross-stage matching per call.
+//
+void TIntermediate::mergeErrorCheck(TInfoSink& infoSink, const TIntermSymbol& symbol, const TIntermSymbol& unitSymbol, bool crossStage)
+{
+ bool writeTypeComparison = false;
+
+ // Types have to match
+ if (symbol.getType() != unitSymbol.getType()) {
+ // but, we make an exception if one is an implicit array and the other is sized
+ if (! (symbol.getType().isArray() && unitSymbol.getType().isArray() &&
+ symbol.getType().sameElementType(unitSymbol.getType()) &&
+ (symbol.getType().isUnsizedArray() || unitSymbol.getType().isUnsizedArray()))) {
+ error(infoSink, "Types must match:");
+ writeTypeComparison = true;
+ }
+ }
+
+ // Qualifiers have to (almost) match
+
+ // Storage...
+ if (symbol.getQualifier().storage != unitSymbol.getQualifier().storage) {
+ error(infoSink, "Storage qualifiers must match:");
+ writeTypeComparison = true;
+ }
+
+ // Precision...
+ if (symbol.getQualifier().precision != unitSymbol.getQualifier().precision) {
+ error(infoSink, "Precision qualifiers must match:");
+ writeTypeComparison = true;
+ }
+
+ // Invariance...
+ if (! crossStage && symbol.getQualifier().invariant != unitSymbol.getQualifier().invariant) {
+ error(infoSink, "Presence of invariant qualifier must match:");
+ writeTypeComparison = true;
+ }
+
+ // Precise...
+ if (! crossStage && symbol.getQualifier().noContraction != unitSymbol.getQualifier().noContraction) {
+ error(infoSink, "Presence of precise qualifier must match:");
+ writeTypeComparison = true;
+ }
+
+ // Auxiliary and interpolation...
+ if (symbol.getQualifier().centroid != unitSymbol.getQualifier().centroid ||
+ symbol.getQualifier().smooth != unitSymbol.getQualifier().smooth ||
+ symbol.getQualifier().flat != unitSymbol.getQualifier().flat ||
+ symbol.getQualifier().sample != unitSymbol.getQualifier().sample ||
+ symbol.getQualifier().patch != unitSymbol.getQualifier().patch ||
+ symbol.getQualifier().nopersp != unitSymbol.getQualifier().nopersp) {
+ error(infoSink, "Interpolation and auxiliary storage qualifiers must match:");
+ writeTypeComparison = true;
+ }
+
+ // Memory...
+ if (symbol.getQualifier().coherent != unitSymbol.getQualifier().coherent ||
+ symbol.getQualifier().devicecoherent != unitSymbol.getQualifier().devicecoherent ||
+ symbol.getQualifier().queuefamilycoherent != unitSymbol.getQualifier().queuefamilycoherent ||
+ symbol.getQualifier().workgroupcoherent != unitSymbol.getQualifier().workgroupcoherent ||
+ symbol.getQualifier().subgroupcoherent != unitSymbol.getQualifier().subgroupcoherent ||
+ symbol.getQualifier().nonprivate != unitSymbol.getQualifier().nonprivate ||
+ symbol.getQualifier().volatil != unitSymbol.getQualifier().volatil ||
+ symbol.getQualifier().restrict != unitSymbol.getQualifier().restrict ||
+ symbol.getQualifier().readonly != unitSymbol.getQualifier().readonly ||
+ symbol.getQualifier().writeonly != unitSymbol.getQualifier().writeonly) {
+ error(infoSink, "Memory qualifiers must match:");
+ writeTypeComparison = true;
+ }
+
+ // Layouts...
+ // TODO: 4.4 enhanced layouts: Generalize to include offset/align: current spec
+ // requires separate user-supplied offset from actual computed offset, but
+ // current implementation only has one offset.
+ if (symbol.getQualifier().layoutMatrix != unitSymbol.getQualifier().layoutMatrix ||
+ symbol.getQualifier().layoutPacking != unitSymbol.getQualifier().layoutPacking ||
+ symbol.getQualifier().layoutLocation != unitSymbol.getQualifier().layoutLocation ||
+ symbol.getQualifier().layoutComponent != unitSymbol.getQualifier().layoutComponent ||
+ symbol.getQualifier().layoutIndex != unitSymbol.getQualifier().layoutIndex ||
+ symbol.getQualifier().layoutBinding != unitSymbol.getQualifier().layoutBinding ||
+ (symbol.getQualifier().hasBinding() && (symbol.getQualifier().layoutOffset != unitSymbol.getQualifier().layoutOffset))) {
+ error(infoSink, "Layout qualification must match:");
+ writeTypeComparison = true;
+ }
+
+ // Initializers have to match, if both are present, and if we don't already know the types don't match
+ if (! writeTypeComparison) {
+ if (! symbol.getConstArray().empty() && ! unitSymbol.getConstArray().empty()) {
+ if (symbol.getConstArray() != unitSymbol.getConstArray()) {
+ error(infoSink, "Initializers must match:");
+ infoSink.info << " " << symbol.getName() << "\n";
+ }
+ }
+ }
+
+ if (writeTypeComparison)
+ infoSink.info << " " << symbol.getName() << ": \"" << symbol.getType().getCompleteString() << "\" versus \"" <<
+ unitSymbol.getType().getCompleteString() << "\"\n";
+}
+
+//
+// Do final link-time error checking of a complete (merged) intermediate representation.
+// (Much error checking was done during merging).
+//
+// Also, lock in defaults of things not set, including array sizes.
+//
+void TIntermediate::finalCheck(TInfoSink& infoSink, bool keepUncalled)
+{
+ if (getTreeRoot() == nullptr)
+ return;
+
+ if (numEntryPoints < 1) {
+ if (source == EShSourceGlsl)
+ error(infoSink, "Missing entry point: Each stage requires one entry point");
+ else
+ warn(infoSink, "Entry point not found");
+ }
+
+ if (numPushConstants > 1)
+ error(infoSink, "Only one push_constant block is allowed per stage");
+
+ // recursion and missing body checking
+ checkCallGraphCycles(infoSink);
+ checkCallGraphBodies(infoSink, keepUncalled);
+
+ // overlap/alias/missing I/O, etc.
+ inOutLocationCheck(infoSink);
+
+ // invocations
+ if (invocations == TQualifier::layoutNotSet)
+ invocations = 1;
+
+ if (inIoAccessed("gl_ClipDistance") && inIoAccessed("gl_ClipVertex"))
+ error(infoSink, "Can only use one of gl_ClipDistance or gl_ClipVertex (gl_ClipDistance is preferred)");
+ if (inIoAccessed("gl_CullDistance") && inIoAccessed("gl_ClipVertex"))
+ error(infoSink, "Can only use one of gl_CullDistance or gl_ClipVertex (gl_ClipDistance is preferred)");
+
+ if (userOutputUsed() && (inIoAccessed("gl_FragColor") || inIoAccessed("gl_FragData")))
+ error(infoSink, "Cannot use gl_FragColor or gl_FragData when using user-defined outputs");
+ if (inIoAccessed("gl_FragColor") && inIoAccessed("gl_FragData"))
+ error(infoSink, "Cannot use both gl_FragColor and gl_FragData");
+
+ for (size_t b = 0; b < xfbBuffers.size(); ++b) {
+ if (xfbBuffers[b].contains64BitType)
+ RoundToPow2(xfbBuffers[b].implicitStride, 8);
+#ifdef AMD_EXTENSIONS
+ else if (xfbBuffers[b].contains32BitType)
+ RoundToPow2(xfbBuffers[b].implicitStride, 4);
+ else if (xfbBuffers[b].contains16BitType)
+ RoundToPow2(xfbBuffers[b].implicitStride, 2);
+#endif
+
+ // "It is a compile-time or link-time error to have
+ // any xfb_offset that overflows xfb_stride, whether stated on declarations before or after the xfb_stride, or
+ // in different compilation units. While xfb_stride can be declared multiple times for the same buffer, it is a
+ // compile-time or link-time error to have different values specified for the stride for the same buffer."
+ if (xfbBuffers[b].stride != TQualifier::layoutXfbStrideEnd && xfbBuffers[b].implicitStride > xfbBuffers[b].stride) {
+ error(infoSink, "xfb_stride is too small to hold all buffer entries:");
+ infoSink.info.prefix(EPrefixError);
+ infoSink.info << " xfb_buffer " << (unsigned int)b << ", xfb_stride " << xfbBuffers[b].stride << ", minimum stride needed: " << xfbBuffers[b].implicitStride << "\n";
+ }
+ if (xfbBuffers[b].stride == TQualifier::layoutXfbStrideEnd)
+ xfbBuffers[b].stride = xfbBuffers[b].implicitStride;
+
+ // "If the buffer is capturing any
+ // outputs with double-precision or 64-bit integer components, the stride must be a multiple of 8, otherwise it must be a
+ // multiple of 4, or a compile-time or link-time error results."
+ if (xfbBuffers[b].contains64BitType && ! IsMultipleOfPow2(xfbBuffers[b].stride, 8)) {
+ error(infoSink, "xfb_stride must be multiple of 8 for buffer holding a double or 64-bit integer:");
+ infoSink.info.prefix(EPrefixError);
+ infoSink.info << " xfb_buffer " << (unsigned int)b << ", xfb_stride " << xfbBuffers[b].stride << "\n";
+#ifdef AMD_EXTENSIONS
+ } else if (xfbBuffers[b].contains32BitType && ! IsMultipleOfPow2(xfbBuffers[b].stride, 4)) {
+#else
+ } else if (! IsMultipleOfPow2(xfbBuffers[b].stride, 4)) {
+#endif
+ error(infoSink, "xfb_stride must be multiple of 4:");
+ infoSink.info.prefix(EPrefixError);
+ infoSink.info << " xfb_buffer " << (unsigned int)b << ", xfb_stride " << xfbBuffers[b].stride << "\n";
+ }
+#ifdef AMD_EXTENSIONS
+ // "If the buffer is capturing any
+ // outputs with half-precision or 16-bit integer components, the stride must be a multiple of 2"
+ else if (xfbBuffers[b].contains16BitType && ! IsMultipleOfPow2(xfbBuffers[b].stride, 2)) {
+ error(infoSink, "xfb_stride must be multiple of 2 for buffer holding a half float or 16-bit integer:");
+ infoSink.info.prefix(EPrefixError);
+ infoSink.info << " xfb_buffer " << (unsigned int)b << ", xfb_stride " << xfbBuffers[b].stride << "\n";
+ }
+
+#endif
+ // "The resulting stride (implicit or explicit), when divided by 4, must be less than or equal to the
+ // implementation-dependent constant gl_MaxTransformFeedbackInterleavedComponents."
+ if (xfbBuffers[b].stride > (unsigned int)(4 * resources.maxTransformFeedbackInterleavedComponents)) {
+ error(infoSink, "xfb_stride is too large:");
+ infoSink.info.prefix(EPrefixError);
+ infoSink.info << " xfb_buffer " << (unsigned int)b << ", components (1/4 stride) needed are " << xfbBuffers[b].stride/4 << ", gl_MaxTransformFeedbackInterleavedComponents is " << resources.maxTransformFeedbackInterleavedComponents << "\n";
+ }
+ }
+
+ switch (language) {
+ case EShLangVertex:
+ break;
+ case EShLangTessControl:
+ if (vertices == TQualifier::layoutNotSet)
+ error(infoSink, "At least one shader must specify an output layout(vertices=...)");
+ break;
+ case EShLangTessEvaluation:
+ if (source == EShSourceGlsl) {
+ if (inputPrimitive == ElgNone)
+ error(infoSink, "At least one shader must specify an input layout primitive");
+ if (vertexSpacing == EvsNone)
+ vertexSpacing = EvsEqual;
+ if (vertexOrder == EvoNone)
+ vertexOrder = EvoCcw;
+ }
+ break;
+ case EShLangGeometry:
+ if (inputPrimitive == ElgNone)
+ error(infoSink, "At least one shader must specify an input layout primitive");
+ if (outputPrimitive == ElgNone)
+ error(infoSink, "At least one shader must specify an output layout primitive");
+ if (vertices == TQualifier::layoutNotSet)
+ error(infoSink, "At least one shader must specify a layout(max_vertices = value)");
+ break;
+ case EShLangFragment:
+ // for GL_ARB_post_depth_coverage, EarlyFragmentTest is set automatically in
+ // ParseHelper.cpp. So if we reach here, this must be GL_EXT_post_depth_coverage
+ // requiring explicit early_fragment_tests
+ if (getPostDepthCoverage() && !getEarlyFragmentTests())
+ error(infoSink, "post_depth_coverage requires early_fragment_tests");
+ break;
+ case EShLangCompute:
+ break;
+
+#ifdef NV_EXTENSIONS
+ case EShLangRayGenNV:
+ case EShLangIntersectNV:
+ case EShLangAnyHitNV:
+ case EShLangClosestHitNV:
+ case EShLangMissNV:
+ case EShLangCallableNV:
+ if (numShaderRecordNVBlocks > 1)
+ error(infoSink, "Only one shaderRecordNV buffer block is allowed per stage");
+ break;
+ case EShLangMeshNV:
+ // NV_mesh_shader doesn't allow use of both single-view and per-view builtins.
+ if (inIoAccessed("gl_Position") && inIoAccessed("gl_PositionPerViewNV"))
+ error(infoSink, "Can only use one of gl_Position or gl_PositionPerViewNV");
+ if (inIoAccessed("gl_ClipDistance") && inIoAccessed("gl_ClipDistancePerViewNV"))
+ error(infoSink, "Can only use one of gl_ClipDistance or gl_ClipDistancePerViewNV");
+ if (inIoAccessed("gl_CullDistance") && inIoAccessed("gl_CullDistancePerViewNV"))
+ error(infoSink, "Can only use one of gl_CullDistance or gl_CullDistancePerViewNV");
+ if (inIoAccessed("gl_Layer") && inIoAccessed("gl_LayerPerViewNV"))
+ error(infoSink, "Can only use one of gl_Layer or gl_LayerPerViewNV");
+ if (inIoAccessed("gl_ViewportMask") && inIoAccessed("gl_ViewportMaskPerViewNV"))
+ error(infoSink, "Can only use one of gl_ViewportMask or gl_ViewportMaskPerViewNV");
+ if (outputPrimitive == ElgNone)
+ error(infoSink, "At least one shader must specify an output layout primitive");
+ if (vertices == TQualifier::layoutNotSet)
+ error(infoSink, "At least one shader must specify a layout(max_vertices = value)");
+ if (primitives == TQualifier::layoutNotSet)
+ error(infoSink, "At least one shader must specify a layout(max_primitives = value)");
+ // fall through
+ case EShLangTaskNV:
+ if (numTaskNVBlocks > 1)
+ error(infoSink, "Only one taskNV interface block is allowed per shader");
+ break;
+#endif
+
+ default:
+ error(infoSink, "Unknown Stage.");
+ break;
+ }
+
+ // Process the tree for any node-specific work.
+ class TFinalLinkTraverser : public TIntermTraverser {
+ public:
+ TFinalLinkTraverser() { }
+ virtual ~TFinalLinkTraverser() { }
+
+ virtual void visitSymbol(TIntermSymbol* symbol)
+ {
+ // Implicitly size arrays.
+ // If an unsized array is left as unsized, it effectively
+ // becomes run-time sized.
+ symbol->getWritableType().adoptImplicitArraySizes(false);
+ }
+ } finalLinkTraverser;
+
+ treeRoot->traverse(&finalLinkTraverser);
+}
+
+//
+// See if the call graph contains any static recursion, which is disallowed
+// by the specification.
+//
+void TIntermediate::checkCallGraphCycles(TInfoSink& infoSink)
+{
+ // Clear fields we'll use for this.
+ for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) {
+ call->visited = false;
+ call->currentPath = false;
+ call->errorGiven = false;
+ }
+
+ //
+ // Loop, looking for a new connected subgraph. One subgraph is handled per loop iteration.
+ //
+
+ TCall* newRoot;
+ do {
+ // See if we have unvisited parts of the graph.
+ newRoot = 0;
+ for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) {
+ if (! call->visited) {
+ newRoot = &(*call);
+ break;
+ }
+ }
+
+ // If not, we are done.
+ if (! newRoot)
+ break;
+
+ // Otherwise, we found a new subgraph, process it:
+ // See what all can be reached by this new root, and if any of
+ // that is recursive. This is done by depth-first traversals, seeing
+ // if a new call is found that was already in the currentPath (a back edge),
+ // thereby detecting recursion.
+ std::list<TCall*> stack;
+ newRoot->currentPath = true; // currentPath will be true iff it is on the stack
+ stack.push_back(newRoot);
+ while (! stack.empty()) {
+ // get a caller
+ TCall* call = stack.back();
+
+ // Add to the stack just one callee.
+ // This algorithm always terminates, because only !visited and !currentPath causes a push
+ // and all pushes change currentPath to true, and all pops change visited to true.
+ TGraph::iterator child = callGraph.begin();
+ for (; child != callGraph.end(); ++child) {
+
+ // If we already visited this node, its whole subgraph has already been processed, so skip it.
+ if (child->visited)
+ continue;
+
+ if (call->callee == child->caller) {
+ if (child->currentPath) {
+ // Then, we found a back edge
+ if (! child->errorGiven) {
+ error(infoSink, "Recursion detected:");
+ infoSink.info << " " << call->callee << " calling " << child->callee << "\n";
+ child->errorGiven = true;
+ recursive = true;
+ }
+ } else {
+ child->currentPath = true;
+ stack.push_back(&(*child));
+ break;
+ }
+ }
+ }
+ if (child == callGraph.end()) {
+ // no more callees, we bottomed out, never look at this node again
+ stack.back()->currentPath = false;
+ stack.back()->visited = true;
+ stack.pop_back();
+ }
+ } // end while, meaning nothing left to process in this subtree
+
+ } while (newRoot); // redundant loop check; should always exit via the 'break' above
+}
+
+//
+// See which functions are reachable from the entry point and which have bodies.
+// Reachable ones with missing bodies are errors.
+// Unreachable bodies are dead code.
+//
+void TIntermediate::checkCallGraphBodies(TInfoSink& infoSink, bool keepUncalled)
+{
+ // Clear fields we'll use for this.
+ for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) {
+ call->visited = false;
+ call->calleeBodyPosition = -1;
+ }
+
+ // The top level of the AST includes function definitions (bodies).
+ // Compare these to function calls in the call graph.
+ // We'll end up knowing which have bodies, and if so,
+ // how to map the call-graph node to the location in the AST.
+ TIntermSequence &functionSequence = getTreeRoot()->getAsAggregate()->getSequence();
+ std::vector<bool> reachable(functionSequence.size(), true); // so that non-functions are reachable
+ for (int f = 0; f < (int)functionSequence.size(); ++f) {
+ glslang::TIntermAggregate* node = functionSequence[f]->getAsAggregate();
+ if (node && (node->getOp() == glslang::EOpFunction)) {
+ if (node->getName().compare(getEntryPointMangledName().c_str()) != 0)
+ reachable[f] = false; // so that function bodies are unreachable, until proven otherwise
+ for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) {
+ if (call->callee == node->getName())
+ call->calleeBodyPosition = f;
+ }
+ }
+ }
+
+ // Start call-graph traversal by visiting the entry point nodes.
+ for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) {
+ if (call->caller.compare(getEntryPointMangledName().c_str()) == 0)
+ call->visited = true;
+ }
+
+ // Propagate 'visited' through the call-graph to every part of the graph it
+ // can reach (seeded with the entry-point setting above).
+ bool changed;
+ do {
+ changed = false;
+ for (auto call1 = callGraph.begin(); call1 != callGraph.end(); ++call1) {
+ if (call1->visited) {
+ for (TGraph::iterator call2 = callGraph.begin(); call2 != callGraph.end(); ++call2) {
+ if (! call2->visited) {
+ if (call1->callee == call2->caller) {
+ changed = true;
+ call2->visited = true;
+ }
+ }
+ }
+ }
+ }
+ } while (changed);
+
+ // Any call-graph node set to visited but without a callee body is an error.
+ for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) {
+ if (call->visited) {
+ if (call->calleeBodyPosition == -1) {
+ error(infoSink, "No function definition (body) found: ");
+ infoSink.info << " " << call->callee << "\n";
+ } else
+ reachable[call->calleeBodyPosition] = true;
+ }
+ }
+
+ // Bodies in the AST not reached by the call graph are dead;
+ // clear them out, since they can't be reached and also can't
+ // be translated further due to possibility of being ill defined.
+ if (! keepUncalled) {
+ for (int f = 0; f < (int)functionSequence.size(); ++f) {
+ if (! reachable[f])
+ functionSequence[f] = nullptr;
+ }
+ functionSequence.erase(std::remove(functionSequence.begin(), functionSequence.end(), nullptr), functionSequence.end());
+ }
+}
+
+//
+// Satisfy rules for location qualifiers on inputs and outputs
+//
+void TIntermediate::inOutLocationCheck(TInfoSink& infoSink)
+{
+ // ES 3.0 requires all outputs to have location qualifiers if there is more than one output
+ bool fragOutWithNoLocation = false;
+ int numFragOut = 0;
+
+ // TODO: linker functionality: location collision checking
+
+ TIntermSequence& linkObjects = findLinkerObjects()->getSequence();
+ for (size_t i = 0; i < linkObjects.size(); ++i) {
+ const TType& type = linkObjects[i]->getAsTyped()->getType();
+ const TQualifier& qualifier = type.getQualifier();
+ if (language == EShLangFragment) {
+ if (qualifier.storage == EvqVaryingOut && qualifier.builtIn == EbvNone) {
+ ++numFragOut;
+ if (!qualifier.hasAnyLocation())
+ fragOutWithNoLocation = true;
+ }
+ }
+ }
+
+ if (profile == EEsProfile) {
+ if (numFragOut > 1 && fragOutWithNoLocation)
+ error(infoSink, "when more than one fragment shader output, all must have location qualifiers");
+ }
+}
+
+TIntermAggregate* TIntermediate::findLinkerObjects() const
+{
+ // Get the top-level globals
+ TIntermSequence& globals = treeRoot->getAsAggregate()->getSequence();
+
+ // Get the last member of the sequences, expected to be the linker-object lists
+ assert(globals.back()->getAsAggregate()->getOp() == EOpLinkerObjects);
+
+ return globals.back()->getAsAggregate();
+}
+
+// See if a variable was both a user-declared output and used.
+// Note: the spec discusses writing to one, but this looks at read or write, which
+// is more useful, and perhaps the spec should be changed to reflect that.
+bool TIntermediate::userOutputUsed() const
+{
+ const TIntermSequence& linkerObjects = findLinkerObjects()->getSequence();
+
+ bool found = false;
+ for (size_t i = 0; i < linkerObjects.size(); ++i) {
+ const TIntermSymbol& symbolNode = *linkerObjects[i]->getAsSymbolNode();
+ if (symbolNode.getQualifier().storage == EvqVaryingOut &&
+ symbolNode.getName().compare(0, 3, "gl_") != 0 &&
+ inIoAccessed(symbolNode.getName())) {
+ found = true;
+ break;
+ }
+ }
+
+ return found;
+}
+
+// Accumulate locations used for inputs, outputs, and uniforms, and check for collisions
+// as the accumulation is done.
+//
+// Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value.
+//
+// typeCollision is set to true if there is no direct collision, but the types in the same location
+// are different.
+//
+int TIntermediate::addUsedLocation(const TQualifier& qualifier, const TType& type, bool& typeCollision)
+{
+ typeCollision = false;
+
+ int set;
+ if (qualifier.isPipeInput())
+ set = 0;
+ else if (qualifier.isPipeOutput())
+ set = 1;
+ else if (qualifier.storage == EvqUniform)
+ set = 2;
+ else if (qualifier.storage == EvqBuffer)
+ set = 3;
+ else
+ return -1;
+
+ int size;
+ if (qualifier.isUniformOrBuffer() || qualifier.isTaskMemory()) {
+ if (type.isSizedArray())
+ size = type.getCumulativeArraySize();
+ else
+ size = 1;
+ } else {
+ // Strip off the outer array dimension for those having an extra one.
+ if (type.isArray() && qualifier.isArrayedIo(language)) {
+ TType elementType(type, 0);
+ size = computeTypeLocationSize(elementType, language);
+ } else
+ size = computeTypeLocationSize(type, language);
+ }
+
+ // Locations, and components within locations.
+ //
+ // Almost always, dealing with components means a single location is involved.
+ // The exception is a dvec3. From the spec:
+ //
+ // "A dvec3 will consume all four components of the first location and components 0 and 1 of
+ // the second location. This leaves components 2 and 3 available for other component-qualified
+ // declarations."
+ //
+ // That means, without ever mentioning a component, a component range
+ // for a different location gets specified, if it's not a vertex shader input. (!)
+ // (A vertex shader input will show using only one location, even for a dvec3/4.)
+ //
+ // So, for the case of dvec3, we need two independent ioRanges.
+
+ int collision = -1; // no collision
+ if (size == 2 && type.getBasicType() == EbtDouble && type.getVectorSize() == 3 &&
+ (qualifier.isPipeInput() || qualifier.isPipeOutput())) {
+ // Dealing with dvec3 in/out split across two locations.
+ // Need two io-ranges.
+ // The case where the dvec3 doesn't start at component 0 was previously caught as overflow.
+
+ // First range:
+ TRange locationRange(qualifier.layoutLocation, qualifier.layoutLocation);
+ TRange componentRange(0, 3);
+ TIoRange range(locationRange, componentRange, type.getBasicType(), 0);
+
+ // check for collisions
+ collision = checkLocationRange(set, range, type, typeCollision);
+ if (collision < 0) {
+ usedIo[set].push_back(range);
+
+ // Second range:
+ TRange locationRange2(qualifier.layoutLocation + 1, qualifier.layoutLocation + 1);
+ TRange componentRange2(0, 1);
+ TIoRange range2(locationRange2, componentRange2, type.getBasicType(), 0);
+
+ // check for collisions
+ collision = checkLocationRange(set, range2, type, typeCollision);
+ if (collision < 0)
+ usedIo[set].push_back(range2);
+ }
+ } else {
+ // Not a dvec3 in/out split across two locations, generic path.
+ // Need a single IO-range block.
+
+ TRange locationRange(qualifier.layoutLocation, qualifier.layoutLocation + size - 1);
+ TRange componentRange(0, 3);
+ if (qualifier.hasComponent() || type.getVectorSize() > 0) {
+ int consumedComponents = type.getVectorSize() * (type.getBasicType() == EbtDouble ? 2 : 1);
+ if (qualifier.hasComponent())
+ componentRange.start = qualifier.layoutComponent;
+ componentRange.last = componentRange.start + consumedComponents - 1;
+ }
+
+ // combine location and component ranges
+ TIoRange range(locationRange, componentRange, type.getBasicType(), qualifier.hasIndex() ? qualifier.layoutIndex : 0);
+
+ // check for collisions, except for vertex inputs on desktop targeting OpenGL
+ if (! (profile != EEsProfile && language == EShLangVertex && qualifier.isPipeInput()) || spvVersion.vulkan > 0)
+ collision = checkLocationRange(set, range, type, typeCollision);
+
+ if (collision < 0)
+ usedIo[set].push_back(range);
+ }
+
+ return collision;
+}
+
+// Compare a new (the passed in) 'range' against the existing set, and see
+// if there are any collisions.
+//
+// Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value.
+//
+int TIntermediate::checkLocationRange(int set, const TIoRange& range, const TType& type, bool& typeCollision)
+{
+ for (size_t r = 0; r < usedIo[set].size(); ++r) {
+ if (range.overlap(usedIo[set][r])) {
+ // there is a collision; pick one
+ return std::max(range.location.start, usedIo[set][r].location.start);
+ } else if (range.location.overlap(usedIo[set][r].location) && type.getBasicType() != usedIo[set][r].basicType) {
+ // aliased-type mismatch
+ typeCollision = true;
+ return std::max(range.location.start, usedIo[set][r].location.start);
+ }
+ }
+
+ return -1; // no collision
+}
+
+// Accumulate bindings and offsets, and check for collisions
+// as the accumulation is done.
+//
+// Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value.
+//
+int TIntermediate::addUsedOffsets(int binding, int offset, int numOffsets)
+{
+ TRange bindingRange(binding, binding);
+ TRange offsetRange(offset, offset + numOffsets - 1);
+ TOffsetRange range(bindingRange, offsetRange);
+
+ // check for collisions, except for vertex inputs on desktop
+ for (size_t r = 0; r < usedAtomics.size(); ++r) {
+ if (range.overlap(usedAtomics[r])) {
+ // there is a collision; pick one
+ return std::max(offset, usedAtomics[r].offset.start);
+ }
+ }
+
+ usedAtomics.push_back(range);
+
+ return -1; // no collision
+}
+
+// Accumulate used constant_id values.
+//
+// Return false is one was already used.
+bool TIntermediate::addUsedConstantId(int id)
+{
+ if (usedConstantId.find(id) != usedConstantId.end())
+ return false;
+
+ usedConstantId.insert(id);
+
+ return true;
+}
+
+// Recursively figure out how many locations are used up by an input or output type.
+// Return the size of type, as measured by "locations".
+int TIntermediate::computeTypeLocationSize(const TType& type, EShLanguage stage)
+{
+ // "If the declared input is an array of size n and each element takes m locations, it will be assigned m * n
+ // consecutive locations..."
+ if (type.isArray()) {
+ // TODO: perf: this can be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness
+ // TODO: are there valid cases of having an unsized array with a location? If so, running this code too early.
+ TType elementType(type, 0);
+ if (type.isSizedArray()
+#ifdef NV_EXTENSIONS
+ && !type.getQualifier().isPerView()
+#endif
+ )
+ return type.getOuterArraySize() * computeTypeLocationSize(elementType, stage);
+ else {
+#ifdef NV_EXTENSIONS
+ // unset perViewNV attributes for arrayed per-view outputs: "perviewNV vec4 v[MAX_VIEWS][3];"
+ elementType.getQualifier().perViewNV = false;
+#endif
+ return computeTypeLocationSize(elementType, stage);
+ }
+ }
+
+ // "The locations consumed by block and structure members are determined by applying the rules above
+ // recursively..."
+ if (type.isStruct()) {
+ int size = 0;
+ for (int member = 0; member < (int)type.getStruct()->size(); ++member) {
+ TType memberType(type, member);
+ size += computeTypeLocationSize(memberType, stage);
+ }
+ return size;
+ }
+
+ // ES: "If a shader input is any scalar or vector type, it will consume a single location."
+
+ // Desktop: "If a vertex shader input is any scalar or vector type, it will consume a single location. If a non-vertex
+ // shader input is a scalar or vector type other than dvec3 or dvec4, it will consume a single location, while
+ // types dvec3 or dvec4 will consume two consecutive locations. Inputs of type double and dvec2 will
+ // consume only a single location, in all stages."
+ if (type.isScalar())
+ return 1;
+ if (type.isVector()) {
+ if (stage == EShLangVertex && type.getQualifier().isPipeInput())
+ return 1;
+ if (type.getBasicType() == EbtDouble && type.getVectorSize() > 2)
+ return 2;
+ else
+ return 1;
+ }
+
+ // "If the declared input is an n x m single- or double-precision matrix, ...
+ // The number of locations assigned for each matrix will be the same as
+ // for an n-element array of m-component vectors..."
+ if (type.isMatrix()) {
+ TType columnType(type, 0);
+ return type.getMatrixCols() * computeTypeLocationSize(columnType, stage);
+ }
+
+ assert(0);
+ return 1;
+}
+
+// Same as computeTypeLocationSize but for uniforms
+int TIntermediate::computeTypeUniformLocationSize(const TType& type)
+{
+ // "Individual elements of a uniform array are assigned
+ // consecutive locations with the first element taking location
+ // location."
+ if (type.isArray()) {
+ // TODO: perf: this can be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness
+ TType elementType(type, 0);
+ if (type.isSizedArray()) {
+ return type.getOuterArraySize() * computeTypeUniformLocationSize(elementType);
+ } else {
+ // TODO: are there valid cases of having an implicitly-sized array with a location? If so, running this code too early.
+ return computeTypeUniformLocationSize(elementType);
+ }
+ }
+
+ // "Each subsequent inner-most member or element gets incremental
+ // locations for the entire structure or array."
+ if (type.isStruct()) {
+ int size = 0;
+ for (int member = 0; member < (int)type.getStruct()->size(); ++member) {
+ TType memberType(type, member);
+ size += computeTypeUniformLocationSize(memberType);
+ }
+ return size;
+ }
+
+ return 1;
+}
+
+// Accumulate xfb buffer ranges and check for collisions as the accumulation is done.
+//
+// Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value.
+//
+int TIntermediate::addXfbBufferOffset(const TType& type)
+{
+ const TQualifier& qualifier = type.getQualifier();
+
+ assert(qualifier.hasXfbOffset() && qualifier.hasXfbBuffer());
+ TXfbBuffer& buffer = xfbBuffers[qualifier.layoutXfbBuffer];
+
+ // compute the range
+#ifdef AMD_EXTENSIONS
+ unsigned int size = computeTypeXfbSize(type, buffer.contains64BitType, buffer.contains32BitType, buffer.contains16BitType);
+#else
+ unsigned int size = computeTypeXfbSize(type, buffer.contains64BitType);
+#endif
+ buffer.implicitStride = std::max(buffer.implicitStride, qualifier.layoutXfbOffset + size);
+ TRange range(qualifier.layoutXfbOffset, qualifier.layoutXfbOffset + size - 1);
+
+ // check for collisions
+ for (size_t r = 0; r < buffer.ranges.size(); ++r) {
+ if (range.overlap(buffer.ranges[r])) {
+ // there is a collision; pick an example to return
+ return std::max(range.start, buffer.ranges[r].start);
+ }
+ }
+
+ buffer.ranges.push_back(range);
+
+ return -1; // no collision
+}
+
+// Recursively figure out how many bytes of xfb buffer are used by the given type.
+// Return the size of type, in bytes.
+// Sets contains64BitType to true if the type contains a 64-bit data type.
+#ifdef AMD_EXTENSIONS
+// Sets contains32BitType to true if the type contains a 32-bit data type.
+// Sets contains16BitType to true if the type contains a 16-bit data type.
+// N.B. Caller must set contains64BitType, contains32BitType, and contains16BitType to false before calling.
+unsigned int TIntermediate::computeTypeXfbSize(const TType& type, bool& contains64BitType, bool& contains32BitType, bool& contains16BitType) const
+#else
+// N.B. Caller must set contains64BitType to false before calling.
+unsigned int TIntermediate::computeTypeXfbSize(const TType& type, bool& contains64BitType) const
+#endif
+{
+ // "...if applied to an aggregate containing a double or 64-bit integer, the offset must also be a multiple of 8,
+ // and the space taken in the buffer will be a multiple of 8.
+ // ...within the qualified entity, subsequent components are each
+ // assigned, in order, to the next available offset aligned to a multiple of
+ // that component's size. Aggregate types are flattened down to the component
+ // level to get this sequence of components."
+
+ if (type.isArray()) {
+ // TODO: perf: this can be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness
+ assert(type.isSizedArray());
+ TType elementType(type, 0);
+#ifdef AMD_EXTENSIONS
+ return type.getOuterArraySize() * computeTypeXfbSize(elementType, contains64BitType, contains16BitType, contains16BitType);
+#else
+ return type.getOuterArraySize() * computeTypeXfbSize(elementType, contains64BitType);
+#endif
+ }
+
+ if (type.isStruct()) {
+ unsigned int size = 0;
+ bool structContains64BitType = false;
+#ifdef AMD_EXTENSIONS
+ bool structContains32BitType = false;
+ bool structContains16BitType = false;
+#endif
+ for (int member = 0; member < (int)type.getStruct()->size(); ++member) {
+ TType memberType(type, member);
+ // "... if applied to
+ // an aggregate containing a double or 64-bit integer, the offset must also be a multiple of 8,
+ // and the space taken in the buffer will be a multiple of 8."
+ bool memberContains64BitType = false;
+#ifdef AMD_EXTENSIONS
+ bool memberContains32BitType = false;
+ bool memberContains16BitType = false;
+ int memberSize = computeTypeXfbSize(memberType, memberContains64BitType, memberContains32BitType, memberContains16BitType);
+#else
+ int memberSize = computeTypeXfbSize(memberType, memberContains64BitType);
+#endif
+ if (memberContains64BitType) {
+ structContains64BitType = true;
+ RoundToPow2(size, 8);
+#ifdef AMD_EXTENSIONS
+ } else if (memberContains32BitType) {
+ structContains32BitType = true;
+ RoundToPow2(size, 4);
+ } else if (memberContains16BitType) {
+ structContains16BitType = true;
+ RoundToPow2(size, 2);
+#endif
+ }
+ size += memberSize;
+ }
+
+ if (structContains64BitType) {
+ contains64BitType = true;
+ RoundToPow2(size, 8);
+#ifdef AMD_EXTENSIONS
+ } else if (structContains32BitType) {
+ contains32BitType = true;
+ RoundToPow2(size, 4);
+ } else if (structContains16BitType) {
+ contains16BitType = true;
+ RoundToPow2(size, 2);
+#endif
+ }
+ return size;
+ }
+
+ int numComponents;
+ if (type.isScalar())
+ numComponents = 1;
+ else if (type.isVector())
+ numComponents = type.getVectorSize();
+ else if (type.isMatrix())
+ numComponents = type.getMatrixCols() * type.getMatrixRows();
+ else {
+ assert(0);
+ numComponents = 1;
+ }
+
+ if (type.getBasicType() == EbtDouble || type.getBasicType() == EbtInt64 || type.getBasicType() == EbtUint64) {
+ contains64BitType = true;
+ return 8 * numComponents;
+#ifdef AMD_EXTENSIONS
+ } else if (type.getBasicType() == EbtFloat16 || type.getBasicType() == EbtInt16 || type.getBasicType() == EbtUint16) {
+ contains16BitType = true;
+ return 2 * numComponents;
+ } else if (type.getBasicType() == EbtInt8 || type.getBasicType() == EbtUint8)
+ return numComponents;
+ else {
+ contains32BitType = true;
+ return 4 * numComponents;
+ }
+#else
+ } else
+ return 4 * numComponents;
+#endif
+}
+
+const int baseAlignmentVec4Std140 = 16;
+
+// Return the size and alignment of a component of the given type.
+// The size is returned in the 'size' parameter
+// Return value is the alignment..
+int TIntermediate::getBaseAlignmentScalar(const TType& type, int& size)
+{
+ switch (type.getBasicType()) {
+ case EbtInt64:
+ case EbtUint64:
+ case EbtDouble: size = 8; return 8;
+ case EbtFloat16: size = 2; return 2;
+ case EbtInt8:
+ case EbtUint8: size = 1; return 1;
+ case EbtInt16:
+ case EbtUint16: size = 2; return 2;
+ case EbtReference: size = 8; return 8;
+ default: size = 4; return 4;
+ }
+}
+
+// Implement base-alignment and size rules from section 7.6.2.2 Standard Uniform Block Layout
+// Operates recursively.
+//
+// If std140 is true, it does the rounding up to vec4 size required by std140,
+// otherwise it does not, yielding std430 rules.
+//
+// The size is returned in the 'size' parameter
+//
+// The stride is only non-0 for arrays or matrices, and is the stride of the
+// top-level object nested within the type. E.g., for an array of matrices,
+// it is the distances needed between matrices, despite the rules saying the
+// stride comes from the flattening down to vectors.
+//
+// Return value is the alignment of the type.
+int TIntermediate::getBaseAlignment(const TType& type, int& size, int& stride, TLayoutPacking layoutPacking, bool rowMajor)
+{
+ int alignment;
+
+ bool std140 = layoutPacking == glslang::ElpStd140;
+ // When using the std140 storage layout, structures will be laid out in buffer
+ // storage with its members stored in monotonically increasing order based on their
+ // location in the declaration. A structure and each structure member have a base
+ // offset and a base alignment, from which an aligned offset is computed by rounding
+ // the base offset up to a multiple of the base alignment. The base offset of the first
+ // member of a structure is taken from the aligned offset of the structure itself. The
+ // base offset of all other structure members is derived by taking the offset of the
+ // last basic machine unit consumed by the previous member and adding one. Each
+ // structure member is stored in memory at its aligned offset. The members of a top-
+ // level uniform block are laid out in buffer storage by treating the uniform block as
+ // a structure with a base offset of zero.
+ //
+ // 1. If the member is a scalar consuming N basic machine units, the base alignment is N.
+ //
+ // 2. If the member is a two- or four-component vector with components consuming N basic
+ // machine units, the base alignment is 2N or 4N, respectively.
+ //
+ // 3. If the member is a three-component vector with components consuming N
+ // basic machine units, the base alignment is 4N.
+ //
+ // 4. If the member is an array of scalars or vectors, the base alignment and array
+ // stride are set to match the base alignment of a single array element, according
+ // to rules (1), (2), and (3), and rounded up to the base alignment of a vec4. The
+ // array may have padding at the end; the base offset of the member following
+ // the array is rounded up to the next multiple of the base alignment.
+ //
+ // 5. If the member is a column-major matrix with C columns and R rows, the
+ // matrix is stored identically to an array of C column vectors with R
+ // components each, according to rule (4).
+ //
+ // 6. If the member is an array of S column-major matrices with C columns and
+ // R rows, the matrix is stored identically to a row of S X C column vectors
+ // with R components each, according to rule (4).
+ //
+ // 7. If the member is a row-major matrix with C columns and R rows, the matrix
+ // is stored identically to an array of R row vectors with C components each,
+ // according to rule (4).
+ //
+ // 8. If the member is an array of S row-major matrices with C columns and R
+ // rows, the matrix is stored identically to a row of S X R row vectors with C
+ // components each, according to rule (4).
+ //
+ // 9. If the member is a structure, the base alignment of the structure is N , where
+ // N is the largest base alignment value of any of its members, and rounded
+ // up to the base alignment of a vec4. The individual members of this substructure
+ // are then assigned offsets by applying this set of rules recursively,
+ // where the base offset of the first member of the sub-structure is equal to the
+ // aligned offset of the structure. The structure may have padding at the end;
+ // the base offset of the member following the sub-structure is rounded up to
+ // the next multiple of the base alignment of the structure.
+ //
+ // 10. If the member is an array of S structures, the S elements of the array are laid
+ // out in order, according to rule (9).
+ //
+ // Assuming, for rule 10: The stride is the same as the size of an element.
+
+ stride = 0;
+ int dummyStride;
+
+ // rules 4, 6, 8, and 10
+ if (type.isArray()) {
+ // TODO: perf: this might be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness
+ TType derefType(type, 0);
+ alignment = getBaseAlignment(derefType, size, dummyStride, layoutPacking, rowMajor);
+ if (std140)
+ alignment = std::max(baseAlignmentVec4Std140, alignment);
+ RoundToPow2(size, alignment);
+ stride = size; // uses full matrix size for stride of an array of matrices (not quite what rule 6/8, but what's expected)
+ // uses the assumption for rule 10 in the comment above
+ size = stride * type.getOuterArraySize();
+ return alignment;
+ }
+
+ // rule 9
+ if (type.getBasicType() == EbtStruct) {
+ const TTypeList& memberList = *type.getStruct();
+
+ size = 0;
+ int maxAlignment = std140 ? baseAlignmentVec4Std140 : 0;
+ for (size_t m = 0; m < memberList.size(); ++m) {
+ int memberSize;
+ // modify just the children's view of matrix layout, if there is one for this member
+ TLayoutMatrix subMatrixLayout = memberList[m].type->getQualifier().layoutMatrix;
+ int memberAlignment = getBaseAlignment(*memberList[m].type, memberSize, dummyStride, layoutPacking,
+ (subMatrixLayout != ElmNone) ? (subMatrixLayout == ElmRowMajor) : rowMajor);
+ maxAlignment = std::max(maxAlignment, memberAlignment);
+ RoundToPow2(size, memberAlignment);
+ size += memberSize;
+ }
+
+ // The structure may have padding at the end; the base offset of
+ // the member following the sub-structure is rounded up to the next
+ // multiple of the base alignment of the structure.
+ RoundToPow2(size, maxAlignment);
+
+ return maxAlignment;
+ }
+
+ // rule 1
+ if (type.isScalar())
+ return getBaseAlignmentScalar(type, size);
+
+ // rules 2 and 3
+ if (type.isVector()) {
+ int scalarAlign = getBaseAlignmentScalar(type, size);
+ switch (type.getVectorSize()) {
+ case 1: // HLSL has this, GLSL does not
+ return scalarAlign;
+ case 2:
+ size *= 2;
+ return 2 * scalarAlign;
+ default:
+ size *= type.getVectorSize();
+ return 4 * scalarAlign;
+ }
+ }
+
+ // rules 5 and 7
+ if (type.isMatrix()) {
+ // rule 5: deref to row, not to column, meaning the size of vector is num columns instead of num rows
+ TType derefType(type, 0, rowMajor);
+
+ alignment = getBaseAlignment(derefType, size, dummyStride, layoutPacking, rowMajor);
+ if (std140)
+ alignment = std::max(baseAlignmentVec4Std140, alignment);
+ RoundToPow2(size, alignment);
+ stride = size; // use intra-matrix stride for stride of a just a matrix
+ if (rowMajor)
+ size = stride * type.getMatrixRows();
+ else
+ size = stride * type.getMatrixCols();
+
+ return alignment;
+ }
+
+ assert(0); // all cases should be covered above
+ size = baseAlignmentVec4Std140;
+ return baseAlignmentVec4Std140;
+}
+
+// To aid the basic HLSL rule about crossing vec4 boundaries.
+bool TIntermediate::improperStraddle(const TType& type, int size, int offset)
+{
+ if (! type.isVector() || type.isArray())
+ return false;
+
+ return size <= 16 ? offset / 16 != (offset + size - 1) / 16
+ : offset % 16 != 0;
+}
+
+int TIntermediate::getScalarAlignment(const TType& type, int& size, int& stride, bool rowMajor)
+{
+ int alignment;
+
+ stride = 0;
+ int dummyStride;
+
+ if (type.isArray()) {
+ TType derefType(type, 0);
+ alignment = getScalarAlignment(derefType, size, dummyStride, rowMajor);
+
+ stride = size;
+ RoundToPow2(stride, alignment);
+
+ size = stride * (type.getOuterArraySize() - 1) + size;
+ return alignment;
+ }
+
+ if (type.getBasicType() == EbtStruct) {
+ const TTypeList& memberList = *type.getStruct();
+
+ size = 0;
+ int maxAlignment = 0;
+ for (size_t m = 0; m < memberList.size(); ++m) {
+ int memberSize;
+ // modify just the children's view of matrix layout, if there is one for this member
+ TLayoutMatrix subMatrixLayout = memberList[m].type->getQualifier().layoutMatrix;
+ int memberAlignment = getScalarAlignment(*memberList[m].type, memberSize, dummyStride,
+ (subMatrixLayout != ElmNone) ? (subMatrixLayout == ElmRowMajor) : rowMajor);
+ maxAlignment = std::max(maxAlignment, memberAlignment);
+ RoundToPow2(size, memberAlignment);
+ size += memberSize;
+ }
+
+ return maxAlignment;
+ }
+
+ if (type.isScalar())
+ return getBaseAlignmentScalar(type, size);
+
+ if (type.isVector()) {
+ int scalarAlign = getBaseAlignmentScalar(type, size);
+
+ size *= type.getVectorSize();
+ return scalarAlign;
+ }
+
+ if (type.isMatrix()) {
+ TType derefType(type, 0, rowMajor);
+
+ alignment = getScalarAlignment(derefType, size, dummyStride, rowMajor);
+
+ stride = size; // use intra-matrix stride for stride of a just a matrix
+ if (rowMajor)
+ size = stride * type.getMatrixRows();
+ else
+ size = stride * type.getMatrixCols();
+
+ return alignment;
+ }
+
+ assert(0); // all cases should be covered above
+ size = 1;
+ return 1;
+}
+
+int TIntermediate::getMemberAlignment(const TType& type, int& size, int& stride, TLayoutPacking layoutPacking, bool rowMajor)
+{
+ if (layoutPacking == glslang::ElpScalar) {
+ return getScalarAlignment(type, size, stride, rowMajor);
+ } else {
+ return getBaseAlignment(type, size, stride, layoutPacking, rowMajor);
+ }
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/localintermediate.h b/src/3rdparty/glslang/glslang/MachineIndependent/localintermediate.h
new file mode 100644
index 0000000..ba17725
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/localintermediate.h
@@ -0,0 +1,896 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2016 LunarG, Inc.
+// Copyright (C) 2017 ARM Limited.
+// Copyright (C) 2015-2018 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef _LOCAL_INTERMEDIATE_INCLUDED_
+#define _LOCAL_INTERMEDIATE_INCLUDED_
+
+#include "../Include/intermediate.h"
+#include "../Public/ShaderLang.h"
+#include "Versions.h"
+
+#include <string>
+#include <vector>
+#include <algorithm>
+#include <set>
+#include <array>
+
+class TInfoSink;
+
+namespace glslang {
+
+struct TMatrixSelector {
+ int coord1; // stay agnostic about column/row; this is parse order
+ int coord2;
+};
+
+typedef int TVectorSelector;
+
+const int MaxSwizzleSelectors = 4;
+
+template<typename selectorType>
+class TSwizzleSelectors {
+public:
+ TSwizzleSelectors() : size_(0) { }
+
+ void push_back(selectorType comp)
+ {
+ if (size_ < MaxSwizzleSelectors)
+ components[size_++] = comp;
+ }
+ void resize(int s)
+ {
+ assert(s <= size_);
+ size_ = s;
+ }
+ int size() const { return size_; }
+ selectorType operator[](int i) const
+ {
+ assert(i < MaxSwizzleSelectors);
+ return components[i];
+ }
+
+private:
+ int size_;
+ selectorType components[MaxSwizzleSelectors];
+};
+
+//
+// Some helper structures for TIntermediate. Their contents are encapsulated
+// by TIntermediate.
+//
+
+// Used for call-graph algorithms for detecting recursion, missing bodies, and dead bodies.
+// A "call" is a pair: <caller, callee>.
+// There can be duplicates. General assumption is the list is small.
+struct TCall {
+ TCall(const TString& pCaller, const TString& pCallee) : caller(pCaller), callee(pCallee) { }
+ TString caller;
+ TString callee;
+ bool visited;
+ bool currentPath;
+ bool errorGiven;
+ int calleeBodyPosition;
+};
+
+// A generic 1-D range.
+struct TRange {
+ TRange(int start, int last) : start(start), last(last) { }
+ bool overlap(const TRange& rhs) const
+ {
+ return last >= rhs.start && start <= rhs.last;
+ }
+ int start;
+ int last;
+};
+
+// An IO range is a 3-D rectangle; the set of (location, component, index) triples all lying
+// within the same location range, component range, and index value. Locations don't alias unless
+// all other dimensions of their range overlap.
+struct TIoRange {
+ TIoRange(TRange location, TRange component, TBasicType basicType, int index)
+ : location(location), component(component), basicType(basicType), index(index) { }
+ bool overlap(const TIoRange& rhs) const
+ {
+ return location.overlap(rhs.location) && component.overlap(rhs.component) && index == rhs.index;
+ }
+ TRange location;
+ TRange component;
+ TBasicType basicType;
+ int index;
+};
+
+// An offset range is a 2-D rectangle; the set of (binding, offset) pairs all lying
+// within the same binding and offset range.
+struct TOffsetRange {
+ TOffsetRange(TRange binding, TRange offset)
+ : binding(binding), offset(offset) { }
+ bool overlap(const TOffsetRange& rhs) const
+ {
+ return binding.overlap(rhs.binding) && offset.overlap(rhs.offset);
+ }
+ TRange binding;
+ TRange offset;
+};
+
+// Things that need to be tracked per xfb buffer.
+struct TXfbBuffer {
+#ifdef AMD_EXTENSIONS
+ TXfbBuffer() : stride(TQualifier::layoutXfbStrideEnd), implicitStride(0), contains64BitType(false),
+ contains32BitType(false), contains16BitType(false) { }
+#else
+ TXfbBuffer() : stride(TQualifier::layoutXfbStrideEnd), implicitStride(0), contains64BitType(false) { }
+#endif
+ std::vector<TRange> ranges; // byte offsets that have already been assigned
+ unsigned int stride;
+ unsigned int implicitStride;
+ bool contains64BitType;
+#ifdef AMD_EXTENSIONS
+ bool contains32BitType;
+ bool contains16BitType;
+#endif
+};
+
+// Track a set of strings describing how the module was processed.
+// Using the form:
+// process arg0 arg1 arg2 ...
+// process arg0 arg1 arg2 ...
+// where everything is textual, and there can be zero or more arguments
+class TProcesses {
+public:
+ TProcesses() {}
+ ~TProcesses() {}
+
+ void addProcess(const char* process)
+ {
+ processes.push_back(process);
+ }
+ void addProcess(const std::string& process)
+ {
+ processes.push_back(process);
+ }
+ void addArgument(int arg)
+ {
+ processes.back().append(" ");
+ std::string argString = std::to_string(arg);
+ processes.back().append(argString);
+ }
+ void addArgument(const char* arg)
+ {
+ processes.back().append(" ");
+ processes.back().append(arg);
+ }
+ void addArgument(const std::string& arg)
+ {
+ processes.back().append(" ");
+ processes.back().append(arg);
+ }
+ void addIfNonZero(const char* process, int value)
+ {
+ if (value != 0) {
+ addProcess(process);
+ addArgument(value);
+ }
+ }
+
+ const std::vector<std::string>& getProcesses() const { return processes; }
+
+private:
+ std::vector<std::string> processes;
+};
+
+class TSymbolTable;
+class TSymbol;
+class TVariable;
+
+#ifdef NV_EXTENSIONS
+//
+// Texture and Sampler transformation mode.
+//
+enum ComputeDerivativeMode {
+ LayoutDerivativeNone, // default layout as SPV_NV_compute_shader_derivatives not enabled
+ LayoutDerivativeGroupQuads, // derivative_group_quadsNV
+ LayoutDerivativeGroupLinear, // derivative_group_linearNV
+};
+#endif
+
+//
+// Set of helper functions to help parse and build the tree.
+//
+class TIntermediate {
+public:
+ explicit TIntermediate(EShLanguage l, int v = 0, EProfile p = ENoProfile) :
+ implicitThisName("@this"), implicitCounterName("@count"),
+ language(l), source(EShSourceNone), profile(p), version(v), treeRoot(0),
+ numEntryPoints(0), numErrors(0), numPushConstants(0), recursive(false),
+ invocations(TQualifier::layoutNotSet), vertices(TQualifier::layoutNotSet),
+ inputPrimitive(ElgNone), outputPrimitive(ElgNone),
+ pixelCenterInteger(false), originUpperLeft(false),
+ vertexSpacing(EvsNone), vertexOrder(EvoNone), pointMode(false), earlyFragmentTests(false),
+ postDepthCoverage(false), depthLayout(EldNone), depthReplacing(false),
+ hlslFunctionality1(false),
+ blendEquations(0), xfbMode(false), multiStream(false),
+#ifdef NV_EXTENSIONS
+ layoutOverrideCoverage(false),
+ geoPassthroughEXT(false),
+ numShaderRecordNVBlocks(0),
+ computeDerivativeMode(LayoutDerivativeNone),
+ primitives(TQualifier::layoutNotSet),
+ numTaskNVBlocks(0),
+#endif
+ autoMapBindings(false),
+ autoMapLocations(false),
+ invertY(false),
+ flattenUniformArrays(false),
+ useUnknownFormat(false),
+ hlslOffsets(false),
+ useStorageBuffer(false),
+ useVulkanMemoryModel(false),
+ hlslIoMapping(false),
+ useVariablePointers(false),
+ textureSamplerTransformMode(EShTexSampTransKeep),
+ needToLegalize(false),
+ binaryDoubleOutput(false),
+ usePhysicalStorageBuffer(false),
+ uniformLocationBase(0)
+ {
+ localSize[0] = 1;
+ localSize[1] = 1;
+ localSize[2] = 1;
+ localSizeSpecId[0] = TQualifier::layoutNotSet;
+ localSizeSpecId[1] = TQualifier::layoutNotSet;
+ localSizeSpecId[2] = TQualifier::layoutNotSet;
+ xfbBuffers.resize(TQualifier::layoutXfbBufferEnd);
+
+ shiftBinding.fill(0);
+ }
+ void setLimits(const TBuiltInResource& r) { resources = r; }
+
+ bool postProcess(TIntermNode*, EShLanguage);
+ void output(TInfoSink&, bool tree);
+ void removeTree();
+
+ void setSource(EShSource s) { source = s; }
+ EShSource getSource() const { return source; }
+ void setEntryPointName(const char* ep)
+ {
+ entryPointName = ep;
+ processes.addProcess("entry-point");
+ processes.addArgument(entryPointName);
+ }
+ void setEntryPointMangledName(const char* ep) { entryPointMangledName = ep; }
+ const std::string& getEntryPointName() const { return entryPointName; }
+ const std::string& getEntryPointMangledName() const { return entryPointMangledName; }
+
+ void setShiftBinding(TResourceType res, unsigned int shift)
+ {
+ shiftBinding[res] = shift;
+
+ const char* name = getResourceName(res);
+ if (name != nullptr)
+ processes.addIfNonZero(name, shift);
+ }
+
+ unsigned int getShiftBinding(TResourceType res) const { return shiftBinding[res]; }
+
+ void setShiftBindingForSet(TResourceType res, unsigned int shift, unsigned int set)
+ {
+ if (shift == 0) // ignore if there's no shift: it's a no-op.
+ return;
+
+ shiftBindingForSet[res][set] = shift;
+
+ const char* name = getResourceName(res);
+ if (name != nullptr) {
+ processes.addProcess(name);
+ processes.addArgument(shift);
+ processes.addArgument(set);
+ }
+ }
+
+ int getShiftBindingForSet(TResourceType res, unsigned int set) const
+ {
+ const auto shift = shiftBindingForSet[res].find(set);
+ return shift == shiftBindingForSet[res].end() ? -1 : shift->second;
+ }
+ bool hasShiftBindingForSet(TResourceType res) const { return !shiftBindingForSet[res].empty(); }
+
+ void setResourceSetBinding(const std::vector<std::string>& shift)
+ {
+ resourceSetBinding = shift;
+ if (shift.size() > 0) {
+ processes.addProcess("resource-set-binding");
+ for (int s = 0; s < (int)shift.size(); ++s)
+ processes.addArgument(shift[s]);
+ }
+ }
+ const std::vector<std::string>& getResourceSetBinding() const { return resourceSetBinding; }
+ void setAutoMapBindings(bool map)
+ {
+ autoMapBindings = map;
+ if (autoMapBindings)
+ processes.addProcess("auto-map-bindings");
+ }
+ bool getAutoMapBindings() const { return autoMapBindings; }
+ void setAutoMapLocations(bool map)
+ {
+ autoMapLocations = map;
+ if (autoMapLocations)
+ processes.addProcess("auto-map-locations");
+ }
+ bool getAutoMapLocations() const { return autoMapLocations; }
+ void setInvertY(bool invert)
+ {
+ invertY = invert;
+ if (invertY)
+ processes.addProcess("invert-y");
+ }
+ bool getInvertY() const { return invertY; }
+
+ void setFlattenUniformArrays(bool flatten)
+ {
+ flattenUniformArrays = flatten;
+ if (flattenUniformArrays)
+ processes.addProcess("flatten-uniform-arrays");
+ }
+ bool getFlattenUniformArrays() const { return flattenUniformArrays; }
+ void setNoStorageFormat(bool b)
+ {
+ useUnknownFormat = b;
+ if (useUnknownFormat)
+ processes.addProcess("no-storage-format");
+ }
+ bool getNoStorageFormat() const { return useUnknownFormat; }
+ void setHlslOffsets()
+ {
+ hlslOffsets = true;
+ if (hlslOffsets)
+ processes.addProcess("hlsl-offsets");
+ }
+ bool usingHlslOffsets() const { return hlslOffsets; }
+ void setUseStorageBuffer()
+ {
+ useStorageBuffer = true;
+ processes.addProcess("use-storage-buffer");
+ }
+ bool usingStorageBuffer() const { return useStorageBuffer; }
+ void setHlslIoMapping(bool b)
+ {
+ hlslIoMapping = b;
+ if (hlslIoMapping)
+ processes.addProcess("hlsl-iomap");
+ }
+ bool usingHlslIoMapping() { return hlslIoMapping; }
+ void setUseVulkanMemoryModel()
+ {
+ useVulkanMemoryModel = true;
+ processes.addProcess("use-vulkan-memory-model");
+ }
+ bool usingVulkanMemoryModel() const { return useVulkanMemoryModel; }
+ void setUsePhysicalStorageBuffer()
+ {
+ usePhysicalStorageBuffer = true;
+ }
+ bool usingPhysicalStorageBuffer() const { return usePhysicalStorageBuffer; }
+ void setUseVariablePointers()
+ {
+ useVariablePointers = true;
+ processes.addProcess("use-variable-pointers");
+ }
+ bool usingVariablePointers() const { return useVariablePointers; }
+
+ template<class T> T addCounterBufferName(const T& name) const { return name + implicitCounterName; }
+ bool hasCounterBufferName(const TString& name) const {
+ size_t len = strlen(implicitCounterName);
+ return name.size() > len &&
+ name.compare(name.size() - len, len, implicitCounterName) == 0;
+ }
+
+ void setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode) { textureSamplerTransformMode = mode; }
+
+ void setVersion(int v) { version = v; }
+ int getVersion() const { return version; }
+ void setProfile(EProfile p) { profile = p; }
+ EProfile getProfile() const { return profile; }
+ void setSpv(const SpvVersion& s)
+ {
+ spvVersion = s;
+
+ // client processes
+ if (spvVersion.vulkan > 0)
+ processes.addProcess("client vulkan100");
+ if (spvVersion.openGl > 0)
+ processes.addProcess("client opengl100");
+
+ // target SPV
+ switch (spvVersion.spv) {
+ case 0:
+ break;
+ case EShTargetSpv_1_0:
+ break;
+ case EShTargetSpv_1_1:
+ processes.addProcess("target-env spirv1.1");
+ break;
+ case EShTargetSpv_1_2:
+ processes.addProcess("target-env spirv1.2");
+ break;
+ case EShTargetSpv_1_3:
+ processes.addProcess("target-env spirv1.3");
+ break;
+ default:
+ processes.addProcess("target-env spirvUnknown");
+ break;
+ }
+
+ // target-environment processes
+ switch (spvVersion.vulkan) {
+ case 0:
+ break;
+ case EShTargetVulkan_1_0:
+ processes.addProcess("target-env vulkan1.0");
+ break;
+ case EShTargetVulkan_1_1:
+ processes.addProcess("target-env vulkan1.1");
+ break;
+ default:
+ processes.addProcess("target-env vulkanUnknown");
+ break;
+ }
+ if (spvVersion.openGl > 0)
+ processes.addProcess("target-env opengl");
+ }
+ const SpvVersion& getSpv() const { return spvVersion; }
+ EShLanguage getStage() const { return language; }
+ void addRequestedExtension(const char* extension) { requestedExtensions.insert(extension); }
+ const std::set<std::string>& getRequestedExtensions() const { return requestedExtensions; }
+
+ void setTreeRoot(TIntermNode* r) { treeRoot = r; }
+ TIntermNode* getTreeRoot() const { return treeRoot; }
+ void incrementEntryPointCount() { ++numEntryPoints; }
+ int getNumEntryPoints() const { return numEntryPoints; }
+ int getNumErrors() const { return numErrors; }
+ void addPushConstantCount() { ++numPushConstants; }
+#ifdef NV_EXTENSIONS
+ void addShaderRecordNVCount() { ++numShaderRecordNVBlocks; }
+ void addTaskNVCount() { ++numTaskNVBlocks; }
+#endif
+
+ bool isRecursive() const { return recursive; }
+
+ TIntermSymbol* addSymbol(const TVariable&);
+ TIntermSymbol* addSymbol(const TVariable&, const TSourceLoc&);
+ TIntermSymbol* addSymbol(const TType&, const TSourceLoc&);
+ TIntermSymbol* addSymbol(const TIntermSymbol&);
+ TIntermTyped* addConversion(TOperator, const TType&, TIntermTyped*);
+ std::tuple<TIntermTyped*, TIntermTyped*> addConversion(TOperator op, TIntermTyped* node0, TIntermTyped* node1);
+ TIntermTyped* addUniShapeConversion(TOperator, const TType&, TIntermTyped*);
+ TIntermTyped* addConversion(TBasicType convertTo, TIntermTyped* node) const;
+ void addBiShapeConversion(TOperator, TIntermTyped*& lhsNode, TIntermTyped*& rhsNode);
+ TIntermTyped* addShapeConversion(const TType&, TIntermTyped*);
+ TIntermTyped* addBinaryMath(TOperator, TIntermTyped* left, TIntermTyped* right, TSourceLoc);
+ TIntermTyped* addAssign(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc);
+ TIntermTyped* addIndex(TOperator op, TIntermTyped* base, TIntermTyped* index, TSourceLoc);
+ TIntermTyped* addUnaryMath(TOperator, TIntermTyped* child, TSourceLoc);
+ TIntermTyped* addBuiltInFunctionCall(const TSourceLoc& line, TOperator, bool unary, TIntermNode*, const TType& returnType);
+ bool canImplicitlyPromote(TBasicType from, TBasicType to, TOperator op = EOpNull) const;
+ bool isIntegralPromotion(TBasicType from, TBasicType to) const;
+ bool isFPPromotion(TBasicType from, TBasicType to) const;
+ bool isIntegralConversion(TBasicType from, TBasicType to) const;
+ bool isFPConversion(TBasicType from, TBasicType to) const;
+ bool isFPIntegralConversion(TBasicType from, TBasicType to) const;
+ TOperator mapTypeToConstructorOp(const TType&) const;
+ TIntermAggregate* growAggregate(TIntermNode* left, TIntermNode* right);
+ TIntermAggregate* growAggregate(TIntermNode* left, TIntermNode* right, const TSourceLoc&);
+ TIntermAggregate* makeAggregate(TIntermNode* node);
+ TIntermAggregate* makeAggregate(TIntermNode* node, const TSourceLoc&);
+ TIntermAggregate* makeAggregate(const TSourceLoc&);
+ TIntermTyped* setAggregateOperator(TIntermNode*, TOperator, const TType& type, TSourceLoc);
+ bool areAllChildConst(TIntermAggregate* aggrNode);
+ TIntermSelection* addSelection(TIntermTyped* cond, TIntermNodePair code, const TSourceLoc&);
+ TIntermTyped* addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, const TSourceLoc&);
+ TIntermTyped* addComma(TIntermTyped* left, TIntermTyped* right, const TSourceLoc&);
+ TIntermTyped* addMethod(TIntermTyped*, const TType&, const TString*, const TSourceLoc&);
+ TIntermConstantUnion* addConstantUnion(const TConstUnionArray&, const TType&, const TSourceLoc&, bool literal = false) const;
+ TIntermConstantUnion* addConstantUnion(signed char, const TSourceLoc&, bool literal = false) const;
+ TIntermConstantUnion* addConstantUnion(unsigned char, const TSourceLoc&, bool literal = false) const;
+ TIntermConstantUnion* addConstantUnion(signed short, const TSourceLoc&, bool literal = false) const;
+ TIntermConstantUnion* addConstantUnion(unsigned short, const TSourceLoc&, bool literal = false) const;
+ TIntermConstantUnion* addConstantUnion(int, const TSourceLoc&, bool literal = false) const;
+ TIntermConstantUnion* addConstantUnion(unsigned int, const TSourceLoc&, bool literal = false) const;
+ TIntermConstantUnion* addConstantUnion(long long, const TSourceLoc&, bool literal = false) const;
+ TIntermConstantUnion* addConstantUnion(unsigned long long, const TSourceLoc&, bool literal = false) const;
+ TIntermConstantUnion* addConstantUnion(bool, const TSourceLoc&, bool literal = false) const;
+ TIntermConstantUnion* addConstantUnion(double, TBasicType, const TSourceLoc&, bool literal = false) const;
+ TIntermConstantUnion* addConstantUnion(const TString*, const TSourceLoc&, bool literal = false) const;
+ TIntermTyped* promoteConstantUnion(TBasicType, TIntermConstantUnion*) const;
+ bool parseConstTree(TIntermNode*, TConstUnionArray, TOperator, const TType&, bool singleConstantParam = false);
+ TIntermLoop* addLoop(TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, const TSourceLoc&);
+ TIntermAggregate* addForLoop(TIntermNode*, TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst,
+ const TSourceLoc&, TIntermLoop*&);
+ TIntermBranch* addBranch(TOperator, const TSourceLoc&);
+ TIntermBranch* addBranch(TOperator, TIntermTyped*, const TSourceLoc&);
+ template<typename selectorType> TIntermTyped* addSwizzle(TSwizzleSelectors<selectorType>&, const TSourceLoc&);
+
+ // Low level functions to add nodes (no conversions or other higher level transformations)
+ // If a type is provided, the node's type will be set to it.
+ TIntermBinary* addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc) const;
+ TIntermBinary* addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc, const TType&) const;
+ TIntermUnary* addUnaryNode(TOperator op, TIntermTyped* child, TSourceLoc) const;
+ TIntermUnary* addUnaryNode(TOperator op, TIntermTyped* child, TSourceLoc, const TType&) const;
+
+ // Constant folding (in Constant.cpp)
+ TIntermTyped* fold(TIntermAggregate* aggrNode);
+ TIntermTyped* foldConstructor(TIntermAggregate* aggrNode);
+ TIntermTyped* foldDereference(TIntermTyped* node, int index, const TSourceLoc&);
+ TIntermTyped* foldSwizzle(TIntermTyped* node, TSwizzleSelectors<TVectorSelector>& fields, const TSourceLoc&);
+
+ // Tree ops
+ static const TIntermTyped* findLValueBase(const TIntermTyped*, bool swizzleOkay);
+
+ // Linkage related
+ void addSymbolLinkageNodes(TIntermAggregate*& linkage, EShLanguage, TSymbolTable&);
+ void addSymbolLinkageNode(TIntermAggregate*& linkage, const TSymbol&);
+
+ bool setInvocations(int i)
+ {
+ if (invocations != TQualifier::layoutNotSet)
+ return invocations == i;
+ invocations = i;
+ return true;
+ }
+ int getInvocations() const { return invocations; }
+ bool setVertices(int m)
+ {
+ if (vertices != TQualifier::layoutNotSet)
+ return vertices == m;
+ vertices = m;
+ return true;
+ }
+ int getVertices() const { return vertices; }
+ bool setInputPrimitive(TLayoutGeometry p)
+ {
+ if (inputPrimitive != ElgNone)
+ return inputPrimitive == p;
+ inputPrimitive = p;
+ return true;
+ }
+ TLayoutGeometry getInputPrimitive() const { return inputPrimitive; }
+ bool setVertexSpacing(TVertexSpacing s)
+ {
+ if (vertexSpacing != EvsNone)
+ return vertexSpacing == s;
+ vertexSpacing = s;
+ return true;
+ }
+ TVertexSpacing getVertexSpacing() const { return vertexSpacing; }
+ bool setVertexOrder(TVertexOrder o)
+ {
+ if (vertexOrder != EvoNone)
+ return vertexOrder == o;
+ vertexOrder = o;
+ return true;
+ }
+ TVertexOrder getVertexOrder() const { return vertexOrder; }
+ void setPointMode() { pointMode = true; }
+ bool getPointMode() const { return pointMode; }
+
+ bool setLocalSize(int dim, int size)
+ {
+ if (localSize[dim] > 1)
+ return size == localSize[dim];
+ localSize[dim] = size;
+ return true;
+ }
+ unsigned int getLocalSize(int dim) const { return localSize[dim]; }
+
+ bool setLocalSizeSpecId(int dim, int id)
+ {
+ if (localSizeSpecId[dim] != TQualifier::layoutNotSet)
+ return id == localSizeSpecId[dim];
+ localSizeSpecId[dim] = id;
+ return true;
+ }
+ int getLocalSizeSpecId(int dim) const { return localSizeSpecId[dim]; }
+
+ void setXfbMode() { xfbMode = true; }
+ bool getXfbMode() const { return xfbMode; }
+ void setMultiStream() { multiStream = true; }
+ bool isMultiStream() const { return multiStream; }
+ bool setOutputPrimitive(TLayoutGeometry p)
+ {
+ if (outputPrimitive != ElgNone)
+ return outputPrimitive == p;
+ outputPrimitive = p;
+ return true;
+ }
+ TLayoutGeometry getOutputPrimitive() const { return outputPrimitive; }
+ void setOriginUpperLeft() { originUpperLeft = true; }
+ bool getOriginUpperLeft() const { return originUpperLeft; }
+ void setPixelCenterInteger() { pixelCenterInteger = true; }
+ bool getPixelCenterInteger() const { return pixelCenterInteger; }
+ void setEarlyFragmentTests() { earlyFragmentTests = true; }
+ bool getEarlyFragmentTests() const { return earlyFragmentTests; }
+ void setPostDepthCoverage() { postDepthCoverage = true; }
+ bool getPostDepthCoverage() const { return postDepthCoverage; }
+ bool setDepth(TLayoutDepth d)
+ {
+ if (depthLayout != EldNone)
+ return depthLayout == d;
+ depthLayout = d;
+ return true;
+ }
+ TLayoutDepth getDepth() const { return depthLayout; }
+ void setDepthReplacing() { depthReplacing = true; }
+ bool isDepthReplacing() const { return depthReplacing; }
+
+ void setHlslFunctionality1() { hlslFunctionality1 = true; }
+ bool getHlslFunctionality1() const { return hlslFunctionality1; }
+
+ void addBlendEquation(TBlendEquationShift b) { blendEquations |= (1 << b); }
+ unsigned int getBlendEquations() const { return blendEquations; }
+
+ void addToCallGraph(TInfoSink&, const TString& caller, const TString& callee);
+ void merge(TInfoSink&, TIntermediate&);
+ void finalCheck(TInfoSink&, bool keepUncalled);
+
+ void addIoAccessed(const TString& name) { ioAccessed.insert(name); }
+ bool inIoAccessed(const TString& name) const { return ioAccessed.find(name) != ioAccessed.end(); }
+
+ int addUsedLocation(const TQualifier&, const TType&, bool& typeCollision);
+ int checkLocationRange(int set, const TIoRange& range, const TType&, bool& typeCollision);
+ int addUsedOffsets(int binding, int offset, int numOffsets);
+ bool addUsedConstantId(int id);
+ static int computeTypeLocationSize(const TType&, EShLanguage);
+ static int computeTypeUniformLocationSize(const TType&);
+
+ bool setXfbBufferStride(int buffer, unsigned stride)
+ {
+ if (xfbBuffers[buffer].stride != TQualifier::layoutXfbStrideEnd)
+ return xfbBuffers[buffer].stride == stride;
+ xfbBuffers[buffer].stride = stride;
+ return true;
+ }
+ unsigned getXfbStride(int buffer) const { return xfbBuffers[buffer].stride; }
+ int addXfbBufferOffset(const TType&);
+#ifdef AMD_EXTENSIONS
+ unsigned int computeTypeXfbSize(const TType&, bool& contains64BitType, bool& contains32BitType, bool& contains16BitType) const;
+#else
+ unsigned int computeTypeXfbSize(const TType&, bool& contains64BitType) const;
+#endif
+ static int getBaseAlignmentScalar(const TType&, int& size);
+ static int getBaseAlignment(const TType&, int& size, int& stride, TLayoutPacking layoutPacking, bool rowMajor);
+ static int getScalarAlignment(const TType&, int& size, int& stride, bool rowMajor);
+ static int getMemberAlignment(const TType&, int& size, int& stride, TLayoutPacking layoutPacking, bool rowMajor);
+ static bool improperStraddle(const TType& type, int size, int offset);
+ bool promote(TIntermOperator*);
+
+#ifdef NV_EXTENSIONS
+ void setLayoutOverrideCoverage() { layoutOverrideCoverage = true; }
+ bool getLayoutOverrideCoverage() const { return layoutOverrideCoverage; }
+ void setGeoPassthroughEXT() { geoPassthroughEXT = true; }
+ bool getGeoPassthroughEXT() const { return geoPassthroughEXT; }
+ void setLayoutDerivativeMode(ComputeDerivativeMode mode) { computeDerivativeMode = mode; }
+ ComputeDerivativeMode getLayoutDerivativeModeNone() const { return computeDerivativeMode; }
+ bool setPrimitives(int m)
+ {
+ if (primitives != TQualifier::layoutNotSet)
+ return primitives == m;
+ primitives = m;
+ return true;
+ }
+ int getPrimitives() const { return primitives; }
+#endif
+
+ const char* addSemanticName(const TString& name)
+ {
+ return semanticNameSet.insert(name).first->c_str();
+ }
+
+ void setSourceFile(const char* file) { if (file != nullptr) sourceFile = file; }
+ const std::string& getSourceFile() const { return sourceFile; }
+ void addSourceText(const char* text, size_t len) { sourceText.append(text, len); }
+ const std::string& getSourceText() const { return sourceText; }
+ const std::map<std::string, std::string>& getIncludeText() const { return includeText; }
+ void addIncludeText(const char* name, const char* text, size_t len) { includeText[name].assign(text,len); }
+ void addProcesses(const std::vector<std::string>& p)
+ {
+ for (int i = 0; i < (int)p.size(); ++i)
+ processes.addProcess(p[i]);
+ }
+ void addProcess(const std::string& process) { processes.addProcess(process); }
+ void addProcessArgument(const std::string& arg) { processes.addArgument(arg); }
+ const std::vector<std::string>& getProcesses() const { return processes.getProcesses(); }
+
+ void addUniformLocationOverride(const char* nameStr, int location)
+ {
+ std::string name = nameStr;
+ uniformLocationOverrides[name] = location;
+ }
+
+ int getUniformLocationOverride(const char* nameStr) const
+ {
+ std::string name = nameStr;
+ auto pos = uniformLocationOverrides.find(name);
+ if (pos == uniformLocationOverrides.end())
+ return -1;
+ else
+ return pos->second;
+ }
+
+ void setUniformLocationBase(int base) { uniformLocationBase = base; }
+ int getUniformLocationBase() const { return uniformLocationBase; }
+
+ void setNeedsLegalization() { needToLegalize = true; }
+ bool needsLegalization() const { return needToLegalize; }
+
+ void setBinaryDoubleOutput() { binaryDoubleOutput = true; }
+ bool getBinaryDoubleOutput() { return binaryDoubleOutput; }
+
+ const char* const implicitThisName;
+ const char* const implicitCounterName;
+
+protected:
+ TIntermSymbol* addSymbol(int Id, const TString&, const TType&, const TConstUnionArray&, TIntermTyped* subtree, const TSourceLoc&);
+ void error(TInfoSink& infoSink, const char*);
+ void warn(TInfoSink& infoSink, const char*);
+ void mergeCallGraphs(TInfoSink&, TIntermediate&);
+ void mergeModes(TInfoSink&, TIntermediate&);
+ void mergeTrees(TInfoSink&, TIntermediate&);
+ void seedIdMap(TMap<TString, int>& idMap, int& maxId);
+ void remapIds(const TMap<TString, int>& idMap, int idShift, TIntermediate&);
+ void mergeBodies(TInfoSink&, TIntermSequence& globals, const TIntermSequence& unitGlobals);
+ void mergeLinkerObjects(TInfoSink&, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects);
+ void mergeImplicitArraySizes(TType&, const TType&);
+ void mergeErrorCheck(TInfoSink&, const TIntermSymbol&, const TIntermSymbol&, bool crossStage);
+ void checkCallGraphCycles(TInfoSink&);
+ void checkCallGraphBodies(TInfoSink&, bool keepUncalled);
+ void inOutLocationCheck(TInfoSink&);
+ TIntermAggregate* findLinkerObjects() const;
+ bool userOutputUsed() const;
+ bool isSpecializationOperation(const TIntermOperator&) const;
+ bool isNonuniformPropagating(TOperator) const;
+ bool promoteUnary(TIntermUnary&);
+ bool promoteBinary(TIntermBinary&);
+ void addSymbolLinkageNode(TIntermAggregate*& linkage, TSymbolTable&, const TString&);
+ bool promoteAggregate(TIntermAggregate&);
+ void pushSelector(TIntermSequence&, const TVectorSelector&, const TSourceLoc&);
+ void pushSelector(TIntermSequence&, const TMatrixSelector&, const TSourceLoc&);
+ bool specConstantPropagates(const TIntermTyped&, const TIntermTyped&);
+ void performTextureUpgradeAndSamplerRemovalTransformation(TIntermNode* root);
+ bool isConversionAllowed(TOperator op, TIntermTyped* node) const;
+ TIntermTyped* createConversion(TBasicType convertTo, TIntermTyped* node) const;
+ std::tuple<TBasicType, TBasicType> getConversionDestinatonType(TBasicType type0, TBasicType type1, TOperator op) const;
+ bool extensionRequested(const char *extension) const {return requestedExtensions.find(extension) != requestedExtensions.end();}
+ static const char* getResourceName(TResourceType);
+
+ const EShLanguage language; // stage, known at construction time
+ EShSource source; // source language, known a bit later
+ std::string entryPointName;
+ std::string entryPointMangledName;
+ typedef std::list<TCall> TGraph;
+ TGraph callGraph;
+
+ EProfile profile; // source profile
+ int version; // source version
+ SpvVersion spvVersion;
+ TIntermNode* treeRoot;
+ std::set<std::string> requestedExtensions; // cumulation of all enabled or required extensions; not connected to what subset of the shader used them
+ TBuiltInResource resources;
+ int numEntryPoints;
+ int numErrors;
+ int numPushConstants;
+ bool recursive;
+ int invocations;
+ int vertices;
+ TLayoutGeometry inputPrimitive;
+ TLayoutGeometry outputPrimitive;
+ bool pixelCenterInteger;
+ bool originUpperLeft;
+ TVertexSpacing vertexSpacing;
+ TVertexOrder vertexOrder;
+ bool pointMode;
+ int localSize[3];
+ int localSizeSpecId[3];
+ bool earlyFragmentTests;
+ bool postDepthCoverage;
+ TLayoutDepth depthLayout;
+ bool depthReplacing;
+ bool hlslFunctionality1;
+ int blendEquations; // an 'or'ing of masks of shifts of TBlendEquationShift
+ bool xfbMode;
+ std::vector<TXfbBuffer> xfbBuffers; // all the data we need to track per xfb buffer
+ bool multiStream;
+
+#ifdef NV_EXTENSIONS
+ bool layoutOverrideCoverage;
+ bool geoPassthroughEXT;
+ int numShaderRecordNVBlocks;
+ ComputeDerivativeMode computeDerivativeMode;
+ int primitives;
+ int numTaskNVBlocks;
+#endif
+
+ // Base shift values
+ std::array<unsigned int, EResCount> shiftBinding;
+
+ // Per-descriptor-set shift values
+ std::array<std::map<int, int>, EResCount> shiftBindingForSet;
+
+ std::vector<std::string> resourceSetBinding;
+ bool autoMapBindings;
+ bool autoMapLocations;
+ bool invertY;
+ bool flattenUniformArrays;
+ bool useUnknownFormat;
+ bool hlslOffsets;
+ bool useStorageBuffer;
+ bool useVulkanMemoryModel;
+ bool hlslIoMapping;
+ bool useVariablePointers;
+
+ std::set<TString> ioAccessed; // set of names of statically read/written I/O that might need extra checking
+ std::vector<TIoRange> usedIo[4]; // sets of used locations, one for each of in, out, uniform, and buffers
+ std::vector<TOffsetRange> usedAtomics; // sets of bindings used by atomic counters
+ std::unordered_set<int> usedConstantId; // specialization constant ids used
+ std::set<TString> semanticNameSet;
+
+ EShTextureSamplerTransformMode textureSamplerTransformMode;
+
+ // source code of shader, useful as part of debug information
+ std::string sourceFile;
+ std::string sourceText;
+
+ // Included text. First string is a name, second is the included text
+ std::map<std::string, std::string> includeText;
+
+ // for OpModuleProcessed, or equivalent
+ TProcesses processes;
+
+ bool needToLegalize;
+ bool binaryDoubleOutput;
+ bool usePhysicalStorageBuffer;
+
+ std::unordered_map<std::string, int> uniformLocationOverrides;
+ int uniformLocationBase;
+
+private:
+ void operator=(TIntermediate&); // prevent assignments
+};
+
+} // end namespace glslang
+
+#endif // _LOCAL_INTERMEDIATE_INCLUDED_
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/parseConst.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/parseConst.cpp
new file mode 100644
index 0000000..1a8e6d9
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/parseConst.cpp
@@ -0,0 +1,204 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+//
+// Traverse a tree of constants to create a single folded constant.
+// It should only be used when the whole tree is known to be constant.
+//
+
+#include "ParseHelper.h"
+
+namespace glslang {
+
+class TConstTraverser : public TIntermTraverser {
+public:
+ TConstTraverser(const TConstUnionArray& cUnion, bool singleConstParam, TOperator constructType, const TType& t)
+ : unionArray(cUnion), type(t),
+ constructorType(constructType), singleConstantParam(singleConstParam), error(false), isMatrix(false),
+ matrixCols(0), matrixRows(0) { index = 0; tOp = EOpNull; }
+
+ virtual void visitConstantUnion(TIntermConstantUnion* node);
+ virtual bool visitAggregate(TVisit, TIntermAggregate* node);
+
+ int index;
+ TConstUnionArray unionArray;
+ TOperator tOp;
+ const TType& type;
+ TOperator constructorType;
+ bool singleConstantParam;
+ bool error;
+ int size; // size of the constructor ( 4 for vec4)
+ bool isMatrix;
+ int matrixCols;
+ int matrixRows;
+
+protected:
+ TConstTraverser(TConstTraverser&);
+ TConstTraverser& operator=(TConstTraverser&);
+};
+
+bool TConstTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node)
+{
+ if (! node->isConstructor() && node->getOp() != EOpComma) {
+ error = true;
+
+ return false;
+ }
+
+ bool flag = node->getSequence().size() == 1 && node->getSequence()[0]->getAsTyped()->getAsConstantUnion();
+ if (flag) {
+ singleConstantParam = true;
+ constructorType = node->getOp();
+ size = node->getType().computeNumComponents();
+
+ if (node->getType().isMatrix()) {
+ isMatrix = true;
+ matrixCols = node->getType().getMatrixCols();
+ matrixRows = node->getType().getMatrixRows();
+ }
+ }
+
+ for (TIntermSequence::iterator p = node->getSequence().begin();
+ p != node->getSequence().end(); p++) {
+
+ if (node->getOp() == EOpComma)
+ index = 0;
+
+ (*p)->traverse(this);
+ }
+ if (flag)
+ {
+ singleConstantParam = false;
+ constructorType = EOpNull;
+ size = 0;
+ isMatrix = false;
+ matrixCols = 0;
+ matrixRows = 0;
+ }
+
+ return false;
+}
+
+void TConstTraverser::visitConstantUnion(TIntermConstantUnion* node)
+{
+ TConstUnionArray leftUnionArray(unionArray);
+ int instanceSize = type.computeNumComponents();
+
+ if (index >= instanceSize)
+ return;
+
+ if (! singleConstantParam) {
+ int rightUnionSize = node->getType().computeNumComponents();
+
+ const TConstUnionArray& rightUnionArray = node->getConstArray();
+ for (int i = 0; i < rightUnionSize; i++) {
+ if (index >= instanceSize)
+ return;
+ leftUnionArray[index] = rightUnionArray[i];
+
+ index++;
+ }
+ } else {
+ int endIndex = index + size;
+ const TConstUnionArray& rightUnionArray = node->getConstArray();
+ if (! isMatrix) {
+ int count = 0;
+ int nodeComps = node->getType().computeNumComponents();
+ for (int i = index; i < endIndex; i++) {
+ if (i >= instanceSize)
+ return;
+
+ leftUnionArray[i] = rightUnionArray[count];
+
+ (index)++;
+
+ if (nodeComps > 1)
+ count++;
+ }
+ } else {
+ // constructing a matrix, but from what?
+ if (node->isMatrix()) {
+ // Matrix from a matrix; this has the outer matrix, node is the argument matrix.
+ // Traverse the outer, potentially bigger matrix, fill in missing pieces with the
+ // identity matrix.
+ for (int c = 0; c < matrixCols; ++c) {
+ for (int r = 0; r < matrixRows; ++r) {
+ int targetOffset = index + c * matrixRows + r;
+ if (r < node->getType().getMatrixRows() && c < node->getType().getMatrixCols()) {
+ int srcOffset = c * node->getType().getMatrixRows() + r;
+ leftUnionArray[targetOffset] = rightUnionArray[srcOffset];
+ } else if (r == c)
+ leftUnionArray[targetOffset].setDConst(1.0);
+ else
+ leftUnionArray[targetOffset].setDConst(0.0);
+ }
+ }
+ } else {
+ // matrix from vector
+ int count = 0;
+ const int startIndex = index;
+ int nodeComps = node->getType().computeNumComponents();
+ for (int i = startIndex; i < endIndex; i++) {
+ if (i >= instanceSize)
+ return;
+ if (i == startIndex || (i - startIndex) % (matrixRows + 1) == 0 )
+ leftUnionArray[i] = rightUnionArray[count];
+ else
+ leftUnionArray[i].setDConst(0.0);
+
+ index++;
+
+ if (nodeComps > 1)
+ count++;
+ }
+ }
+ }
+ }
+}
+
+bool TIntermediate::parseConstTree(TIntermNode* root, TConstUnionArray unionArray, TOperator constructorType, const TType& t, bool singleConstantParam)
+{
+ if (root == 0)
+ return false;
+
+ TConstTraverser it(unionArray, singleConstantParam, constructorType, t);
+
+ root->traverse(&it);
+ if (it.error)
+ return true;
+ else
+ return false;
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/parseVersions.h b/src/3rdparty/glslang/glslang/MachineIndependent/parseVersions.h
new file mode 100644
index 0000000..02af76a
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/parseVersions.h
@@ -0,0 +1,159 @@
+//
+// Copyright (C) 2015-2018 Google, Inc.
+// Copyright (C) 2017 ARM Limited.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+// This is implemented in Versions.cpp
+
+#ifndef _PARSE_VERSIONS_INCLUDED_
+#define _PARSE_VERSIONS_INCLUDED_
+
+#include "../Public/ShaderLang.h"
+#include "../Include/InfoSink.h"
+#include "Scan.h"
+
+#include <map>
+
+namespace glslang {
+
+//
+// Base class for parse helpers.
+// This just has version-related information and checking.
+// This class should be sufficient for preprocessing.
+//
+class TParseVersions {
+public:
+ TParseVersions(TIntermediate& interm, int version, EProfile profile,
+ const SpvVersion& spvVersion, EShLanguage language, TInfoSink& infoSink,
+ bool forwardCompatible, EShMessages messages)
+ : infoSink(infoSink), version(version), profile(profile), language(language),
+ spvVersion(spvVersion), forwardCompatible(forwardCompatible),
+ intermediate(interm), messages(messages), numErrors(0), currentScanner(0) { }
+ virtual ~TParseVersions() { }
+ virtual void initializeExtensionBehavior();
+ virtual void requireProfile(const TSourceLoc&, int queryProfiles, const char* featureDesc);
+ virtual void profileRequires(const TSourceLoc&, int queryProfiles, int minVersion, int numExtensions, const char* const extensions[], const char* featureDesc);
+ virtual void profileRequires(const TSourceLoc&, int queryProfiles, int minVersion, const char* const extension, const char* featureDesc);
+ virtual void requireStage(const TSourceLoc&, EShLanguageMask, const char* featureDesc);
+ virtual void requireStage(const TSourceLoc&, EShLanguage, const char* featureDesc);
+ virtual void checkDeprecated(const TSourceLoc&, int queryProfiles, int depVersion, const char* featureDesc);
+ virtual void requireNotRemoved(const TSourceLoc&, int queryProfiles, int removedVersion, const char* featureDesc);
+ virtual void unimplemented(const TSourceLoc&, const char* featureDesc);
+ virtual void requireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], const char* featureDesc);
+ virtual void ppRequireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], const char* featureDesc);
+ virtual TExtensionBehavior getExtensionBehavior(const char*);
+ virtual bool extensionTurnedOn(const char* const extension);
+ virtual bool extensionsTurnedOn(int numExtensions, const char* const extensions[]);
+ virtual void updateExtensionBehavior(int line, const char* const extension, const char* behavior);
+ virtual void fullIntegerCheck(const TSourceLoc&, const char* op);
+ virtual void doubleCheck(const TSourceLoc&, const char* op);
+ virtual void float16Check(const TSourceLoc&, const char* op, bool builtIn = false);
+ virtual void float16ScalarVectorCheck(const TSourceLoc&, const char* op, bool builtIn = false);
+ virtual bool float16Arithmetic();
+ virtual void requireFloat16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc);
+ virtual void int16ScalarVectorCheck(const TSourceLoc&, const char* op, bool builtIn = false);
+ virtual bool int16Arithmetic();
+ virtual void requireInt16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc);
+ virtual void int8ScalarVectorCheck(const TSourceLoc&, const char* op, bool builtIn = false);
+ virtual bool int8Arithmetic();
+ virtual void requireInt8Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc);
+#ifdef AMD_EXTENSIONS
+ virtual void float16OpaqueCheck(const TSourceLoc&, const char* op, bool builtIn = false);
+#endif
+ virtual void int64Check(const TSourceLoc&, const char* op, bool builtIn = false);
+ virtual void explicitInt8Check(const TSourceLoc&, const char* op, bool builtIn = false);
+ virtual void explicitInt16Check(const TSourceLoc&, const char* op, bool builtIn = false);
+ virtual void explicitInt32Check(const TSourceLoc&, const char* op, bool builtIn = false);
+ virtual void explicitFloat32Check(const TSourceLoc&, const char* op, bool builtIn = false);
+ virtual void explicitFloat64Check(const TSourceLoc&, const char* op, bool builtIn = false);
+ virtual void spvRemoved(const TSourceLoc&, const char* op);
+ virtual void vulkanRemoved(const TSourceLoc&, const char* op);
+ virtual void requireVulkan(const TSourceLoc&, const char* op);
+ virtual void requireSpv(const TSourceLoc&, const char* op);
+ virtual bool checkExtensionsRequested(const TSourceLoc&, int numExtensions, const char* const extensions[], const char* featureDesc);
+ virtual void updateExtensionBehavior(const char* const extension, TExtensionBehavior);
+ virtual void checkExtensionStage(const TSourceLoc&, const char* const extension);
+ virtual void fcoopmatCheck(const TSourceLoc&, const char* op, bool builtIn = false);
+
+ virtual void C_DECL error(const TSourceLoc&, const char* szReason, const char* szToken,
+ const char* szExtraInfoFormat, ...) = 0;
+ virtual void C_DECL warn(const TSourceLoc&, const char* szReason, const char* szToken,
+ const char* szExtraInfoFormat, ...) = 0;
+ virtual void C_DECL ppError(const TSourceLoc&, const char* szReason, const char* szToken,
+ const char* szExtraInfoFormat, ...) = 0;
+ virtual void C_DECL ppWarn(const TSourceLoc&, const char* szReason, const char* szToken,
+ const char* szExtraInfoFormat, ...) = 0;
+
+ void addError() { ++numErrors; }
+ int getNumErrors() const { return numErrors; }
+
+ void setScanner(TInputScanner* scanner) { currentScanner = scanner; }
+ TInputScanner* getScanner() const { return currentScanner; }
+ const TSourceLoc& getCurrentLoc() const { return currentScanner->getSourceLoc(); }
+ void setCurrentLine(int line) { currentScanner->setLine(line); }
+ void setCurrentColumn(int col) { currentScanner->setColumn(col); }
+ void setCurrentSourceName(const char* name) { currentScanner->setFile(name); }
+ void setCurrentString(int string) { currentScanner->setString(string); }
+
+ void getPreamble(std::string&);
+ bool relaxedErrors() const { return (messages & EShMsgRelaxedErrors) != 0; }
+ bool suppressWarnings() const { return (messages & EShMsgSuppressWarnings) != 0; }
+ bool isReadingHLSL() const { return (messages & EShMsgReadHlsl) == EShMsgReadHlsl; }
+ bool hlslEnable16BitTypes() const { return (messages & EShMsgHlslEnable16BitTypes) != 0; }
+ bool hlslDX9Compatible() const { return (messages & EShMsgHlslDX9Compatible) != 0; }
+
+ TInfoSink& infoSink;
+
+ // compilation mode
+ int version; // version, updated by #version in the shader
+ EProfile profile; // the declared profile in the shader (core by default)
+ EShLanguage language; // really the stage
+ SpvVersion spvVersion;
+ bool forwardCompatible; // true if errors are to be given for use of deprecated features
+ TIntermediate& intermediate; // helper for making and hooking up pieces of the parse tree
+
+protected:
+ TMap<TString, TExtensionBehavior> extensionBehavior; // for each extension string, what its current behavior is set to
+ EShMessages messages; // errors/warnings/rule-sets
+ int numErrors; // number of compile-time errors encountered
+ TInputScanner* currentScanner;
+
+private:
+ explicit TParseVersions(const TParseVersions&);
+ TParseVersions& operator=(const TParseVersions&);
+};
+
+} // end namespace glslang
+
+#endif // _PARSE_VERSIONS_INCLUDED_
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/pch.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/pch.cpp
new file mode 100644
index 0000000..b7a0865
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/pch.cpp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2018 The Khronos Group Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "pch.h"
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/pch.h b/src/3rdparty/glslang/glslang/MachineIndependent/pch.h
new file mode 100644
index 0000000..6ea3761
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/pch.h
@@ -0,0 +1,49 @@
+#ifndef _PCH_H
+#define _PCH_H
+//
+// Copyright (C) 2018 The Khronos Group Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+#include <sstream>
+#include <cstdlib>
+#include <cstring>
+#include <cctype>
+#include <climits>
+#include <iostream>
+#include <sstream>
+#include <memory>
+#include "SymbolTable.h"
+#include "ParseHelper.h"
+#include "Scan.h"
+#include "ScanContext.h"
+
+#endif /* _PCH_H */
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp
new file mode 100644
index 0000000..c74e44f
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp
@@ -0,0 +1,1320 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2013 LunarG, Inc.
+// Copyright (C) 2015-2018 Google, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+/****************************************************************************\
+Copyright (c) 2002, NVIDIA Corporation.
+
+NVIDIA Corporation("NVIDIA") supplies this software to you in
+consideration of your agreement to the following terms, and your use,
+installation, modification or redistribution of this NVIDIA software
+constitutes acceptance of these terms. If you do not agree with these
+terms, please do not use, install, modify or redistribute this NVIDIA
+software.
+
+In consideration of your agreement to abide by the following terms, and
+subject to these terms, NVIDIA grants you a personal, non-exclusive
+license, under NVIDIA's copyrights in this original NVIDIA software (the
+"NVIDIA Software"), to use, reproduce, modify and redistribute the
+NVIDIA Software, with or without modifications, in source and/or binary
+forms; provided that if you redistribute the NVIDIA Software, you must
+retain the copyright notice of NVIDIA, this notice and the following
+text and disclaimers in all such redistributions of the NVIDIA Software.
+Neither the name, trademarks, service marks nor logos of NVIDIA
+Corporation may be used to endorse or promote products derived from the
+NVIDIA Software without specific prior written permission from NVIDIA.
+Except as expressly stated in this notice, no other rights or licenses
+express or implied, are granted by NVIDIA herein, including but not
+limited to any patent rights that may be infringed by your derivative
+works or by other works in which the NVIDIA Software may be
+incorporated. No hardware is licensed hereunder.
+
+THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED,
+INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
+ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER
+PRODUCTS.
+
+IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT,
+INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY
+OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE
+NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT,
+TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+\****************************************************************************/
+
+#ifndef _CRT_SECURE_NO_WARNINGS
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include <sstream>
+#include <cstdlib>
+#include <cstring>
+#include <cctype>
+#include <climits>
+
+#include "PpContext.h"
+#include "PpTokens.h"
+
+namespace glslang {
+
+// Handle #define
+int TPpContext::CPPdefine(TPpToken* ppToken)
+{
+ MacroSymbol mac;
+
+ // get the macro name
+ int token = scanToken(ppToken);
+ if (token != PpAtomIdentifier) {
+ parseContext.ppError(ppToken->loc, "must be followed by macro name", "#define", "");
+ return token;
+ }
+ if (ppToken->loc.string >= 0) {
+ // We are in user code; check for reserved name use:
+ parseContext.reservedPpErrorCheck(ppToken->loc, ppToken->name, "#define");
+ }
+
+ // save the macro name
+ const int defAtom = atomStrings.getAddAtom(ppToken->name);
+ TSourceLoc defineLoc = ppToken->loc; // because ppToken might go to the next line before we report errors
+
+ // gather parameters to the macro, between (...)
+ token = scanToken(ppToken);
+ if (token == '(' && !ppToken->space) {
+ mac.functionLike = 1;
+ do {
+ token = scanToken(ppToken);
+ if (mac.args.size() == 0 && token == ')')
+ break;
+ if (token != PpAtomIdentifier) {
+ parseContext.ppError(ppToken->loc, "bad argument", "#define", "");
+
+ return token;
+ }
+ const int argAtom = atomStrings.getAddAtom(ppToken->name);
+
+ // check for duplication of parameter name
+ bool duplicate = false;
+ for (size_t a = 0; a < mac.args.size(); ++a) {
+ if (mac.args[a] == argAtom) {
+ parseContext.ppError(ppToken->loc, "duplicate macro parameter", "#define", "");
+ duplicate = true;
+ break;
+ }
+ }
+ if (! duplicate)
+ mac.args.push_back(argAtom);
+ token = scanToken(ppToken);
+ } while (token == ',');
+ if (token != ')') {
+ parseContext.ppError(ppToken->loc, "missing parenthesis", "#define", "");
+
+ return token;
+ }
+
+ token = scanToken(ppToken);
+ } else if (token != '\n' && token != EndOfInput && !ppToken->space) {
+ parseContext.ppWarn(ppToken->loc, "missing space after macro name", "#define", "");
+
+ return token;
+ }
+
+ // record the definition of the macro
+ while (token != '\n' && token != EndOfInput) {
+ mac.body.putToken(token, ppToken);
+ token = scanToken(ppToken);
+ if (token != '\n' && ppToken->space)
+ mac.body.putToken(' ', ppToken);
+ }
+
+ // check for duplicate definition
+ MacroSymbol* existing = lookupMacroDef(defAtom);
+ if (existing != nullptr) {
+ if (! existing->undef) {
+ // Already defined -- need to make sure they are identical:
+ // "Two replacement lists are identical if and only if the
+ // preprocessing tokens in both have the same number,
+ // ordering, spelling, and white-space separation, where all
+ // white-space separations are considered identical."
+ if (existing->functionLike != mac.functionLike) {
+ parseContext.ppError(defineLoc, "Macro redefined; function-like versus object-like:", "#define",
+ atomStrings.getString(defAtom));
+ } else if (existing->args.size() != mac.args.size()) {
+ parseContext.ppError(defineLoc, "Macro redefined; different number of arguments:", "#define",
+ atomStrings.getString(defAtom));
+ } else {
+ if (existing->args != mac.args) {
+ parseContext.ppError(defineLoc, "Macro redefined; different argument names:", "#define",
+ atomStrings.getString(defAtom));
+ }
+ // set up to compare the two
+ existing->body.reset();
+ mac.body.reset();
+ int newToken;
+ bool firstToken = true;
+ do {
+ int oldToken;
+ TPpToken oldPpToken;
+ TPpToken newPpToken;
+ oldToken = existing->body.getToken(parseContext, &oldPpToken);
+ newToken = mac.body.getToken(parseContext, &newPpToken);
+ // for the first token, preceding spaces don't matter
+ if (firstToken) {
+ newPpToken.space = oldPpToken.space;
+ firstToken = false;
+ }
+ if (oldToken != newToken || oldPpToken != newPpToken) {
+ parseContext.ppError(defineLoc, "Macro redefined; different substitutions:", "#define",
+ atomStrings.getString(defAtom));
+ break;
+ }
+ } while (newToken != EndOfInput);
+ }
+ }
+ *existing = mac;
+ } else
+ addMacroDef(defAtom, mac);
+
+ return '\n';
+}
+
+// Handle #undef
+int TPpContext::CPPundef(TPpToken* ppToken)
+{
+ int token = scanToken(ppToken);
+ if (token != PpAtomIdentifier) {
+ parseContext.ppError(ppToken->loc, "must be followed by macro name", "#undef", "");
+
+ return token;
+ }
+
+ parseContext.reservedPpErrorCheck(ppToken->loc, ppToken->name, "#undef");
+
+ MacroSymbol* macro = lookupMacroDef(atomStrings.getAtom(ppToken->name));
+ if (macro != nullptr)
+ macro->undef = 1;
+ token = scanToken(ppToken);
+ if (token != '\n')
+ parseContext.ppError(ppToken->loc, "can only be followed by a single macro name", "#undef", "");
+
+ return token;
+}
+
+// Handle #else
+/* Skip forward to appropriate spot. This is used both
+** to skip to a #endif after seeing an #else, AND to skip to a #else,
+** #elif, or #endif after a #if/#ifdef/#ifndef/#elif test was false.
+*/
+int TPpContext::CPPelse(int matchelse, TPpToken* ppToken)
+{
+ int depth = 0;
+ int token = scanToken(ppToken);
+
+ while (token != EndOfInput) {
+ if (token != '#') {
+ while (token != '\n' && token != EndOfInput)
+ token = scanToken(ppToken);
+
+ if (token == EndOfInput)
+ return token;
+
+ token = scanToken(ppToken);
+ continue;
+ }
+
+ if ((token = scanToken(ppToken)) != PpAtomIdentifier)
+ continue;
+
+ int nextAtom = atomStrings.getAtom(ppToken->name);
+ if (nextAtom == PpAtomIf || nextAtom == PpAtomIfdef || nextAtom == PpAtomIfndef) {
+ depth++;
+ if (ifdepth >= maxIfNesting || elsetracker >= maxIfNesting) {
+ parseContext.ppError(ppToken->loc, "maximum nesting depth exceeded", "#if/#ifdef/#ifndef", "");
+ return EndOfInput;
+ } else {
+ ifdepth++;
+ elsetracker++;
+ }
+ } else if (nextAtom == PpAtomEndif) {
+ token = extraTokenCheck(nextAtom, ppToken, scanToken(ppToken));
+ elseSeen[elsetracker] = false;
+ --elsetracker;
+ if (depth == 0) {
+ // found the #endif we are looking for
+ if (ifdepth > 0)
+ --ifdepth;
+ break;
+ }
+ --depth;
+ --ifdepth;
+ } else if (matchelse && depth == 0) {
+ if (nextAtom == PpAtomElse) {
+ elseSeen[elsetracker] = true;
+ token = extraTokenCheck(nextAtom, ppToken, scanToken(ppToken));
+ // found the #else we are looking for
+ break;
+ } else if (nextAtom == PpAtomElif) {
+ if (elseSeen[elsetracker])
+ parseContext.ppError(ppToken->loc, "#elif after #else", "#elif", "");
+ /* we decrement ifdepth here, because CPPif will increment
+ * it and we really want to leave it alone */
+ if (ifdepth > 0) {
+ --ifdepth;
+ elseSeen[elsetracker] = false;
+ --elsetracker;
+ }
+
+ return CPPif(ppToken);
+ }
+ } else if (nextAtom == PpAtomElse) {
+ if (elseSeen[elsetracker])
+ parseContext.ppError(ppToken->loc, "#else after #else", "#else", "");
+ else
+ elseSeen[elsetracker] = true;
+ token = extraTokenCheck(nextAtom, ppToken, scanToken(ppToken));
+ } else if (nextAtom == PpAtomElif) {
+ if (elseSeen[elsetracker])
+ parseContext.ppError(ppToken->loc, "#elif after #else", "#elif", "");
+ }
+ }
+
+ return token;
+}
+
+// Call when there should be no more tokens left on a line.
+int TPpContext::extraTokenCheck(int contextAtom, TPpToken* ppToken, int token)
+{
+ if (token != '\n' && token != EndOfInput) {
+ static const char* message = "unexpected tokens following directive";
+
+ const char* label;
+ if (contextAtom == PpAtomElse)
+ label = "#else";
+ else if (contextAtom == PpAtomElif)
+ label = "#elif";
+ else if (contextAtom == PpAtomEndif)
+ label = "#endif";
+ else if (contextAtom == PpAtomIf)
+ label = "#if";
+ else if (contextAtom == PpAtomLine)
+ label = "#line";
+ else
+ label = "";
+
+ if (parseContext.relaxedErrors())
+ parseContext.ppWarn(ppToken->loc, message, label, "");
+ else
+ parseContext.ppError(ppToken->loc, message, label, "");
+
+ while (token != '\n' && token != EndOfInput)
+ token = scanToken(ppToken);
+ }
+
+ return token;
+}
+
+enum eval_prec {
+ MIN_PRECEDENCE,
+ COND, LOGOR, LOGAND, OR, XOR, AND, EQUAL, RELATION, SHIFT, ADD, MUL, UNARY,
+ MAX_PRECEDENCE
+};
+
+namespace {
+
+ int op_logor(int a, int b) { return a || b; }
+ int op_logand(int a, int b) { return a && b; }
+ int op_or(int a, int b) { return a | b; }
+ int op_xor(int a, int b) { return a ^ b; }
+ int op_and(int a, int b) { return a & b; }
+ int op_eq(int a, int b) { return a == b; }
+ int op_ne(int a, int b) { return a != b; }
+ int op_ge(int a, int b) { return a >= b; }
+ int op_le(int a, int b) { return a <= b; }
+ int op_gt(int a, int b) { return a > b; }
+ int op_lt(int a, int b) { return a < b; }
+ int op_shl(int a, int b) { return a << b; }
+ int op_shr(int a, int b) { return a >> b; }
+ int op_add(int a, int b) { return a + b; }
+ int op_sub(int a, int b) { return a - b; }
+ int op_mul(int a, int b) { return a * b; }
+ int op_div(int a, int b) { return a == INT_MIN && b == -1 ? 0 : a / b; }
+ int op_mod(int a, int b) { return a == INT_MIN && b == -1 ? 0 : a % b; }
+ int op_pos(int a) { return a; }
+ int op_neg(int a) { return -a; }
+ int op_cmpl(int a) { return ~a; }
+ int op_not(int a) { return !a; }
+
+};
+
+struct TBinop {
+ int token, precedence, (*op)(int, int);
+} binop[] = {
+ { PpAtomOr, LOGOR, op_logor },
+ { PpAtomAnd, LOGAND, op_logand },
+ { '|', OR, op_or },
+ { '^', XOR, op_xor },
+ { '&', AND, op_and },
+ { PpAtomEQ, EQUAL, op_eq },
+ { PpAtomNE, EQUAL, op_ne },
+ { '>', RELATION, op_gt },
+ { PpAtomGE, RELATION, op_ge },
+ { '<', RELATION, op_lt },
+ { PpAtomLE, RELATION, op_le },
+ { PpAtomLeft, SHIFT, op_shl },
+ { PpAtomRight, SHIFT, op_shr },
+ { '+', ADD, op_add },
+ { '-', ADD, op_sub },
+ { '*', MUL, op_mul },
+ { '/', MUL, op_div },
+ { '%', MUL, op_mod },
+};
+
+struct TUnop {
+ int token, (*op)(int);
+} unop[] = {
+ { '+', op_pos },
+ { '-', op_neg },
+ { '~', op_cmpl },
+ { '!', op_not },
+};
+
+#define NUM_ELEMENTS(A) (sizeof(A) / sizeof(A[0]))
+
+int TPpContext::eval(int token, int precedence, bool shortCircuit, int& res, bool& err, TPpToken* ppToken)
+{
+ TSourceLoc loc = ppToken->loc; // because we sometimes read the newline before reporting the error
+ if (token == PpAtomIdentifier) {
+ if (strcmp("defined", ppToken->name) == 0) {
+ if (! parseContext.isReadingHLSL() && isMacroInput()) {
+ if (parseContext.relaxedErrors())
+ parseContext.ppWarn(ppToken->loc, "nonportable when expanded from macros for preprocessor expression",
+ "defined", "");
+ else
+ parseContext.ppError(ppToken->loc, "cannot use in preprocessor expression when expanded from macros",
+ "defined", "");
+ }
+ bool needclose = 0;
+ token = scanToken(ppToken);
+ if (token == '(') {
+ needclose = true;
+ token = scanToken(ppToken);
+ }
+ if (token != PpAtomIdentifier) {
+ parseContext.ppError(loc, "incorrect directive, expected identifier", "preprocessor evaluation", "");
+ err = true;
+ res = 0;
+
+ return token;
+ }
+
+ MacroSymbol* macro = lookupMacroDef(atomStrings.getAtom(ppToken->name));
+ res = macro != nullptr ? !macro->undef : 0;
+ token = scanToken(ppToken);
+ if (needclose) {
+ if (token != ')') {
+ parseContext.ppError(loc, "expected ')'", "preprocessor evaluation", "");
+ err = true;
+ res = 0;
+
+ return token;
+ }
+ token = scanToken(ppToken);
+ }
+ } else {
+ token = evalToToken(token, shortCircuit, res, err, ppToken);
+ return eval(token, precedence, shortCircuit, res, err, ppToken);
+ }
+ } else if (token == PpAtomConstInt) {
+ res = ppToken->ival;
+ token = scanToken(ppToken);
+ } else if (token == '(') {
+ token = scanToken(ppToken);
+ token = eval(token, MIN_PRECEDENCE, shortCircuit, res, err, ppToken);
+ if (! err) {
+ if (token != ')') {
+ parseContext.ppError(loc, "expected ')'", "preprocessor evaluation", "");
+ err = true;
+ res = 0;
+
+ return token;
+ }
+ token = scanToken(ppToken);
+ }
+ } else {
+ int op = NUM_ELEMENTS(unop) - 1;
+ for (; op >= 0; op--) {
+ if (unop[op].token == token)
+ break;
+ }
+ if (op >= 0) {
+ token = scanToken(ppToken);
+ token = eval(token, UNARY, shortCircuit, res, err, ppToken);
+ res = unop[op].op(res);
+ } else {
+ parseContext.ppError(loc, "bad expression", "preprocessor evaluation", "");
+ err = true;
+ res = 0;
+
+ return token;
+ }
+ }
+
+ token = evalToToken(token, shortCircuit, res, err, ppToken);
+
+ // Perform evaluation of binary operation, if there is one, otherwise we are done.
+ while (! err) {
+ if (token == ')' || token == '\n')
+ break;
+ int op;
+ for (op = NUM_ELEMENTS(binop) - 1; op >= 0; op--) {
+ if (binop[op].token == token)
+ break;
+ }
+ if (op < 0 || binop[op].precedence <= precedence)
+ break;
+ int leftSide = res;
+
+ // Setup short-circuiting, needed for ES, unless already in a short circuit.
+ // (Once in a short-circuit, can't turn off again, until that whole subexpression is done.
+ if (! shortCircuit) {
+ if ((token == PpAtomOr && leftSide == 1) ||
+ (token == PpAtomAnd && leftSide == 0))
+ shortCircuit = true;
+ }
+
+ token = scanToken(ppToken);
+ token = eval(token, binop[op].precedence, shortCircuit, res, err, ppToken);
+
+ if (binop[op].op == op_div || binop[op].op == op_mod) {
+ if (res == 0) {
+ parseContext.ppError(loc, "division by 0", "preprocessor evaluation", "");
+ res = 1;
+ }
+ }
+ res = binop[op].op(leftSide, res);
+ }
+
+ return token;
+}
+
+// Expand macros, skipping empty expansions, to get to the first real token in those expansions.
+int TPpContext::evalToToken(int token, bool shortCircuit, int& res, bool& err, TPpToken* ppToken)
+{
+ while (token == PpAtomIdentifier && strcmp("defined", ppToken->name) != 0) {
+ switch (MacroExpand(ppToken, true, false)) {
+ case MacroExpandNotStarted:
+ case MacroExpandError:
+ parseContext.ppError(ppToken->loc, "can't evaluate expression", "preprocessor evaluation", "");
+ err = true;
+ res = 0;
+ break;
+ case MacroExpandStarted:
+ break;
+ case MacroExpandUndef:
+ if (! shortCircuit && parseContext.profile == EEsProfile) {
+ const char* message = "undefined macro in expression not allowed in es profile";
+ if (parseContext.relaxedErrors())
+ parseContext.ppWarn(ppToken->loc, message, "preprocessor evaluation", ppToken->name);
+ else
+ parseContext.ppError(ppToken->loc, message, "preprocessor evaluation", ppToken->name);
+ }
+ break;
+ }
+ token = scanToken(ppToken);
+ if (err)
+ break;
+ }
+
+ return token;
+}
+
+// Handle #if
+int TPpContext::CPPif(TPpToken* ppToken)
+{
+ int token = scanToken(ppToken);
+ if (ifdepth >= maxIfNesting || elsetracker >= maxIfNesting) {
+ parseContext.ppError(ppToken->loc, "maximum nesting depth exceeded", "#if", "");
+ return EndOfInput;
+ } else {
+ elsetracker++;
+ ifdepth++;
+ }
+ int res = 0;
+ bool err = false;
+ token = eval(token, MIN_PRECEDENCE, false, res, err, ppToken);
+ token = extraTokenCheck(PpAtomIf, ppToken, token);
+ if (!res && !err)
+ token = CPPelse(1, ppToken);
+
+ return token;
+}
+
+// Handle #ifdef
+int TPpContext::CPPifdef(int defined, TPpToken* ppToken)
+{
+ int token = scanToken(ppToken);
+ if (ifdepth > maxIfNesting || elsetracker > maxIfNesting) {
+ parseContext.ppError(ppToken->loc, "maximum nesting depth exceeded", "#ifdef", "");
+ return EndOfInput;
+ } else {
+ elsetracker++;
+ ifdepth++;
+ }
+
+ if (token != PpAtomIdentifier) {
+ if (defined)
+ parseContext.ppError(ppToken->loc, "must be followed by macro name", "#ifdef", "");
+ else
+ parseContext.ppError(ppToken->loc, "must be followed by macro name", "#ifndef", "");
+ } else {
+ MacroSymbol* macro = lookupMacroDef(atomStrings.getAtom(ppToken->name));
+ token = scanToken(ppToken);
+ if (token != '\n') {
+ parseContext.ppError(ppToken->loc, "unexpected tokens following #ifdef directive - expected a newline", "#ifdef", "");
+ while (token != '\n' && token != EndOfInput)
+ token = scanToken(ppToken);
+ }
+ if (((macro != nullptr && !macro->undef) ? 1 : 0) != defined)
+ token = CPPelse(1, ppToken);
+ }
+
+ return token;
+}
+
+// Handle #include ...
+// TODO: Handle macro expansions for the header name
+int TPpContext::CPPinclude(TPpToken* ppToken)
+{
+ const TSourceLoc directiveLoc = ppToken->loc;
+ bool startWithLocalSearch = true; // to additionally include the extra "" paths
+ int token = scanToken(ppToken);
+
+ // handle <header-name>-style #include
+ if (token == '<') {
+ startWithLocalSearch = false;
+ token = scanHeaderName(ppToken, '>');
+ }
+ // otherwise ppToken already has the header name and it was "header-name" style
+
+ if (token != PpAtomConstString) {
+ parseContext.ppError(directiveLoc, "must be followed by a header name", "#include", "");
+ return token;
+ }
+
+ // Make a copy of the name because it will be overwritten by the next token scan.
+ const std::string filename = ppToken->name;
+
+ // See if the directive was well formed
+ token = scanToken(ppToken);
+ if (token != '\n') {
+ if (token == EndOfInput)
+ parseContext.ppError(ppToken->loc, "expected newline after header name:", "#include", "%s", filename.c_str());
+ else
+ parseContext.ppError(ppToken->loc, "extra content after header name:", "#include", "%s", filename.c_str());
+ return token;
+ }
+
+ // Process well-formed directive
+
+ // Find the inclusion, first look in "Local" ("") paths, if requested,
+ // otherwise, only search the "System" (<>) paths.
+ TShader::Includer::IncludeResult* res = nullptr;
+ if (startWithLocalSearch)
+ res = includer.includeLocal(filename.c_str(), currentSourceFile.c_str(), includeStack.size() + 1);
+ if (res == nullptr || res->headerName.empty()) {
+ includer.releaseInclude(res);
+ res = includer.includeSystem(filename.c_str(), currentSourceFile.c_str(), includeStack.size() + 1);
+ }
+
+ // Process the results
+ if (res != nullptr && !res->headerName.empty()) {
+ if (res->headerData != nullptr && res->headerLength > 0) {
+ // path for processing one or more tokens from an included header, hand off 'res'
+ const bool forNextLine = parseContext.lineDirectiveShouldSetNextLine();
+ std::ostringstream prologue;
+ std::ostringstream epilogue;
+ prologue << "#line " << forNextLine << " " << "\"" << res->headerName << "\"\n";
+ epilogue << (res->headerData[res->headerLength - 1] == '\n'? "" : "\n") <<
+ "#line " << directiveLoc.line + forNextLine << " " << directiveLoc.getStringNameOrNum() << "\n";
+ pushInput(new TokenizableIncludeFile(directiveLoc, prologue.str(), res, epilogue.str(), this));
+ parseContext.intermediate.addIncludeText(res->headerName.c_str(), res->headerData, res->headerLength);
+ // There's no "current" location anymore.
+ parseContext.setCurrentColumn(0);
+ } else {
+ // things are okay, but there is nothing to process
+ includer.releaseInclude(res);
+ }
+ } else {
+ // error path, clean up
+ std::string message =
+ res != nullptr ? std::string(res->headerData, res->headerLength)
+ : std::string("Could not process include directive");
+ parseContext.ppError(directiveLoc, message.c_str(), "#include", "for header name: %s", filename.c_str());
+ includer.releaseInclude(res);
+ }
+
+ return token;
+}
+
+// Handle #line
+int TPpContext::CPPline(TPpToken* ppToken)
+{
+ // "#line must have, after macro substitution, one of the following forms:
+ // "#line line
+ // "#line line source-string-number"
+
+ int token = scanToken(ppToken);
+ const TSourceLoc directiveLoc = ppToken->loc;
+ if (token == '\n') {
+ parseContext.ppError(ppToken->loc, "must by followed by an integral literal", "#line", "");
+ return token;
+ }
+
+ int lineRes = 0; // Line number after macro expansion.
+ int lineToken = 0;
+ bool hasFile = false;
+ int fileRes = 0; // Source file number after macro expansion.
+ const char* sourceName = nullptr; // Optional source file name.
+ bool lineErr = false;
+ bool fileErr = false;
+ token = eval(token, MIN_PRECEDENCE, false, lineRes, lineErr, ppToken);
+ if (! lineErr) {
+ lineToken = lineRes;
+ if (token == '\n')
+ ++lineRes;
+
+ if (parseContext.lineDirectiveShouldSetNextLine())
+ --lineRes;
+ parseContext.setCurrentLine(lineRes);
+
+ if (token != '\n') {
+ if (token == PpAtomConstString) {
+ parseContext.ppRequireExtensions(directiveLoc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based #line");
+ // We need to save a copy of the string instead of pointing
+ // to the name field of the token since the name field
+ // will likely be overwritten by the next token scan.
+ sourceName = atomStrings.getString(atomStrings.getAddAtom(ppToken->name));
+ parseContext.setCurrentSourceName(sourceName);
+ hasFile = true;
+ token = scanToken(ppToken);
+ } else {
+ token = eval(token, MIN_PRECEDENCE, false, fileRes, fileErr, ppToken);
+ if (! fileErr) {
+ parseContext.setCurrentString(fileRes);
+ hasFile = true;
+ }
+ }
+ }
+ }
+ if (!fileErr && !lineErr) {
+ parseContext.notifyLineDirective(directiveLoc.line, lineToken, hasFile, fileRes, sourceName);
+ }
+ token = extraTokenCheck(PpAtomLine, ppToken, token);
+
+ return token;
+}
+
+// Handle #error
+int TPpContext::CPPerror(TPpToken* ppToken)
+{
+ int token = scanToken(ppToken);
+ std::string message;
+ TSourceLoc loc = ppToken->loc;
+
+ while (token != '\n' && token != EndOfInput) {
+ if (token == PpAtomConstInt16 || token == PpAtomConstUint16 ||
+ token == PpAtomConstInt || token == PpAtomConstUint ||
+ token == PpAtomConstInt64 || token == PpAtomConstUint64 ||
+ token == PpAtomConstFloat16 ||
+ token == PpAtomConstFloat || token == PpAtomConstDouble) {
+ message.append(ppToken->name);
+ } else if (token == PpAtomIdentifier || token == PpAtomConstString) {
+ message.append(ppToken->name);
+ } else {
+ message.append(atomStrings.getString(token));
+ }
+ message.append(" ");
+ token = scanToken(ppToken);
+ }
+ parseContext.notifyErrorDirective(loc.line, message.c_str());
+ // store this msg into the shader's information log..set the Compile Error flag!!!!
+ parseContext.ppError(loc, message.c_str(), "#error", "");
+
+ return '\n';
+}
+
+// Handle #pragma
+int TPpContext::CPPpragma(TPpToken* ppToken)
+{
+ char SrcStrName[2];
+ TVector<TString> tokens;
+
+ TSourceLoc loc = ppToken->loc; // because we go to the next line before processing
+ int token = scanToken(ppToken);
+ while (token != '\n' && token != EndOfInput) {
+ switch (token) {
+ case PpAtomIdentifier:
+ case PpAtomConstInt:
+ case PpAtomConstUint:
+ case PpAtomConstInt64:
+ case PpAtomConstUint64:
+#ifdef AMD_EXTENSIONS
+ case PpAtomConstInt16:
+ case PpAtomConstUint16:
+#endif
+ case PpAtomConstFloat:
+ case PpAtomConstDouble:
+ case PpAtomConstFloat16:
+ tokens.push_back(ppToken->name);
+ break;
+ default:
+ SrcStrName[0] = (char)token;
+ SrcStrName[1] = '\0';
+ tokens.push_back(SrcStrName);
+ }
+ token = scanToken(ppToken);
+ }
+
+ if (token == EndOfInput)
+ parseContext.ppError(loc, "directive must end with a newline", "#pragma", "");
+ else
+ parseContext.handlePragma(loc, tokens);
+
+ return token;
+}
+
+// #version: This is just for error checking: the version and profile are decided before preprocessing starts
+int TPpContext::CPPversion(TPpToken* ppToken)
+{
+ int token = scanToken(ppToken);
+
+ if (errorOnVersion || versionSeen) {
+ if (parseContext.isReadingHLSL())
+ parseContext.ppError(ppToken->loc, "invalid preprocessor command", "#version", "");
+ else
+ parseContext.ppError(ppToken->loc, "must occur first in shader", "#version", "");
+ }
+ versionSeen = true;
+
+ if (token == '\n') {
+ parseContext.ppError(ppToken->loc, "must be followed by version number", "#version", "");
+
+ return token;
+ }
+
+ if (token != PpAtomConstInt)
+ parseContext.ppError(ppToken->loc, "must be followed by version number", "#version", "");
+
+ ppToken->ival = atoi(ppToken->name);
+ int versionNumber = ppToken->ival;
+ int line = ppToken->loc.line;
+ token = scanToken(ppToken);
+
+ if (token == '\n') {
+ parseContext.notifyVersion(line, versionNumber, nullptr);
+ return token;
+ } else {
+ int profileAtom = atomStrings.getAtom(ppToken->name);
+ if (profileAtom != PpAtomCore &&
+ profileAtom != PpAtomCompatibility &&
+ profileAtom != PpAtomEs)
+ parseContext.ppError(ppToken->loc, "bad profile name; use es, core, or compatibility", "#version", "");
+ parseContext.notifyVersion(line, versionNumber, ppToken->name);
+ token = scanToken(ppToken);
+
+ if (token == '\n')
+ return token;
+ else
+ parseContext.ppError(ppToken->loc, "bad tokens following profile -- expected newline", "#version", "");
+ }
+
+ return token;
+}
+
+// Handle #extension
+int TPpContext::CPPextension(TPpToken* ppToken)
+{
+ int line = ppToken->loc.line;
+ int token = scanToken(ppToken);
+ char extensionName[MaxTokenLength + 1];
+
+ if (token=='\n') {
+ parseContext.ppError(ppToken->loc, "extension name not specified", "#extension", "");
+ return token;
+ }
+
+ if (token != PpAtomIdentifier)
+ parseContext.ppError(ppToken->loc, "extension name expected", "#extension", "");
+
+ snprintf(extensionName, sizeof(extensionName), "%s", ppToken->name);
+
+ token = scanToken(ppToken);
+ if (token != ':') {
+ parseContext.ppError(ppToken->loc, "':' missing after extension name", "#extension", "");
+ return token;
+ }
+
+ token = scanToken(ppToken);
+ if (token != PpAtomIdentifier) {
+ parseContext.ppError(ppToken->loc, "behavior for extension not specified", "#extension", "");
+ return token;
+ }
+
+ parseContext.updateExtensionBehavior(line, extensionName, ppToken->name);
+ parseContext.notifyExtensionDirective(line, extensionName, ppToken->name);
+
+ token = scanToken(ppToken);
+ if (token == '\n')
+ return token;
+ else
+ parseContext.ppError(ppToken->loc, "extra tokens -- expected newline", "#extension","");
+
+ return token;
+}
+
+int TPpContext::readCPPline(TPpToken* ppToken)
+{
+ int token = scanToken(ppToken);
+
+ if (token == PpAtomIdentifier) {
+ switch (atomStrings.getAtom(ppToken->name)) {
+ case PpAtomDefine:
+ token = CPPdefine(ppToken);
+ break;
+ case PpAtomElse:
+ if (elseSeen[elsetracker])
+ parseContext.ppError(ppToken->loc, "#else after #else", "#else", "");
+ elseSeen[elsetracker] = true;
+ if (ifdepth == 0)
+ parseContext.ppError(ppToken->loc, "mismatched statements", "#else", "");
+ token = extraTokenCheck(PpAtomElse, ppToken, scanToken(ppToken));
+ token = CPPelse(0, ppToken);
+ break;
+ case PpAtomElif:
+ if (ifdepth == 0)
+ parseContext.ppError(ppToken->loc, "mismatched statements", "#elif", "");
+ if (elseSeen[elsetracker])
+ parseContext.ppError(ppToken->loc, "#elif after #else", "#elif", "");
+ // this token is really a dont care, but we still need to eat the tokens
+ token = scanToken(ppToken);
+ while (token != '\n' && token != EndOfInput)
+ token = scanToken(ppToken);
+ token = CPPelse(0, ppToken);
+ break;
+ case PpAtomEndif:
+ if (ifdepth == 0)
+ parseContext.ppError(ppToken->loc, "mismatched statements", "#endif", "");
+ else {
+ elseSeen[elsetracker] = false;
+ --elsetracker;
+ --ifdepth;
+ }
+ token = extraTokenCheck(PpAtomEndif, ppToken, scanToken(ppToken));
+ break;
+ case PpAtomIf:
+ token = CPPif(ppToken);
+ break;
+ case PpAtomIfdef:
+ token = CPPifdef(1, ppToken);
+ break;
+ case PpAtomIfndef:
+ token = CPPifdef(0, ppToken);
+ break;
+ case PpAtomInclude:
+ if(!parseContext.isReadingHLSL()) {
+ parseContext.ppRequireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_include_directive, "#include");
+ }
+ token = CPPinclude(ppToken);
+ break;
+ case PpAtomLine:
+ token = CPPline(ppToken);
+ break;
+ case PpAtomPragma:
+ token = CPPpragma(ppToken);
+ break;
+ case PpAtomUndef:
+ token = CPPundef(ppToken);
+ break;
+ case PpAtomError:
+ token = CPPerror(ppToken);
+ break;
+ case PpAtomVersion:
+ token = CPPversion(ppToken);
+ break;
+ case PpAtomExtension:
+ token = CPPextension(ppToken);
+ break;
+ default:
+ parseContext.ppError(ppToken->loc, "invalid directive:", "#", ppToken->name);
+ break;
+ }
+ } else if (token != '\n' && token != EndOfInput)
+ parseContext.ppError(ppToken->loc, "invalid directive", "#", "");
+
+ while (token != '\n' && token != EndOfInput)
+ token = scanToken(ppToken);
+
+ return token;
+}
+
+// Context-dependent parsing of a #include <header-name>.
+// Assumes no macro expansions etc. are being done; the name is just on the current input.
+// Always creates a name and returns PpAtomicConstString, unless we run out of input.
+int TPpContext::scanHeaderName(TPpToken* ppToken, char delimit)
+{
+ bool tooLong = false;
+
+ if (inputStack.empty())
+ return EndOfInput;
+
+ int len = 0;
+ ppToken->name[0] = '\0';
+ do {
+ int ch = inputStack.back()->getch();
+
+ // done yet?
+ if (ch == delimit) {
+ ppToken->name[len] = '\0';
+ if (tooLong)
+ parseContext.ppError(ppToken->loc, "header name too long", "", "");
+ return PpAtomConstString;
+ } else if (ch == EndOfInput)
+ return EndOfInput;
+
+ // found a character to expand the name with
+ if (len < MaxTokenLength)
+ ppToken->name[len++] = (char)ch;
+ else
+ tooLong = true;
+ } while (true);
+}
+
+// Macro-expand a macro argument 'arg' to create 'expandedArg'.
+// Does not replace 'arg'.
+// Returns nullptr if no expanded argument is created.
+TPpContext::TokenStream* TPpContext::PrescanMacroArg(TokenStream& arg, TPpToken* ppToken, bool newLineOkay)
+{
+ // expand the argument
+ TokenStream* expandedArg = new TokenStream;
+ pushInput(new tMarkerInput(this));
+ pushTokenStreamInput(arg);
+ int token;
+ while ((token = scanToken(ppToken)) != tMarkerInput::marker && token != EndOfInput) {
+ token = tokenPaste(token, *ppToken);
+ if (token == PpAtomIdentifier) {
+ switch (MacroExpand(ppToken, false, newLineOkay)) {
+ case MacroExpandNotStarted:
+ break;
+ case MacroExpandError:
+ // toss the rest of the pushed-input argument by scanning until tMarkerInput
+ while ((token = scanToken(ppToken)) != tMarkerInput::marker && token != EndOfInput)
+ ;
+ break;
+ case MacroExpandStarted:
+ case MacroExpandUndef:
+ continue;
+ }
+ }
+ if (token == tMarkerInput::marker || token == EndOfInput)
+ break;
+ expandedArg->putToken(token, ppToken);
+ }
+
+ if (token != tMarkerInput::marker) {
+ // Error, or MacroExpand ate the marker, so had bad input, recover
+ delete expandedArg;
+ expandedArg = nullptr;
+ }
+
+ return expandedArg;
+}
+
+//
+// Return the next token for a macro expansion, handling macro arguments,
+// whose semantics are dependent on being adjacent to ##.
+//
+int TPpContext::tMacroInput::scan(TPpToken* ppToken)
+{
+ int token;
+ do {
+ token = mac->body.getToken(pp->parseContext, ppToken);
+ } while (token == ' '); // handle white space in macro
+
+ // Hash operators basically turn off a round of macro substitution
+ // (the round done on the argument before the round done on the RHS of the
+ // macro definition):
+ //
+ // "A parameter in the replacement list, unless preceded by a # or ##
+ // preprocessing token or followed by a ## preprocessing token (see below),
+ // is replaced by the corresponding argument after all macros contained
+ // therein have been expanded."
+ //
+ // "If, in the replacement list, a parameter is immediately preceded or
+ // followed by a ## preprocessing token, the parameter is replaced by the
+ // corresponding argument's preprocessing token sequence."
+
+ bool pasting = false;
+ if (postpaste) {
+ // don't expand next token
+ pasting = true;
+ postpaste = false;
+ }
+
+ if (prepaste) {
+ // already know we should be on a ##, verify
+ assert(token == PpAtomPaste);
+ prepaste = false;
+ postpaste = true;
+ }
+
+ // see if are preceding a ##
+ if (mac->body.peekUntokenizedPasting()) {
+ prepaste = true;
+ pasting = true;
+ }
+
+ // HLSL does expand macros before concatenation
+ if (pasting && pp->parseContext.isReadingHLSL())
+ pasting = false;
+
+ // TODO: preprocessor: properly handle whitespace (or lack of it) between tokens when expanding
+ if (token == PpAtomIdentifier) {
+ int i;
+ for (i = (int)mac->args.size() - 1; i >= 0; i--)
+ if (strcmp(pp->atomStrings.getString(mac->args[i]), ppToken->name) == 0)
+ break;
+ if (i >= 0) {
+ TokenStream* arg = expandedArgs[i];
+ if (arg == nullptr || pasting)
+ arg = args[i];
+ pp->pushTokenStreamInput(*arg, prepaste);
+
+ return pp->scanToken(ppToken);
+ }
+ }
+
+ if (token == EndOfInput)
+ mac->busy = 0;
+
+ return token;
+}
+
+// return a textual zero, for scanning a macro that was never defined
+int TPpContext::tZeroInput::scan(TPpToken* ppToken)
+{
+ if (done)
+ return EndOfInput;
+
+ ppToken->name[0] = '0';
+ ppToken->name[1] = 0;
+ ppToken->ival = 0;
+ ppToken->space = false;
+ done = true;
+
+ return PpAtomConstInt;
+}
+
+//
+// Check a token to see if it is a macro that should be expanded:
+// - If it is, and defined, push a tInput that will produce the appropriate
+// expansion and return MacroExpandStarted.
+// - If it is, but undefined, and expandUndef is requested, push a tInput
+// that will expand to 0 and return MacroExpandUndef.
+// - Otherwise, there is no expansion, and there are two cases:
+// * It might be okay there is no expansion, and no specific error was
+// detected. Returns MacroExpandNotStarted.
+// * The expansion was started, but could not be completed, due to an error
+// that cannot be recovered from. Returns MacroExpandError.
+//
+MacroExpandResult TPpContext::MacroExpand(TPpToken* ppToken, bool expandUndef, bool newLineOkay)
+{
+ ppToken->space = false;
+ int macroAtom = atomStrings.getAtom(ppToken->name);
+ switch (macroAtom) {
+ case PpAtomLineMacro:
+ ppToken->ival = parseContext.getCurrentLoc().line;
+ snprintf(ppToken->name, sizeof(ppToken->name), "%d", ppToken->ival);
+ UngetToken(PpAtomConstInt, ppToken);
+ return MacroExpandStarted;
+
+ case PpAtomFileMacro: {
+ if (parseContext.getCurrentLoc().name)
+ parseContext.ppRequireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based __FILE__");
+ ppToken->ival = parseContext.getCurrentLoc().string;
+ snprintf(ppToken->name, sizeof(ppToken->name), "%s", ppToken->loc.getStringNameOrNum().c_str());
+ UngetToken(PpAtomConstInt, ppToken);
+ return MacroExpandStarted;
+ }
+
+ case PpAtomVersionMacro:
+ ppToken->ival = parseContext.version;
+ snprintf(ppToken->name, sizeof(ppToken->name), "%d", ppToken->ival);
+ UngetToken(PpAtomConstInt, ppToken);
+ return MacroExpandStarted;
+
+ default:
+ break;
+ }
+
+ MacroSymbol* macro = macroAtom == 0 ? nullptr : lookupMacroDef(macroAtom);
+
+ // no recursive expansions
+ if (macro != nullptr && macro->busy)
+ return MacroExpandNotStarted;
+
+ // not expanding undefined macros
+ if ((macro == nullptr || macro->undef) && ! expandUndef)
+ return MacroExpandNotStarted;
+
+ // 0 is the value of an undefined macro
+ if ((macro == nullptr || macro->undef) && expandUndef) {
+ pushInput(new tZeroInput(this));
+ return MacroExpandUndef;
+ }
+
+ tMacroInput *in = new tMacroInput(this);
+
+ TSourceLoc loc = ppToken->loc; // in case we go to the next line before discovering the error
+ in->mac = macro;
+ if (macro->functionLike) {
+ // We don't know yet if this will be a successful call of a
+ // function-like macro; need to look for a '(', but without trashing
+ // the passed in ppToken, until we know we are no longer speculative.
+ TPpToken parenToken;
+ int token = scanToken(&parenToken);
+ if (newLineOkay) {
+ while (token == '\n')
+ token = scanToken(&parenToken);
+ }
+ if (token != '(') {
+ // Function-like macro called with object-like syntax: okay, don't expand.
+ // (We ate exactly one token that might not be white space; put it back.
+ UngetToken(token, &parenToken);
+ delete in;
+ return MacroExpandNotStarted;
+ }
+ in->args.resize(in->mac->args.size());
+ for (size_t i = 0; i < in->mac->args.size(); i++)
+ in->args[i] = new TokenStream;
+ in->expandedArgs.resize(in->mac->args.size());
+ for (size_t i = 0; i < in->mac->args.size(); i++)
+ in->expandedArgs[i] = nullptr;
+ size_t arg = 0;
+ bool tokenRecorded = false;
+ do {
+ TVector<char> nestStack;
+ while (true) {
+ token = scanToken(ppToken);
+ if (token == EndOfInput || token == tMarkerInput::marker) {
+ parseContext.ppError(loc, "End of input in macro", "macro expansion", atomStrings.getString(macroAtom));
+ delete in;
+ return MacroExpandError;
+ }
+ if (token == '\n') {
+ if (! newLineOkay) {
+ parseContext.ppError(loc, "End of line in macro substitution:", "macro expansion", atomStrings.getString(macroAtom));
+ delete in;
+ return MacroExpandError;
+ }
+ continue;
+ }
+ if (token == '#') {
+ parseContext.ppError(ppToken->loc, "unexpected '#'", "macro expansion", atomStrings.getString(macroAtom));
+ delete in;
+ return MacroExpandError;
+ }
+ if (in->mac->args.size() == 0 && token != ')')
+ break;
+ if (nestStack.size() == 0 && (token == ',' || token == ')'))
+ break;
+ if (token == '(')
+ nestStack.push_back(')');
+ else if (token == '{' && parseContext.isReadingHLSL())
+ nestStack.push_back('}');
+ else if (nestStack.size() > 0 && token == nestStack.back())
+ nestStack.pop_back();
+ in->args[arg]->putToken(token, ppToken);
+ tokenRecorded = true;
+ }
+ // end of single argument scan
+
+ if (token == ')') {
+ // closing paren of call
+ if (in->mac->args.size() == 1 && !tokenRecorded)
+ break;
+ arg++;
+ break;
+ }
+ arg++;
+ } while (arg < in->mac->args.size());
+ // end of all arguments scan
+
+ if (arg < in->mac->args.size())
+ parseContext.ppError(loc, "Too few args in Macro", "macro expansion", atomStrings.getString(macroAtom));
+ else if (token != ')') {
+ // Error recover code; find end of call, if possible
+ int depth = 0;
+ while (token != EndOfInput && (depth > 0 || token != ')')) {
+ if (token == ')' || token == '}')
+ depth--;
+ token = scanToken(ppToken);
+ if (token == '(' || token == '{')
+ depth++;
+ }
+
+ if (token == EndOfInput) {
+ parseContext.ppError(loc, "End of input in macro", "macro expansion", atomStrings.getString(macroAtom));
+ delete in;
+ return MacroExpandError;
+ }
+ parseContext.ppError(loc, "Too many args in macro", "macro expansion", atomStrings.getString(macroAtom));
+ }
+
+ // We need both expanded and non-expanded forms of the argument, for whether or
+ // not token pasting will be applied later when the argument is consumed next to ##.
+ for (size_t i = 0; i < in->mac->args.size(); i++)
+ in->expandedArgs[i] = PrescanMacroArg(*in->args[i], ppToken, newLineOkay);
+ }
+
+ pushInput(in);
+ macro->busy = 1;
+ macro->body.reset();
+
+ return MacroExpandStarted;
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp
new file mode 100644
index 0000000..06c2333
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp
@@ -0,0 +1,181 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2013 LunarG, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+/****************************************************************************\
+Copyright (c) 2002, NVIDIA Corporation.
+
+NVIDIA Corporation("NVIDIA") supplies this software to you in
+consideration of your agreement to the following terms, and your use,
+installation, modification or redistribution of this NVIDIA software
+constitutes acceptance of these terms. If you do not agree with these
+terms, please do not use, install, modify or redistribute this NVIDIA
+software.
+
+In consideration of your agreement to abide by the following terms, and
+subject to these terms, NVIDIA grants you a personal, non-exclusive
+license, under NVIDIA's copyrights in this original NVIDIA software (the
+"NVIDIA Software"), to use, reproduce, modify and redistribute the
+NVIDIA Software, with or without modifications, in source and/or binary
+forms; provided that if you redistribute the NVIDIA Software, you must
+retain the copyright notice of NVIDIA, this notice and the following
+text and disclaimers in all such redistributions of the NVIDIA Software.
+Neither the name, trademarks, service marks nor logos of NVIDIA
+Corporation may be used to endorse or promote products derived from the
+NVIDIA Software without specific prior written permission from NVIDIA.
+Except as expressly stated in this notice, no other rights or licenses
+express or implied, are granted by NVIDIA herein, including but not
+limited to any patent rights that may be infringed by your derivative
+works or by other works in which the NVIDIA Software may be
+incorporated. No hardware is licensed hereunder.
+
+THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED,
+INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
+ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER
+PRODUCTS.
+
+IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT,
+INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY
+OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE
+NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT,
+TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+\****************************************************************************/
+
+#ifndef _CRT_SECURE_NO_WARNINGS
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include <cassert>
+#include <cstdlib>
+#include <cstring>
+
+#include "PpContext.h"
+#include "PpTokens.h"
+
+namespace {
+
+using namespace glslang;
+
+const struct {
+ int val;
+ const char* str;
+} tokens[] = {
+
+ { PPAtomAddAssign, "+=" },
+ { PPAtomSubAssign, "-=" },
+ { PPAtomMulAssign, "*=" },
+ { PPAtomDivAssign, "/=" },
+ { PPAtomModAssign, "%=" },
+
+ { PpAtomRight, ">>" },
+ { PpAtomLeft, "<<" },
+ { PpAtomAnd, "&&" },
+ { PpAtomOr, "||" },
+ { PpAtomXor, "^^" },
+
+ { PpAtomRightAssign, ">>=" },
+ { PpAtomLeftAssign, "<<=" },
+ { PpAtomAndAssign, "&=" },
+ { PpAtomOrAssign, "|=" },
+ { PpAtomXorAssign, "^=" },
+
+ { PpAtomEQ, "==" },
+ { PpAtomNE, "!=" },
+ { PpAtomGE, ">=" },
+ { PpAtomLE, "<=" },
+
+ { PpAtomDecrement, "--" },
+ { PpAtomIncrement, "++" },
+
+ { PpAtomColonColon, "::" },
+
+ { PpAtomDefine, "define" },
+ { PpAtomUndef, "undef" },
+ { PpAtomIf, "if" },
+ { PpAtomElif, "elif" },
+ { PpAtomElse, "else" },
+ { PpAtomEndif, "endif" },
+ { PpAtomIfdef, "ifdef" },
+ { PpAtomIfndef, "ifndef" },
+ { PpAtomLine, "line" },
+ { PpAtomPragma, "pragma" },
+ { PpAtomError, "error" },
+
+ { PpAtomVersion, "version" },
+ { PpAtomCore, "core" },
+ { PpAtomCompatibility, "compatibility" },
+ { PpAtomEs, "es" },
+ { PpAtomExtension, "extension" },
+
+ { PpAtomLineMacro, "__LINE__" },
+ { PpAtomFileMacro, "__FILE__" },
+ { PpAtomVersionMacro, "__VERSION__" },
+
+ { PpAtomInclude, "include" },
+};
+
+} // end anonymous namespace
+
+namespace glslang {
+
+//
+// Initialize the atom table.
+//
+TStringAtomMap::TStringAtomMap()
+{
+ badToken.assign("<bad token>");
+
+ // Add single character tokens to the atom table:
+ const char* s = "~!%^&*()-+=|,.<>/?;:[]{}#\\";
+ char t[2];
+
+ t[1] = '\0';
+ while (*s) {
+ t[0] = *s;
+ addAtomFixed(t, s[0]);
+ s++;
+ }
+
+ // Add multiple character scanner tokens :
+ for (size_t ii = 0; ii < sizeof(tokens)/sizeof(tokens[0]); ii++)
+ addAtomFixed(tokens[ii].str, tokens[ii].val);
+
+ nextAtom = PpAtomLast;
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp
new file mode 100644
index 0000000..cc003a8
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp
@@ -0,0 +1,119 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2013 LunarG, Inc.
+// Copyright (C) 2015-2018 Google, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+/****************************************************************************\
+Copyright (c) 2002, NVIDIA Corporation.
+
+NVIDIA Corporation("NVIDIA") supplies this software to you in
+consideration of your agreement to the following terms, and your use,
+installation, modification or redistribution of this NVIDIA software
+constitutes acceptance of these terms. If you do not agree with these
+terms, please do not use, install, modify or redistribute this NVIDIA
+software.
+
+In consideration of your agreement to abide by the following terms, and
+subject to these terms, NVIDIA grants you a personal, non-exclusive
+license, under NVIDIA's copyrights in this original NVIDIA software (the
+"NVIDIA Software"), to use, reproduce, modify and redistribute the
+NVIDIA Software, with or without modifications, in source and/or binary
+forms; provided that if you redistribute the NVIDIA Software, you must
+retain the copyright notice of NVIDIA, this notice and the following
+text and disclaimers in all such redistributions of the NVIDIA Software.
+Neither the name, trademarks, service marks nor logos of NVIDIA
+Corporation may be used to endorse or promote products derived from the
+NVIDIA Software without specific prior written permission from NVIDIA.
+Except as expressly stated in this notice, no other rights or licenses
+express or implied, are granted by NVIDIA herein, including but not
+limited to any patent rights that may be infringed by your derivative
+works or by other works in which the NVIDIA Software may be
+incorporated. No hardware is licensed hereunder.
+
+THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED,
+INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
+ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER
+PRODUCTS.
+
+IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT,
+INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY
+OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE
+NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT,
+TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+\****************************************************************************/
+
+#include <cstdlib>
+#include <locale>
+
+#include "PpContext.h"
+
+namespace glslang {
+
+TPpContext::TPpContext(TParseContextBase& pc, const std::string& rootFileName, TShader::Includer& inclr) :
+ preamble(0), strings(0), previous_token('\n'), parseContext(pc), includer(inclr), inComment(false),
+ rootFileName(rootFileName),
+ currentSourceFile(rootFileName)
+{
+ ifdepth = 0;
+ for (elsetracker = 0; elsetracker < maxIfNesting; elsetracker++)
+ elseSeen[elsetracker] = false;
+ elsetracker = 0;
+
+ strtodStream.imbue(std::locale::classic());
+}
+
+TPpContext::~TPpContext()
+{
+ delete [] preamble;
+
+ // free up the inputStack
+ while (! inputStack.empty())
+ popInput();
+}
+
+void TPpContext::setInput(TInputScanner& input, bool versionWillBeError)
+{
+ assert(inputStack.size() == 0);
+
+ pushInput(new tStringInput(this, input));
+
+ errorOnVersion = versionWillBeError;
+ versionSeen = false;
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpContext.h b/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpContext.h
new file mode 100644
index 0000000..8470e17
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpContext.h
@@ -0,0 +1,702 @@
+//
+// Copyright (C) 2013 LunarG, Inc.
+// Copyright (C) 2015-2018 Google, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+/****************************************************************************\
+Copyright (c) 2002, NVIDIA Corporation.
+
+NVIDIA Corporation("NVIDIA") supplies this software to you in
+consideration of your agreement to the following terms, and your use,
+installation, modification or redistribution of this NVIDIA software
+constitutes acceptance of these terms. If you do not agree with these
+terms, please do not use, install, modify or redistribute this NVIDIA
+software.
+
+In consideration of your agreement to abide by the following terms, and
+subject to these terms, NVIDIA grants you a personal, non-exclusive
+license, under NVIDIA's copyrights in this original NVIDIA software (the
+"NVIDIA Software"), to use, reproduce, modify and redistribute the
+NVIDIA Software, with or without modifications, in source and/or binary
+forms; provided that if you redistribute the NVIDIA Software, you must
+retain the copyright notice of NVIDIA, this notice and the following
+text and disclaimers in all such redistributions of the NVIDIA Software.
+Neither the name, trademarks, service marks nor logos of NVIDIA
+Corporation may be used to endorse or promote products derived from the
+NVIDIA Software without specific prior written permission from NVIDIA.
+Except as expressly stated in this notice, no other rights or licenses
+express or implied, are granted by NVIDIA herein, including but not
+limited to any patent rights that may be infringed by your derivative
+works or by other works in which the NVIDIA Software may be
+incorporated. No hardware is licensed hereunder.
+
+THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED,
+INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
+ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER
+PRODUCTS.
+
+IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT,
+INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY
+OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE
+NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT,
+TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+\****************************************************************************/
+
+#ifndef PPCONTEXT_H
+#define PPCONTEXT_H
+
+#include <stack>
+#include <unordered_map>
+#include <sstream>
+
+#include "../ParseHelper.h"
+#include "PpTokens.h"
+
+/* windows only pragma */
+#ifdef _MSC_VER
+ #pragma warning(disable : 4127)
+#endif
+
+namespace glslang {
+
+class TPpToken {
+public:
+ TPpToken() { clear(); }
+ void clear()
+ {
+ space = false;
+ i64val = 0;
+ loc.init();
+ name[0] = 0;
+ }
+
+ // Used for comparing macro definitions, so checks what is relevant for that.
+ bool operator==(const TPpToken& right)
+ {
+ return space == right.space &&
+ ival == right.ival && dval == right.dval && i64val == right.i64val &&
+ strncmp(name, right.name, MaxTokenLength) == 0;
+ }
+ bool operator!=(const TPpToken& right) { return ! operator==(right); }
+
+ TSourceLoc loc;
+ // True if a space (for white space or a removed comment) should also be
+ // recognized, in front of the token returned:
+ bool space;
+ // Numeric value of the token:
+ union {
+ int ival;
+ double dval;
+ long long i64val;
+ };
+ // Text string of the token:
+ char name[MaxTokenLength + 1];
+};
+
+class TStringAtomMap {
+//
+// Implementation is in PpAtom.cpp
+//
+// Maintain a bi-directional mapping between relevant preprocessor strings and
+// "atoms" which a unique integers (small, contiguous, not hash-like) per string.
+//
+public:
+ TStringAtomMap();
+
+ // Map string -> atom.
+ // Return 0 if no existing string.
+ int getAtom(const char* s) const
+ {
+ auto it = atomMap.find(s);
+ return it == atomMap.end() ? 0 : it->second;
+ }
+
+ // Map a new or existing string -> atom, inventing a new atom if necessary.
+ int getAddAtom(const char* s)
+ {
+ int atom = getAtom(s);
+ if (atom == 0) {
+ atom = nextAtom++;
+ addAtomFixed(s, atom);
+ }
+ return atom;
+ }
+
+ // Map atom -> string.
+ const char* getString(int atom) const { return stringMap[atom]->c_str(); }
+
+protected:
+ TStringAtomMap(TStringAtomMap&);
+ TStringAtomMap& operator=(TStringAtomMap&);
+
+ TUnorderedMap<TString, int> atomMap;
+ TVector<const TString*> stringMap; // these point into the TString in atomMap
+ int nextAtom;
+
+ // Bad source characters can lead to bad atoms, so gracefully handle those by
+ // pre-filling the table with them (to avoid if tests later).
+ TString badToken;
+
+ // Add bi-directional mappings:
+ // - string -> atom
+ // - atom -> string
+ void addAtomFixed(const char* s, int atom)
+ {
+ auto it = atomMap.insert(std::pair<TString, int>(s, atom)).first;
+ if (stringMap.size() < (size_t)atom + 1)
+ stringMap.resize(atom + 100, &badToken);
+ stringMap[atom] = &it->first;
+ }
+};
+
+class TInputScanner;
+
+enum MacroExpandResult {
+ MacroExpandNotStarted, // macro not expanded, which might not be an error
+ MacroExpandError, // a clear error occurred while expanding, no expansion
+ MacroExpandStarted, // macro expansion process has started
+ MacroExpandUndef // macro is undefined and will be expanded
+};
+
+// This class is the result of turning a huge pile of C code communicating through globals
+// into a class. This was done to allowing instancing to attain thread safety.
+// Don't expect too much in terms of OO design.
+class TPpContext {
+public:
+ TPpContext(TParseContextBase&, const std::string& rootFileName, TShader::Includer&);
+ virtual ~TPpContext();
+
+ void setPreamble(const char* preamble, size_t length);
+
+ int tokenize(TPpToken& ppToken);
+ int tokenPaste(int token, TPpToken&);
+
+ class tInput {
+ public:
+ tInput(TPpContext* p) : done(false), pp(p) { }
+ virtual ~tInput() { }
+
+ virtual int scan(TPpToken*) = 0;
+ virtual int getch() = 0;
+ virtual void ungetch() = 0;
+ virtual bool peekPasting() { return false; } // true when about to see ##
+ virtual bool peekContinuedPasting(int) { return false; } // true when non-spaced tokens can paste
+ virtual bool endOfReplacementList() { return false; } // true when at the end of a macro replacement list (RHS of #define)
+ virtual bool isMacroInput() { return false; }
+
+ // Will be called when we start reading tokens from this instance
+ virtual void notifyActivated() {}
+ // Will be called when we do not read tokens from this instance anymore
+ virtual void notifyDeleted() {}
+ protected:
+ bool done;
+ TPpContext* pp;
+ };
+
+ void setInput(TInputScanner& input, bool versionWillBeError);
+
+ void pushInput(tInput* in)
+ {
+ inputStack.push_back(in);
+ in->notifyActivated();
+ }
+ void popInput()
+ {
+ inputStack.back()->notifyDeleted();
+ delete inputStack.back();
+ inputStack.pop_back();
+ }
+
+ //
+ // From PpTokens.cpp
+ //
+
+ // Capture the needed parts of a token stream for macro recording/playback.
+ class TokenStream {
+ public:
+ // Manage a stream of these 'Token', which capture the relevant parts
+ // of a TPpToken, plus its atom.
+ class Token {
+ public:
+ Token(int atom, const TPpToken& ppToken) :
+ atom(atom),
+ space(ppToken.space),
+ i64val(ppToken.i64val),
+ name(ppToken.name) { }
+ int get(TPpToken& ppToken)
+ {
+ ppToken.clear();
+ ppToken.space = space;
+ ppToken.i64val = i64val;
+ snprintf(ppToken.name, sizeof(ppToken.name), "%s", name.c_str());
+ return atom;
+ }
+ bool isAtom(int a) const { return atom == a; }
+ int getAtom() const { return atom; }
+ bool nonSpaced() const { return !space; }
+ protected:
+ Token() {}
+ int atom;
+ bool space; // did a space precede the token?
+ long long i64val;
+ TString name;
+ };
+
+ TokenStream() : currentPos(0) { }
+
+ void putToken(int token, TPpToken* ppToken);
+ bool peekToken(int atom) { return !atEnd() && stream[currentPos].isAtom(atom); }
+ bool peekContinuedPasting(int atom)
+ {
+ // This is basically necessary because, for example, the PP
+ // tokenizer only accepts valid numeric-literals plus suffixes, so
+ // separates numeric-literals plus bad suffix into two tokens, which
+ // should get both pasted together as one token when token pasting.
+ //
+ // The following code is a bit more generalized than the above example.
+ if (!atEnd() && atom == PpAtomIdentifier && stream[currentPos].nonSpaced()) {
+ switch(stream[currentPos].getAtom()) {
+ case PpAtomConstInt:
+ case PpAtomConstUint:
+ case PpAtomConstInt64:
+ case PpAtomConstUint64:
+ case PpAtomConstInt16:
+ case PpAtomConstUint16:
+ case PpAtomConstFloat:
+ case PpAtomConstDouble:
+ case PpAtomConstFloat16:
+ case PpAtomConstString:
+ case PpAtomIdentifier:
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+ }
+ int getToken(TParseContextBase&, TPpToken*);
+ bool atEnd() { return currentPos >= stream.size(); }
+ bool peekTokenizedPasting(bool lastTokenPastes);
+ bool peekUntokenizedPasting();
+ void reset() { currentPos = 0; }
+
+ protected:
+ TVector<Token> stream;
+ size_t currentPos;
+ };
+
+ //
+ // From Pp.cpp
+ //
+
+ struct MacroSymbol {
+ MacroSymbol() : functionLike(0), busy(0), undef(0) { }
+ TVector<int> args;
+ TokenStream body;
+ unsigned functionLike : 1; // 0 means object-like, 1 means function-like
+ unsigned busy : 1;
+ unsigned undef : 1;
+ };
+
+ typedef TMap<int, MacroSymbol> TSymbolMap;
+ TSymbolMap macroDefs; // map atoms to macro definitions
+ MacroSymbol* lookupMacroDef(int atom)
+ {
+ auto existingMacroIt = macroDefs.find(atom);
+ return (existingMacroIt == macroDefs.end()) ? nullptr : &(existingMacroIt->second);
+ }
+ void addMacroDef(int atom, MacroSymbol& macroDef) { macroDefs[atom] = macroDef; }
+
+protected:
+ TPpContext(TPpContext&);
+ TPpContext& operator=(TPpContext&);
+
+ TStringAtomMap atomStrings;
+ char* preamble; // string to parse, all before line 1 of string 0, it is 0 if no preamble
+ int preambleLength;
+ char** strings; // official strings of shader, starting a string 0 line 1
+ size_t* lengths;
+ int numStrings; // how many official strings there are
+ int currentString; // which string we're currently parsing (-1 for preamble)
+
+ // Scanner data:
+ int previous_token;
+ TParseContextBase& parseContext;
+
+ // Get the next token from *stack* of input sources, popping input sources
+ // that are out of tokens, down until an input source is found that has a token.
+ // Return EndOfInput when there are no more tokens to be found by doing this.
+ int scanToken(TPpToken* ppToken)
+ {
+ int token = EndOfInput;
+
+ while (! inputStack.empty()) {
+ token = inputStack.back()->scan(ppToken);
+ if (token != EndOfInput || inputStack.empty())
+ break;
+ popInput();
+ }
+
+ return token;
+ }
+ int getChar() { return inputStack.back()->getch(); }
+ void ungetChar() { inputStack.back()->ungetch(); }
+ bool peekPasting() { return !inputStack.empty() && inputStack.back()->peekPasting(); }
+ bool peekContinuedPasting(int a)
+ {
+ return !inputStack.empty() && inputStack.back()->peekContinuedPasting(a);
+ }
+ bool endOfReplacementList() { return inputStack.empty() || inputStack.back()->endOfReplacementList(); }
+ bool isMacroInput() { return inputStack.size() > 0 && inputStack.back()->isMacroInput(); }
+
+ static const int maxIfNesting = 65;
+
+ int ifdepth; // current #if-#else-#endif nesting in the cpp.c file (pre-processor)
+ bool elseSeen[maxIfNesting]; // Keep a track of whether an else has been seen at a particular depth
+ int elsetracker; // #if-#else and #endif constructs...Counter.
+
+ class tMacroInput : public tInput {
+ public:
+ tMacroInput(TPpContext* pp) : tInput(pp), prepaste(false), postpaste(false) { }
+ virtual ~tMacroInput()
+ {
+ for (size_t i = 0; i < args.size(); ++i)
+ delete args[i];
+ for (size_t i = 0; i < expandedArgs.size(); ++i)
+ delete expandedArgs[i];
+ }
+
+ virtual int scan(TPpToken*) override;
+ virtual int getch() override { assert(0); return EndOfInput; }
+ virtual void ungetch() override { assert(0); }
+ bool peekPasting() override { return prepaste; }
+ bool peekContinuedPasting(int a) override { return mac->body.peekContinuedPasting(a); }
+ bool endOfReplacementList() override { return mac->body.atEnd(); }
+ bool isMacroInput() override { return true; }
+
+ MacroSymbol *mac;
+ TVector<TokenStream*> args;
+ TVector<TokenStream*> expandedArgs;
+
+ protected:
+ bool prepaste; // true if we are just before ##
+ bool postpaste; // true if we are right after ##
+ };
+
+ class tMarkerInput : public tInput {
+ public:
+ tMarkerInput(TPpContext* pp) : tInput(pp) { }
+ virtual int scan(TPpToken*) override
+ {
+ if (done)
+ return EndOfInput;
+ done = true;
+
+ return marker;
+ }
+ virtual int getch() override { assert(0); return EndOfInput; }
+ virtual void ungetch() override { assert(0); }
+ static const int marker = -3;
+ };
+
+ class tZeroInput : public tInput {
+ public:
+ tZeroInput(TPpContext* pp) : tInput(pp) { }
+ virtual int scan(TPpToken*) override;
+ virtual int getch() override { assert(0); return EndOfInput; }
+ virtual void ungetch() override { assert(0); }
+ };
+
+ std::vector<tInput*> inputStack;
+ bool errorOnVersion;
+ bool versionSeen;
+
+ //
+ // from Pp.cpp
+ //
+
+ // Used to obtain #include content.
+ TShader::Includer& includer;
+
+ int CPPdefine(TPpToken * ppToken);
+ int CPPundef(TPpToken * ppToken);
+ int CPPelse(int matchelse, TPpToken * ppToken);
+ int extraTokenCheck(int atom, TPpToken* ppToken, int token);
+ int eval(int token, int precedence, bool shortCircuit, int& res, bool& err, TPpToken * ppToken);
+ int evalToToken(int token, bool shortCircuit, int& res, bool& err, TPpToken * ppToken);
+ int CPPif (TPpToken * ppToken);
+ int CPPifdef(int defined, TPpToken * ppToken);
+ int CPPinclude(TPpToken * ppToken);
+ int CPPline(TPpToken * ppToken);
+ int CPPerror(TPpToken * ppToken);
+ int CPPpragma(TPpToken * ppToken);
+ int CPPversion(TPpToken * ppToken);
+ int CPPextension(TPpToken * ppToken);
+ int readCPPline(TPpToken * ppToken);
+ int scanHeaderName(TPpToken* ppToken, char delimit);
+ TokenStream* PrescanMacroArg(TokenStream&, TPpToken*, bool newLineOkay);
+ MacroExpandResult MacroExpand(TPpToken* ppToken, bool expandUndef, bool newLineOkay);
+
+ //
+ // From PpTokens.cpp
+ //
+ void pushTokenStreamInput(TokenStream&, bool pasting = false);
+ void UngetToken(int token, TPpToken*);
+
+ class tTokenInput : public tInput {
+ public:
+ tTokenInput(TPpContext* pp, TokenStream* t, bool prepasting) :
+ tInput(pp),
+ tokens(t),
+ lastTokenPastes(prepasting) { }
+ virtual int scan(TPpToken *ppToken) override { return tokens->getToken(pp->parseContext, ppToken); }
+ virtual int getch() override { assert(0); return EndOfInput; }
+ virtual void ungetch() override { assert(0); }
+ virtual bool peekPasting() override { return tokens->peekTokenizedPasting(lastTokenPastes); }
+ bool peekContinuedPasting(int a) override { return tokens->peekContinuedPasting(a); }
+ protected:
+ TokenStream* tokens;
+ bool lastTokenPastes; // true if the last token in the input is to be pasted, rather than consumed as a token
+ };
+
+ class tUngotTokenInput : public tInput {
+ public:
+ tUngotTokenInput(TPpContext* pp, int t, TPpToken* p) : tInput(pp), token(t), lval(*p) { }
+ virtual int scan(TPpToken *) override;
+ virtual int getch() override { assert(0); return EndOfInput; }
+ virtual void ungetch() override { assert(0); }
+ protected:
+ int token;
+ TPpToken lval;
+ };
+
+ //
+ // From PpScanner.cpp
+ //
+ class tStringInput : public tInput {
+ public:
+ tStringInput(TPpContext* pp, TInputScanner& i) : tInput(pp), input(&i) { }
+ virtual int scan(TPpToken*) override;
+
+ // Scanner used to get source stream characters.
+ // - Escaped newlines are handled here, invisibly to the caller.
+ // - All forms of newline are handled, and turned into just a '\n'.
+ int getch() override
+ {
+ int ch = input->get();
+
+ if (ch == '\\') {
+ // Move past escaped newlines, as many as sequentially exist
+ do {
+ if (input->peek() == '\r' || input->peek() == '\n') {
+ bool allowed = pp->parseContext.lineContinuationCheck(input->getSourceLoc(), pp->inComment);
+ if (! allowed && pp->inComment)
+ return '\\';
+
+ // escape one newline now
+ ch = input->get();
+ int nextch = input->get();
+ if (ch == '\r' && nextch == '\n')
+ ch = input->get();
+ else
+ ch = nextch;
+ } else
+ return '\\';
+ } while (ch == '\\');
+ }
+
+ // handle any non-escaped newline
+ if (ch == '\r' || ch == '\n') {
+ if (ch == '\r' && input->peek() == '\n')
+ input->get();
+ return '\n';
+ }
+
+ return ch;
+ }
+
+ // Scanner used to backup the source stream characters. Newlines are
+ // handled here, invisibly to the caller, meaning have to undo exactly
+ // what getch() above does (e.g., don't leave things in the middle of a
+ // sequence of escaped newlines).
+ void ungetch() override
+ {
+ input->unget();
+
+ do {
+ int ch = input->peek();
+ if (ch == '\r' || ch == '\n') {
+ if (ch == '\n') {
+ // correct for two-character newline
+ input->unget();
+ if (input->peek() != '\r')
+ input->get();
+ }
+ // now in front of a complete newline, move past an escape character
+ input->unget();
+ if (input->peek() == '\\')
+ input->unget();
+ else {
+ input->get();
+ break;
+ }
+ } else
+ break;
+ } while (true);
+ }
+
+ protected:
+ TInputScanner* input;
+ };
+
+ // Holds a reference to included file data, as well as a
+ // prologue and an epilogue string. This can be scanned using the tInput
+ // interface and acts as a single source string.
+ class TokenizableIncludeFile : public tInput {
+ public:
+ // Copies prologue and epilogue. The includedFile must remain valid
+ // until this TokenizableIncludeFile is no longer used.
+ TokenizableIncludeFile(const TSourceLoc& startLoc,
+ const std::string& prologue,
+ TShader::Includer::IncludeResult* includedFile,
+ const std::string& epilogue,
+ TPpContext* pp)
+ : tInput(pp),
+ prologue_(prologue),
+ epilogue_(epilogue),
+ includedFile_(includedFile),
+ scanner(3, strings, lengths, nullptr, 0, 0, true),
+ prevScanner(nullptr),
+ stringInput(pp, scanner)
+ {
+ strings[0] = prologue_.data();
+ strings[1] = includedFile_->headerData;
+ strings[2] = epilogue_.data();
+
+ lengths[0] = prologue_.size();
+ lengths[1] = includedFile_->headerLength;
+ lengths[2] = epilogue_.size();
+
+ scanner.setLine(startLoc.line);
+ scanner.setString(startLoc.string);
+
+ scanner.setFile(startLoc.getFilenameStr(), 0);
+ scanner.setFile(startLoc.getFilenameStr(), 1);
+ scanner.setFile(startLoc.getFilenameStr(), 2);
+ }
+
+ // tInput methods:
+ int scan(TPpToken* t) override { return stringInput.scan(t); }
+ int getch() override { return stringInput.getch(); }
+ void ungetch() override { stringInput.ungetch(); }
+
+ void notifyActivated() override
+ {
+ prevScanner = pp->parseContext.getScanner();
+ pp->parseContext.setScanner(&scanner);
+ pp->push_include(includedFile_);
+ }
+
+ void notifyDeleted() override
+ {
+ pp->parseContext.setScanner(prevScanner);
+ pp->pop_include();
+ }
+
+ private:
+ TokenizableIncludeFile& operator=(const TokenizableIncludeFile&);
+
+ // Stores the prologue for this string.
+ const std::string prologue_;
+
+ // Stores the epilogue for this string.
+ const std::string epilogue_;
+
+ // Points to the IncludeResult that this TokenizableIncludeFile represents.
+ TShader::Includer::IncludeResult* includedFile_;
+
+ // Will point to prologue_, includedFile_->headerData and epilogue_
+ // This is passed to scanner constructor.
+ // These do not own the storage and it must remain valid until this
+ // object has been destroyed.
+ const char* strings[3];
+ // Length of str_, passed to scanner constructor.
+ size_t lengths[3];
+ // Scans over str_.
+ TInputScanner scanner;
+ // The previous effective scanner before the scanner in this instance
+ // has been activated.
+ TInputScanner* prevScanner;
+ // Delegate object implementing the tInput interface.
+ tStringInput stringInput;
+ };
+
+ int ScanFromString(char* s);
+ void missingEndifCheck();
+ int lFloatConst(int len, int ch, TPpToken* ppToken);
+ int characterLiteral(TPpToken* ppToken);
+
+ void push_include(TShader::Includer::IncludeResult* result)
+ {
+ currentSourceFile = result->headerName;
+ includeStack.push(result);
+ }
+
+ void pop_include()
+ {
+ TShader::Includer::IncludeResult* include = includeStack.top();
+ includeStack.pop();
+ includer.releaseInclude(include);
+ if (includeStack.empty()) {
+ currentSourceFile = rootFileName;
+ } else {
+ currentSourceFile = includeStack.top()->headerName;
+ }
+ }
+
+ bool inComment;
+ std::string rootFileName;
+ std::stack<TShader::Includer::IncludeResult*> includeStack;
+ std::string currentSourceFile;
+
+ std::istringstream strtodStream;
+};
+
+} // end namespace glslang
+
+#endif // PPCONTEXT_H
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp
new file mode 100644
index 0000000..f6f52d7
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp
@@ -0,0 +1,1246 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2013 LunarG, Inc.
+// Copyright (C) 2017 ARM Limited.
+// Copyright (C) 2015-2018 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+/****************************************************************************\
+Copyright (c) 2002, NVIDIA Corporation.
+
+NVIDIA Corporation("NVIDIA") supplies this software to you in
+consideration of your agreement to the following terms, and your use,
+installation, modification or redistribution of this NVIDIA software
+constitutes acceptance of these terms. If you do not agree with these
+terms, please do not use, install, modify or redistribute this NVIDIA
+software.
+
+In consideration of your agreement to abide by the following terms, and
+subject to these terms, NVIDIA grants you a personal, non-exclusive
+license, under NVIDIA's copyrights in this original NVIDIA software (the
+"NVIDIA Software"), to use, reproduce, modify and redistribute the
+NVIDIA Software, with or without modifications, in source and/or binary
+forms; provided that if you redistribute the NVIDIA Software, you must
+retain the copyright notice of NVIDIA, this notice and the following
+text and disclaimers in all such redistributions of the NVIDIA Software.
+Neither the name, trademarks, service marks nor logos of NVIDIA
+Corporation may be used to endorse or promote products derived from the
+NVIDIA Software without specific prior written permission from NVIDIA.
+Except as expressly stated in this notice, no other rights or licenses
+express or implied, are granted by NVIDIA herein, including but not
+limited to any patent rights that may be infringed by your derivative
+works or by other works in which the NVIDIA Software may be
+incorporated. No hardware is licensed hereunder.
+
+THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED,
+INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
+ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER
+PRODUCTS.
+
+IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT,
+INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY
+OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE
+NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT,
+TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+\****************************************************************************/
+
+#ifndef _CRT_SECURE_NO_WARNINGS
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include <cstdlib>
+#include <cstring>
+
+#include "PpContext.h"
+#include "PpTokens.h"
+#include "../Scan.h"
+
+namespace glslang {
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////// Floating point constants: /////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+//
+// Scan a single- or double-precision floating point constant.
+// Assumes that the scanner has seen at least one digit,
+// followed by either a decimal '.' or the letter 'e', or a
+// precision ending (e.g., F or LF).
+//
+// This is technically not correct, as the preprocessor should just
+// accept the numeric literal along with whatever suffix it has, but
+// currently, it stops on seeing a bad suffix, treating that as the
+// next token. This effects things like token pasting, where it is
+// relevant how many tokens something was broken into.
+//
+// See peekContinuedPasting().
+int TPpContext::lFloatConst(int len, int ch, TPpToken* ppToken)
+{
+ const auto saveName = [&](int ch) {
+ if (len <= MaxTokenLength)
+ ppToken->name[len++] = static_cast<char>(ch);
+ };
+
+ // find the range of non-zero digits before the decimal point
+ int startNonZero = 0;
+ while (startNonZero < len && ppToken->name[startNonZero] == '0')
+ ++startNonZero;
+ int endNonZero = len;
+ while (endNonZero > startNonZero && ppToken->name[endNonZero-1] == '0')
+ --endNonZero;
+ int numWholeNumberDigits = endNonZero - startNonZero;
+
+ // accumulate the range's value
+ bool fastPath = numWholeNumberDigits <= 15; // when the number gets too complex, set to false
+ unsigned long long wholeNumber = 0;
+ if (fastPath) {
+ for (int i = startNonZero; i < endNonZero; ++i)
+ wholeNumber = wholeNumber * 10 + (ppToken->name[i] - '0');
+ }
+ int decimalShift = len - endNonZero;
+
+ // Decimal point:
+ bool hasDecimalOrExponent = false;
+ if (ch == '.') {
+ hasDecimalOrExponent = true;
+ saveName(ch);
+ ch = getChar();
+ int firstDecimal = len;
+
+ // 1.#INF or -1.#INF
+ if (ch == '#' && (ifdepth > 0 || parseContext.intermediate.getSource() == EShSourceHlsl)) {
+ if ((len < 2) ||
+ (len == 2 && ppToken->name[0] != '1') ||
+ (len == 3 && ppToken->name[1] != '1' && !(ppToken->name[0] == '-' || ppToken->name[0] == '+')) ||
+ (len > 3))
+ parseContext.ppError(ppToken->loc, "unexpected use of", "#", "");
+ else {
+ // we have 1.# or -1.# or +1.#, check for 'INF'
+ if ((ch = getChar()) != 'I' ||
+ (ch = getChar()) != 'N' ||
+ (ch = getChar()) != 'F')
+ parseContext.ppError(ppToken->loc, "expected 'INF'", "#", "");
+ else {
+ // we have [+-].#INF, and we are targeting IEEE 754, so wrap it up:
+ saveName('I');
+ saveName('N');
+ saveName('F');
+ ppToken->name[len] = '\0';
+ if (ppToken->name[0] == '-')
+ ppToken->i64val = 0xfff0000000000000; // -Infinity
+ else
+ ppToken->i64val = 0x7ff0000000000000; // +Infinity
+ return PpAtomConstFloat;
+ }
+ }
+ }
+
+ // Consume leading-zero digits after the decimal point
+ while (ch == '0') {
+ saveName(ch);
+ ch = getChar();
+ }
+ int startNonZeroDecimal = len;
+ int endNonZeroDecimal = len;
+
+ // Consume remaining digits, up to the exponent
+ while (ch >= '0' && ch <= '9') {
+ saveName(ch);
+ if (ch != '0')
+ endNonZeroDecimal = len;
+ ch = getChar();
+ }
+
+ // Compute accumulation up to the last non-zero digit
+ if (endNonZeroDecimal > startNonZeroDecimal) {
+ numWholeNumberDigits += endNonZeroDecimal - endNonZero - 1; // don't include the "."
+ if (numWholeNumberDigits > 15)
+ fastPath = false;
+ if (fastPath) {
+ for (int i = endNonZero; i < endNonZeroDecimal; ++i) {
+ if (ppToken->name[i] != '.')
+ wholeNumber = wholeNumber * 10 + (ppToken->name[i] - '0');
+ }
+ }
+ decimalShift = firstDecimal - endNonZeroDecimal;
+ }
+ }
+
+ // Exponent:
+ bool negativeExponent = false;
+ double exponentValue = 0.0;
+ int exponent = 0;
+ {
+ if (ch == 'e' || ch == 'E') {
+ hasDecimalOrExponent = true;
+ saveName(ch);
+ ch = getChar();
+ if (ch == '+' || ch == '-') {
+ negativeExponent = ch == '-';
+ saveName(ch);
+ ch = getChar();
+ }
+ if (ch >= '0' && ch <= '9') {
+ while (ch >= '0' && ch <= '9') {
+ exponent = exponent * 10 + (ch - '0');
+ saveName(ch);
+ ch = getChar();
+ }
+ } else {
+ parseContext.ppError(ppToken->loc, "bad character in float exponent", "", "");
+ }
+ }
+
+ // Compensate for location of decimal
+ if (negativeExponent)
+ exponent -= decimalShift;
+ else {
+ exponent += decimalShift;
+ if (exponent < 0) {
+ negativeExponent = true;
+ exponent = -exponent;
+ }
+ }
+ if (exponent > 22)
+ fastPath = false;
+
+ if (fastPath) {
+ // Compute the floating-point value of the exponent
+ exponentValue = 1.0;
+ if (exponent > 0) {
+ double expFactor = 10;
+ while (exponent > 0) {
+ if (exponent & 0x1)
+ exponentValue *= expFactor;
+ expFactor *= expFactor;
+ exponent >>= 1;
+ }
+ }
+ }
+ }
+
+ // Suffix:
+ bool isDouble = false;
+ bool isFloat16 = false;
+ if (ch == 'l' || ch == 'L') {
+ if (ifdepth == 0 && parseContext.intermediate.getSource() == EShSourceGlsl)
+ parseContext.doubleCheck(ppToken->loc, "double floating-point suffix");
+ if (ifdepth == 0 && !hasDecimalOrExponent)
+ parseContext.ppError(ppToken->loc, "float literal needs a decimal point or exponent", "", "");
+ if (parseContext.intermediate.getSource() == EShSourceGlsl) {
+ int ch2 = getChar();
+ if (ch2 != 'f' && ch2 != 'F') {
+ ungetChar();
+ ungetChar();
+ } else {
+ saveName(ch);
+ saveName(ch2);
+ isDouble = true;
+ }
+ } else if (parseContext.intermediate.getSource() == EShSourceHlsl) {
+ saveName(ch);
+ isDouble = true;
+ }
+ } else if (ch == 'h' || ch == 'H') {
+ if (ifdepth == 0 && parseContext.intermediate.getSource() == EShSourceGlsl)
+ parseContext.float16Check(ppToken->loc, "half floating-point suffix");
+ if (ifdepth == 0 && !hasDecimalOrExponent)
+ parseContext.ppError(ppToken->loc, "float literal needs a decimal point or exponent", "", "");
+ if (parseContext.intermediate.getSource() == EShSourceGlsl) {
+ int ch2 = getChar();
+ if (ch2 != 'f' && ch2 != 'F') {
+ ungetChar();
+ ungetChar();
+ } else {
+ saveName(ch);
+ saveName(ch2);
+ isFloat16 = true;
+ }
+ } else if (parseContext.intermediate.getSource() == EShSourceHlsl) {
+ saveName(ch);
+ isFloat16 = true;
+ }
+ } else if (ch == 'f' || ch == 'F') {
+ if (ifdepth == 0)
+ parseContext.profileRequires(ppToken->loc, EEsProfile, 300, nullptr, "floating-point suffix");
+ if (ifdepth == 0 && !parseContext.relaxedErrors())
+ parseContext.profileRequires(ppToken->loc, ~EEsProfile, 120, nullptr, "floating-point suffix");
+ if (ifdepth == 0 && !hasDecimalOrExponent)
+ parseContext.ppError(ppToken->loc, "float literal needs a decimal point or exponent", "", "");
+ saveName(ch);
+ } else
+ ungetChar();
+
+ // Patch up the name and length for overflow
+
+ if (len > MaxTokenLength) {
+ len = MaxTokenLength;
+ parseContext.ppError(ppToken->loc, "float literal too long", "", "");
+ }
+ ppToken->name[len] = '\0';
+
+ // Compute the numerical value
+ if (fastPath) {
+ // compute the floating-point value of the exponent
+ if (exponentValue == 0.0)
+ ppToken->dval = (double)wholeNumber;
+ else if (negativeExponent)
+ ppToken->dval = (double)wholeNumber / exponentValue;
+ else
+ ppToken->dval = (double)wholeNumber * exponentValue;
+ } else {
+ // slow path
+ ppToken->dval = 0.0;
+
+ // remove suffix
+ TString numstr(ppToken->name);
+ if (numstr.back() == 'f' || numstr.back() == 'F')
+ numstr.pop_back();
+ if (numstr.back() == 'h' || numstr.back() == 'H')
+ numstr.pop_back();
+ if (numstr.back() == 'l' || numstr.back() == 'L')
+ numstr.pop_back();
+
+ // use platform library
+ strtodStream.clear();
+ strtodStream.str(numstr.c_str());
+ strtodStream >> ppToken->dval;
+ if (strtodStream.fail()) {
+ // Assume failure combined with a large exponent was overflow, in
+ // an attempt to set INF.
+ if (!negativeExponent && exponent + numWholeNumberDigits > 300)
+ ppToken->i64val = 0x7ff0000000000000; // +Infinity
+ // Assume failure combined with a small exponent was overflow.
+ if (negativeExponent && exponent + numWholeNumberDigits > 300)
+ ppToken->dval = 0.0;
+ // Unknown reason for failure. Theory is that either
+ // - the 0.0 is still there, or
+ // - something reasonable was written that is better than 0.0
+ }
+ }
+
+ // Return the right token type
+ if (isDouble)
+ return PpAtomConstDouble;
+ else if (isFloat16)
+ return PpAtomConstFloat16;
+ else
+ return PpAtomConstFloat;
+}
+
+// Recognize a character literal.
+//
+// The first ' has already been accepted, read the rest, through the closing '.
+//
+// Always returns PpAtomConstInt.
+//
+int TPpContext::characterLiteral(TPpToken* ppToken)
+{
+ ppToken->name[0] = 0;
+ ppToken->ival = 0;
+
+ if (parseContext.intermediate.getSource() != EShSourceHlsl) {
+ // illegal, except in macro definition, for which case we report the character
+ return '\'';
+ }
+
+ int ch = getChar();
+ switch (ch) {
+ case '\'':
+ // As empty sequence: ''
+ parseContext.ppError(ppToken->loc, "unexpected", "\'", "");
+ return PpAtomConstInt;
+ case '\\':
+ // As escape sequence: '\XXX'
+ switch (ch = getChar()) {
+ case 'a':
+ ppToken->ival = 7;
+ break;
+ case 'b':
+ ppToken->ival = 8;
+ break;
+ case 't':
+ ppToken->ival = 9;
+ break;
+ case 'n':
+ ppToken->ival = 10;
+ break;
+ case 'v':
+ ppToken->ival = 11;
+ break;
+ case 'f':
+ ppToken->ival = 12;
+ break;
+ case 'r':
+ ppToken->ival = 13;
+ break;
+ case 'x':
+ case '0':
+ parseContext.ppError(ppToken->loc, "octal and hex sequences not supported", "\\", "");
+ break;
+ default:
+ // This catches '\'', '\"', '\?', etc.
+ // Also, things like '\C' mean the same thing as 'C'
+ // (after the above cases are filtered out).
+ ppToken->ival = ch;
+ break;
+ }
+ break;
+ default:
+ ppToken->ival = ch;
+ break;
+ }
+ ppToken->name[0] = (char)ppToken->ival;
+ ppToken->name[1] = '\0';
+ ch = getChar();
+ if (ch != '\'') {
+ parseContext.ppError(ppToken->loc, "expected", "\'", "");
+ // Look ahead for a closing '
+ do {
+ ch = getChar();
+ } while (ch != '\'' && ch != EndOfInput && ch != '\n');
+ }
+
+ return PpAtomConstInt;
+}
+
+//
+// Scanner used to tokenize source stream.
+//
+// N.B. Invalid numeric suffixes are not consumed.//
+// This is technically not correct, as the preprocessor should just
+// accept the numeric literal along with whatever suffix it has, but
+// currently, it stops on seeing a bad suffix, treating that as the
+// next token. This effects things like token pasting, where it is
+// relevant how many tokens something was broken into.
+// See peekContinuedPasting().
+//
+int TPpContext::tStringInput::scan(TPpToken* ppToken)
+{
+ int AlreadyComplained = 0;
+ int len = 0;
+ int ch = 0;
+ int ii = 0;
+ unsigned long long ival = 0;
+ const auto floatingPointChar = [&](int ch) { return ch == '.' || ch == 'e' || ch == 'E' ||
+ ch == 'f' || ch == 'F' ||
+ ch == 'h' || ch == 'H'; };
+
+ static const char* const Int64_Extensions[] = {
+ E_GL_ARB_gpu_shader_int64,
+ E_GL_EXT_shader_explicit_arithmetic_types,
+ E_GL_EXT_shader_explicit_arithmetic_types_int64 };
+ static const int Num_Int64_Extensions = sizeof(Int64_Extensions) / sizeof(Int64_Extensions[0]);
+
+ static const char* const Int16_Extensions[] = {
+#ifdef AMD_EXTENSIONS
+ E_GL_AMD_gpu_shader_int16,
+#endif
+ E_GL_EXT_shader_explicit_arithmetic_types,
+ E_GL_EXT_shader_explicit_arithmetic_types_int16 };
+ static const int Num_Int16_Extensions = sizeof(Int16_Extensions) / sizeof(Int16_Extensions[0]);
+
+ ppToken->ival = 0;
+ ppToken->i64val = 0;
+ ppToken->space = false;
+ ch = getch();
+ for (;;) {
+ while (ch == ' ' || ch == '\t') {
+ ppToken->space = true;
+ ch = getch();
+ }
+
+ ppToken->loc = pp->parseContext.getCurrentLoc();
+ len = 0;
+ switch (ch) {
+ default:
+ // Single character token, including EndOfInput, '#' and '\' (escaped newlines are handled at a lower level, so this is just a '\' token)
+ if (ch > PpAtomMaxSingle)
+ ch = PpAtomBadToken;
+ return ch;
+
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F': case 'G': case 'H': case 'I': case 'J':
+ case 'K': case 'L': case 'M': case 'N': case 'O':
+ case 'P': case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X': case 'Y':
+ case 'Z': case '_':
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f': case 'g': case 'h': case 'i': case 'j':
+ case 'k': case 'l': case 'm': case 'n': case 'o':
+ case 'p': case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x': case 'y':
+ case 'z':
+ do {
+ if (len < MaxTokenLength) {
+ ppToken->name[len++] = (char)ch;
+ ch = getch();
+ } else {
+ if (! AlreadyComplained) {
+ pp->parseContext.ppError(ppToken->loc, "name too long", "", "");
+ AlreadyComplained = 1;
+ }
+ ch = getch();
+ }
+ } while ((ch >= 'a' && ch <= 'z') ||
+ (ch >= 'A' && ch <= 'Z') ||
+ (ch >= '0' && ch <= '9') ||
+ ch == '_');
+
+ // line continuation with no token before or after makes len == 0, and need to start over skipping white space, etc.
+ if (len == 0)
+ continue;
+
+ ppToken->name[len] = '\0';
+ ungetch();
+ return PpAtomIdentifier;
+ case '0':
+ ppToken->name[len++] = (char)ch;
+ ch = getch();
+ if (ch == 'x' || ch == 'X') {
+ // must be hexadecimal
+
+ bool isUnsigned = false;
+ bool isInt64 = false;
+ bool isInt16 = false;
+ ppToken->name[len++] = (char)ch;
+ ch = getch();
+ if ((ch >= '0' && ch <= '9') ||
+ (ch >= 'A' && ch <= 'F') ||
+ (ch >= 'a' && ch <= 'f')) {
+
+ ival = 0;
+ do {
+ if (len < MaxTokenLength && ival <= 0x0fffffffffffffffull) {
+ ppToken->name[len++] = (char)ch;
+ if (ch >= '0' && ch <= '9') {
+ ii = ch - '0';
+ } else if (ch >= 'A' && ch <= 'F') {
+ ii = ch - 'A' + 10;
+ } else if (ch >= 'a' && ch <= 'f') {
+ ii = ch - 'a' + 10;
+ } else
+ pp->parseContext.ppError(ppToken->loc, "bad digit in hexadecimal literal", "", "");
+ ival = (ival << 4) | ii;
+ } else {
+ if (! AlreadyComplained) {
+ if(len < MaxTokenLength)
+ pp->parseContext.ppError(ppToken->loc, "hexadecimal literal too big", "", "");
+ else
+ pp->parseContext.ppError(ppToken->loc, "hexadecimal literal too long", "", "");
+ AlreadyComplained = 1;
+ }
+ ival = 0xffffffffffffffffull;
+ }
+ ch = getch();
+ } while ((ch >= '0' && ch <= '9') ||
+ (ch >= 'A' && ch <= 'F') ||
+ (ch >= 'a' && ch <= 'f'));
+ } else {
+ pp->parseContext.ppError(ppToken->loc, "bad digit in hexadecimal literal", "", "");
+ }
+ if (ch == 'u' || ch == 'U') {
+ if (len < MaxTokenLength)
+ ppToken->name[len++] = (char)ch;
+ isUnsigned = true;
+
+ int nextCh = getch();
+ if (nextCh == 'l' || nextCh == 'L') {
+ if (len < MaxTokenLength)
+ ppToken->name[len++] = (char)nextCh;
+ isInt64 = true;
+ } else
+ ungetch();
+
+#ifdef AMD_EXTENSIONS
+ nextCh = getch();
+ if ((nextCh == 's' || nextCh == 'S') &&
+ pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
+ if (len < MaxTokenLength)
+ ppToken->name[len++] = (char)nextCh;
+ isInt16 = true;
+ } else
+ ungetch();
+#endif
+ } else if (ch == 'l' || ch == 'L') {
+ if (len < MaxTokenLength)
+ ppToken->name[len++] = (char)ch;
+ isInt64 = true;
+#ifdef AMD_EXTENSIONS
+ } else if ((ch == 's' || ch == 'S') &&
+ pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
+ if (len < MaxTokenLength)
+ ppToken->name[len++] = (char)ch;
+ isInt16 = true;
+#endif
+ } else
+ ungetch();
+ ppToken->name[len] = '\0';
+
+ if (isInt64 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
+ if (pp->ifdepth == 0) {
+ pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile,
+ "64-bit hexadecimal literal");
+ pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0,
+ Num_Int64_Extensions, Int64_Extensions, "64-bit hexadecimal literal");
+ }
+ ppToken->i64val = ival;
+ return isUnsigned ? PpAtomConstUint64 : PpAtomConstInt64;
+ } else if (isInt16) {
+ if (pp->ifdepth == 0) {
+ if (pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
+ pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile,
+ "16-bit hexadecimal literal");
+ pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0,
+ Num_Int16_Extensions, Int16_Extensions, "16-bit hexadecimal literal");
+ }
+ }
+ ppToken->ival = (int)ival;
+ return isUnsigned ? PpAtomConstUint16 : PpAtomConstInt16;
+ } else {
+ if (ival > 0xffffffffu && !AlreadyComplained)
+ pp->parseContext.ppError(ppToken->loc, "hexadecimal literal too big", "", "");
+ ppToken->ival = (int)ival;
+ return isUnsigned ? PpAtomConstUint : PpAtomConstInt;
+ }
+ } else {
+ // could be octal integer or floating point, speculative pursue octal until it must be floating point
+
+ bool isUnsigned = false;
+ bool isInt64 = false;
+ bool isInt16 = false;
+ bool octalOverflow = false;
+ bool nonOctal = false;
+ ival = 0;
+
+ // see how much octal-like stuff we can read
+ while (ch >= '0' && ch <= '7') {
+ if (len < MaxTokenLength)
+ ppToken->name[len++] = (char)ch;
+ else if (! AlreadyComplained) {
+ pp->parseContext.ppError(ppToken->loc, "numeric literal too long", "", "");
+ AlreadyComplained = 1;
+ }
+ if (ival <= 0x1fffffffffffffffull) {
+ ii = ch - '0';
+ ival = (ival << 3) | ii;
+ } else
+ octalOverflow = true;
+ ch = getch();
+ }
+
+ // could be part of a float...
+ if (ch == '8' || ch == '9') {
+ nonOctal = true;
+ do {
+ if (len < MaxTokenLength)
+ ppToken->name[len++] = (char)ch;
+ else if (! AlreadyComplained) {
+ pp->parseContext.ppError(ppToken->loc, "numeric literal too long", "", "");
+ AlreadyComplained = 1;
+ }
+ ch = getch();
+ } while (ch >= '0' && ch <= '9');
+ }
+ if (floatingPointChar(ch))
+ return pp->lFloatConst(len, ch, ppToken);
+
+ // wasn't a float, so must be octal...
+ if (nonOctal)
+ pp->parseContext.ppError(ppToken->loc, "octal literal digit too large", "", "");
+
+ if (ch == 'u' || ch == 'U') {
+ if (len < MaxTokenLength)
+ ppToken->name[len++] = (char)ch;
+ isUnsigned = true;
+
+ int nextCh = getch();
+ if (nextCh == 'l' || nextCh == 'L') {
+ if (len < MaxTokenLength)
+ ppToken->name[len++] = (char)nextCh;
+ isInt64 = true;
+ } else
+ ungetch();
+
+#ifdef AMD_EXTENSIONS
+ nextCh = getch();
+ if ((nextCh == 's' || nextCh == 'S') &&
+ pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
+ if (len < MaxTokenLength)
+ ppToken->name[len++] = (char)nextCh;
+ isInt16 = true;
+ } else
+ ungetch();
+#endif
+ } else if (ch == 'l' || ch == 'L') {
+ if (len < MaxTokenLength)
+ ppToken->name[len++] = (char)ch;
+ isInt64 = true;
+#ifdef AMD_EXTENSIONS
+ } else if ((ch == 's' || ch == 'S') &&
+ pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
+ if (len < MaxTokenLength)
+ ppToken->name[len++] = (char)ch;
+ isInt16 = true;
+#endif
+ } else
+ ungetch();
+ ppToken->name[len] = '\0';
+
+ if (!isInt64 && ival > 0xffffffffu)
+ octalOverflow = true;
+
+ if (octalOverflow)
+ pp->parseContext.ppError(ppToken->loc, "octal literal too big", "", "");
+
+ if (isInt64 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
+ if (pp->ifdepth == 0) {
+ pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile,
+ "64-bit octal literal");
+ pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0,
+ Num_Int64_Extensions, Int64_Extensions, "64-bit octal literal");
+ }
+ ppToken->i64val = ival;
+ return isUnsigned ? PpAtomConstUint64 : PpAtomConstInt64;
+ } else if (isInt16) {
+ if (pp->ifdepth == 0) {
+ if (pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
+ pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile,
+ "16-bit octal literal");
+ pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0,
+ Num_Int16_Extensions, Int16_Extensions, "16-bit octal literal");
+ }
+ }
+ ppToken->ival = (int)ival;
+ return isUnsigned ? PpAtomConstUint16 : PpAtomConstInt16;
+ } else {
+ ppToken->ival = (int)ival;
+ return isUnsigned ? PpAtomConstUint : PpAtomConstInt;
+ }
+ }
+ break;
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ // can't be hexadecimal or octal, is either decimal or floating point
+
+ do {
+ if (len < MaxTokenLength)
+ ppToken->name[len++] = (char)ch;
+ else if (! AlreadyComplained) {
+ pp->parseContext.ppError(ppToken->loc, "numeric literal too long", "", "");
+ AlreadyComplained = 1;
+ }
+ ch = getch();
+ } while (ch >= '0' && ch <= '9');
+ if (floatingPointChar(ch))
+ return pp->lFloatConst(len, ch, ppToken);
+ else {
+ // Finish handling signed and unsigned integers
+ int numericLen = len;
+ bool isUnsigned = false;
+ bool isInt64 = false;
+ bool isInt16 = false;
+ if (ch == 'u' || ch == 'U') {
+ if (len < MaxTokenLength)
+ ppToken->name[len++] = (char)ch;
+ isUnsigned = true;
+
+ int nextCh = getch();
+ if (nextCh == 'l' || nextCh == 'L') {
+ if (len < MaxTokenLength)
+ ppToken->name[len++] = (char)nextCh;
+ isInt64 = true;
+ } else
+ ungetch();
+
+#ifdef AMD_EXTENSIONS
+ nextCh = getch();
+ if ((nextCh == 's' || nextCh == 'S') &&
+ pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
+ if (len < MaxTokenLength)
+ ppToken->name[len++] = (char)nextCh;
+ isInt16 = true;
+ } else
+ ungetch();
+#endif
+ } else if (ch == 'l' || ch == 'L') {
+ if (len < MaxTokenLength)
+ ppToken->name[len++] = (char)ch;
+ isInt64 = true;
+#ifdef AMD_EXTENSIONS
+ } else if ((ch == 's' || ch == 'S') &&
+ pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
+ if (len < MaxTokenLength)
+ ppToken->name[len++] = (char)ch;
+ isInt16 = true;
+#endif
+ } else
+ ungetch();
+
+ ppToken->name[len] = '\0';
+ ival = 0;
+ const unsigned oneTenthMaxInt = 0xFFFFFFFFu / 10;
+ const unsigned remainderMaxInt = 0xFFFFFFFFu - 10 * oneTenthMaxInt;
+ const unsigned long long oneTenthMaxInt64 = 0xFFFFFFFFFFFFFFFFull / 10;
+ const unsigned long long remainderMaxInt64 = 0xFFFFFFFFFFFFFFFFull - 10 * oneTenthMaxInt64;
+ const unsigned short oneTenthMaxInt16 = 0xFFFFu / 10;
+ const unsigned short remainderMaxInt16 = 0xFFFFu - 10 * oneTenthMaxInt16;
+ for (int i = 0; i < numericLen; i++) {
+ ch = ppToken->name[i] - '0';
+ bool overflow = false;
+ if (isInt64)
+ overflow = (ival > oneTenthMaxInt64 || (ival == oneTenthMaxInt64 && (unsigned long long)ch > remainderMaxInt64));
+ else if (isInt16)
+ overflow = (ival > oneTenthMaxInt16 || (ival == oneTenthMaxInt16 && (unsigned short)ch > remainderMaxInt16));
+ else
+ overflow = (ival > oneTenthMaxInt || (ival == oneTenthMaxInt && (unsigned)ch > remainderMaxInt));
+ if (overflow) {
+ pp->parseContext.ppError(ppToken->loc, "numeric literal too big", "", "");
+ ival = 0xFFFFFFFFFFFFFFFFull;
+ break;
+ } else
+ ival = ival * 10 + ch;
+ }
+
+ if (isInt64 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
+ if (pp->ifdepth == 0) {
+ pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile,
+ "64-bit literal");
+ pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0,
+ Num_Int64_Extensions, Int64_Extensions, "64-bit literal");
+ }
+ ppToken->i64val = ival;
+ return isUnsigned ? PpAtomConstUint64 : PpAtomConstInt64;
+ } else if (isInt16) {
+ if (pp->ifdepth == 0 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) {
+ pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile,
+ "16-bit literal");
+ pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0,
+ Num_Int16_Extensions, Int16_Extensions, "16-bit literal");
+ }
+ ppToken->ival = (int)ival;
+ return isUnsigned ? PpAtomConstUint16 : PpAtomConstInt16;
+ } else {
+ ppToken->ival = (int)ival;
+ return isUnsigned ? PpAtomConstUint : PpAtomConstInt;
+ }
+ }
+ break;
+ case '-':
+ ch = getch();
+ if (ch == '-') {
+ return PpAtomDecrement;
+ } else if (ch == '=') {
+ return PPAtomSubAssign;
+ } else {
+ ungetch();
+ return '-';
+ }
+ case '+':
+ ch = getch();
+ if (ch == '+') {
+ return PpAtomIncrement;
+ } else if (ch == '=') {
+ return PPAtomAddAssign;
+ } else {
+ ungetch();
+ return '+';
+ }
+ case '*':
+ ch = getch();
+ if (ch == '=') {
+ return PPAtomMulAssign;
+ } else {
+ ungetch();
+ return '*';
+ }
+ case '%':
+ ch = getch();
+ if (ch == '=') {
+ return PPAtomModAssign;
+ } else {
+ ungetch();
+ return '%';
+ }
+ case '^':
+ ch = getch();
+ if (ch == '^') {
+ return PpAtomXor;
+ } else {
+ if (ch == '=')
+ return PpAtomXorAssign;
+ else{
+ ungetch();
+ return '^';
+ }
+ }
+
+ case '=':
+ ch = getch();
+ if (ch == '=') {
+ return PpAtomEQ;
+ } else {
+ ungetch();
+ return '=';
+ }
+ case '!':
+ ch = getch();
+ if (ch == '=') {
+ return PpAtomNE;
+ } else {
+ ungetch();
+ return '!';
+ }
+ case '|':
+ ch = getch();
+ if (ch == '|') {
+ return PpAtomOr;
+ } else if (ch == '=') {
+ return PpAtomOrAssign;
+ } else {
+ ungetch();
+ return '|';
+ }
+ case '&':
+ ch = getch();
+ if (ch == '&') {
+ return PpAtomAnd;
+ } else if (ch == '=') {
+ return PpAtomAndAssign;
+ } else {
+ ungetch();
+ return '&';
+ }
+ case '<':
+ ch = getch();
+ if (ch == '<') {
+ ch = getch();
+ if (ch == '=')
+ return PpAtomLeftAssign;
+ else {
+ ungetch();
+ return PpAtomLeft;
+ }
+ } else if (ch == '=') {
+ return PpAtomLE;
+ } else {
+ ungetch();
+ return '<';
+ }
+ case '>':
+ ch = getch();
+ if (ch == '>') {
+ ch = getch();
+ if (ch == '=')
+ return PpAtomRightAssign;
+ else {
+ ungetch();
+ return PpAtomRight;
+ }
+ } else if (ch == '=') {
+ return PpAtomGE;
+ } else {
+ ungetch();
+ return '>';
+ }
+ case '.':
+ ch = getch();
+ if (ch >= '0' && ch <= '9') {
+ ungetch();
+ return pp->lFloatConst(0, '.', ppToken);
+ } else {
+ ungetch();
+ return '.';
+ }
+ case '/':
+ ch = getch();
+ if (ch == '/') {
+ pp->inComment = true;
+ do {
+ ch = getch();
+ } while (ch != '\n' && ch != EndOfInput);
+ ppToken->space = true;
+ pp->inComment = false;
+
+ return ch;
+ } else if (ch == '*') {
+ ch = getch();
+ do {
+ while (ch != '*') {
+ if (ch == EndOfInput) {
+ pp->parseContext.ppError(ppToken->loc, "End of input in comment", "comment", "");
+ return ch;
+ }
+ ch = getch();
+ }
+ ch = getch();
+ if (ch == EndOfInput) {
+ pp->parseContext.ppError(ppToken->loc, "End of input in comment", "comment", "");
+ return ch;
+ }
+ } while (ch != '/');
+ ppToken->space = true;
+ // loop again to get the next token...
+ break;
+ } else if (ch == '=') {
+ return PPAtomDivAssign;
+ } else {
+ ungetch();
+ return '/';
+ }
+ break;
+ case '\'':
+ return pp->characterLiteral(ppToken);
+ case '"':
+ // TODO: If this gets enhanced to handle escape sequences, or
+ // anything that is different than what #include needs, then
+ // #include needs to use scanHeaderName() for this.
+ ch = getch();
+ while (ch != '"' && ch != '\n' && ch != EndOfInput) {
+ if (len < MaxTokenLength) {
+ ppToken->name[len] = (char)ch;
+ len++;
+ ch = getch();
+ } else
+ break;
+ };
+ ppToken->name[len] = '\0';
+ if (ch != '"') {
+ ungetch();
+ pp->parseContext.ppError(ppToken->loc, "End of line in string", "string", "");
+ }
+ return PpAtomConstString;
+ case ':':
+ ch = getch();
+ if (ch == ':')
+ return PpAtomColonColon;
+ ungetch();
+ return ':';
+ }
+
+ ch = getch();
+ }
+}
+
+//
+// The main functional entry point into the preprocessor, which will
+// scan the source strings to figure out and return the next processing token.
+//
+// Return the token, or EndOfInput when no more tokens.
+//
+int TPpContext::tokenize(TPpToken& ppToken)
+{
+ for(;;) {
+ int token = scanToken(&ppToken);
+
+ // Handle token-pasting logic
+ token = tokenPaste(token, ppToken);
+
+ if (token == EndOfInput) {
+ missingEndifCheck();
+ return EndOfInput;
+ }
+ if (token == '#') {
+ if (previous_token == '\n') {
+ token = readCPPline(&ppToken);
+ if (token == EndOfInput) {
+ missingEndifCheck();
+ return EndOfInput;
+ }
+ continue;
+ } else {
+ parseContext.ppError(ppToken.loc, "preprocessor directive cannot be preceded by another token", "#", "");
+ return EndOfInput;
+ }
+ }
+ previous_token = token;
+
+ if (token == '\n')
+ continue;
+
+ // expand macros
+ if (token == PpAtomIdentifier) {
+ switch (MacroExpand(&ppToken, false, true)) {
+ case MacroExpandNotStarted:
+ break;
+ case MacroExpandError:
+ return EndOfInput;
+ case MacroExpandStarted:
+ case MacroExpandUndef:
+ continue;
+ }
+ }
+
+ switch (token) {
+ case PpAtomIdentifier:
+ case PpAtomConstInt:
+ case PpAtomConstUint:
+ case PpAtomConstFloat:
+ case PpAtomConstInt64:
+ case PpAtomConstUint64:
+ case PpAtomConstInt16:
+ case PpAtomConstUint16:
+ case PpAtomConstDouble:
+ case PpAtomConstFloat16:
+ if (ppToken.name[0] == '\0')
+ continue;
+ break;
+ case PpAtomConstString:
+ if (ifdepth == 0 && parseContext.intermediate.getSource() != EShSourceHlsl) {
+ // HLSL allows string literals.
+ parseContext.ppError(ppToken.loc, "string literals not supported", "\"\"", "");
+ continue;
+ }
+ break;
+ case '\'':
+ parseContext.ppError(ppToken.loc, "character literals not supported", "\'", "");
+ continue;
+ default:
+ snprintf(ppToken.name, sizeof(ppToken.name), "%s", atomStrings.getString(token));
+ break;
+ }
+
+ return token;
+ }
+}
+
+//
+// Do all token-pasting related combining of two pasted tokens when getting a
+// stream of tokens from a replacement list. Degenerates to no processing if a
+// replacement list is not the source of the token stream.
+//
+int TPpContext::tokenPaste(int token, TPpToken& ppToken)
+{
+ // starting with ## is illegal, skip to next token
+ if (token == PpAtomPaste) {
+ parseContext.ppError(ppToken.loc, "unexpected location", "##", "");
+ return scanToken(&ppToken);
+ }
+
+ int resultToken = token; // "foo" pasted with "35" is an identifier, not a number
+
+ // ## can be chained, process all in the chain at once
+ while (peekPasting()) {
+ TPpToken pastedPpToken;
+
+ // next token has to be ##
+ token = scanToken(&pastedPpToken);
+ assert(token == PpAtomPaste);
+
+ // This covers end of macro expansion
+ if (endOfReplacementList()) {
+ parseContext.ppError(ppToken.loc, "unexpected location; end of replacement list", "##", "");
+ break;
+ }
+
+ // Get the token(s) after the ##.
+ // Because of "space" semantics, and prior tokenization, what
+ // appeared a single token, e.g. "3A", might have been tokenized
+ // into two tokens "3" and "A", but the "A" will have 'space' set to
+ // false. Accumulate all of these to recreate the original lexical
+ // appearing token.
+ do {
+ token = scanToken(&pastedPpToken);
+
+ // This covers end of argument expansion
+ if (token == tMarkerInput::marker) {
+ parseContext.ppError(ppToken.loc, "unexpected location; end of argument", "##", "");
+ return resultToken;
+ }
+
+ // get the token text
+ switch (resultToken) {
+ case PpAtomIdentifier:
+ // already have the correct text in token.names
+ break;
+ case '=':
+ case '!':
+ case '-':
+ case '~':
+ case '+':
+ case '*':
+ case '/':
+ case '%':
+ case '<':
+ case '>':
+ case '|':
+ case '^':
+ case '&':
+ case PpAtomRight:
+ case PpAtomLeft:
+ case PpAtomAnd:
+ case PpAtomOr:
+ case PpAtomXor:
+ snprintf(ppToken.name, sizeof(ppToken.name), "%s", atomStrings.getString(resultToken));
+ snprintf(pastedPpToken.name, sizeof(pastedPpToken.name), "%s", atomStrings.getString(token));
+ break;
+ default:
+ parseContext.ppError(ppToken.loc, "not supported for these tokens", "##", "");
+ return resultToken;
+ }
+
+ // combine the tokens
+ if (strlen(ppToken.name) + strlen(pastedPpToken.name) > MaxTokenLength) {
+ parseContext.ppError(ppToken.loc, "combined tokens are too long", "##", "");
+ return resultToken;
+ }
+ snprintf(&ppToken.name[0] + strlen(ppToken.name), sizeof(ppToken.name) - strlen(ppToken.name),
+ "%s", pastedPpToken.name);
+
+ // correct the kind of token we are making, if needed (identifiers stay identifiers)
+ if (resultToken != PpAtomIdentifier) {
+ int newToken = atomStrings.getAtom(ppToken.name);
+ if (newToken > 0)
+ resultToken = newToken;
+ else
+ parseContext.ppError(ppToken.loc, "combined token is invalid", "##", "");
+ }
+ } while (peekContinuedPasting(resultToken));
+ }
+
+ return resultToken;
+}
+
+// Checks if we've seen balanced #if...#endif
+void TPpContext::missingEndifCheck()
+{
+ if (ifdepth > 0)
+ parseContext.ppError(parseContext.getCurrentLoc(), "missing #endif", "", "");
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp
new file mode 100644
index 0000000..ac9d8ac
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp
@@ -0,0 +1,219 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2013 LunarG, Inc.
+// Copyright (C) 2015-2018 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+/****************************************************************************\
+Copyright (c) 2002, NVIDIA Corporation.
+
+NVIDIA Corporation("NVIDIA") supplies this software to you in
+consideration of your agreement to the following terms, and your use,
+installation, modification or redistribution of this NVIDIA software
+constitutes acceptance of these terms. If you do not agree with these
+terms, please do not use, install, modify or redistribute this NVIDIA
+software.
+
+In consideration of your agreement to abide by the following terms, and
+subject to these terms, NVIDIA grants you a personal, non-exclusive
+license, under NVIDIA's copyrights in this original NVIDIA software (the
+"NVIDIA Software"), to use, reproduce, modify and redistribute the
+NVIDIA Software, with or without modifications, in source and/or binary
+forms; provided that if you redistribute the NVIDIA Software, you must
+retain the copyright notice of NVIDIA, this notice and the following
+text and disclaimers in all such redistributions of the NVIDIA Software.
+Neither the name, trademarks, service marks nor logos of NVIDIA
+Corporation may be used to endorse or promote products derived from the
+NVIDIA Software without specific prior written permission from NVIDIA.
+Except as expressly stated in this notice, no other rights or licenses
+express or implied, are granted by NVIDIA herein, including but not
+limited to any patent rights that may be infringed by your derivative
+works or by other works in which the NVIDIA Software may be
+incorporated. No hardware is licensed hereunder.
+
+THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED,
+INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
+ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER
+PRODUCTS.
+
+IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT,
+INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY
+OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE
+NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT,
+TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+\****************************************************************************/
+
+//
+// For recording and playing back the stream of tokens in a macro definition.
+//
+
+#ifndef _CRT_SECURE_NO_WARNINGS
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+#if (defined(_MSC_VER) && _MSC_VER < 1900 /*vs2015*/)
+#define snprintf sprintf_s
+#endif
+
+#include <cassert>
+#include <cstdlib>
+#include <cstring>
+#include <cctype>
+
+#include "PpContext.h"
+#include "PpTokens.h"
+
+namespace glslang {
+
+// Add a token (including backing string) to the end of a macro
+// token stream, for later playback.
+void TPpContext::TokenStream::putToken(int atom, TPpToken* ppToken)
+{
+ TokenStream::Token streamToken(atom, *ppToken);
+ stream.push_back(streamToken);
+}
+
+// Read the next token from a macro token stream.
+int TPpContext::TokenStream::getToken(TParseContextBase& parseContext, TPpToken *ppToken)
+{
+ if (atEnd())
+ return EndOfInput;
+
+ int atom = stream[currentPos++].get(*ppToken);
+ ppToken->loc = parseContext.getCurrentLoc();
+
+ // Check for ##, unless the current # is the last character
+ if (atom == '#') {
+ if (peekToken('#')) {
+ parseContext.requireProfile(ppToken->loc, ~EEsProfile, "token pasting (##)");
+ parseContext.profileRequires(ppToken->loc, ~EEsProfile, 130, 0, "token pasting (##)");
+ currentPos++;
+ atom = PpAtomPaste;
+ }
+ }
+
+ return atom;
+}
+
+// We are pasting if
+// 1. we are preceding a pasting operator within this stream
+// or
+// 2. the entire macro is preceding a pasting operator (lastTokenPastes)
+// and we are also on the last token
+bool TPpContext::TokenStream::peekTokenizedPasting(bool lastTokenPastes)
+{
+ // 1. preceding ##?
+
+ size_t savePos = currentPos;
+ // skip white space
+ while (peekToken(' '))
+ ++currentPos;
+ if (peekToken(PpAtomPaste)) {
+ currentPos = savePos;
+ return true;
+ }
+
+ // 2. last token and we've been told after this there will be a ##
+
+ if (! lastTokenPastes)
+ return false;
+ // Getting here means the last token will be pasted, after this
+
+ // Are we at the last non-whitespace token?
+ savePos = currentPos;
+ bool moreTokens = false;
+ do {
+ if (atEnd())
+ break;
+ if (!peekToken(' ')) {
+ moreTokens = true;
+ break;
+ }
+ ++currentPos;
+ } while (true);
+ currentPos = savePos;
+
+ return !moreTokens;
+}
+
+// See if the next non-white-space tokens are two consecutive #
+bool TPpContext::TokenStream::peekUntokenizedPasting()
+{
+ // don't return early, have to restore this
+ size_t savePos = currentPos;
+
+ // skip white-space
+ while (peekToken(' '))
+ ++currentPos;
+
+ // check for ##
+ bool pasting = false;
+ if (peekToken('#')) {
+ ++currentPos;
+ if (peekToken('#'))
+ pasting = true;
+ }
+
+ currentPos = savePos;
+
+ return pasting;
+}
+
+void TPpContext::pushTokenStreamInput(TokenStream& ts, bool prepasting)
+{
+ pushInput(new tTokenInput(this, &ts, prepasting));
+ ts.reset();
+}
+
+int TPpContext::tUngotTokenInput::scan(TPpToken* ppToken)
+{
+ if (done)
+ return EndOfInput;
+
+ int ret = token;
+ *ppToken = lval;
+ done = true;
+
+ return ret;
+}
+
+void TPpContext::UngetToken(int token, TPpToken* ppToken)
+{
+ pushInput(new tUngotTokenInput(this, token, ppToken));
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpTokens.h b/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpTokens.h
new file mode 100644
index 0000000..7b0f815
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/preprocessor/PpTokens.h
@@ -0,0 +1,179 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+/****************************************************************************\
+Copyright (c) 2002, NVIDIA Corporation.
+
+NVIDIA Corporation("NVIDIA") supplies this software to you in
+consideration of your agreement to the following terms, and your use,
+installation, modification or redistribution of this NVIDIA software
+constitutes acceptance of these terms. If you do not agree with these
+terms, please do not use, install, modify or redistribute this NVIDIA
+software.
+
+In consideration of your agreement to abide by the following terms, and
+subject to these terms, NVIDIA grants you a personal, non-exclusive
+license, under NVIDIA's copyrights in this original NVIDIA software (the
+"NVIDIA Software"), to use, reproduce, modify and redistribute the
+NVIDIA Software, with or without modifications, in source and/or binary
+forms; provided that if you redistribute the NVIDIA Software, you must
+retain the copyright notice of NVIDIA, this notice and the following
+text and disclaimers in all such redistributions of the NVIDIA Software.
+Neither the name, trademarks, service marks nor logos of NVIDIA
+Corporation may be used to endorse or promote products derived from the
+NVIDIA Software without specific prior written permission from NVIDIA.
+Except as expressly stated in this notice, no other rights or licenses
+express or implied, are granted by NVIDIA herein, including but not
+limited to any patent rights that may be infringed by your derivative
+works or by other works in which the NVIDIA Software may be
+incorporated. No hardware is licensed hereunder.
+
+THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED,
+INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
+ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER
+PRODUCTS.
+
+IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT,
+INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY
+OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE
+NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT,
+TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+\****************************************************************************/
+
+#ifndef PARSER_H
+#define PARSER_H
+
+namespace glslang {
+
+// Multi-character tokens
+enum EFixedAtoms {
+ // single character tokens get their own char value as their token; start here for multi-character tokens
+ PpAtomMaxSingle = 127,
+
+ // replace bad character tokens with this, to avoid accidental aliasing with the below
+ PpAtomBadToken,
+
+ // Operators
+
+ PPAtomAddAssign,
+ PPAtomSubAssign,
+ PPAtomMulAssign,
+ PPAtomDivAssign,
+ PPAtomModAssign,
+
+ PpAtomRight,
+ PpAtomLeft,
+
+ PpAtomRightAssign,
+ PpAtomLeftAssign,
+ PpAtomAndAssign,
+ PpAtomOrAssign,
+ PpAtomXorAssign,
+
+ PpAtomAnd,
+ PpAtomOr,
+ PpAtomXor,
+
+ PpAtomEQ,
+ PpAtomNE,
+ PpAtomGE,
+ PpAtomLE,
+
+ PpAtomDecrement,
+ PpAtomIncrement,
+
+ PpAtomColonColon,
+
+ PpAtomPaste,
+
+ // Constants
+
+ PpAtomConstInt,
+ PpAtomConstUint,
+ PpAtomConstInt64,
+ PpAtomConstUint64,
+ PpAtomConstInt16,
+ PpAtomConstUint16,
+ PpAtomConstFloat,
+ PpAtomConstDouble,
+ PpAtomConstFloat16,
+ PpAtomConstString,
+
+ // Identifiers
+ PpAtomIdentifier,
+
+ // preprocessor "keywords"
+
+ PpAtomDefine,
+ PpAtomUndef,
+
+ PpAtomIf,
+ PpAtomIfdef,
+ PpAtomIfndef,
+ PpAtomElse,
+ PpAtomElif,
+ PpAtomEndif,
+
+ PpAtomLine,
+ PpAtomPragma,
+ PpAtomError,
+
+ // #version ...
+ PpAtomVersion,
+ PpAtomCore,
+ PpAtomCompatibility,
+ PpAtomEs,
+
+ // #extension
+ PpAtomExtension,
+
+ // __LINE__, __FILE__, __VERSION__
+
+ PpAtomLineMacro,
+ PpAtomFileMacro,
+ PpAtomVersionMacro,
+
+ // #include
+ PpAtomInclude,
+
+ PpAtomLast,
+};
+
+} // end namespace glslang
+
+#endif /* not PARSER_H */
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/propagateNoContraction.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/propagateNoContraction.cpp
new file mode 100644
index 0000000..ae95688
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/propagateNoContraction.cpp
@@ -0,0 +1,866 @@
+//
+// Copyright (C) 2015-2016 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+//
+// Visit the nodes in the glslang intermediate tree representation to
+// propagate the 'noContraction' qualifier.
+//
+
+#include "propagateNoContraction.h"
+
+#include <cstdlib>
+#include <string>
+#include <tuple>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "localintermediate.h"
+namespace {
+
+// Use a string to hold the access chain information, as in most cases the
+// access chain is short and may contain only one element, which is the symbol
+// ID.
+// Example: struct {float a; float b;} s;
+// Object s.a will be represented with: <symbol ID of s>/0
+// Object s.b will be represented with: <symbol ID of s>/1
+// Object s will be represented with: <symbol ID of s>
+// For members of vector, matrix and arrays, they will be represented with the
+// same symbol ID of their container symbol objects. This is because their
+// preciseness is always the same as their container symbol objects.
+typedef std::string ObjectAccessChain;
+
+// The delimiter used in the ObjectAccessChain string to separate symbol ID and
+// different level of struct indices.
+const char ObjectAccesschainDelimiter = '/';
+
+// Mapping from Symbol IDs of symbol nodes, to their defining operation
+// nodes.
+typedef std::unordered_multimap<ObjectAccessChain, glslang::TIntermOperator*> NodeMapping;
+// Mapping from object nodes to their access chain info string.
+typedef std::unordered_map<glslang::TIntermTyped*, ObjectAccessChain> AccessChainMapping;
+
+// Set of object IDs.
+typedef std::unordered_set<ObjectAccessChain> ObjectAccesschainSet;
+// Set of return branch nodes.
+typedef std::unordered_set<glslang::TIntermBranch*> ReturnBranchNodeSet;
+
+// A helper function to tell whether a node is 'noContraction'. Returns true if
+// the node has 'noContraction' qualifier, otherwise false.
+bool isPreciseObjectNode(glslang::TIntermTyped* node)
+{
+ return node->getType().getQualifier().noContraction;
+}
+
+// Returns true if the opcode is a dereferencing one.
+bool isDereferenceOperation(glslang::TOperator op)
+{
+ switch (op) {
+ case glslang::EOpIndexDirect:
+ case glslang::EOpIndexDirectStruct:
+ case glslang::EOpIndexIndirect:
+ case glslang::EOpVectorSwizzle:
+ case glslang::EOpMatrixSwizzle:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Returns true if the opcode leads to an assignment operation.
+bool isAssignOperation(glslang::TOperator op)
+{
+ switch (op) {
+ case glslang::EOpAssign:
+ case glslang::EOpAddAssign:
+ case glslang::EOpSubAssign:
+ case glslang::EOpMulAssign:
+ case glslang::EOpVectorTimesMatrixAssign:
+ case glslang::EOpVectorTimesScalarAssign:
+ case glslang::EOpMatrixTimesScalarAssign:
+ case glslang::EOpMatrixTimesMatrixAssign:
+ case glslang::EOpDivAssign:
+ case glslang::EOpModAssign:
+ case glslang::EOpAndAssign:
+ case glslang::EOpLeftShiftAssign:
+ case glslang::EOpRightShiftAssign:
+ case glslang::EOpInclusiveOrAssign:
+ case glslang::EOpExclusiveOrAssign:
+
+ case glslang::EOpPostIncrement:
+ case glslang::EOpPostDecrement:
+ case glslang::EOpPreIncrement:
+ case glslang::EOpPreDecrement:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// A helper function to get the unsigned int from a given constant union node.
+// Note the node should only hold a uint scalar.
+unsigned getStructIndexFromConstantUnion(glslang::TIntermTyped* node)
+{
+ assert(node->getAsConstantUnion() && node->getAsConstantUnion()->isScalar());
+ unsigned struct_dereference_index = node->getAsConstantUnion()->getConstArray()[0].getUConst();
+ return struct_dereference_index;
+}
+
+// A helper function to generate symbol_label.
+ObjectAccessChain generateSymbolLabel(glslang::TIntermSymbol* node)
+{
+ ObjectAccessChain symbol_id =
+ std::to_string(node->getId()) + "(" + node->getName().c_str() + ")";
+ return symbol_id;
+}
+
+// Returns true if the operation is an arithmetic operation and valid for
+// the 'NoContraction' decoration.
+bool isArithmeticOperation(glslang::TOperator op)
+{
+ switch (op) {
+ case glslang::EOpAddAssign:
+ case glslang::EOpSubAssign:
+ case glslang::EOpMulAssign:
+ case glslang::EOpVectorTimesMatrixAssign:
+ case glslang::EOpVectorTimesScalarAssign:
+ case glslang::EOpMatrixTimesScalarAssign:
+ case glslang::EOpMatrixTimesMatrixAssign:
+ case glslang::EOpDivAssign:
+ case glslang::EOpModAssign:
+
+ case glslang::EOpNegative:
+
+ case glslang::EOpAdd:
+ case glslang::EOpSub:
+ case glslang::EOpMul:
+ case glslang::EOpDiv:
+ case glslang::EOpMod:
+
+ case glslang::EOpVectorTimesScalar:
+ case glslang::EOpVectorTimesMatrix:
+ case glslang::EOpMatrixTimesVector:
+ case glslang::EOpMatrixTimesScalar:
+ case glslang::EOpMatrixTimesMatrix:
+
+ case glslang::EOpDot:
+
+ case glslang::EOpPostIncrement:
+ case glslang::EOpPostDecrement:
+ case glslang::EOpPreIncrement:
+ case glslang::EOpPreDecrement:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// A helper class to help manage the populating_initial_no_contraction_ flag.
+template <typename T> class StateSettingGuard {
+public:
+ StateSettingGuard(T* state_ptr, T new_state_value)
+ : state_ptr_(state_ptr), previous_state_(*state_ptr)
+ {
+ *state_ptr = new_state_value;
+ }
+ StateSettingGuard(T* state_ptr) : state_ptr_(state_ptr), previous_state_(*state_ptr) {}
+ void setState(T new_state_value) { *state_ptr_ = new_state_value; }
+ ~StateSettingGuard() { *state_ptr_ = previous_state_; }
+
+private:
+ T* state_ptr_;
+ T previous_state_;
+};
+
+// A helper function to get the front element from a given ObjectAccessChain
+ObjectAccessChain getFrontElement(const ObjectAccessChain& chain)
+{
+ size_t pos_delimiter = chain.find(ObjectAccesschainDelimiter);
+ return pos_delimiter == std::string::npos ? chain : chain.substr(0, pos_delimiter);
+}
+
+// A helper function to get the access chain starting from the second element.
+ObjectAccessChain subAccessChainFromSecondElement(const ObjectAccessChain& chain)
+{
+ size_t pos_delimiter = chain.find(ObjectAccesschainDelimiter);
+ return pos_delimiter == std::string::npos ? "" : chain.substr(pos_delimiter + 1);
+}
+
+// A helper function to get the access chain after removing a given prefix.
+ObjectAccessChain getSubAccessChainAfterPrefix(const ObjectAccessChain& chain,
+ const ObjectAccessChain& prefix)
+{
+ size_t pos = chain.find(prefix);
+ if (pos != 0)
+ return chain;
+ return chain.substr(prefix.length() + sizeof(ObjectAccesschainDelimiter));
+}
+
+//
+// A traverser which traverses the whole AST and populates:
+// 1) A mapping from symbol nodes' IDs to their defining operation nodes.
+// 2) A set of access chains of the initial precise object nodes.
+//
+class TSymbolDefinitionCollectingTraverser : public glslang::TIntermTraverser {
+public:
+ TSymbolDefinitionCollectingTraverser(NodeMapping* symbol_definition_mapping,
+ AccessChainMapping* accesschain_mapping,
+ ObjectAccesschainSet* precise_objects,
+ ReturnBranchNodeSet* precise_return_nodes);
+
+ bool visitUnary(glslang::TVisit, glslang::TIntermUnary*) override;
+ bool visitBinary(glslang::TVisit, glslang::TIntermBinary*) override;
+ void visitSymbol(glslang::TIntermSymbol*) override;
+ bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate*) override;
+ bool visitBranch(glslang::TVisit, glslang::TIntermBranch*) override;
+
+protected:
+ TSymbolDefinitionCollectingTraverser& operator=(const TSymbolDefinitionCollectingTraverser&);
+
+ // The mapping from symbol node IDs to their defining nodes. This should be
+ // populated along traversing the AST.
+ NodeMapping& symbol_definition_mapping_;
+ // The set of symbol node IDs for precise symbol nodes, the ones marked as
+ // 'noContraction'.
+ ObjectAccesschainSet& precise_objects_;
+ // The set of precise return nodes.
+ ReturnBranchNodeSet& precise_return_nodes_;
+ // A temporary cache of the symbol node whose defining node is to be found
+ // currently along traversing the AST.
+ ObjectAccessChain current_object_;
+ // A map from object node to its access chain. This traverser stores
+ // the built access chains into this map for each object node it has
+ // visited.
+ AccessChainMapping& accesschain_mapping_;
+ // The pointer to the Function Definition node, so we can get the
+ // preciseness of the return expression from it when we traverse the
+ // return branch node.
+ glslang::TIntermAggregate* current_function_definition_node_;
+};
+
+TSymbolDefinitionCollectingTraverser::TSymbolDefinitionCollectingTraverser(
+ NodeMapping* symbol_definition_mapping, AccessChainMapping* accesschain_mapping,
+ ObjectAccesschainSet* precise_objects,
+ std::unordered_set<glslang::TIntermBranch*>* precise_return_nodes)
+ : TIntermTraverser(true, false, false), symbol_definition_mapping_(*symbol_definition_mapping),
+ precise_objects_(*precise_objects), precise_return_nodes_(*precise_return_nodes),
+ current_object_(), accesschain_mapping_(*accesschain_mapping),
+ current_function_definition_node_(nullptr) {}
+
+// Visits a symbol node, set the current_object_ to the
+// current node symbol ID, and record a mapping from this node to the current
+// current_object_, which is the just obtained symbol
+// ID.
+void TSymbolDefinitionCollectingTraverser::visitSymbol(glslang::TIntermSymbol* node)
+{
+ current_object_ = generateSymbolLabel(node);
+ accesschain_mapping_[node] = current_object_;
+}
+
+// Visits an aggregate node, traverses all of its children.
+bool TSymbolDefinitionCollectingTraverser::visitAggregate(glslang::TVisit,
+ glslang::TIntermAggregate* node)
+{
+ // This aggregate node might be a function definition node, in which case we need to
+ // cache this node, so we can get the preciseness information of the return value
+ // of this function later.
+ StateSettingGuard<glslang::TIntermAggregate*> current_function_definition_node_setting_guard(
+ &current_function_definition_node_);
+ if (node->getOp() == glslang::EOpFunction) {
+ // This is function definition node, we need to cache this node so that we can
+ // get the preciseness of the return value later.
+ current_function_definition_node_setting_guard.setState(node);
+ }
+ // Traverse the items in the sequence.
+ glslang::TIntermSequence& seq = node->getSequence();
+ for (int i = 0; i < (int)seq.size(); ++i) {
+ current_object_.clear();
+ seq[i]->traverse(this);
+ }
+ return false;
+}
+
+bool TSymbolDefinitionCollectingTraverser::visitBranch(glslang::TVisit,
+ glslang::TIntermBranch* node)
+{
+ if (node->getFlowOp() == glslang::EOpReturn && node->getExpression() &&
+ current_function_definition_node_ &&
+ current_function_definition_node_->getType().getQualifier().noContraction) {
+ // This node is a return node with an expression, and its function has a
+ // precise return value. We need to find the involved objects in its
+ // expression and add them to the set of initial precise objects.
+ precise_return_nodes_.insert(node);
+ node->getExpression()->traverse(this);
+ }
+ return false;
+}
+
+// Visits a unary node. This might be an implicit assignment like i++, i--. etc.
+bool TSymbolDefinitionCollectingTraverser::visitUnary(glslang::TVisit /* visit */,
+ glslang::TIntermUnary* node)
+{
+ current_object_.clear();
+ node->getOperand()->traverse(this);
+ if (isAssignOperation(node->getOp())) {
+ // We should always be able to get an access chain of the operand node.
+ assert(!current_object_.empty());
+
+ // If the operand node object is 'precise', we collect its access chain
+ // for the initial set of 'precise' objects.
+ if (isPreciseObjectNode(node->getOperand())) {
+ // The operand node is an 'precise' object node, add its
+ // access chain to the set of 'precise' objects. This is to collect
+ // the initial set of 'precise' objects.
+ precise_objects_.insert(current_object_);
+ }
+ // Gets the symbol ID from the object's access chain.
+ ObjectAccessChain id_symbol = getFrontElement(current_object_);
+ // Add a mapping from the symbol ID to this assignment operation node.
+ symbol_definition_mapping_.insert(std::make_pair(id_symbol, node));
+ }
+ // A unary node is not a dereference node, so we clear the access chain which
+ // is under construction.
+ current_object_.clear();
+ return false;
+}
+
+// Visits a binary node and updates the mapping from symbol IDs to the definition
+// nodes. Also collects the access chains for the initial precise objects.
+bool TSymbolDefinitionCollectingTraverser::visitBinary(glslang::TVisit /* visit */,
+ glslang::TIntermBinary* node)
+{
+ // Traverses the left node to build the access chain info for the object.
+ current_object_.clear();
+ node->getLeft()->traverse(this);
+
+ if (isAssignOperation(node->getOp())) {
+ // We should always be able to get an access chain for the left node.
+ assert(!current_object_.empty());
+
+ // If the left node object is 'precise', it is an initial precise object
+ // specified in the shader source. Adds it to the initial work list to
+ // process later.
+ if (isPreciseObjectNode(node->getLeft())) {
+ // The left node is an 'precise' object node, add its access chain to
+ // the set of 'precise' objects. This is to collect the initial set
+ // of 'precise' objects.
+ precise_objects_.insert(current_object_);
+ }
+ // Gets the symbol ID from the object access chain, which should be the
+ // first element recorded in the access chain.
+ ObjectAccessChain id_symbol = getFrontElement(current_object_);
+ // Adds a mapping from the symbol ID to this assignment operation node.
+ symbol_definition_mapping_.insert(std::make_pair(id_symbol, node));
+
+ // Traverses the right node, there may be other 'assignment'
+ // operations in the right.
+ current_object_.clear();
+ node->getRight()->traverse(this);
+
+ } else if (isDereferenceOperation(node->getOp())) {
+ // The left node (parent node) is a struct type object. We need to
+ // record the access chain information of the current node into its
+ // object id.
+ if (node->getOp() == glslang::EOpIndexDirectStruct) {
+ unsigned struct_dereference_index = getStructIndexFromConstantUnion(node->getRight());
+ current_object_.push_back(ObjectAccesschainDelimiter);
+ current_object_.append(std::to_string(struct_dereference_index));
+ }
+ accesschain_mapping_[node] = current_object_;
+
+ // For a dereference node, there is no need to traverse the right child
+ // node as the right node should always be an integer type object.
+
+ } else {
+ // For other binary nodes, still traverse the right node.
+ current_object_.clear();
+ node->getRight()->traverse(this);
+ }
+ return false;
+}
+
+// Traverses the AST and returns a tuple of four members:
+// 1) a mapping from symbol IDs to the definition nodes (aka. assignment nodes) of these symbols.
+// 2) a mapping from object nodes in the AST to the access chains of these objects.
+// 3) a set of access chains of precise objects.
+// 4) a set of return nodes with precise expressions.
+std::tuple<NodeMapping, AccessChainMapping, ObjectAccesschainSet, ReturnBranchNodeSet>
+getSymbolToDefinitionMappingAndPreciseSymbolIDs(const glslang::TIntermediate& intermediate)
+{
+ auto result_tuple = std::make_tuple(NodeMapping(), AccessChainMapping(), ObjectAccesschainSet(),
+ ReturnBranchNodeSet());
+
+ TIntermNode* root = intermediate.getTreeRoot();
+ if (root == 0)
+ return result_tuple;
+
+ NodeMapping& symbol_definition_mapping = std::get<0>(result_tuple);
+ AccessChainMapping& accesschain_mapping = std::get<1>(result_tuple);
+ ObjectAccesschainSet& precise_objects = std::get<2>(result_tuple);
+ ReturnBranchNodeSet& precise_return_nodes = std::get<3>(result_tuple);
+
+ // Traverses the AST and populate the results.
+ TSymbolDefinitionCollectingTraverser collector(&symbol_definition_mapping, &accesschain_mapping,
+ &precise_objects, &precise_return_nodes);
+ root->traverse(&collector);
+
+ return result_tuple;
+}
+
+//
+// A traverser that determine whether the left node (or operand node for unary
+// node) of an assignment node is 'precise', containing 'precise' or not,
+// according to the access chain a given precise object which share the same
+// symbol as the left node.
+//
+// Post-orderly traverses the left node subtree of an binary assignment node and:
+//
+// 1) Propagates the 'precise' from the left object nodes to this object node.
+//
+// 2) Builds object access chain along the traversal, and also compares with
+// the access chain of the given 'precise' object along with the traversal to
+// tell if the node to be defined is 'precise' or not.
+//
+class TNoContractionAssigneeCheckingTraverser : public glslang::TIntermTraverser {
+
+ enum DecisionStatus {
+ // The object node to be assigned to may contain 'precise' objects and also not 'precise' objects.
+ Mixed = 0,
+ // The object node to be assigned to is either a 'precise' object or a struct objects whose members are all 'precise'.
+ Precise = 1,
+ // The object node to be assigned to is not a 'precise' object.
+ NotPreicse = 2,
+ };
+
+public:
+ TNoContractionAssigneeCheckingTraverser(const AccessChainMapping& accesschain_mapping)
+ : TIntermTraverser(true, false, false), accesschain_mapping_(accesschain_mapping),
+ precise_object_(nullptr) {}
+
+ // Checks the preciseness of a given assignment node with a precise object
+ // represented as access chain. The precise object shares the same symbol
+ // with the assignee of the given assignment node. Return a tuple of two:
+ //
+ // 1) The preciseness of the assignee node of this assignment node. True
+ // if the assignee contains 'precise' objects or is 'precise', false if
+ // the assignee is not 'precise' according to the access chain of the given
+ // precise object.
+ //
+ // 2) The incremental access chain from the assignee node to its nested
+ // 'precise' object, according to the access chain of the given precise
+ // object. This incremental access chain can be empty, which means the
+ // assignee is 'precise'. Otherwise it shows the path to the nested
+ // precise object.
+ std::tuple<bool, ObjectAccessChain>
+ getPrecisenessAndRemainedAccessChain(glslang::TIntermOperator* node,
+ const ObjectAccessChain& precise_object)
+ {
+ assert(isAssignOperation(node->getOp()));
+ precise_object_ = &precise_object;
+ ObjectAccessChain assignee_object;
+ if (glslang::TIntermBinary* BN = node->getAsBinaryNode()) {
+ // This is a binary assignment node, we need to check the
+ // preciseness of the left node.
+ assert(accesschain_mapping_.count(BN->getLeft()));
+ // The left node (assignee node) is an object node, traverse the
+ // node to let the 'precise' of nesting objects being transfered to
+ // nested objects.
+ BN->getLeft()->traverse(this);
+ // After traversing the left node, if the left node is 'precise',
+ // we can conclude this assignment should propagate 'precise'.
+ if (isPreciseObjectNode(BN->getLeft())) {
+ return make_tuple(true, ObjectAccessChain());
+ }
+ // If the preciseness of the left node (assignee node) can not
+ // be determined by now, we need to compare the access chain string
+ // of the assignee object with the given precise object.
+ assignee_object = accesschain_mapping_.at(BN->getLeft());
+
+ } else if (glslang::TIntermUnary* UN = node->getAsUnaryNode()) {
+ // This is a unary assignment node, we need to check the
+ // preciseness of the operand node. For unary assignment node, the
+ // operand node should always be an object node.
+ assert(accesschain_mapping_.count(UN->getOperand()));
+ // Traverse the operand node to let the 'precise' being propagated
+ // from lower nodes to upper nodes.
+ UN->getOperand()->traverse(this);
+ // After traversing the operand node, if the operand node is
+ // 'precise', this assignment should propagate 'precise'.
+ if (isPreciseObjectNode(UN->getOperand())) {
+ return make_tuple(true, ObjectAccessChain());
+ }
+ // If the preciseness of the operand node (assignee node) can not
+ // be determined by now, we need to compare the access chain string
+ // of the assignee object with the given precise object.
+ assignee_object = accesschain_mapping_.at(UN->getOperand());
+ } else {
+ // Not a binary or unary node, should not happen.
+ assert(false);
+ }
+
+ // Compare the access chain string of the assignee node with the given
+ // precise object to determine if this assignment should propagate
+ // 'precise'.
+ if (assignee_object.find(precise_object) == 0) {
+ // The access chain string of the given precise object is a prefix
+ // of assignee's access chain string. The assignee should be
+ // 'precise'.
+ return make_tuple(true, ObjectAccessChain());
+ } else if (precise_object.find(assignee_object) == 0) {
+ // The assignee's access chain string is a prefix of the given
+ // precise object, the assignee object contains 'precise' object,
+ // and we need to pass the remained access chain to the object nodes
+ // in the right.
+ return make_tuple(true, getSubAccessChainAfterPrefix(precise_object, assignee_object));
+ } else {
+ // The access chain strings do not match, the assignee object can
+ // not be labeled as 'precise' according to the given precise
+ // object.
+ return make_tuple(false, ObjectAccessChain());
+ }
+ }
+
+protected:
+ TNoContractionAssigneeCheckingTraverser& operator=(const TNoContractionAssigneeCheckingTraverser&);
+
+ bool visitBinary(glslang::TVisit, glslang::TIntermBinary* node) override;
+ void visitSymbol(glslang::TIntermSymbol* node) override;
+
+ // A map from object nodes to their access chain string (used as object ID).
+ const AccessChainMapping& accesschain_mapping_;
+ // A given precise object, represented in it access chain string. This
+ // precise object is used to be compared with the assignee node to tell if
+ // the assignee node is 'precise', contains 'precise' object or not
+ // 'precise'.
+ const ObjectAccessChain* precise_object_;
+};
+
+// Visits a binary node. If the node is an object node, it must be a dereference
+// node. In such cases, if the left node is 'precise', this node should also be
+// 'precise'.
+bool TNoContractionAssigneeCheckingTraverser::visitBinary(glslang::TVisit,
+ glslang::TIntermBinary* node)
+{
+ // Traverses the left so that we transfer the 'precise' from nesting object
+ // to its nested object.
+ node->getLeft()->traverse(this);
+ // If this binary node is an object node, we should have it in the
+ // accesschain_mapping_.
+ if (accesschain_mapping_.count(node)) {
+ // A binary object node must be a dereference node.
+ assert(isDereferenceOperation(node->getOp()));
+ // If the left node is 'precise', this node should also be precise,
+ // otherwise, compare with the given precise_object_. If the
+ // access chain of this node matches with the given precise_object_,
+ // this node should be marked as 'precise'.
+ if (isPreciseObjectNode(node->getLeft())) {
+ node->getWritableType().getQualifier().noContraction = true;
+ } else if (accesschain_mapping_.at(node) == *precise_object_) {
+ node->getWritableType().getQualifier().noContraction = true;
+ }
+ }
+ return false;
+}
+
+// Visits a symbol node, if the symbol node ID (its access chain string) matches
+// with the given precise object, this node should be 'precise'.
+void TNoContractionAssigneeCheckingTraverser::visitSymbol(glslang::TIntermSymbol* node)
+{
+ // A symbol node should always be an object node, and should have been added
+ // to the map from object nodes to their access chain strings.
+ assert(accesschain_mapping_.count(node));
+ if (accesschain_mapping_.at(node) == *precise_object_) {
+ node->getWritableType().getQualifier().noContraction = true;
+ }
+}
+
+//
+// A traverser that only traverses the right side of binary assignment nodes
+// and the operand node of unary assignment nodes.
+//
+// 1) Marks arithmetic operations as 'NoContraction'.
+//
+// 2) Find the object which should be marked as 'precise' in the right and
+// update the 'precise' object work list.
+//
+class TNoContractionPropagator : public glslang::TIntermTraverser {
+public:
+ TNoContractionPropagator(ObjectAccesschainSet* precise_objects,
+ const AccessChainMapping& accesschain_mapping)
+ : TIntermTraverser(true, false, false),
+ precise_objects_(*precise_objects), added_precise_object_ids_(),
+ remained_accesschain_(), accesschain_mapping_(accesschain_mapping) {}
+
+ // Propagates 'precise' in the right nodes of a given assignment node with
+ // access chain record from the assignee node to a 'precise' object it
+ // contains.
+ void
+ propagateNoContractionInOneExpression(glslang::TIntermTyped* defining_node,
+ const ObjectAccessChain& assignee_remained_accesschain)
+ {
+ remained_accesschain_ = assignee_remained_accesschain;
+ if (glslang::TIntermBinary* BN = defining_node->getAsBinaryNode()) {
+ assert(isAssignOperation(BN->getOp()));
+ BN->getRight()->traverse(this);
+ if (isArithmeticOperation(BN->getOp())) {
+ BN->getWritableType().getQualifier().noContraction = true;
+ }
+ } else if (glslang::TIntermUnary* UN = defining_node->getAsUnaryNode()) {
+ assert(isAssignOperation(UN->getOp()));
+ UN->getOperand()->traverse(this);
+ if (isArithmeticOperation(UN->getOp())) {
+ UN->getWritableType().getQualifier().noContraction = true;
+ }
+ }
+ }
+
+ // Propagates 'precise' in a given precise return node.
+ void propagateNoContractionInReturnNode(glslang::TIntermBranch* return_node)
+ {
+ remained_accesschain_ = "";
+ assert(return_node->getFlowOp() == glslang::EOpReturn && return_node->getExpression());
+ return_node->getExpression()->traverse(this);
+ }
+
+protected:
+ TNoContractionPropagator& operator=(const TNoContractionPropagator&);
+
+ // Visits an aggregate node. The node can be a initializer list, in which
+ // case we need to find the 'precise' or 'precise' containing object node
+ // with the access chain record. In other cases, just need to traverse all
+ // the children nodes.
+ bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate* node) override
+ {
+ if (!remained_accesschain_.empty() && node->getOp() == glslang::EOpConstructStruct) {
+ // This is a struct initializer node, and the remained
+ // access chain is not empty, we need to refer to the
+ // assignee_remained_access_chain_ to find the nested
+ // 'precise' object. And we don't need to visit other nodes in this
+ // aggregate node.
+
+ // Gets the struct dereference index that leads to 'precise' object.
+ ObjectAccessChain precise_accesschain_index_str =
+ getFrontElement(remained_accesschain_);
+ unsigned precise_accesschain_index = (unsigned)strtoul(precise_accesschain_index_str.c_str(), nullptr, 10);
+ // Gets the node pointed by the access chain index extracted before.
+ glslang::TIntermTyped* potential_precise_node =
+ node->getSequence()[precise_accesschain_index]->getAsTyped();
+ assert(potential_precise_node);
+ // Pop the front access chain index from the path, and visit the nested node.
+ {
+ ObjectAccessChain next_level_accesschain =
+ subAccessChainFromSecondElement(remained_accesschain_);
+ StateSettingGuard<ObjectAccessChain> setup_remained_accesschain_for_next_level(
+ &remained_accesschain_, next_level_accesschain);
+ potential_precise_node->traverse(this);
+ }
+ return false;
+ }
+ return true;
+ }
+
+ // Visits a binary node. A binary node can be an object node, e.g. a dereference node.
+ // As only the top object nodes in the right side of an assignment needs to be visited
+ // and added to 'precise' work list, this traverser won't visit the children nodes of
+ // an object node. If the binary node does not represent an object node, it should
+ // go on to traverse its children nodes and if it is an arithmetic operation node, this
+ // operation should be marked as 'noContraction'.
+ bool visitBinary(glslang::TVisit, glslang::TIntermBinary* node) override
+ {
+ if (isDereferenceOperation(node->getOp())) {
+ // This binary node is an object node. Need to update the precise
+ // object set with the access chain of this node + remained
+ // access chain .
+ ObjectAccessChain new_precise_accesschain = accesschain_mapping_.at(node);
+ if (remained_accesschain_.empty()) {
+ node->getWritableType().getQualifier().noContraction = true;
+ } else {
+ new_precise_accesschain += ObjectAccesschainDelimiter + remained_accesschain_;
+ }
+ // Cache the access chain as added precise object, so we won't add the
+ // same object to the work list again.
+ if (!added_precise_object_ids_.count(new_precise_accesschain)) {
+ precise_objects_.insert(new_precise_accesschain);
+ added_precise_object_ids_.insert(new_precise_accesschain);
+ }
+ // Only the upper-most object nodes should be visited, so do not
+ // visit children of this object node.
+ return false;
+ }
+ // If this is an arithmetic operation, marks this node as 'noContraction'.
+ if (isArithmeticOperation(node->getOp()) && node->getBasicType() != glslang::EbtInt) {
+ node->getWritableType().getQualifier().noContraction = true;
+ }
+ // As this node is not an object node, need to traverse the children nodes.
+ return true;
+ }
+
+ // Visits a unary node. A unary node can not be an object node. If the operation
+ // is an arithmetic operation, need to mark this node as 'noContraction'.
+ bool visitUnary(glslang::TVisit /* visit */, glslang::TIntermUnary* node) override
+ {
+ // If this is an arithmetic operation, marks this with 'noContraction'
+ if (isArithmeticOperation(node->getOp())) {
+ node->getWritableType().getQualifier().noContraction = true;
+ }
+ return true;
+ }
+
+ // Visits a symbol node. A symbol node is always an object node. So we
+ // should always be able to find its in our collected mapping from object
+ // nodes to access chains. As an object node, a symbol node can be either
+ // 'precise' or containing 'precise' objects according to unused
+ // access chain information we have when we visit this node.
+ void visitSymbol(glslang::TIntermSymbol* node) override
+ {
+ // Symbol nodes are object nodes and should always have an
+ // access chain collected before matches with it.
+ assert(accesschain_mapping_.count(node));
+ ObjectAccessChain new_precise_accesschain = accesschain_mapping_.at(node);
+ // If the unused access chain is empty, this symbol node should be
+ // marked as 'precise'. Otherwise, the unused access chain should be
+ // appended to the symbol ID to build a new access chain which points to
+ // the nested 'precise' object in this symbol object.
+ if (remained_accesschain_.empty()) {
+ node->getWritableType().getQualifier().noContraction = true;
+ } else {
+ new_precise_accesschain += ObjectAccesschainDelimiter + remained_accesschain_;
+ }
+ // Add the new 'precise' access chain to the work list and make sure we
+ // don't visit it again.
+ if (!added_precise_object_ids_.count(new_precise_accesschain)) {
+ precise_objects_.insert(new_precise_accesschain);
+ added_precise_object_ids_.insert(new_precise_accesschain);
+ }
+ }
+
+ // A set of precise objects, represented as access chains.
+ ObjectAccesschainSet& precise_objects_;
+ // Visited symbol nodes, should not revisit these nodes.
+ ObjectAccesschainSet added_precise_object_ids_;
+ // The left node of an assignment operation might be an parent of 'precise' objects.
+ // This means the left node might not be an 'precise' object node, but it may contains
+ // 'precise' qualifier which should be propagated to the corresponding child node in
+ // the right. So we need the path from the left node to its nested 'precise' node to
+ // tell us how to find the corresponding 'precise' node in the right.
+ ObjectAccessChain remained_accesschain_;
+ // A map from node pointers to their access chains.
+ const AccessChainMapping& accesschain_mapping_;
+};
+}
+
+namespace glslang {
+
+void PropagateNoContraction(const glslang::TIntermediate& intermediate)
+{
+ // First, traverses the AST, records symbols with their defining operations
+ // and collects the initial set of precise symbols (symbol nodes that marked
+ // as 'noContraction') and precise return nodes.
+ auto mappings_and_precise_objects =
+ getSymbolToDefinitionMappingAndPreciseSymbolIDs(intermediate);
+
+ // The mapping of symbol node IDs to their defining nodes. This enables us
+ // to get the defining node directly from a given symbol ID without
+ // traversing the tree again.
+ NodeMapping& symbol_definition_mapping = std::get<0>(mappings_and_precise_objects);
+
+ // The mapping of object nodes to their access chains recorded.
+ AccessChainMapping& accesschain_mapping = std::get<1>(mappings_and_precise_objects);
+
+ // The initial set of 'precise' objects which are represented as the
+ // access chain toward them.
+ ObjectAccesschainSet& precise_object_accesschains = std::get<2>(mappings_and_precise_objects);
+
+ // The set of 'precise' return nodes.
+ ReturnBranchNodeSet& precise_return_nodes = std::get<3>(mappings_and_precise_objects);
+
+ // Second, uses the initial set of precise objects as a work list, pops an
+ // access chain, extract the symbol ID from it. Then:
+ // 1) Check the assignee object, see if it is 'precise' object node or
+ // contains 'precise' object. Obtain the incremental access chain from the
+ // assignee node to its nested 'precise' node (if any).
+ // 2) If the assignee object node is 'precise' or it contains 'precise'
+ // objects, traverses the right side of the assignment operation
+ // expression to mark arithmetic operations as 'noContration' and update
+ // 'precise' access chain work list with new found object nodes.
+ // Repeat above steps until the work list is empty.
+ TNoContractionAssigneeCheckingTraverser checker(accesschain_mapping);
+ TNoContractionPropagator propagator(&precise_object_accesschains, accesschain_mapping);
+
+ // We have two initial precise work lists to handle:
+ // 1) precise return nodes
+ // 2) precise object access chains
+ // We should process the precise return nodes first and the involved
+ // objects in the return expression should be added to the precise object
+ // access chain set.
+ while (!precise_return_nodes.empty()) {
+ glslang::TIntermBranch* precise_return_node = *precise_return_nodes.begin();
+ propagator.propagateNoContractionInReturnNode(precise_return_node);
+ precise_return_nodes.erase(precise_return_node);
+ }
+
+ while (!precise_object_accesschains.empty()) {
+ // Get the access chain of a precise object from the work list.
+ ObjectAccessChain precise_object_accesschain = *precise_object_accesschains.begin();
+ // Get the symbol id from the access chain.
+ ObjectAccessChain symbol_id = getFrontElement(precise_object_accesschain);
+ // Get all the defining nodes of that symbol ID.
+ std::pair<NodeMapping::iterator, NodeMapping::iterator> range =
+ symbol_definition_mapping.equal_range(symbol_id);
+ // Visits all the assignment nodes of that symbol ID and
+ // 1) Check if the assignee node is 'precise' or contains 'precise'
+ // objects.
+ // 2) Propagate the 'precise' to the top layer object nodes
+ // in the right side of the assignment operation, update the 'precise'
+ // work list with new access chains representing the new 'precise'
+ // objects, and mark arithmetic operations as 'noContraction'.
+ for (NodeMapping::iterator defining_node_iter = range.first;
+ defining_node_iter != range.second; defining_node_iter++) {
+ TIntermOperator* defining_node = defining_node_iter->second;
+ // Check the assignee node.
+ auto checker_result = checker.getPrecisenessAndRemainedAccessChain(
+ defining_node, precise_object_accesschain);
+ bool& contain_precise = std::get<0>(checker_result);
+ ObjectAccessChain& remained_accesschain = std::get<1>(checker_result);
+ // If the assignee node is 'precise' or contains 'precise', propagate the
+ // 'precise' to the right. Otherwise just skip this assignment node.
+ if (contain_precise) {
+ propagator.propagateNoContractionInOneExpression(defining_node,
+ remained_accesschain);
+ }
+ }
+ // Remove the last processed 'precise' object from the work list.
+ precise_object_accesschains.erase(precise_object_accesschain);
+ }
+}
+};
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/propagateNoContraction.h b/src/3rdparty/glslang/glslang/MachineIndependent/propagateNoContraction.h
new file mode 100644
index 0000000..8521ad7
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/propagateNoContraction.h
@@ -0,0 +1,55 @@
+//
+// Copyright (C) 2015-2016 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+//
+// Visit the nodes in the glslang intermediate tree representation to
+// propagate 'noContraction' qualifier.
+//
+
+#pragma once
+
+#include "../Include/intermediate.h"
+
+namespace glslang {
+
+// Propagates the 'precise' qualifier for objects (objects marked with
+// 'noContraction' qualifier) from the shader source specified 'precise'
+// variables to all the involved objects, and add 'noContraction' qualifier for
+// the involved arithmetic operations.
+// Note that the same qualifier: 'noContraction' is used in both object nodes
+// and arithmetic operation nodes, but has different meaning. For object nodes,
+// 'noContraction' means the object is 'precise'; and for arithmetic operation
+// nodes, it means the operation should not be contracted.
+void PropagateNoContraction(const glslang::TIntermediate& intermediate);
+};
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/reflection.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/reflection.cpp
new file mode 100644
index 0000000..2b28403
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/reflection.cpp
@@ -0,0 +1,1256 @@
+//
+// Copyright (C) 2013-2016 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "../Include/Common.h"
+#include "reflection.h"
+#include "LiveTraverser.h"
+#include "localintermediate.h"
+
+#include "gl_types.h"
+
+//
+// Grow the reflection database through a friend traverser class of TReflection and a
+// collection of functions to do a liveness traversal that note what uniforms are used
+// in semantically non-dead code.
+//
+// Can be used multiple times, once per stage, to grow a program reflection.
+//
+// High-level algorithm for one stage:
+//
+// 1. Put the entry point on the list of live functions.
+//
+// 2. Traverse any live function, while skipping if-tests with a compile-time constant
+// condition of false, and while adding any encountered function calls to the live
+// function list.
+//
+// Repeat until the live function list is empty.
+//
+// 3. Add any encountered uniform variables and blocks to the reflection database.
+//
+// Can be attempted with a failed link, but will return false if recursion had been detected, or
+// there wasn't exactly one entry point.
+//
+
+namespace glslang {
+
+//
+// The traverser: mostly pass through, except
+// - processing binary nodes to see if they are dereferences of an aggregates to track
+// - processing symbol nodes to see if they are non-aggregate objects to track
+//
+// This ignores semantically dead code by using TLiveTraverser.
+//
+// This is in the glslang namespace directly so it can be a friend of TReflection.
+//
+
+class TReflectionTraverser : public TLiveTraverser {
+public:
+ TReflectionTraverser(const TIntermediate& i, TReflection& r) :
+ TLiveTraverser(i), reflection(r) { }
+
+ virtual bool visitBinary(TVisit, TIntermBinary* node);
+ virtual void visitSymbol(TIntermSymbol* base);
+
+ // Add a simple reference to a uniform variable to the uniform database, no dereference involved.
+ // However, no dereference doesn't mean simple... it could be a complex aggregate.
+ void addUniform(const TIntermSymbol& base)
+ {
+ if (processedDerefs.find(&base) == processedDerefs.end()) {
+ processedDerefs.insert(&base);
+
+ // Use a degenerate (empty) set of dereferences to immediately put as at the end of
+ // the dereference change expected by blowUpActiveAggregate.
+ TList<TIntermBinary*> derefs;
+ blowUpActiveAggregate(base.getType(), base.getName(), derefs, derefs.end(), -1, -1, 0, 0,
+ base.getQualifier().storage, true);
+ }
+ }
+
+ void addPipeIOVariable(const TIntermSymbol& base)
+ {
+ if (processedDerefs.find(&base) == processedDerefs.end()) {
+ processedDerefs.insert(&base);
+
+ const TString &name = base.getName();
+ const TType &type = base.getType();
+ const bool input = base.getQualifier().isPipeInput();
+
+ TReflection::TMapIndexToReflection &ioItems =
+ input ? reflection.indexToPipeInput : reflection.indexToPipeOutput;
+
+ if (reflection.options & EShReflectionUnwrapIOBlocks) {
+ bool anonymous = IsAnonymous(name);
+
+ TString baseName;
+ if (type.getBasicType() == EbtBlock) {
+ baseName = anonymous ? TString() : type.getTypeName();
+ } else {
+ baseName = anonymous ? TString() : name;
+ }
+
+ // by convention if this is an arrayed block we ignore the array in the reflection
+ if (type.isArray() && type.getBasicType() == EbtBlock) {
+ blowUpIOAggregate(input, baseName, TType(type, 0));
+ } else {
+ blowUpIOAggregate(input, baseName, type);
+ }
+ } else {
+ TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(name.c_str());
+ if (it == reflection.nameToIndex.end()) {
+ reflection.nameToIndex[name.c_str()] = (int)ioItems.size();
+ ioItems.push_back(
+ TObjectReflection(name.c_str(), type, 0, mapToGlType(type), mapToGlArraySize(type), 0));
+
+ EShLanguageMask& stages = ioItems.back().stages;
+ stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
+ } else {
+ EShLanguageMask& stages = ioItems[it->second].stages;
+ stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
+ }
+ }
+ }
+ }
+
+ // shared calculation by getOffset and getOffsets
+ void updateOffset(const TType& parentType, const TType& memberType, int& offset, int& memberSize)
+ {
+ int dummyStride;
+
+ // modify just the children's view of matrix layout, if there is one for this member
+ TLayoutMatrix subMatrixLayout = memberType.getQualifier().layoutMatrix;
+ int memberAlignment = intermediate.getMemberAlignment(memberType, memberSize, dummyStride,
+ parentType.getQualifier().layoutPacking,
+ subMatrixLayout != ElmNone
+ ? subMatrixLayout == ElmRowMajor
+ : parentType.getQualifier().layoutMatrix == ElmRowMajor);
+ RoundToPow2(offset, memberAlignment);
+ }
+
+ // Lookup or calculate the offset of a block member, using the recursively
+ // defined block offset rules.
+ int getOffset(const TType& type, int index)
+ {
+ const TTypeList& memberList = *type.getStruct();
+
+ // Don't calculate offset if one is present, it could be user supplied
+ // and different than what would be calculated. That is, this is faster,
+ // but not just an optimization.
+ if (memberList[index].type->getQualifier().hasOffset())
+ return memberList[index].type->getQualifier().layoutOffset;
+
+ int memberSize = 0;
+ int offset = 0;
+ for (int m = 0; m <= index; ++m) {
+ updateOffset(type, *memberList[m].type, offset, memberSize);
+
+ if (m < index)
+ offset += memberSize;
+ }
+
+ return offset;
+ }
+
+ // Lookup or calculate the offset of all block members at once, using the recursively
+ // defined block offset rules.
+ void getOffsets(const TType& type, TVector<int>& offsets)
+ {
+ const TTypeList& memberList = *type.getStruct();
+
+ int memberSize = 0;
+ int offset = 0;
+ for (size_t m = 0; m < offsets.size(); ++m) {
+ // if the user supplied an offset, snap to it now
+ if (memberList[m].type->getQualifier().hasOffset())
+ offset = memberList[m].type->getQualifier().layoutOffset;
+
+ // calculate the offset of the next member and align the current offset to this member
+ updateOffset(type, *memberList[m].type, offset, memberSize);
+
+ // save the offset of this member
+ offsets[m] = offset;
+
+ // update for the next member
+ offset += memberSize;
+ }
+ }
+
+ // Calculate the stride of an array type
+ int getArrayStride(const TType& baseType, const TType& type)
+ {
+ int dummySize;
+ int stride;
+
+ // consider blocks to have 0 stride, so that all offsets are relative to the start of their block
+ if (type.getBasicType() == EbtBlock)
+ return 0;
+
+ TLayoutMatrix subMatrixLayout = type.getQualifier().layoutMatrix;
+ intermediate.getMemberAlignment(type, dummySize, stride,
+ baseType.getQualifier().layoutPacking,
+ subMatrixLayout != ElmNone
+ ? subMatrixLayout == ElmRowMajor
+ : baseType.getQualifier().layoutMatrix == ElmRowMajor);
+
+ return stride;
+ }
+
+ // Calculate the block data size.
+ // Block arrayness is not taken into account, each element is backed by a separate buffer.
+ int getBlockSize(const TType& blockType)
+ {
+ const TTypeList& memberList = *blockType.getStruct();
+ int lastIndex = (int)memberList.size() - 1;
+ int lastOffset = getOffset(blockType, lastIndex);
+
+ int lastMemberSize;
+ int dummyStride;
+ intermediate.getMemberAlignment(*memberList[lastIndex].type, lastMemberSize, dummyStride,
+ blockType.getQualifier().layoutPacking,
+ blockType.getQualifier().layoutMatrix == ElmRowMajor);
+
+ return lastOffset + lastMemberSize;
+ }
+
+ // count the total number of leaf members from iterating out of a block type
+ int countAggregateMembers(const TType& parentType)
+ {
+ if (! parentType.isStruct())
+ return 1;
+
+ const bool strictArraySuffix = (reflection.options & EShReflectionStrictArraySuffix);
+
+ bool blockParent = (parentType.getBasicType() == EbtBlock && parentType.getQualifier().storage == EvqBuffer);
+
+ const TTypeList &memberList = *parentType.getStruct();
+
+ int ret = 0;
+
+ for (size_t i = 0; i < memberList.size(); i++)
+ {
+ const TType &memberType = *memberList[i].type;
+ int numMembers = countAggregateMembers(memberType);
+ // for sized arrays of structs, apply logic to expand out the same as we would below in
+ // blowUpActiveAggregate
+ if (memberType.isArray() && ! memberType.getArraySizes()->hasUnsized() && memberType.isStruct()) {
+ if (! strictArraySuffix || ! blockParent)
+ numMembers *= memberType.getArraySizes()->getCumulativeSize();
+ }
+ ret += numMembers;
+ }
+
+ return ret;
+ }
+
+ // Traverse the provided deref chain, including the base, and
+ // - build a full reflection-granularity name, array size, etc. entry out of it, if it goes down to that granularity
+ // - recursively expand any variable array index in the middle of that traversal
+ // - recursively expand what's left at the end if the deref chain did not reach down to reflection granularity
+ //
+ // arraySize tracks, just for the final dereference in the chain, if there was a specific known size.
+ // A value of 0 for arraySize will mean to use the full array's size.
+ void blowUpActiveAggregate(const TType& baseType, const TString& baseName, const TList<TIntermBinary*>& derefs,
+ TList<TIntermBinary*>::const_iterator deref, int offset, int blockIndex, int arraySize,
+ int topLevelArrayStride, TStorageQualifier baseStorage, bool active)
+ {
+ // when strictArraySuffix is enabled, we closely follow the rules from ARB_program_interface_query.
+ // Broadly:
+ // * arrays-of-structs always have a [x] suffix.
+ // * with array-of-struct variables in the root of a buffer block, only ever return [0].
+ // * otherwise, array suffixes are added whenever we iterate, even if that means expanding out an array.
+ const bool strictArraySuffix = (reflection.options & EShReflectionStrictArraySuffix);
+
+ // is this variable inside a buffer block. This flag is set back to false after we iterate inside the first array element.
+ bool blockParent = (baseType.getBasicType() == EbtBlock && baseType.getQualifier().storage == EvqBuffer);
+
+ // process the part of the dereference chain that was explicit in the shader
+ TString name = baseName;
+ const TType* terminalType = &baseType;
+ for (; deref != derefs.end(); ++deref) {
+ TIntermBinary* visitNode = *deref;
+ terminalType = &visitNode->getType();
+ int index;
+ switch (visitNode->getOp()) {
+ case EOpIndexIndirect: {
+ int stride = getArrayStride(baseType, visitNode->getLeft()->getType());
+
+ if (topLevelArrayStride == 0)
+ topLevelArrayStride = stride;
+
+ // Visit all the indices of this array, and for each one add on the remaining dereferencing
+ for (int i = 0; i < std::max(visitNode->getLeft()->getType().getOuterArraySize(), 1); ++i) {
+ TString newBaseName = name;
+ if (strictArraySuffix && blockParent)
+ newBaseName.append(TString("[0]"));
+ else if (strictArraySuffix || baseType.getBasicType() != EbtBlock)
+ newBaseName.append(TString("[") + String(i) + "]");
+ TList<TIntermBinary*>::const_iterator nextDeref = deref;
+ ++nextDeref;
+ blowUpActiveAggregate(*terminalType, newBaseName, derefs, nextDeref, offset, blockIndex, arraySize,
+ topLevelArrayStride, baseStorage, active);
+
+ if (offset >= 0)
+ offset += stride;
+ }
+
+ // it was all completed in the recursive calls above
+ return;
+ }
+ case EOpIndexDirect: {
+ int stride = getArrayStride(baseType, visitNode->getLeft()->getType());
+
+ index = visitNode->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst();
+ if (strictArraySuffix && blockParent) {
+ name.append(TString("[0]"));
+ } else if (strictArraySuffix || baseType.getBasicType() != EbtBlock) {
+ name.append(TString("[") + String(index) + "]");
+
+ if (offset >= 0)
+ offset += stride * index;
+ }
+
+ if (topLevelArrayStride == 0)
+ topLevelArrayStride = stride;
+
+ blockParent = false;
+ break;
+ }
+ case EOpIndexDirectStruct:
+ index = visitNode->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst();
+ if (offset >= 0)
+ offset += getOffset(visitNode->getLeft()->getType(), index);
+ if (name.size() > 0)
+ name.append(".");
+ name.append((*visitNode->getLeft()->getType().getStruct())[index].type->getFieldName());
+ break;
+ default:
+ break;
+ }
+ }
+
+ // if the terminalType is still too coarse a granularity, this is still an aggregate to expand, expand it...
+ if (! isReflectionGranularity(*terminalType)) {
+ // the base offset of this node, that children are relative to
+ int baseOffset = offset;
+
+ if (terminalType->isArray()) {
+ // Visit all the indices of this array, and for each one,
+ // fully explode the remaining aggregate to dereference
+
+ int stride = 0;
+ if (offset >= 0)
+ stride = getArrayStride(baseType, *terminalType);
+
+ if (topLevelArrayStride == 0)
+ topLevelArrayStride = stride;
+
+ int arrayIterateSize = std::max(terminalType->getOuterArraySize(), 1);
+
+ // for top-level arrays in blocks, only expand [0] to avoid explosion of items
+ if (strictArraySuffix && blockParent)
+ arrayIterateSize = 1;
+
+ for (int i = 0; i < arrayIterateSize; ++i) {
+ TString newBaseName = name;
+ newBaseName.append(TString("[") + String(i) + "]");
+ TType derefType(*terminalType, 0);
+ if (offset >= 0)
+ offset = baseOffset + stride * i;
+
+ blowUpActiveAggregate(derefType, newBaseName, derefs, derefs.end(), offset, blockIndex, 0,
+ topLevelArrayStride, baseStorage, active);
+ }
+ } else {
+ // Visit all members of this aggregate, and for each one,
+ // fully explode the remaining aggregate to dereference
+ const TTypeList& typeList = *terminalType->getStruct();
+
+ TVector<int> memberOffsets;
+
+ if (baseOffset >= 0) {
+ memberOffsets.resize(typeList.size());
+ getOffsets(*terminalType, memberOffsets);
+ }
+
+ for (int i = 0; i < (int)typeList.size(); ++i) {
+ TString newBaseName = name;
+ if (newBaseName.size() > 0)
+ newBaseName.append(".");
+ newBaseName.append(typeList[i].type->getFieldName());
+ TType derefType(*terminalType, i);
+ if (offset >= 0)
+ offset = baseOffset + memberOffsets[i];
+
+ int arrayStride = topLevelArrayStride;
+ if (terminalType->getBasicType() == EbtBlock && terminalType->getQualifier().storage == EvqBuffer &&
+ derefType.isArray()) {
+ arrayStride = getArrayStride(baseType, derefType);
+ }
+
+ blowUpActiveAggregate(derefType, newBaseName, derefs, derefs.end(), offset, blockIndex, 0,
+ arrayStride, baseStorage, active);
+ }
+ }
+
+ // it was all completed in the recursive calls above
+ return;
+ }
+
+ if ((reflection.options & EShReflectionBasicArraySuffix) && terminalType->isArray()) {
+ name.append(TString("[0]"));
+ }
+
+ // Finally, add a full string to the reflection database, and update the array size if necessary.
+ // If the dereferenced entity to record is an array, compute the size and update the maximum size.
+
+ // there might not be a final array dereference, it could have been copied as an array object
+ if (arraySize == 0)
+ arraySize = mapToGlArraySize(*terminalType);
+
+ TReflection::TMapIndexToReflection& variables = reflection.GetVariableMapForStorage(baseStorage);
+
+ TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(name.c_str());
+ if (it == reflection.nameToIndex.end()) {
+ int uniformIndex = (int)variables.size();
+ reflection.nameToIndex[name.c_str()] = uniformIndex;
+ variables.push_back(TObjectReflection(name.c_str(), *terminalType, offset, mapToGlType(*terminalType),
+ arraySize, blockIndex));
+ if (terminalType->isArray()) {
+ variables.back().arrayStride = getArrayStride(baseType, *terminalType);
+ if (topLevelArrayStride == 0)
+ topLevelArrayStride = variables.back().arrayStride;
+ }
+
+ if ((reflection.options & EShReflectionSeparateBuffers) && terminalType->getBasicType() == EbtAtomicUint)
+ reflection.atomicCounterUniformIndices.push_back(uniformIndex);
+
+ variables.back().topLevelArrayStride = topLevelArrayStride;
+
+ if ((reflection.options & EShReflectionAllBlockVariables) && active) {
+ EShLanguageMask& stages = variables.back().stages;
+ stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
+ }
+ } else {
+ if (arraySize > 1) {
+ int& reflectedArraySize = variables[it->second].size;
+ reflectedArraySize = std::max(arraySize, reflectedArraySize);
+ }
+
+ if ((reflection.options & EShReflectionAllBlockVariables) && active) {
+ EShLanguageMask& stages = variables[it->second].stages;
+ stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
+ }
+ }
+ }
+
+ // similar to blowUpActiveAggregate, but with simpler rules and no dereferences to follow.
+ void blowUpIOAggregate(bool input, const TString &baseName, const TType &type)
+ {
+ TString name = baseName;
+
+ // if the type is still too coarse a granularity, this is still an aggregate to expand, expand it...
+ if (! isReflectionGranularity(type)) {
+ if (type.isArray()) {
+ // Visit all the indices of this array, and for each one,
+ // fully explode the remaining aggregate to dereference
+ for (int i = 0; i < std::max(type.getOuterArraySize(), 1); ++i) {
+ TString newBaseName = name;
+ newBaseName.append(TString("[") + String(i) + "]");
+ TType derefType(type, 0);
+
+ blowUpIOAggregate(input, newBaseName, derefType);
+ }
+ } else {
+ // Visit all members of this aggregate, and for each one,
+ // fully explode the remaining aggregate to dereference
+ const TTypeList& typeList = *type.getStruct();
+
+ for (int i = 0; i < (int)typeList.size(); ++i) {
+ TString newBaseName = name;
+ if (newBaseName.size() > 0)
+ newBaseName.append(".");
+ newBaseName.append(typeList[i].type->getFieldName());
+ TType derefType(type, i);
+
+ blowUpIOAggregate(input, newBaseName, derefType);
+ }
+ }
+
+ // it was all completed in the recursive calls above
+ return;
+ }
+
+ if ((reflection.options & EShReflectionBasicArraySuffix) && type.isArray()) {
+ name.append(TString("[0]"));
+ }
+
+ TReflection::TMapIndexToReflection &ioItems =
+ input ? reflection.indexToPipeInput : reflection.indexToPipeOutput;
+
+ std::string namespacedName = input ? "in " : "out ";
+ namespacedName += name.c_str();
+
+ TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(namespacedName);
+ if (it == reflection.nameToIndex.end()) {
+ reflection.nameToIndex[namespacedName] = (int)ioItems.size();
+ ioItems.push_back(
+ TObjectReflection(name.c_str(), type, 0, mapToGlType(type), mapToGlArraySize(type), 0));
+
+ EShLanguageMask& stages = ioItems.back().stages;
+ stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
+ } else {
+ EShLanguageMask& stages = ioItems[it->second].stages;
+ stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
+ }
+ }
+
+ // Add a uniform dereference where blocks/struct/arrays are involved in the access.
+ // Handles the situation where the left node is at the correct or too coarse a
+ // granularity for reflection. (That is, further dereferences up the tree will be
+ // skipped.) Earlier dereferences, down the tree, will be handled
+ // at the same time, and logged to prevent reprocessing as the tree is traversed.
+ //
+ // Note: Other things like the following must be caught elsewhere:
+ // - a simple non-array, non-struct variable (no dereference even conceivable)
+ // - an aggregrate consumed en masse, without a dereference
+ //
+ // So, this code is for cases like
+ // - a struct/block dereferencing a member (whether the member is array or not)
+ // - an array of struct
+ // - structs/arrays containing the above
+ //
+ void addDereferencedUniform(TIntermBinary* topNode)
+ {
+ // See if too fine-grained to process (wait to get further down the tree)
+ const TType& leftType = topNode->getLeft()->getType();
+ if ((leftType.isVector() || leftType.isMatrix()) && ! leftType.isArray())
+ return;
+
+ // We have an array or structure or block dereference, see if it's a uniform
+ // based dereference (if not, skip it).
+ TIntermSymbol* base = findBase(topNode);
+ if (! base || ! base->getQualifier().isUniformOrBuffer())
+ return;
+
+ // See if we've already processed this (e.g., in the middle of something
+ // we did earlier), and if so skip it
+ if (processedDerefs.find(topNode) != processedDerefs.end())
+ return;
+
+ // Process this uniform dereference
+
+ int offset = -1;
+ int blockIndex = -1;
+ bool anonymous = false;
+
+ // See if we need to record the block itself
+ bool block = base->getBasicType() == EbtBlock;
+ if (block) {
+ offset = 0;
+ anonymous = IsAnonymous(base->getName());
+
+ const TString& blockName = base->getType().getTypeName();
+ TString baseName;
+
+ if (! anonymous)
+ baseName = blockName;
+
+ if (base->getType().isArray()) {
+ TType derefType(base->getType(), 0);
+
+ assert(! anonymous);
+ for (int e = 0; e < base->getType().getCumulativeArraySize(); ++e)
+ blockIndex = addBlockName(blockName + "[" + String(e) + "]", derefType,
+ getBlockSize(base->getType()));
+ baseName.append(TString("[0]"));
+ } else
+ blockIndex = addBlockName(blockName, base->getType(), getBlockSize(base->getType()));
+
+ if (reflection.options & EShReflectionAllBlockVariables) {
+ // Use a degenerate (empty) set of dereferences to immediately put as at the end of
+ // the dereference change expected by blowUpActiveAggregate.
+ TList<TIntermBinary*> derefs;
+
+ // because we don't have any derefs, the first thing blowUpActiveAggregate will do is iterate over each
+ // member in the struct definition. This will lose any information about whether the parent was a buffer
+ // block. So if we're using strict array rules which don't expand the first child of a buffer block we
+ // instead iterate over the children here.
+ const bool strictArraySuffix = (reflection.options & EShReflectionStrictArraySuffix);
+ bool blockParent = (base->getType().getBasicType() == EbtBlock && base->getQualifier().storage == EvqBuffer);
+
+ if (strictArraySuffix && blockParent) {
+ const TTypeList& typeList = *base->getType().getStruct();
+
+ TVector<int> memberOffsets;
+
+ memberOffsets.resize(typeList.size());
+ getOffsets(base->getType(), memberOffsets);
+
+ for (int i = 0; i < (int)typeList.size(); ++i) {
+ TType derefType(base->getType(), i);
+ TString name = baseName;
+ if (name.size() > 0)
+ name.append(".");
+ name.append(typeList[i].type->getFieldName());
+
+ // if this member is an array, store the top-level array stride but start the explosion from
+ // the inner struct type.
+ if (derefType.isArray() && derefType.isStruct()) {
+ name.append("[0]");
+ blowUpActiveAggregate(TType(derefType, 0), name, derefs, derefs.end(), memberOffsets[i],
+ blockIndex, 0, getArrayStride(base->getType(), derefType),
+ base->getQualifier().storage, false);
+ } else {
+ blowUpActiveAggregate(derefType, name, derefs, derefs.end(), memberOffsets[i], blockIndex,
+ 0, 0, base->getQualifier().storage, false);
+ }
+ }
+ } else {
+ // otherwise - if we're not using strict array suffix rules, or this isn't a block so we are
+ // expanding root arrays anyway, just start the iteration from the base block type.
+ blowUpActiveAggregate(base->getType(), baseName, derefs, derefs.end(), 0, blockIndex, 0, 0,
+ base->getQualifier().storage, false);
+ }
+ }
+ }
+
+ // Process the dereference chain, backward, accumulating the pieces for later forward traversal.
+ // If the topNode is a reflection-granularity-array dereference, don't include that last dereference.
+ TList<TIntermBinary*> derefs;
+ for (TIntermBinary* visitNode = topNode; visitNode; visitNode = visitNode->getLeft()->getAsBinaryNode()) {
+ if (isReflectionGranularity(visitNode->getLeft()->getType()))
+ continue;
+
+ derefs.push_front(visitNode);
+ processedDerefs.insert(visitNode);
+ }
+ processedDerefs.insert(base);
+
+ // See if we have a specific array size to stick to while enumerating the explosion of the aggregate
+ int arraySize = 0;
+ if (isReflectionGranularity(topNode->getLeft()->getType()) && topNode->getLeft()->isArray()) {
+ if (topNode->getOp() == EOpIndexDirect)
+ arraySize = topNode->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst() + 1;
+ }
+
+ // Put the dereference chain together, forward
+ TString baseName;
+ if (! anonymous) {
+ if (block)
+ baseName = base->getType().getTypeName();
+ else
+ baseName = base->getName();
+ }
+ blowUpActiveAggregate(base->getType(), baseName, derefs, derefs.begin(), offset, blockIndex, arraySize, 0,
+ base->getQualifier().storage, true);
+ }
+
+ int addBlockName(const TString& name, const TType& type, int size)
+ {
+ TReflection::TMapIndexToReflection& blocks = reflection.GetBlockMapForStorage(type.getQualifier().storage);
+
+ int blockIndex;
+ TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(name.c_str());
+ if (reflection.nameToIndex.find(name.c_str()) == reflection.nameToIndex.end()) {
+ blockIndex = (int)blocks.size();
+ reflection.nameToIndex[name.c_str()] = blockIndex;
+ blocks.push_back(TObjectReflection(name.c_str(), type, -1, -1, size, -1));
+
+ blocks.back().numMembers = countAggregateMembers(type);
+
+ EShLanguageMask& stages = blocks.back().stages;
+ stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
+ } else {
+ blockIndex = it->second;
+
+ EShLanguageMask& stages = blocks[blockIndex].stages;
+ stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
+ }
+
+ return blockIndex;
+ }
+
+ // Are we at a level in a dereference chain at which individual active uniform queries are made?
+ bool isReflectionGranularity(const TType& type)
+ {
+ return type.getBasicType() != EbtBlock && type.getBasicType() != EbtStruct && !type.isArrayOfArrays();
+ }
+
+ // For a binary operation indexing into an aggregate, chase down the base of the aggregate.
+ // Return 0 if the topology does not fit this situation.
+ TIntermSymbol* findBase(const TIntermBinary* node)
+ {
+ TIntermSymbol *base = node->getLeft()->getAsSymbolNode();
+ if (base)
+ return base;
+ TIntermBinary* left = node->getLeft()->getAsBinaryNode();
+ if (! left)
+ return nullptr;
+
+ return findBase(left);
+ }
+
+ //
+ // Translate a glslang sampler type into the GL API #define number.
+ //
+ int mapSamplerToGlType(TSampler sampler)
+ {
+ if (! sampler.image) {
+ // a sampler...
+ switch (sampler.type) {
+ case EbtFloat:
+ switch ((int)sampler.dim) {
+ case Esd1D:
+ switch ((int)sampler.shadow) {
+ case false: return sampler.arrayed ? GL_SAMPLER_1D_ARRAY : GL_SAMPLER_1D;
+ case true: return sampler.arrayed ? GL_SAMPLER_1D_ARRAY_SHADOW : GL_SAMPLER_1D_SHADOW;
+ }
+ case Esd2D:
+ switch ((int)sampler.ms) {
+ case false:
+ switch ((int)sampler.shadow) {
+ case false: return sampler.arrayed ? GL_SAMPLER_2D_ARRAY : GL_SAMPLER_2D;
+ case true: return sampler.arrayed ? GL_SAMPLER_2D_ARRAY_SHADOW : GL_SAMPLER_2D_SHADOW;
+ }
+ case true: return sampler.arrayed ? GL_SAMPLER_2D_MULTISAMPLE_ARRAY : GL_SAMPLER_2D_MULTISAMPLE;
+ }
+ case Esd3D:
+ return GL_SAMPLER_3D;
+ case EsdCube:
+ switch ((int)sampler.shadow) {
+ case false: return sampler.arrayed ? GL_SAMPLER_CUBE_MAP_ARRAY : GL_SAMPLER_CUBE;
+ case true: return sampler.arrayed ? GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW : GL_SAMPLER_CUBE_SHADOW;
+ }
+ case EsdRect:
+ return sampler.shadow ? GL_SAMPLER_2D_RECT_SHADOW : GL_SAMPLER_2D_RECT;
+ case EsdBuffer:
+ return GL_SAMPLER_BUFFER;
+ }
+#ifdef AMD_EXTENSIONS
+ case EbtFloat16:
+ switch ((int)sampler.dim) {
+ case Esd1D:
+ switch ((int)sampler.shadow) {
+ case false: return sampler.arrayed ? GL_FLOAT16_SAMPLER_1D_ARRAY_AMD : GL_FLOAT16_SAMPLER_1D_AMD;
+ case true: return sampler.arrayed ? GL_FLOAT16_SAMPLER_1D_ARRAY_SHADOW_AMD : GL_FLOAT16_SAMPLER_1D_SHADOW_AMD;
+ }
+ case Esd2D:
+ switch ((int)sampler.ms) {
+ case false:
+ switch ((int)sampler.shadow) {
+ case false: return sampler.arrayed ? GL_FLOAT16_SAMPLER_2D_ARRAY_AMD : GL_FLOAT16_SAMPLER_2D_AMD;
+ case true: return sampler.arrayed ? GL_FLOAT16_SAMPLER_2D_ARRAY_SHADOW_AMD : GL_FLOAT16_SAMPLER_2D_SHADOW_AMD;
+ }
+ case true: return sampler.arrayed ? GL_FLOAT16_SAMPLER_2D_MULTISAMPLE_ARRAY_AMD : GL_FLOAT16_SAMPLER_2D_MULTISAMPLE_AMD;
+ }
+ case Esd3D:
+ return GL_FLOAT16_SAMPLER_3D_AMD;
+ case EsdCube:
+ switch ((int)sampler.shadow) {
+ case false: return sampler.arrayed ? GL_FLOAT16_SAMPLER_CUBE_MAP_ARRAY_AMD : GL_FLOAT16_SAMPLER_CUBE_AMD;
+ case true: return sampler.arrayed ? GL_FLOAT16_SAMPLER_CUBE_MAP_ARRAY_SHADOW_AMD : GL_FLOAT16_SAMPLER_CUBE_SHADOW_AMD;
+ }
+ case EsdRect:
+ return sampler.shadow ? GL_FLOAT16_SAMPLER_2D_RECT_SHADOW_AMD : GL_FLOAT16_SAMPLER_2D_RECT_AMD;
+ case EsdBuffer:
+ return GL_FLOAT16_SAMPLER_BUFFER_AMD;
+ }
+#endif
+ case EbtInt:
+ switch ((int)sampler.dim) {
+ case Esd1D:
+ return sampler.arrayed ? GL_INT_SAMPLER_1D_ARRAY : GL_INT_SAMPLER_1D;
+ case Esd2D:
+ switch ((int)sampler.ms) {
+ case false: return sampler.arrayed ? GL_INT_SAMPLER_2D_ARRAY : GL_INT_SAMPLER_2D;
+ case true: return sampler.arrayed ? GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY
+ : GL_INT_SAMPLER_2D_MULTISAMPLE;
+ }
+ case Esd3D:
+ return GL_INT_SAMPLER_3D;
+ case EsdCube:
+ return sampler.arrayed ? GL_INT_SAMPLER_CUBE_MAP_ARRAY : GL_INT_SAMPLER_CUBE;
+ case EsdRect:
+ return GL_INT_SAMPLER_2D_RECT;
+ case EsdBuffer:
+ return GL_INT_SAMPLER_BUFFER;
+ }
+ case EbtUint:
+ switch ((int)sampler.dim) {
+ case Esd1D:
+ return sampler.arrayed ? GL_UNSIGNED_INT_SAMPLER_1D_ARRAY : GL_UNSIGNED_INT_SAMPLER_1D;
+ case Esd2D:
+ switch ((int)sampler.ms) {
+ case false: return sampler.arrayed ? GL_UNSIGNED_INT_SAMPLER_2D_ARRAY : GL_UNSIGNED_INT_SAMPLER_2D;
+ case true: return sampler.arrayed ? GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY
+ : GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE;
+ }
+ case Esd3D:
+ return GL_UNSIGNED_INT_SAMPLER_3D;
+ case EsdCube:
+ return sampler.arrayed ? GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY : GL_UNSIGNED_INT_SAMPLER_CUBE;
+ case EsdRect:
+ return GL_UNSIGNED_INT_SAMPLER_2D_RECT;
+ case EsdBuffer:
+ return GL_UNSIGNED_INT_SAMPLER_BUFFER;
+ }
+ default:
+ return 0;
+ }
+ } else {
+ // an image...
+ switch (sampler.type) {
+ case EbtFloat:
+ switch ((int)sampler.dim) {
+ case Esd1D:
+ return sampler.arrayed ? GL_IMAGE_1D_ARRAY : GL_IMAGE_1D;
+ case Esd2D:
+ switch ((int)sampler.ms) {
+ case false: return sampler.arrayed ? GL_IMAGE_2D_ARRAY : GL_IMAGE_2D;
+ case true: return sampler.arrayed ? GL_IMAGE_2D_MULTISAMPLE_ARRAY : GL_IMAGE_2D_MULTISAMPLE;
+ }
+ case Esd3D:
+ return GL_IMAGE_3D;
+ case EsdCube:
+ return sampler.arrayed ? GL_IMAGE_CUBE_MAP_ARRAY : GL_IMAGE_CUBE;
+ case EsdRect:
+ return GL_IMAGE_2D_RECT;
+ case EsdBuffer:
+ return GL_IMAGE_BUFFER;
+ }
+#ifdef AMD_EXTENSIONS
+ case EbtFloat16:
+ switch ((int)sampler.dim) {
+ case Esd1D:
+ return sampler.arrayed ? GL_FLOAT16_IMAGE_1D_ARRAY_AMD : GL_FLOAT16_IMAGE_1D_AMD;
+ case Esd2D:
+ switch ((int)sampler.ms) {
+ case false: return sampler.arrayed ? GL_FLOAT16_IMAGE_2D_ARRAY_AMD : GL_FLOAT16_IMAGE_2D_AMD;
+ case true: return sampler.arrayed ? GL_FLOAT16_IMAGE_2D_MULTISAMPLE_ARRAY_AMD : GL_FLOAT16_IMAGE_2D_MULTISAMPLE_AMD;
+ }
+ case Esd3D:
+ return GL_FLOAT16_IMAGE_3D_AMD;
+ case EsdCube:
+ return sampler.arrayed ? GL_FLOAT16_IMAGE_CUBE_MAP_ARRAY_AMD : GL_FLOAT16_IMAGE_CUBE_AMD;
+ case EsdRect:
+ return GL_FLOAT16_IMAGE_2D_RECT_AMD;
+ case EsdBuffer:
+ return GL_FLOAT16_IMAGE_BUFFER_AMD;
+ }
+#endif
+ case EbtInt:
+ switch ((int)sampler.dim) {
+ case Esd1D:
+ return sampler.arrayed ? GL_INT_IMAGE_1D_ARRAY : GL_INT_IMAGE_1D;
+ case Esd2D:
+ switch ((int)sampler.ms) {
+ case false: return sampler.arrayed ? GL_INT_IMAGE_2D_ARRAY : GL_INT_IMAGE_2D;
+ case true: return sampler.arrayed ? GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY : GL_INT_IMAGE_2D_MULTISAMPLE;
+ }
+ case Esd3D:
+ return GL_INT_IMAGE_3D;
+ case EsdCube:
+ return sampler.arrayed ? GL_INT_IMAGE_CUBE_MAP_ARRAY : GL_INT_IMAGE_CUBE;
+ case EsdRect:
+ return GL_INT_IMAGE_2D_RECT;
+ case EsdBuffer:
+ return GL_INT_IMAGE_BUFFER;
+ }
+ case EbtUint:
+ switch ((int)sampler.dim) {
+ case Esd1D:
+ return sampler.arrayed ? GL_UNSIGNED_INT_IMAGE_1D_ARRAY : GL_UNSIGNED_INT_IMAGE_1D;
+ case Esd2D:
+ switch ((int)sampler.ms) {
+ case false: return sampler.arrayed ? GL_UNSIGNED_INT_IMAGE_2D_ARRAY : GL_UNSIGNED_INT_IMAGE_2D;
+ case true: return sampler.arrayed ? GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY
+ : GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE;
+ }
+ case Esd3D:
+ return GL_UNSIGNED_INT_IMAGE_3D;
+ case EsdCube:
+ return sampler.arrayed ? GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY : GL_UNSIGNED_INT_IMAGE_CUBE;
+ case EsdRect:
+ return GL_UNSIGNED_INT_IMAGE_2D_RECT;
+ case EsdBuffer:
+ return GL_UNSIGNED_INT_IMAGE_BUFFER;
+ }
+ default:
+ return 0;
+ }
+ }
+ }
+
+ //
+ // Translate a glslang type into the GL API #define number.
+ // Ignores arrayness.
+ //
+ int mapToGlType(const TType& type)
+ {
+ switch (type.getBasicType()) {
+ case EbtSampler:
+ return mapSamplerToGlType(type.getSampler());
+ case EbtStruct:
+ case EbtBlock:
+ case EbtVoid:
+ return 0;
+ default:
+ break;
+ }
+
+ if (type.isVector()) {
+ int offset = type.getVectorSize() - 2;
+ switch (type.getBasicType()) {
+ case EbtFloat: return GL_FLOAT_VEC2 + offset;
+ case EbtDouble: return GL_DOUBLE_VEC2 + offset;
+#ifdef AMD_EXTENSIONS
+ case EbtFloat16: return GL_FLOAT16_VEC2_NV + offset;
+#endif
+ case EbtInt: return GL_INT_VEC2 + offset;
+ case EbtUint: return GL_UNSIGNED_INT_VEC2 + offset;
+ case EbtInt64: return GL_INT64_ARB + offset;
+ case EbtUint64: return GL_UNSIGNED_INT64_ARB + offset;
+ case EbtBool: return GL_BOOL_VEC2 + offset;
+ case EbtAtomicUint: return GL_UNSIGNED_INT_ATOMIC_COUNTER + offset;
+ default: return 0;
+ }
+ }
+ if (type.isMatrix()) {
+ switch (type.getBasicType()) {
+ case EbtFloat:
+ switch (type.getMatrixCols()) {
+ case 2:
+ switch (type.getMatrixRows()) {
+ case 2: return GL_FLOAT_MAT2;
+ case 3: return GL_FLOAT_MAT2x3;
+ case 4: return GL_FLOAT_MAT2x4;
+ default: return 0;
+ }
+ case 3:
+ switch (type.getMatrixRows()) {
+ case 2: return GL_FLOAT_MAT3x2;
+ case 3: return GL_FLOAT_MAT3;
+ case 4: return GL_FLOAT_MAT3x4;
+ default: return 0;
+ }
+ case 4:
+ switch (type.getMatrixRows()) {
+ case 2: return GL_FLOAT_MAT4x2;
+ case 3: return GL_FLOAT_MAT4x3;
+ case 4: return GL_FLOAT_MAT4;
+ default: return 0;
+ }
+ }
+ case EbtDouble:
+ switch (type.getMatrixCols()) {
+ case 2:
+ switch (type.getMatrixRows()) {
+ case 2: return GL_DOUBLE_MAT2;
+ case 3: return GL_DOUBLE_MAT2x3;
+ case 4: return GL_DOUBLE_MAT2x4;
+ default: return 0;
+ }
+ case 3:
+ switch (type.getMatrixRows()) {
+ case 2: return GL_DOUBLE_MAT3x2;
+ case 3: return GL_DOUBLE_MAT3;
+ case 4: return GL_DOUBLE_MAT3x4;
+ default: return 0;
+ }
+ case 4:
+ switch (type.getMatrixRows()) {
+ case 2: return GL_DOUBLE_MAT4x2;
+ case 3: return GL_DOUBLE_MAT4x3;
+ case 4: return GL_DOUBLE_MAT4;
+ default: return 0;
+ }
+ }
+#ifdef AMD_EXTENSIONS
+ case EbtFloat16:
+ switch (type.getMatrixCols()) {
+ case 2:
+ switch (type.getMatrixRows()) {
+ case 2: return GL_FLOAT16_MAT2_AMD;
+ case 3: return GL_FLOAT16_MAT2x3_AMD;
+ case 4: return GL_FLOAT16_MAT2x4_AMD;
+ default: return 0;
+ }
+ case 3:
+ switch (type.getMatrixRows()) {
+ case 2: return GL_FLOAT16_MAT3x2_AMD;
+ case 3: return GL_FLOAT16_MAT3_AMD;
+ case 4: return GL_FLOAT16_MAT3x4_AMD;
+ default: return 0;
+ }
+ case 4:
+ switch (type.getMatrixRows()) {
+ case 2: return GL_FLOAT16_MAT4x2_AMD;
+ case 3: return GL_FLOAT16_MAT4x3_AMD;
+ case 4: return GL_FLOAT16_MAT4_AMD;
+ default: return 0;
+ }
+ }
+#endif
+ default:
+ return 0;
+ }
+ }
+ if (type.getVectorSize() == 1) {
+ switch (type.getBasicType()) {
+ case EbtFloat: return GL_FLOAT;
+ case EbtDouble: return GL_DOUBLE;
+#ifdef AMD_EXTENSIONS
+ case EbtFloat16: return GL_FLOAT16_NV;
+#endif
+ case EbtInt: return GL_INT;
+ case EbtUint: return GL_UNSIGNED_INT;
+ case EbtInt64: return GL_INT64_ARB;
+ case EbtUint64: return GL_UNSIGNED_INT64_ARB;
+ case EbtBool: return GL_BOOL;
+ case EbtAtomicUint: return GL_UNSIGNED_INT_ATOMIC_COUNTER;
+ default: return 0;
+ }
+ }
+
+ return 0;
+ }
+
+ int mapToGlArraySize(const TType& type)
+ {
+ return type.isArray() ? type.getOuterArraySize() : 1;
+ }
+
+ TReflection& reflection;
+ std::set<const TIntermNode*> processedDerefs;
+
+protected:
+ TReflectionTraverser(TReflectionTraverser&);
+ TReflectionTraverser& operator=(TReflectionTraverser&);
+};
+
+//
+// Implement the traversal functions of interest.
+//
+
+// To catch dereferenced aggregates that must be reflected.
+// This catches them at the highest level possible in the tree.
+bool TReflectionTraverser::visitBinary(TVisit /* visit */, TIntermBinary* node)
+{
+ switch (node->getOp()) {
+ case EOpIndexDirect:
+ case EOpIndexIndirect:
+ case EOpIndexDirectStruct:
+ addDereferencedUniform(node);
+ break;
+ default:
+ break;
+ }
+
+ // still need to visit everything below, which could contain sub-expressions
+ // containing different uniforms
+ return true;
+}
+
+// To reflect non-dereferenced objects.
+void TReflectionTraverser::visitSymbol(TIntermSymbol* base)
+{
+ if (base->getQualifier().storage == EvqUniform)
+ addUniform(*base);
+
+ if ((intermediate.getStage() == reflection.firstStage && base->getQualifier().isPipeInput()) ||
+ (intermediate.getStage() == reflection.lastStage && base->getQualifier().isPipeOutput()))
+ addPipeIOVariable(*base);
+}
+
+//
+// Implement TObjectReflection methods.
+//
+
+TObjectReflection::TObjectReflection(const std::string &pName, const TType &pType, int pOffset, int pGLDefineType,
+ int pSize, int pIndex)
+ : name(pName), offset(pOffset), glDefineType(pGLDefineType), size(pSize), index(pIndex), counterIndex(-1),
+ numMembers(-1), arrayStride(0), topLevelArrayStride(0), stages(EShLanguageMask(0)), type(pType.clone())
+{
+}
+
+int TObjectReflection::getBinding() const
+{
+ if (type == nullptr || !type->getQualifier().hasBinding())
+ return -1;
+ return type->getQualifier().layoutBinding;
+}
+
+void TObjectReflection::dump() const
+{
+ printf("%s: offset %d, type %x, size %d, index %d, binding %d, stages %d", name.c_str(), offset, glDefineType, size,
+ index, getBinding(), stages);
+
+ if (counterIndex != -1)
+ printf(", counter %d", counterIndex);
+
+ if (numMembers != -1)
+ printf(", numMembers %d", numMembers);
+
+ if (arrayStride != 0)
+ printf(", arrayStride %d", arrayStride);
+
+ if (topLevelArrayStride != 0)
+ printf(", topLevelArrayStride %d", topLevelArrayStride);
+
+ printf("\n");
+}
+
+//
+// Implement TReflection methods.
+//
+
+// Track any required attribute reflection, such as compute shader numthreads.
+//
+void TReflection::buildAttributeReflection(EShLanguage stage, const TIntermediate& intermediate)
+{
+ if (stage == EShLangCompute) {
+ // Remember thread dimensions
+ for (int dim=0; dim<3; ++dim)
+ localSize[dim] = intermediate.getLocalSize(dim);
+ }
+}
+
+// build counter block index associations for buffers
+void TReflection::buildCounterIndices(const TIntermediate& intermediate)
+{
+ // search for ones that have counters
+ for (int i = 0; i < int(indexToUniformBlock.size()); ++i) {
+ const TString counterName(intermediate.addCounterBufferName(indexToUniformBlock[i].name).c_str());
+ const int index = getIndex(counterName);
+
+ if (index >= 0)
+ indexToUniformBlock[i].counterIndex = index;
+ }
+}
+
+// build Shader Stages mask for all uniforms
+void TReflection::buildUniformStageMask(const TIntermediate& intermediate)
+{
+ if (options & EShReflectionAllBlockVariables)
+ return;
+
+ for (int i = 0; i < int(indexToUniform.size()); ++i) {
+ indexToUniform[i].stages = static_cast<EShLanguageMask>(indexToUniform[i].stages | 1 << intermediate.getStage());
+ }
+
+ for (int i = 0; i < int(indexToBufferVariable.size()); ++i) {
+ indexToBufferVariable[i].stages =
+ static_cast<EShLanguageMask>(indexToBufferVariable[i].stages | 1 << intermediate.getStage());
+ }
+}
+
+// Merge live symbols from 'intermediate' into the existing reflection database.
+//
+// Returns false if the input is too malformed to do this.
+bool TReflection::addStage(EShLanguage stage, const TIntermediate& intermediate)
+{
+ if (intermediate.getTreeRoot() == nullptr ||
+ intermediate.getNumEntryPoints() != 1 ||
+ intermediate.isRecursive())
+ return false;
+
+ buildAttributeReflection(stage, intermediate);
+
+ TReflectionTraverser it(intermediate, *this);
+
+ // put the entry point on the list of functions to process
+ it.pushFunction(intermediate.getEntryPointMangledName().c_str());
+
+ // process all the functions
+ while (! it.functions.empty()) {
+ TIntermNode* function = it.functions.back();
+ it.functions.pop_back();
+ function->traverse(&it);
+ }
+
+ buildCounterIndices(intermediate);
+ buildUniformStageMask(intermediate);
+
+ return true;
+}
+
+void TReflection::dump()
+{
+ printf("Uniform reflection:\n");
+ for (size_t i = 0; i < indexToUniform.size(); ++i)
+ indexToUniform[i].dump();
+ printf("\n");
+
+ printf("Uniform block reflection:\n");
+ for (size_t i = 0; i < indexToUniformBlock.size(); ++i)
+ indexToUniformBlock[i].dump();
+ printf("\n");
+
+ printf("Buffer variable reflection:\n");
+ for (size_t i = 0; i < indexToBufferVariable.size(); ++i)
+ indexToBufferVariable[i].dump();
+ printf("\n");
+
+ printf("Buffer block reflection:\n");
+ for (size_t i = 0; i < indexToBufferBlock.size(); ++i)
+ indexToBufferBlock[i].dump();
+ printf("\n");
+
+ printf("Pipeline input reflection:\n");
+ for (size_t i = 0; i < indexToPipeInput.size(); ++i)
+ indexToPipeInput[i].dump();
+ printf("\n");
+
+ printf("Pipeline output reflection:\n");
+ for (size_t i = 0; i < indexToPipeOutput.size(); ++i)
+ indexToPipeOutput[i].dump();
+ printf("\n");
+
+ if (getLocalSize(0) > 1) {
+ static const char* axis[] = { "X", "Y", "Z" };
+
+ for (int dim=0; dim<3; ++dim)
+ if (getLocalSize(dim) > 1)
+ printf("Local size %s: %d\n", axis[dim], getLocalSize(dim));
+
+ printf("\n");
+ }
+
+ // printf("Live names\n");
+ // for (TNameToIndex::const_iterator it = nameToIndex.begin(); it != nameToIndex.end(); ++it)
+ // printf("%s: %d\n", it->first.c_str(), it->second);
+ // printf("\n");
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/reflection.h b/src/3rdparty/glslang/glslang/MachineIndependent/reflection.h
new file mode 100644
index 0000000..44b17a0
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/reflection.h
@@ -0,0 +1,203 @@
+//
+// Copyright (C) 2013-2016 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef _REFLECTION_INCLUDED
+#define _REFLECTION_INCLUDED
+
+#include "../Public/ShaderLang.h"
+#include "../Include/Types.h"
+
+#include <list>
+#include <set>
+
+//
+// A reflection database and its interface, consistent with the OpenGL API reflection queries.
+//
+
+namespace glslang {
+
+class TIntermediate;
+class TIntermAggregate;
+class TReflectionTraverser;
+
+// The full reflection database
+class TReflection {
+public:
+ TReflection(EShReflectionOptions opts, EShLanguage first, EShLanguage last)
+ : options(opts), firstStage(first), lastStage(last), badReflection(TObjectReflection::badReflection())
+ {
+ for (int dim=0; dim<3; ++dim)
+ localSize[dim] = 0;
+ }
+
+ virtual ~TReflection() {}
+
+ // grow the reflection stage by stage
+ bool addStage(EShLanguage, const TIntermediate&);
+
+ // for mapping a uniform index to a uniform object's description
+ int getNumUniforms() { return (int)indexToUniform.size(); }
+ const TObjectReflection& getUniform(int i) const
+ {
+ if (i >= 0 && i < (int)indexToUniform.size())
+ return indexToUniform[i];
+ else
+ return badReflection;
+ }
+
+ // for mapping a block index to the block's description
+ int getNumUniformBlocks() const { return (int)indexToUniformBlock.size(); }
+ const TObjectReflection& getUniformBlock(int i) const
+ {
+ if (i >= 0 && i < (int)indexToUniformBlock.size())
+ return indexToUniformBlock[i];
+ else
+ return badReflection;
+ }
+
+ // for mapping an pipeline input index to the input's description
+ int getNumPipeInputs() { return (int)indexToPipeInput.size(); }
+ const TObjectReflection& getPipeInput(int i) const
+ {
+ if (i >= 0 && i < (int)indexToPipeInput.size())
+ return indexToPipeInput[i];
+ else
+ return badReflection;
+ }
+
+ // for mapping an pipeline output index to the output's description
+ int getNumPipeOutputs() { return (int)indexToPipeOutput.size(); }
+ const TObjectReflection& getPipeOutput(int i) const
+ {
+ if (i >= 0 && i < (int)indexToPipeOutput.size())
+ return indexToPipeOutput[i];
+ else
+ return badReflection;
+ }
+
+ // for mapping from an atomic counter to the uniform index
+ int getNumAtomicCounters() const { return (int)atomicCounterUniformIndices.size(); }
+ const TObjectReflection& getAtomicCounter(int i) const
+ {
+ if (i >= 0 && i < (int)atomicCounterUniformIndices.size())
+ return getUniform(atomicCounterUniformIndices[i]);
+ else
+ return badReflection;
+ }
+
+ // for mapping a buffer variable index to a buffer variable object's description
+ int getNumBufferVariables() { return (int)indexToBufferVariable.size(); }
+ const TObjectReflection& getBufferVariable(int i) const
+ {
+ if (i >= 0 && i < (int)indexToBufferVariable.size())
+ return indexToBufferVariable[i];
+ else
+ return badReflection;
+ }
+
+ // for mapping a storage block index to the storage block's description
+ int getNumStorageBuffers() const { return (int)indexToBufferBlock.size(); }
+ const TObjectReflection& getStorageBufferBlock(int i) const
+ {
+ if (i >= 0 && i < (int)indexToBufferBlock.size())
+ return indexToBufferBlock[i];
+ else
+ return badReflection;
+ }
+
+ // for mapping any name to its index (block names, uniform names and input/output names)
+ int getIndex(const char* name) const
+ {
+ TNameToIndex::const_iterator it = nameToIndex.find(name);
+ if (it == nameToIndex.end())
+ return -1;
+ else
+ return it->second;
+ }
+
+ // see getIndex(const char*)
+ int getIndex(const TString& name) const { return getIndex(name.c_str()); }
+
+ // Thread local size
+ unsigned getLocalSize(int dim) const { return dim <= 2 ? localSize[dim] : 0; }
+
+ void dump();
+
+protected:
+ friend class glslang::TReflectionTraverser;
+
+ void buildCounterIndices(const TIntermediate&);
+ void buildUniformStageMask(const TIntermediate& intermediate);
+ void buildAttributeReflection(EShLanguage, const TIntermediate&);
+
+ // Need a TString hash: typedef std::unordered_map<TString, int> TNameToIndex;
+ typedef std::map<std::string, int> TNameToIndex;
+ typedef std::vector<TObjectReflection> TMapIndexToReflection;
+ typedef std::vector<int> TIndices;
+
+ TMapIndexToReflection& GetBlockMapForStorage(TStorageQualifier storage)
+ {
+ if ((options & EShReflectionSeparateBuffers) && storage == EvqBuffer)
+ return indexToBufferBlock;
+ return indexToUniformBlock;
+ }
+ TMapIndexToReflection& GetVariableMapForStorage(TStorageQualifier storage)
+ {
+ if ((options & EShReflectionSeparateBuffers) && storage == EvqBuffer)
+ return indexToBufferVariable;
+ return indexToUniform;
+ }
+
+ EShReflectionOptions options;
+
+ EShLanguage firstStage;
+ EShLanguage lastStage;
+
+ TObjectReflection badReflection; // return for queries of -1 or generally out of range; has expected descriptions with in it for this
+ TNameToIndex nameToIndex; // maps names to indexes; can hold all types of data: uniform/buffer and which function names have been processed
+ TMapIndexToReflection indexToUniform;
+ TMapIndexToReflection indexToUniformBlock;
+ TMapIndexToReflection indexToBufferVariable;
+ TMapIndexToReflection indexToBufferBlock;
+ TMapIndexToReflection indexToPipeInput;
+ TMapIndexToReflection indexToPipeOutput;
+ TIndices atomicCounterUniformIndices;
+
+ unsigned int localSize[3];
+};
+
+} // end namespace glslang
+
+#endif // _REFLECTION_INCLUDED
diff --git a/src/3rdparty/glslang/glslang/OSDependent/Unix/ossource.cpp b/src/3rdparty/glslang/glslang/OSDependent/Unix/ossource.cpp
new file mode 100644
index 0000000..3f029f0
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/OSDependent/Unix/ossource.cpp
@@ -0,0 +1,207 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+//
+// This file contains the Linux-specific functions
+//
+#include "../osinclude.h"
+#include "../../../OGLCompilersDLL/InitializeDll.h"
+
+#include <pthread.h>
+#include <semaphore.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdint.h>
+#include <cstdio>
+#include <sys/time.h>
+
+#if !defined(__Fuchsia__)
+#include <sys/resource.h>
+#endif
+
+namespace glslang {
+
+//
+// Thread cleanup
+//
+
+//
+// Wrapper for Linux call to DetachThread. This is required as pthread_cleanup_push() expects
+// the cleanup routine to return void.
+//
+static void DetachThreadLinux(void *)
+{
+ DetachThread();
+}
+
+//
+// Registers cleanup handler, sets cancel type and state, and executes the thread specific
+// cleanup handler. This function will be called in the Standalone.cpp for regression
+// testing. When OpenGL applications are run with the driver code, Linux OS does the
+// thread cleanup.
+//
+void OS_CleanupThreadData(void)
+{
+#if defined(__ANDROID__) || defined(__Fuchsia__)
+ DetachThreadLinux(NULL);
+#else
+ int old_cancel_state, old_cancel_type;
+ void *cleanupArg = NULL;
+
+ //
+ // Set thread cancel state and push cleanup handler.
+ //
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_cancel_state);
+ pthread_cleanup_push(DetachThreadLinux, (void *) cleanupArg);
+
+ //
+ // Put the thread in deferred cancellation mode.
+ //
+ pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &old_cancel_type);
+
+ //
+ // Pop cleanup handler and execute it prior to unregistering the cleanup handler.
+ //
+ pthread_cleanup_pop(1);
+
+ //
+ // Restore the thread's previous cancellation mode.
+ //
+ pthread_setcanceltype(old_cancel_state, NULL);
+#endif
+}
+
+//
+// Thread Local Storage Operations
+//
+inline OS_TLSIndex PthreadKeyToTLSIndex(pthread_key_t key)
+{
+ return (OS_TLSIndex)((uintptr_t)key + 1);
+}
+
+inline pthread_key_t TLSIndexToPthreadKey(OS_TLSIndex nIndex)
+{
+ return (pthread_key_t)((uintptr_t)nIndex - 1);
+}
+
+OS_TLSIndex OS_AllocTLSIndex()
+{
+ pthread_key_t pPoolIndex;
+
+ //
+ // Create global pool key.
+ //
+ if ((pthread_key_create(&pPoolIndex, NULL)) != 0) {
+ assert(0 && "OS_AllocTLSIndex(): Unable to allocate Thread Local Storage");
+ return OS_INVALID_TLS_INDEX;
+ }
+ else
+ return PthreadKeyToTLSIndex(pPoolIndex);
+}
+
+bool OS_SetTLSValue(OS_TLSIndex nIndex, void *lpvValue)
+{
+ if (nIndex == OS_INVALID_TLS_INDEX) {
+ assert(0 && "OS_SetTLSValue(): Invalid TLS Index");
+ return false;
+ }
+
+ if (pthread_setspecific(TLSIndexToPthreadKey(nIndex), lpvValue) == 0)
+ return true;
+ else
+ return false;
+}
+
+void* OS_GetTLSValue(OS_TLSIndex nIndex)
+{
+ //
+ // This function should return 0 if nIndex is invalid.
+ //
+ assert(nIndex != OS_INVALID_TLS_INDEX);
+ return pthread_getspecific(TLSIndexToPthreadKey(nIndex));
+}
+
+bool OS_FreeTLSIndex(OS_TLSIndex nIndex)
+{
+ if (nIndex == OS_INVALID_TLS_INDEX) {
+ assert(0 && "OS_SetTLSValue(): Invalid TLS Index");
+ return false;
+ }
+
+ //
+ // Delete the global pool key.
+ //
+ if (pthread_key_delete(TLSIndexToPthreadKey(nIndex)) == 0)
+ return true;
+ else
+ return false;
+}
+
+namespace {
+ pthread_mutex_t gMutex;
+}
+
+void InitGlobalLock()
+{
+ pthread_mutexattr_t mutexattr;
+ pthread_mutexattr_init(&mutexattr);
+ pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&gMutex, &mutexattr);
+}
+
+void GetGlobalLock()
+{
+ pthread_mutex_lock(&gMutex);
+}
+
+void ReleaseGlobalLock()
+{
+ pthread_mutex_unlock(&gMutex);
+}
+
+// #define DUMP_COUNTERS
+
+void OS_DumpMemoryCounters()
+{
+#ifdef DUMP_COUNTERS
+ struct rusage usage;
+
+ if (getrusage(RUSAGE_SELF, &usage) == 0)
+ printf("Working set size: %ld\n", usage.ru_maxrss * 1024);
+#else
+ printf("Recompile with DUMP_COUNTERS defined to see counters.\n");
+#endif
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/glslang/OSDependent/Windows/main.cpp b/src/3rdparty/glslang/glslang/OSDependent/Windows/main.cpp
new file mode 100644
index 0000000..0bcde7b
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/OSDependent/Windows/main.cpp
@@ -0,0 +1,74 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "InitializeDll.h"
+
+#define STRICT
+#define VC_EXTRALEAN 1
+#include <windows.h>
+#include <assert.h>
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+
+ if (! glslang::InitProcess())
+ return FALSE;
+ break;
+ case DLL_THREAD_ATTACH:
+
+ if (! glslang::InitThread())
+ return FALSE;
+ break;
+
+ case DLL_THREAD_DETACH:
+
+ if (! glslang::DetachThread())
+ return FALSE;
+ break;
+
+ case DLL_PROCESS_DETACH:
+
+ glslang::DetachProcess();
+ break;
+
+ default:
+ assert(0 && "DllMain(): Reason for calling DLL Main is unknown");
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/src/3rdparty/glslang/glslang/OSDependent/Windows/ossource.cpp b/src/3rdparty/glslang/glslang/OSDependent/Windows/ossource.cpp
new file mode 100644
index 0000000..870840c
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/OSDependent/Windows/ossource.cpp
@@ -0,0 +1,147 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "../osinclude.h"
+
+#define STRICT
+#define VC_EXTRALEAN 1
+#include <windows.h>
+#include <cassert>
+#include <process.h>
+#include <psapi.h>
+#include <cstdio>
+#include <cstdint>
+
+//
+// This file contains the Window-OS-specific functions
+//
+
+#if !(defined(_WIN32) || defined(_WIN64))
+#error Trying to build a windows specific file in a non windows build.
+#endif
+
+namespace glslang {
+
+inline OS_TLSIndex ToGenericTLSIndex (DWORD handle)
+{
+ return (OS_TLSIndex)((uintptr_t)handle + 1);
+}
+
+inline DWORD ToNativeTLSIndex (OS_TLSIndex nIndex)
+{
+ return (DWORD)((uintptr_t)nIndex - 1);
+}
+
+//
+// Thread Local Storage Operations
+//
+OS_TLSIndex OS_AllocTLSIndex()
+{
+ DWORD dwIndex = TlsAlloc();
+ if (dwIndex == TLS_OUT_OF_INDEXES) {
+ assert(0 && "OS_AllocTLSIndex(): Unable to allocate Thread Local Storage");
+ return OS_INVALID_TLS_INDEX;
+ }
+
+ return ToGenericTLSIndex(dwIndex);
+}
+
+bool OS_SetTLSValue(OS_TLSIndex nIndex, void *lpvValue)
+{
+ if (nIndex == OS_INVALID_TLS_INDEX) {
+ assert(0 && "OS_SetTLSValue(): Invalid TLS Index");
+ return false;
+ }
+
+ if (TlsSetValue(ToNativeTLSIndex(nIndex), lpvValue))
+ return true;
+ else
+ return false;
+}
+
+void* OS_GetTLSValue(OS_TLSIndex nIndex)
+{
+ assert(nIndex != OS_INVALID_TLS_INDEX);
+ return TlsGetValue(ToNativeTLSIndex(nIndex));
+}
+
+bool OS_FreeTLSIndex(OS_TLSIndex nIndex)
+{
+ if (nIndex == OS_INVALID_TLS_INDEX) {
+ assert(0 && "OS_SetTLSValue(): Invalid TLS Index");
+ return false;
+ }
+
+ if (TlsFree(ToNativeTLSIndex(nIndex)))
+ return true;
+ else
+ return false;
+}
+
+HANDLE GlobalLock;
+
+void InitGlobalLock()
+{
+ GlobalLock = CreateMutex(0, false, 0);
+}
+
+void GetGlobalLock()
+{
+ WaitForSingleObject(GlobalLock, INFINITE);
+}
+
+void ReleaseGlobalLock()
+{
+ ReleaseMutex(GlobalLock);
+}
+
+unsigned int __stdcall EnterGenericThread (void* entry)
+{
+ return ((TThreadEntrypoint)entry)(0);
+}
+
+//#define DUMP_COUNTERS
+
+void OS_DumpMemoryCounters()
+{
+#ifdef DUMP_COUNTERS
+ PROCESS_MEMORY_COUNTERS counters;
+ GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters));
+ printf("Working set size: %d\n", counters.WorkingSetSize);
+#else
+ printf("Recompile with DUMP_COUNTERS defined to see counters.\n");
+#endif
+}
+
+} // namespace glslang
diff --git a/src/3rdparty/glslang/glslang/OSDependent/osinclude.h b/src/3rdparty/glslang/glslang/OSDependent/osinclude.h
new file mode 100644
index 0000000..218abe4
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/OSDependent/osinclude.h
@@ -0,0 +1,63 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef __OSINCLUDE_H
+#define __OSINCLUDE_H
+
+namespace glslang {
+
+//
+// Thread Local Storage Operations
+//
+typedef void* OS_TLSIndex;
+#define OS_INVALID_TLS_INDEX ((void*)0)
+
+OS_TLSIndex OS_AllocTLSIndex();
+bool OS_SetTLSValue(OS_TLSIndex nIndex, void *lpvValue);
+bool OS_FreeTLSIndex(OS_TLSIndex nIndex);
+void* OS_GetTLSValue(OS_TLSIndex nIndex);
+
+void InitGlobalLock();
+void GetGlobalLock();
+void ReleaseGlobalLock();
+
+typedef unsigned int (*TThreadEntrypoint)(void*);
+
+void OS_CleanupThreadData(void);
+
+void OS_DumpMemoryCounters();
+
+} // end namespace glslang
+
+#endif // __OSINCLUDE_H
diff --git a/src/3rdparty/glslang/glslang/Public/ShaderLang.h b/src/3rdparty/glslang/glslang/Public/ShaderLang.h
new file mode 100644
index 0000000..0c25569
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/Public/ShaderLang.h
@@ -0,0 +1,846 @@
+//
+// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
+// Copyright (C) 2013-2016 LunarG, Inc.
+// Copyright (C) 2015-2018 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+#ifndef _COMPILER_INTERFACE_INCLUDED_
+#define _COMPILER_INTERFACE_INCLUDED_
+
+#include "../Include/ResourceLimits.h"
+#include "../MachineIndependent/Versions.h"
+
+#include <cstring>
+#include <vector>
+
+#ifdef _WIN32
+#define C_DECL __cdecl
+//#ifdef SH_EXPORTING
+// #define SH_IMPORT_EXPORT __declspec(dllexport)
+//#else
+// #define SH_IMPORT_EXPORT __declspec(dllimport)
+//#endif
+#define SH_IMPORT_EXPORT
+#else
+#define SH_IMPORT_EXPORT
+#define C_DECL
+#endif
+
+//
+// This is the platform independent interface between an OGL driver
+// and the shading language compiler/linker.
+//
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+// This should always increase, as some paths to do not consume
+// a more major number.
+// It should increment by one when new functionality is added.
+#define GLSLANG_MINOR_VERSION 11
+
+//
+// Call before doing any other compiler/linker operations.
+//
+// (Call once per process, not once per thread.)
+//
+SH_IMPORT_EXPORT int ShInitialize();
+
+//
+// Call this at process shutdown to clean up memory.
+//
+SH_IMPORT_EXPORT int ShFinalize();
+
+//
+// Types of languages the compiler can consume.
+//
+typedef enum {
+ EShLangVertex,
+ EShLangTessControl,
+ EShLangTessEvaluation,
+ EShLangGeometry,
+ EShLangFragment,
+ EShLangCompute,
+ EShLangRayGenNV,
+ EShLangIntersectNV,
+ EShLangAnyHitNV,
+ EShLangClosestHitNV,
+ EShLangMissNV,
+ EShLangCallableNV,
+ EShLangTaskNV,
+ EShLangMeshNV,
+ EShLangCount,
+} EShLanguage; // would be better as stage, but this is ancient now
+
+typedef enum {
+ EShLangVertexMask = (1 << EShLangVertex),
+ EShLangTessControlMask = (1 << EShLangTessControl),
+ EShLangTessEvaluationMask = (1 << EShLangTessEvaluation),
+ EShLangGeometryMask = (1 << EShLangGeometry),
+ EShLangFragmentMask = (1 << EShLangFragment),
+ EShLangComputeMask = (1 << EShLangCompute),
+ EShLangRayGenNVMask = (1 << EShLangRayGenNV),
+ EShLangIntersectNVMask = (1 << EShLangIntersectNV),
+ EShLangAnyHitNVMask = (1 << EShLangAnyHitNV),
+ EShLangClosestHitNVMask = (1 << EShLangClosestHitNV),
+ EShLangMissNVMask = (1 << EShLangMissNV),
+ EShLangCallableNVMask = (1 << EShLangCallableNV),
+ EShLangTaskNVMask = (1 << EShLangTaskNV),
+ EShLangMeshNVMask = (1 << EShLangMeshNV),
+} EShLanguageMask;
+
+namespace glslang {
+
+class TType;
+
+typedef enum {
+ EShSourceNone,
+ EShSourceGlsl,
+ EShSourceHlsl,
+} EShSource; // if EShLanguage were EShStage, this could be EShLanguage instead
+
+typedef enum {
+ EShClientNone,
+ EShClientVulkan,
+ EShClientOpenGL,
+} EShClient;
+
+typedef enum {
+ EShTargetNone,
+ EShTargetSpv, // preferred spelling
+ EshTargetSpv = EShTargetSpv, // legacy spelling
+} EShTargetLanguage;
+
+typedef enum {
+ EShTargetVulkan_1_0 = (1 << 22),
+ EShTargetVulkan_1_1 = (1 << 22) | (1 << 12),
+ EShTargetOpenGL_450 = 450,
+} EShTargetClientVersion;
+
+typedef EShTargetClientVersion EshTargetClientVersion;
+
+typedef enum {
+ EShTargetSpv_1_0 = (1 << 16),
+ EShTargetSpv_1_1 = (1 << 16) | (1 << 8),
+ EShTargetSpv_1_2 = (1 << 16) | (2 << 8),
+ EShTargetSpv_1_3 = (1 << 16) | (3 << 8),
+ EShTargetSpv_1_4 = (1 << 16) | (4 << 8),
+} EShTargetLanguageVersion;
+
+struct TInputLanguage {
+ EShSource languageFamily; // redundant information with other input, this one overrides when not EShSourceNone
+ EShLanguage stage; // redundant information with other input, this one overrides when not EShSourceNone
+ EShClient dialect;
+ int dialectVersion; // version of client's language definition, not the client (when not EShClientNone)
+};
+
+struct TClient {
+ EShClient client;
+ EShTargetClientVersion version; // version of client itself (not the client's input dialect)
+};
+
+struct TTarget {
+ EShTargetLanguage language;
+ EShTargetLanguageVersion version; // version to target, if SPIR-V, defined by "word 1" of the SPIR-V header
+ bool hlslFunctionality1; // can target hlsl_functionality1 extension(s)
+};
+
+// All source/client/target versions and settings.
+// Can override previous methods of setting, when items are set here.
+// Expected to grow, as more are added, rather than growing parameter lists.
+struct TEnvironment {
+ TInputLanguage input; // definition of the input language
+ TClient client; // what client is the overall compilation being done for?
+ TTarget target; // what to generate
+};
+
+const char* StageName(EShLanguage);
+
+} // end namespace glslang
+
+//
+// Types of output the linker will create.
+//
+typedef enum {
+ EShExVertexFragment,
+ EShExFragment
+} EShExecutable;
+
+//
+// Optimization level for the compiler.
+//
+typedef enum {
+ EShOptNoGeneration,
+ EShOptNone,
+ EShOptSimple, // Optimizations that can be done quickly
+ EShOptFull, // Optimizations that will take more time
+} EShOptimizationLevel;
+
+//
+// Texture and Sampler transformation mode.
+//
+typedef enum {
+ EShTexSampTransKeep, // keep textures and samplers as is (default)
+ EShTexSampTransUpgradeTextureRemoveSampler, // change texture w/o embeded sampler into sampled texture and throw away all samplers
+} EShTextureSamplerTransformMode;
+
+//
+// Message choices for what errors and warnings are given.
+//
+enum EShMessages {
+ EShMsgDefault = 0, // default is to give all required errors and extra warnings
+ EShMsgRelaxedErrors = (1 << 0), // be liberal in accepting input
+ EShMsgSuppressWarnings = (1 << 1), // suppress all warnings, except those required by the specification
+ EShMsgAST = (1 << 2), // print the AST intermediate representation
+ EShMsgSpvRules = (1 << 3), // issue messages for SPIR-V generation
+ EShMsgVulkanRules = (1 << 4), // issue messages for Vulkan-requirements of GLSL for SPIR-V
+ EShMsgOnlyPreprocessor = (1 << 5), // only print out errors produced by the preprocessor
+ EShMsgReadHlsl = (1 << 6), // use HLSL parsing rules and semantics
+ EShMsgCascadingErrors = (1 << 7), // get cascading errors; risks error-recovery issues, instead of an early exit
+ EShMsgKeepUncalled = (1 << 8), // for testing, don't eliminate uncalled functions
+ EShMsgHlslOffsets = (1 << 9), // allow block offsets to follow HLSL rules instead of GLSL rules
+ EShMsgDebugInfo = (1 << 10), // save debug information
+ EShMsgHlslEnable16BitTypes = (1 << 11), // enable use of 16-bit types in SPIR-V for HLSL
+ EShMsgHlslLegalization = (1 << 12), // enable HLSL Legalization messages
+ EShMsgHlslDX9Compatible = (1 << 13), // enable HLSL DX9 compatible mode (right now only for samplers)
+};
+
+//
+// Options for building reflection
+//
+typedef enum {
+ EShReflectionDefault = 0, // default is original behaviour before options were added
+ EShReflectionStrictArraySuffix = (1 << 0), // reflection will follow stricter rules for array-of-structs suffixes
+ EShReflectionBasicArraySuffix = (1 << 1), // arrays of basic types will be appended with [0] as in GL reflection
+ EShReflectionIntermediateIO = (1 << 2), // reflect inputs and outputs to program, even with no vertex shader
+ EShReflectionSeparateBuffers = (1 << 3), // buffer variables and buffer blocks are reflected separately
+ EShReflectionAllBlockVariables = (1 << 4), // reflect all variables in blocks, even if they are inactive
+ EShReflectionUnwrapIOBlocks = (1 << 5), // unwrap input/output blocks the same as with uniform blocks
+} EShReflectionOptions;
+
+//
+// Build a table for bindings. This can be used for locating
+// attributes, uniforms, globals, etc., as needed.
+//
+typedef struct {
+ const char* name;
+ int binding;
+} ShBinding;
+
+typedef struct {
+ int numBindings;
+ ShBinding* bindings; // array of bindings
+} ShBindingTable;
+
+//
+// ShHandle held by but opaque to the driver. It is allocated,
+// managed, and de-allocated by the compiler/linker. It's contents
+// are defined by and used by the compiler and linker. For example,
+// symbol table information and object code passed from the compiler
+// to the linker can be stored where ShHandle points.
+//
+// If handle creation fails, 0 will be returned.
+//
+typedef void* ShHandle;
+
+//
+// Driver calls these to create and destroy compiler/linker
+// objects.
+//
+SH_IMPORT_EXPORT ShHandle ShConstructCompiler(const EShLanguage, int debugOptions); // one per shader
+SH_IMPORT_EXPORT ShHandle ShConstructLinker(const EShExecutable, int debugOptions); // one per shader pair
+SH_IMPORT_EXPORT ShHandle ShConstructUniformMap(); // one per uniform namespace (currently entire program object)
+SH_IMPORT_EXPORT void ShDestruct(ShHandle);
+
+//
+// The return value of ShCompile is boolean, non-zero indicating
+// success.
+//
+// The info-log should be written by ShCompile into
+// ShHandle, so it can answer future queries.
+//
+SH_IMPORT_EXPORT int ShCompile(
+ const ShHandle,
+ const char* const shaderStrings[],
+ const int numStrings,
+ const int* lengths,
+ const EShOptimizationLevel,
+ const TBuiltInResource *resources,
+ int debugOptions,
+ int defaultVersion = 110, // use 100 for ES environment, overridden by #version in shader
+ bool forwardCompatible = false, // give errors for use of deprecated features
+ EShMessages messages = EShMsgDefault // warnings and errors
+ );
+
+SH_IMPORT_EXPORT int ShLinkExt(
+ const ShHandle, // linker object
+ const ShHandle h[], // compiler objects to link together
+ const int numHandles);
+
+//
+// ShSetEncrpytionMethod is a place-holder for specifying
+// how source code is encrypted.
+//
+SH_IMPORT_EXPORT void ShSetEncryptionMethod(ShHandle);
+
+//
+// All the following return 0 if the information is not
+// available in the object passed down, or the object is bad.
+//
+SH_IMPORT_EXPORT const char* ShGetInfoLog(const ShHandle);
+SH_IMPORT_EXPORT const void* ShGetExecutable(const ShHandle);
+SH_IMPORT_EXPORT int ShSetVirtualAttributeBindings(const ShHandle, const ShBindingTable*); // to detect user aliasing
+SH_IMPORT_EXPORT int ShSetFixedAttributeBindings(const ShHandle, const ShBindingTable*); // to force any physical mappings
+//
+// Tell the linker to never assign a vertex attribute to this list of physical attributes
+//
+SH_IMPORT_EXPORT int ShExcludeAttributes(const ShHandle, int *attributes, int count);
+
+//
+// Returns the location ID of the named uniform.
+// Returns -1 if error.
+//
+SH_IMPORT_EXPORT int ShGetUniformLocation(const ShHandle uniformMap, const char* name);
+
+#ifdef __cplusplus
+ } // end extern "C"
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Deferred-Lowering C++ Interface
+// -----------------------------------
+//
+// Below is a new alternate C++ interface, which deprecates the above
+// opaque handle-based interface.
+//
+// The below is further designed to handle multiple compilation units per stage, where
+// the intermediate results, including the parse tree, are preserved until link time,
+// rather than the above interface which is designed to have each compilation unit
+// lowered at compile time. In the above model, linking occurs on the lowered results,
+// whereas in this model intra-stage linking can occur at the parse tree
+// (treeRoot in TIntermediate) level, and then a full stage can be lowered.
+//
+
+#include <list>
+#include <string>
+#include <utility>
+
+class TCompiler;
+class TInfoSink;
+
+namespace glslang {
+
+const char* GetEsslVersionString();
+const char* GetGlslVersionString();
+int GetKhronosToolId();
+
+class TIntermediate;
+class TProgram;
+class TPoolAllocator;
+
+// Call this exactly once per process before using anything else
+bool InitializeProcess();
+
+// Call once per process to tear down everything
+void FinalizeProcess();
+
+// Resource type for IO resolver
+enum TResourceType {
+ EResSampler,
+ EResTexture,
+ EResImage,
+ EResUbo,
+ EResSsbo,
+ EResUav,
+ EResCount
+};
+
+// Make one TShader per shader that you will link into a program. Then
+// - provide the shader through setStrings() or setStringsWithLengths()
+// - optionally call setEnv*(), see below for more detail
+// - optionally use setPreamble() to set a special shader string that will be
+// processed before all others but won't affect the validity of #version
+// - call parse(): source language and target environment must be selected
+// either by correct setting of EShMessages sent to parse(), or by
+// explicitly calling setEnv*()
+// - query the info logs
+//
+// N.B.: Does not yet support having the same TShader instance being linked into
+// multiple programs.
+//
+// N.B.: Destruct a linked program *before* destructing the shaders linked into it.
+//
+class TShader {
+public:
+ explicit TShader(EShLanguage);
+ virtual ~TShader();
+ void setStrings(const char* const* s, int n);
+ void setStringsWithLengths(const char* const* s, const int* l, int n);
+ void setStringsWithLengthsAndNames(
+ const char* const* s, const int* l, const char* const* names, int n);
+ void setPreamble(const char* s) { preamble = s; }
+ void setEntryPoint(const char* entryPoint);
+ void setSourceEntryPoint(const char* sourceEntryPointName);
+ void addProcesses(const std::vector<std::string>&);
+
+ // IO resolver binding data: see comments in ShaderLang.cpp
+ void setShiftBinding(TResourceType res, unsigned int base);
+ void setShiftSamplerBinding(unsigned int base); // DEPRECATED: use setShiftBinding
+ void setShiftTextureBinding(unsigned int base); // DEPRECATED: use setShiftBinding
+ void setShiftImageBinding(unsigned int base); // DEPRECATED: use setShiftBinding
+ void setShiftUboBinding(unsigned int base); // DEPRECATED: use setShiftBinding
+ void setShiftUavBinding(unsigned int base); // DEPRECATED: use setShiftBinding
+ void setShiftCbufferBinding(unsigned int base); // synonym for setShiftUboBinding
+ void setShiftSsboBinding(unsigned int base); // DEPRECATED: use setShiftBinding
+ void setShiftBindingForSet(TResourceType res, unsigned int base, unsigned int set);
+ void setResourceSetBinding(const std::vector<std::string>& base);
+ void setAutoMapBindings(bool map);
+ void setAutoMapLocations(bool map);
+ void addUniformLocationOverride(const char* name, int loc);
+ void setUniformLocationBase(int base);
+ void setInvertY(bool invert);
+ void setHlslIoMapping(bool hlslIoMap);
+ void setFlattenUniformArrays(bool flatten);
+ void setNoStorageFormat(bool useUnknownFormat);
+ void setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode);
+
+ // For setting up the environment (cleared to nothingness in the constructor).
+ // These must be called so that parsing is done for the right source language and
+ // target environment, either indirectly through TranslateEnvironment() based on
+ // EShMessages et. al., or directly by the user.
+ void setEnvInput(EShSource lang, EShLanguage envStage, EShClient client, int version)
+ {
+ environment.input.languageFamily = lang;
+ environment.input.stage = envStage;
+ environment.input.dialect = client;
+ environment.input.dialectVersion = version;
+ }
+ void setEnvClient(EShClient client, EShTargetClientVersion version)
+ {
+ environment.client.client = client;
+ environment.client.version = version;
+ }
+ void setEnvTarget(EShTargetLanguage lang, EShTargetLanguageVersion version)
+ {
+ environment.target.language = lang;
+ environment.target.version = version;
+ }
+ void setEnvTargetHlslFunctionality1() { environment.target.hlslFunctionality1 = true; }
+ bool getEnvTargetHlslFunctionality1() const { return environment.target.hlslFunctionality1; }
+
+ // Interface to #include handlers.
+ //
+ // To support #include, a client of Glslang does the following:
+ // 1. Call setStringsWithNames to set the source strings and associated
+ // names. For example, the names could be the names of the files
+ // containing the shader sources.
+ // 2. Call parse with an Includer.
+ //
+ // When the Glslang parser encounters an #include directive, it calls
+ // the Includer's include method with the requested include name
+ // together with the current string name. The returned IncludeResult
+ // contains the fully resolved name of the included source, together
+ // with the source text that should replace the #include directive
+ // in the source stream. After parsing that source, Glslang will
+ // release the IncludeResult object.
+ class Includer {
+ public:
+ // An IncludeResult contains the resolved name and content of a source
+ // inclusion.
+ struct IncludeResult {
+ IncludeResult(const std::string& headerName, const char* const headerData, const size_t headerLength, void* userData) :
+ headerName(headerName), headerData(headerData), headerLength(headerLength), userData(userData) { }
+ // For a successful inclusion, the fully resolved name of the requested
+ // include. For example, in a file system-based includer, full resolution
+ // should convert a relative path name into an absolute path name.
+ // For a failed inclusion, this is an empty string.
+ const std::string headerName;
+ // The content and byte length of the requested inclusion. The
+ // Includer producing this IncludeResult retains ownership of the
+ // storage.
+ // For a failed inclusion, the header
+ // field points to a string containing error details.
+ const char* const headerData;
+ const size_t headerLength;
+ // Include resolver's context.
+ void* userData;
+ protected:
+ IncludeResult& operator=(const IncludeResult&);
+ IncludeResult();
+ };
+
+ // For both include methods below:
+ //
+ // Resolves an inclusion request by name, current source name,
+ // and include depth.
+ // On success, returns an IncludeResult containing the resolved name
+ // and content of the include.
+ // On failure, returns a nullptr, or an IncludeResult
+ // with an empty string for the headerName and error details in the
+ // header field.
+ // The Includer retains ownership of the contents
+ // of the returned IncludeResult value, and those contents must
+ // remain valid until the releaseInclude method is called on that
+ // IncludeResult object.
+ //
+ // Note "local" vs. "system" is not an "either/or": "local" is an
+ // extra thing to do over "system". Both might get called, as per
+ // the C++ specification.
+
+ // For the "system" or <>-style includes; search the "system" paths.
+ virtual IncludeResult* includeSystem(const char* /*headerName*/,
+ const char* /*includerName*/,
+ size_t /*inclusionDepth*/) { return nullptr; }
+
+ // For the "local"-only aspect of a "" include. Should not search in the
+ // "system" paths, because on returning a failure, the parser will
+ // call includeSystem() to look in the "system" locations.
+ virtual IncludeResult* includeLocal(const char* /*headerName*/,
+ const char* /*includerName*/,
+ size_t /*inclusionDepth*/) { return nullptr; }
+
+ // Signals that the parser will no longer use the contents of the
+ // specified IncludeResult.
+ virtual void releaseInclude(IncludeResult*) = 0;
+ virtual ~Includer() {}
+ };
+
+ // Fail all Includer searches
+ class ForbidIncluder : public Includer {
+ public:
+ virtual void releaseInclude(IncludeResult*) override { }
+ };
+
+ bool parse(const TBuiltInResource*, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile,
+ bool forwardCompatible, EShMessages, Includer&);
+
+ bool parse(const TBuiltInResource* res, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile,
+ bool forwardCompatible, EShMessages messages)
+ {
+ TShader::ForbidIncluder includer;
+ return parse(res, defaultVersion, defaultProfile, forceDefaultVersionAndProfile, forwardCompatible, messages, includer);
+ }
+
+ // Equivalent to parse() without a default profile and without forcing defaults.
+ bool parse(const TBuiltInResource* builtInResources, int defaultVersion, bool forwardCompatible, EShMessages messages)
+ {
+ return parse(builtInResources, defaultVersion, ENoProfile, false, forwardCompatible, messages);
+ }
+
+ bool parse(const TBuiltInResource* builtInResources, int defaultVersion, bool forwardCompatible, EShMessages messages,
+ Includer& includer)
+ {
+ return parse(builtInResources, defaultVersion, ENoProfile, false, forwardCompatible, messages, includer);
+ }
+
+ // NOTE: Doing just preprocessing to obtain a correct preprocessed shader string
+ // is not an officially supported or fully working path.
+ bool preprocess(const TBuiltInResource* builtInResources,
+ int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile,
+ bool forwardCompatible, EShMessages message, std::string* outputString,
+ Includer& includer);
+
+ const char* getInfoLog();
+ const char* getInfoDebugLog();
+ EShLanguage getStage() const { return stage; }
+ TIntermediate* getIntermediate() const { return intermediate; }
+
+protected:
+ TPoolAllocator* pool;
+ EShLanguage stage;
+ TCompiler* compiler;
+ TIntermediate* intermediate;
+ TInfoSink* infoSink;
+ // strings and lengths follow the standard for glShaderSource:
+ // strings is an array of numStrings pointers to string data.
+ // lengths can be null, but if not it is an array of numStrings
+ // integers containing the length of the associated strings.
+ // if lengths is null or lengths[n] < 0 the associated strings[n] is
+ // assumed to be null-terminated.
+ // stringNames is the optional names for all the strings. If stringNames
+ // is null, then none of the strings has name. If a certain element in
+ // stringNames is null, then the corresponding string does not have name.
+ const char* const* strings;
+ const int* lengths;
+ const char* const* stringNames;
+ const char* preamble;
+ int numStrings;
+
+ // a function in the source string can be renamed FROM this TO the name given in setEntryPoint.
+ std::string sourceEntryPointName;
+
+ TEnvironment environment;
+
+ friend class TProgram;
+
+private:
+ TShader& operator=(TShader&);
+};
+
+//
+// A reflection database and its interface, consistent with the OpenGL API reflection queries.
+//
+
+// Data needed for just a single object at the granularity exchanged by the reflection API
+class TObjectReflection {
+public:
+ TObjectReflection(const std::string& pName, const TType& pType, int pOffset, int pGLDefineType, int pSize, int pIndex);
+
+ const TType* getType() const { return type; }
+ int getBinding() const;
+ void dump() const;
+ static TObjectReflection badReflection() { return TObjectReflection(); }
+
+ std::string name;
+ int offset;
+ int glDefineType;
+ int size; // data size in bytes for a block, array size for a (non-block) object that's an array
+ int index;
+ int counterIndex;
+ int numMembers;
+ int arrayStride; // stride of an array variable
+ int topLevelArrayStride; // stride of the top-level variable in a storage buffer member
+ EShLanguageMask stages;
+
+protected:
+ TObjectReflection()
+ : offset(-1), glDefineType(-1), size(-1), index(-1), counterIndex(-1), numMembers(-1), arrayStride(0),
+ topLevelArrayStride(0), stages(EShLanguageMask(0)), type(nullptr)
+ {
+ }
+
+ const TType* type;
+};
+
+class TReflection;
+class TIoMapper;
+
+// Allows to customize the binding layout after linking.
+// All used uniform variables will invoke at least validateBinding.
+// If validateBinding returned true then the other resolveBinding,
+// resolveSet, and resolveLocation are invoked to resolve the binding
+// and descriptor set index respectively.
+//
+// Invocations happen in a particular order:
+// 1) all shader inputs
+// 2) all shader outputs
+// 3) all uniforms with binding and set already defined
+// 4) all uniforms with binding but no set defined
+// 5) all uniforms with set but no binding defined
+// 6) all uniforms with no binding and no set defined
+//
+// mapIO will use this resolver in two phases. The first
+// phase is a notification phase, calling the corresponging
+// notifiy callbacks, this phase ends with a call to endNotifications.
+// Phase two starts directly after the call to endNotifications
+// and calls all other callbacks to validate and to get the
+// bindings, sets, locations, component and color indices.
+//
+// NOTE: that still limit checks are applied to bindings and sets
+// and may result in an error.
+class TIoMapResolver
+{
+public:
+ virtual ~TIoMapResolver() {}
+
+ // Should return true if the resulting/current binding would be okay.
+ // Basic idea is to do aliasing binding checks with this.
+ virtual bool validateBinding(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0;
+ // Should return a value >= 0 if the current binding should be overridden.
+ // Return -1 if the current binding (including no binding) should be kept.
+ virtual int resolveBinding(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0;
+ // Should return a value >= 0 if the current set should be overridden.
+ // Return -1 if the current set (including no set) should be kept.
+ virtual int resolveSet(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0;
+ // Should return a value >= 0 if the current location should be overridden.
+ // Return -1 if the current location (including no location) should be kept.
+ virtual int resolveUniformLocation(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0;
+ // Should return true if the resulting/current setup would be okay.
+ // Basic idea is to do aliasing checks and reject invalid semantic names.
+ virtual bool validateInOut(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0;
+ // Should return a value >= 0 if the current location should be overridden.
+ // Return -1 if the current location (including no location) should be kept.
+ virtual int resolveInOutLocation(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0;
+ // Should return a value >= 0 if the current component index should be overridden.
+ // Return -1 if the current component index (including no index) should be kept.
+ virtual int resolveInOutComponent(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0;
+ // Should return a value >= 0 if the current color index should be overridden.
+ // Return -1 if the current color index (including no index) should be kept.
+ virtual int resolveInOutIndex(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0;
+ // Notification of a uniform variable
+ virtual void notifyBinding(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0;
+ // Notification of a in or out variable
+ virtual void notifyInOut(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0;
+ // Called by mapIO when it has finished the notify pass
+ virtual void endNotifications(EShLanguage stage) = 0;
+ // Called by mapIO when it starts its notify pass for the given stage
+ virtual void beginNotifications(EShLanguage stage) = 0;
+ // Called by mipIO when it starts its resolve pass for the given stage
+ virtual void beginResolve(EShLanguage stage) = 0;
+ // Called by mapIO when it has finished the resolve pass
+ virtual void endResolve(EShLanguage stage) = 0;
+};
+
+// Make one TProgram per set of shaders that will get linked together. Add all
+// the shaders that are to be linked together. After calling shader.parse()
+// for all shaders, call link().
+//
+// N.B.: Destruct a linked program *before* destructing the shaders linked into it.
+//
+class TProgram {
+public:
+ TProgram();
+ virtual ~TProgram();
+ void addShader(TShader* shader) { stages[shader->stage].push_back(shader); }
+
+ // Link Validation interface
+ bool link(EShMessages);
+ const char* getInfoLog();
+ const char* getInfoDebugLog();
+
+ TIntermediate* getIntermediate(EShLanguage stage) const { return intermediate[stage]; }
+
+ // Reflection Interface
+
+ // call first, to do liveness analysis, index mapping, etc.; returns false on failure
+ bool buildReflection(int opts = EShReflectionDefault);
+
+ unsigned getLocalSize(int dim) const; // return dim'th local size
+ int getReflectionIndex(const char *name) const;
+
+ int getNumUniformVariables() const;
+ const TObjectReflection& getUniform(int index) const;
+ int getNumUniformBlocks() const;
+ const TObjectReflection& getUniformBlock(int index) const;
+ int getNumPipeInputs() const;
+ const TObjectReflection& getPipeInput(int index) const;
+ int getNumPipeOutputs() const;
+ const TObjectReflection& getPipeOutput(int index) const;
+ int getNumBufferVariables() const;
+ const TObjectReflection& getBufferVariable(int index) const;
+ int getNumBufferBlocks() const;
+ const TObjectReflection& getBufferBlock(int index) const;
+ int getNumAtomicCounters() const;
+ const TObjectReflection& getAtomicCounter(int index) const;
+
+ // Legacy Reflection Interface - expressed in terms of above interface
+
+ // can be used for glGetProgramiv(GL_ACTIVE_UNIFORMS)
+ int getNumLiveUniformVariables() const { return getNumUniformVariables(); }
+
+ // can be used for glGetProgramiv(GL_ACTIVE_UNIFORM_BLOCKS)
+ int getNumLiveUniformBlocks() const { return getNumUniformBlocks(); }
+
+ // can be used for glGetProgramiv(GL_ACTIVE_ATTRIBUTES)
+ int getNumLiveAttributes() const { return getNumPipeInputs(); }
+
+ // can be used for glGetUniformIndices()
+ int getUniformIndex(const char *name) const { return getReflectionIndex(name); }
+
+ // can be used for "name" part of glGetActiveUniform()
+ const char *getUniformName(int index) const { return getUniform(index).name.c_str(); }
+
+ // returns the binding number
+ int getUniformBinding(int index) const { return getUniform(index).getBinding(); }
+
+ // returns Shaders Stages where a Uniform is present
+ EShLanguageMask getUniformStages(int index) const { return getUniform(index).stages; }
+
+ // can be used for glGetActiveUniformsiv(GL_UNIFORM_BLOCK_INDEX)
+ int getUniformBlockIndex(int index) const { return getUniform(index).index; }
+
+ // can be used for glGetActiveUniformsiv(GL_UNIFORM_TYPE)
+ int getUniformType(int index) const { return getUniform(index).glDefineType; }
+
+ // can be used for glGetActiveUniformsiv(GL_UNIFORM_OFFSET)
+ int getUniformBufferOffset(int index) const { return getUniform(index).offset; }
+
+ // can be used for glGetActiveUniformsiv(GL_UNIFORM_SIZE)
+ int getUniformArraySize(int index) const { return getUniform(index).size; }
+
+ // returns a TType*
+ const TType *getUniformTType(int index) const { return getUniform(index).getType(); }
+
+ // can be used for glGetActiveUniformBlockName()
+ const char *getUniformBlockName(int index) const { return getUniformBlock(index).name.c_str(); }
+
+ // can be used for glGetActiveUniformBlockiv(UNIFORM_BLOCK_DATA_SIZE)
+ int getUniformBlockSize(int index) const { return getUniformBlock(index).size; }
+
+ // returns the block binding number
+ int getUniformBlockBinding(int index) const { return getUniformBlock(index).getBinding(); }
+
+ // returns block index of associated counter.
+ int getUniformBlockCounterIndex(int index) const { return getUniformBlock(index).counterIndex; }
+
+ // returns a TType*
+ const TType *getUniformBlockTType(int index) const { return getUniformBlock(index).getType(); }
+
+ // can be used for glGetActiveAttrib()
+ const char *getAttributeName(int index) const { return getPipeInput(index).name.c_str(); }
+
+ // can be used for glGetActiveAttrib()
+ int getAttributeType(int index) const { return getPipeInput(index).glDefineType; }
+
+ // returns a TType*
+ const TType *getAttributeTType(int index) const { return getPipeInput(index).getType(); }
+
+ void dumpReflection();
+
+ // I/O mapping: apply base offsets and map live unbound variables
+ // If resolver is not provided it uses the previous approach
+ // and respects auto assignment and offsets.
+ bool mapIO(TIoMapResolver* resolver = NULL);
+
+protected:
+ bool linkStage(EShLanguage, EShMessages);
+
+ TPoolAllocator* pool;
+ std::list<TShader*> stages[EShLangCount];
+ TIntermediate* intermediate[EShLangCount];
+ bool newedIntermediate[EShLangCount]; // track which intermediate were "new" versus reusing a singleton unit in a stage
+ TInfoSink* infoSink;
+ TReflection* reflection;
+ TIoMapper* ioMapper;
+ bool linked;
+
+private:
+ TProgram(TProgram&);
+ TProgram& operator=(TProgram&);
+};
+
+} // end namespace glslang
+
+#endif // _COMPILER_INTERFACE_INCLUDED_
diff --git a/src/3rdparty/glslang/glslang/updateGrammar b/src/3rdparty/glslang/glslang/updateGrammar
new file mode 100644
index 0000000..a546dd2
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/updateGrammar
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+
+bison --defines=MachineIndependent/glslang_tab.cpp.h -t MachineIndependent/glslang.y -o MachineIndependent/glslang_tab.cpp
diff --git a/src/3rdparty/glslang/hlsl/hlslAttributes.cpp b/src/3rdparty/glslang/hlsl/hlslAttributes.cpp
new file mode 100644
index 0000000..261cec3
--- /dev/null
+++ b/src/3rdparty/glslang/hlsl/hlslAttributes.cpp
@@ -0,0 +1,106 @@
+//
+// Copyright (C) 2016 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of Google, Inc., nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "hlslAttributes.h"
+#include "hlslParseHelper.h"
+
+namespace glslang {
+ // Map the given string to an attribute enum from TAttributeType,
+ // or EatNone if invalid.
+ TAttributeType HlslParseContext::attributeFromName(const TString& nameSpace, const TString& name) const
+ {
+ // handle names within a namespace
+
+ if (nameSpace == "vk") {
+ if (name == "input_attachment_index")
+ return EatInputAttachment;
+ else if (name == "location")
+ return EatLocation;
+ else if (name == "binding")
+ return EatBinding;
+ else if (name == "global_cbuffer_binding")
+ return EatGlobalBinding;
+ else if (name == "builtin")
+ return EatBuiltIn;
+ else if (name == "constant_id")
+ return EatConstantId;
+ else if (name == "push_constant")
+ return EatPushConstant;
+ } else if (nameSpace.size() > 0)
+ return EatNone;
+
+ // handle names with no namespace
+
+ if (name == "allow_uav_condition")
+ return EatAllow_uav_condition;
+ else if (name == "branch")
+ return EatBranch;
+ else if (name == "call")
+ return EatCall;
+ else if (name == "domain")
+ return EatDomain;
+ else if (name == "earlydepthstencil")
+ return EatEarlyDepthStencil;
+ else if (name == "fastopt")
+ return EatFastOpt;
+ else if (name == "flatten")
+ return EatFlatten;
+ else if (name == "forcecase")
+ return EatForceCase;
+ else if (name == "instance")
+ return EatInstance;
+ else if (name == "maxtessfactor")
+ return EatMaxTessFactor;
+ else if (name == "maxvertexcount")
+ return EatMaxVertexCount;
+ else if (name == "numthreads")
+ return EatNumThreads;
+ else if (name == "outputcontrolpoints")
+ return EatOutputControlPoints;
+ else if (name == "outputtopology")
+ return EatOutputTopology;
+ else if (name == "partitioning")
+ return EatPartitioning;
+ else if (name == "patchconstantfunc")
+ return EatPatchConstantFunc;
+ else if (name == "unroll")
+ return EatUnroll;
+ else if (name == "loop")
+ return EatLoop;
+ else
+ return EatNone;
+ }
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/hlsl/hlslAttributes.h b/src/3rdparty/glslang/hlsl/hlslAttributes.h
new file mode 100644
index 0000000..b1cc037
--- /dev/null
+++ b/src/3rdparty/glslang/hlsl/hlslAttributes.h
@@ -0,0 +1,59 @@
+//
+// Copyright (C) 2016 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of Google, Inc., nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef HLSLATTRIBUTES_H_
+#define HLSLATTRIBUTES_H_
+
+#include <unordered_map>
+#include <functional>
+
+#include "../glslang/MachineIndependent/attribute.h"
+#include "../glslang/MachineIndependent/SymbolTable.h"
+#include "hlslScanContext.h"
+
+namespace glslang {
+
+ class TFunctionDeclarator {
+ public:
+ TFunctionDeclarator() : function(nullptr), body(nullptr) { }
+ TSourceLoc loc;
+ TFunction* function;
+ TAttributes attributes;
+ TVector<HlslToken>* body;
+ };
+
+} // end namespace glslang
+
+#endif // HLSLATTRIBUTES_H_
diff --git a/src/3rdparty/glslang/hlsl/hlslGrammar.cpp b/src/3rdparty/glslang/hlsl/hlslGrammar.cpp
new file mode 100644
index 0000000..45cf5d5
--- /dev/null
+++ b/src/3rdparty/glslang/hlsl/hlslGrammar.cpp
@@ -0,0 +1,4170 @@
+//
+// Copyright (C) 2016-2018 Google, Inc.
+// Copyright (C) 2016 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of Google, Inc., nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+//
+// This is a set of mutually recursive methods implementing the HLSL grammar.
+// Generally, each returns
+// - through an argument: a type specifically appropriate to which rule it
+// recognized
+// - through the return value: true/false to indicate whether or not it
+// recognized its rule
+//
+// As much as possible, only grammar recognition should happen in this file,
+// with all other work being farmed out to hlslParseHelper.cpp, which in turn
+// will build the AST.
+//
+// The next token, yet to be "accepted" is always sitting in 'token'.
+// When a method says it accepts a rule, that means all tokens involved
+// in the rule will have been consumed, and none left in 'token'.
+//
+
+#include "hlslTokens.h"
+#include "hlslGrammar.h"
+#include "hlslAttributes.h"
+
+namespace glslang {
+
+// Root entry point to this recursive decent parser.
+// Return true if compilation unit was successfully accepted.
+bool HlslGrammar::parse()
+{
+ advanceToken();
+ return acceptCompilationUnit();
+}
+
+void HlslGrammar::expected(const char* syntax)
+{
+ parseContext.error(token.loc, "Expected", syntax, "");
+}
+
+void HlslGrammar::unimplemented(const char* error)
+{
+ parseContext.error(token.loc, "Unimplemented", error, "");
+}
+
+// IDENTIFIER
+// THIS
+// type that can be used as IDENTIFIER
+//
+// Only process the next token if it is an identifier.
+// Return true if it was an identifier.
+bool HlslGrammar::acceptIdentifier(HlslToken& idToken)
+{
+ // IDENTIFIER
+ if (peekTokenClass(EHTokIdentifier)) {
+ idToken = token;
+ advanceToken();
+ return true;
+ }
+
+ // THIS
+ // -> maps to the IDENTIFIER spelled with the internal special name for 'this'
+ if (peekTokenClass(EHTokThis)) {
+ idToken = token;
+ advanceToken();
+ idToken.tokenClass = EHTokIdentifier;
+ idToken.string = NewPoolTString(intermediate.implicitThisName);
+ return true;
+ }
+
+ // type that can be used as IDENTIFIER
+
+ // Even though "sample", "bool", "float", etc keywords (for types, interpolation modifiers),
+ // they ARE still accepted as identifiers. This is not a dense space: e.g, "void" is not a
+ // valid identifier, nor is "linear". This code special cases the known instances of this, so
+ // e.g, "int sample;" or "float float;" is accepted. Other cases can be added here if needed.
+
+ const char* idString = getTypeString(peek());
+ if (idString == nullptr)
+ return false;
+
+ token.string = NewPoolTString(idString);
+ token.tokenClass = EHTokIdentifier;
+ idToken = token;
+ typeIdentifiers = true;
+
+ advanceToken();
+
+ return true;
+}
+
+// compilationUnit
+// : declaration_list EOF
+//
+bool HlslGrammar::acceptCompilationUnit()
+{
+ if (! acceptDeclarationList(unitNode))
+ return false;
+
+ if (! peekTokenClass(EHTokNone))
+ return false;
+
+ // set root of AST
+ if (unitNode && !unitNode->getAsAggregate())
+ unitNode = intermediate.growAggregate(nullptr, unitNode);
+ intermediate.setTreeRoot(unitNode);
+
+ return true;
+}
+
+// Recognize the following, but with the extra condition that it can be
+// successfully terminated by EOF or '}'.
+//
+// declaration_list
+// : list of declaration_or_semicolon followed by EOF or RIGHT_BRACE
+//
+// declaration_or_semicolon
+// : declaration
+// : SEMICOLON
+//
+bool HlslGrammar::acceptDeclarationList(TIntermNode*& nodeList)
+{
+ do {
+ // HLSL allows extra semicolons between global declarations
+ do { } while (acceptTokenClass(EHTokSemicolon));
+
+ // EOF or RIGHT_BRACE
+ if (peekTokenClass(EHTokNone) || peekTokenClass(EHTokRightBrace))
+ return true;
+
+ // declaration
+ if (! acceptDeclaration(nodeList))
+ return false;
+ } while (true);
+
+ return true;
+}
+
+// sampler_state
+// : LEFT_BRACE [sampler_state_assignment ... ] RIGHT_BRACE
+//
+// sampler_state_assignment
+// : sampler_state_identifier EQUAL value SEMICOLON
+//
+// sampler_state_identifier
+// : ADDRESSU
+// | ADDRESSV
+// | ADDRESSW
+// | BORDERCOLOR
+// | FILTER
+// | MAXANISOTROPY
+// | MAXLOD
+// | MINLOD
+// | MIPLODBIAS
+//
+bool HlslGrammar::acceptSamplerState()
+{
+ // TODO: this should be genericized to accept a list of valid tokens and
+ // return token/value pairs. Presently it is specific to texture values.
+
+ if (! acceptTokenClass(EHTokLeftBrace))
+ return true;
+
+ parseContext.warn(token.loc, "unimplemented", "immediate sampler state", "");
+
+ do {
+ // read state name
+ HlslToken state;
+ if (! acceptIdentifier(state))
+ break; // end of list
+
+ // FXC accepts any case
+ TString stateName = *state.string;
+ std::transform(stateName.begin(), stateName.end(), stateName.begin(), ::tolower);
+
+ if (! acceptTokenClass(EHTokAssign)) {
+ expected("assign");
+ return false;
+ }
+
+ if (stateName == "minlod" || stateName == "maxlod") {
+ if (! peekTokenClass(EHTokIntConstant)) {
+ expected("integer");
+ return false;
+ }
+
+ TIntermTyped* lod = nullptr;
+ if (! acceptLiteral(lod)) // should never fail, since we just looked for an integer
+ return false;
+ } else if (stateName == "maxanisotropy") {
+ if (! peekTokenClass(EHTokIntConstant)) {
+ expected("integer");
+ return false;
+ }
+
+ TIntermTyped* maxAnisotropy = nullptr;
+ if (! acceptLiteral(maxAnisotropy)) // should never fail, since we just looked for an integer
+ return false;
+ } else if (stateName == "filter") {
+ HlslToken filterMode;
+ if (! acceptIdentifier(filterMode)) {
+ expected("filter mode");
+ return false;
+ }
+ } else if (stateName == "addressu" || stateName == "addressv" || stateName == "addressw") {
+ HlslToken addrMode;
+ if (! acceptIdentifier(addrMode)) {
+ expected("texture address mode");
+ return false;
+ }
+ } else if (stateName == "miplodbias") {
+ TIntermTyped* lodBias = nullptr;
+ if (! acceptLiteral(lodBias)) {
+ expected("lod bias");
+ return false;
+ }
+ } else if (stateName == "bordercolor") {
+ return false;
+ } else {
+ expected("texture state");
+ return false;
+ }
+
+ // SEMICOLON
+ if (! acceptTokenClass(EHTokSemicolon)) {
+ expected("semicolon");
+ return false;
+ }
+ } while (true);
+
+ if (! acceptTokenClass(EHTokRightBrace))
+ return false;
+
+ return true;
+}
+
+// sampler_declaration_dx9
+// : SAMPLER identifier EQUAL sampler_type sampler_state
+//
+bool HlslGrammar::acceptSamplerDeclarationDX9(TType& /*type*/)
+{
+ if (! acceptTokenClass(EHTokSampler))
+ return false;
+
+ // TODO: remove this when DX9 style declarations are implemented.
+ unimplemented("Direct3D 9 sampler declaration");
+
+ // read sampler name
+ HlslToken name;
+ if (! acceptIdentifier(name)) {
+ expected("sampler name");
+ return false;
+ }
+
+ if (! acceptTokenClass(EHTokAssign)) {
+ expected("=");
+ return false;
+ }
+
+ return false;
+}
+
+// declaration
+// : attributes attributed_declaration
+// | NAMESPACE IDENTIFIER LEFT_BRACE declaration_list RIGHT_BRACE
+//
+// attributed_declaration
+// : sampler_declaration_dx9 post_decls SEMICOLON
+// | fully_specified_type // for cbuffer/tbuffer
+// | fully_specified_type declarator_list SEMICOLON // for non cbuffer/tbuffer
+// | fully_specified_type identifier function_parameters post_decls compound_statement // function definition
+// | fully_specified_type identifier sampler_state post_decls compound_statement // sampler definition
+// | typedef declaration
+//
+// declarator_list
+// : declarator COMMA declarator COMMA declarator... // zero or more declarators
+//
+// declarator
+// : identifier array_specifier post_decls
+// | identifier array_specifier post_decls EQUAL assignment_expression
+// | identifier function_parameters post_decls // function prototype
+//
+// Parsing has to go pretty far in to know whether it's a variable, prototype, or
+// function definition, so the implementation below doesn't perfectly divide up the grammar
+// as above. (The 'identifier' in the first item in init_declarator list is the
+// same as 'identifier' for function declarations.)
+//
+// This can generate more than one subtree, one per initializer or a function body.
+// All initializer subtrees are put in their own aggregate node, making one top-level
+// node for all the initializers. Each function created is a top-level node to grow
+// into the passed-in nodeList.
+//
+// If 'nodeList' is passed in as non-null, it must be an aggregate to extend for
+// each top-level node the declaration creates. Otherwise, if only one top-level
+// node in generated here, that is want is returned in nodeList.
+//
+bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList)
+{
+ // NAMESPACE IDENTIFIER LEFT_BRACE declaration_list RIGHT_BRACE
+ if (acceptTokenClass(EHTokNamespace)) {
+ HlslToken namespaceToken;
+ if (!acceptIdentifier(namespaceToken)) {
+ expected("namespace name");
+ return false;
+ }
+ parseContext.pushNamespace(*namespaceToken.string);
+ if (!acceptTokenClass(EHTokLeftBrace)) {
+ expected("{");
+ return false;
+ }
+ if (!acceptDeclarationList(nodeList)) {
+ expected("declaration list");
+ return false;
+ }
+ if (!acceptTokenClass(EHTokRightBrace)) {
+ expected("}");
+ return false;
+ }
+ parseContext.popNamespace();
+ return true;
+ }
+
+ bool declarator_list = false; // true when processing comma separation
+
+ // attributes
+ TFunctionDeclarator declarator;
+ acceptAttributes(declarator.attributes);
+
+ // typedef
+ bool typedefDecl = acceptTokenClass(EHTokTypedef);
+
+ TType declaredType;
+
+ // DX9 sampler declaration use a different syntax
+ // DX9 shaders need to run through HLSL compiler (fxc) via a back compat mode, it isn't going to
+ // be possible to simultaneously compile D3D10+ style shaders and DX9 shaders. If we want to compile DX9
+ // HLSL shaders, this will have to be a master level switch
+ // As such, the sampler keyword in D3D10+ turns into an automatic sampler type, and is commonly used
+ // For that reason, this line is commented out
+ // if (acceptSamplerDeclarationDX9(declaredType))
+ // return true;
+
+ bool forbidDeclarators = (peekTokenClass(EHTokCBuffer) || peekTokenClass(EHTokTBuffer));
+ // fully_specified_type
+ if (! acceptFullySpecifiedType(declaredType, nodeList, declarator.attributes, forbidDeclarators))
+ return false;
+
+ // cbuffer and tbuffer end with the closing '}'.
+ // No semicolon is included.
+ if (forbidDeclarators)
+ return true;
+
+ // declarator_list
+ // : declarator
+ // : identifier
+ HlslToken idToken;
+ TIntermAggregate* initializers = nullptr;
+ while (acceptIdentifier(idToken)) {
+ TString *fullName = idToken.string;
+ if (parseContext.symbolTable.atGlobalLevel())
+ parseContext.getFullNamespaceName(fullName);
+ if (peekTokenClass(EHTokLeftParen)) {
+ // looks like function parameters
+
+ // merge in the attributes into the return type
+ parseContext.transferTypeAttributes(token.loc, declarator.attributes, declaredType, true);
+
+ // Potentially rename shader entry point function. No-op most of the time.
+ parseContext.renameShaderFunction(fullName);
+
+ // function_parameters
+ declarator.function = new TFunction(fullName, declaredType);
+ if (!acceptFunctionParameters(*declarator.function)) {
+ expected("function parameter list");
+ return false;
+ }
+
+ // post_decls
+ acceptPostDecls(declarator.function->getWritableType().getQualifier());
+
+ // compound_statement (function body definition) or just a prototype?
+ declarator.loc = token.loc;
+ if (peekTokenClass(EHTokLeftBrace)) {
+ if (declarator_list)
+ parseContext.error(idToken.loc, "function body can't be in a declarator list", "{", "");
+ if (typedefDecl)
+ parseContext.error(idToken.loc, "function body can't be in a typedef", "{", "");
+ return acceptFunctionDefinition(declarator, nodeList, nullptr);
+ } else {
+ if (typedefDecl)
+ parseContext.error(idToken.loc, "function typedefs not implemented", "{", "");
+ parseContext.handleFunctionDeclarator(declarator.loc, *declarator.function, true);
+ }
+ } else {
+ // A variable declaration.
+
+ // merge in the attributes, the first time around, into the shared type
+ if (! declarator_list)
+ parseContext.transferTypeAttributes(token.loc, declarator.attributes, declaredType);
+
+ // Fix the storage qualifier if it's a global.
+ if (declaredType.getQualifier().storage == EvqTemporary && parseContext.symbolTable.atGlobalLevel())
+ declaredType.getQualifier().storage = EvqUniform;
+
+ // recognize array_specifier
+ TArraySizes* arraySizes = nullptr;
+ acceptArraySpecifier(arraySizes);
+
+ // We can handle multiple variables per type declaration, so
+ // the number of types can expand when arrayness is different.
+ TType variableType;
+ variableType.shallowCopy(declaredType);
+
+ // In the most general case, arrayness is potentially coming both from the
+ // declared type and from the variable: "int[] a[];" or just one or the other.
+ // Merge it all to the variableType, so all arrayness is part of the variableType.
+ variableType.transferArraySizes(arraySizes);
+ variableType.copyArrayInnerSizes(declaredType.getArraySizes());
+
+ // samplers accept immediate sampler state
+ if (variableType.getBasicType() == EbtSampler) {
+ if (! acceptSamplerState())
+ return false;
+ }
+
+ // post_decls
+ acceptPostDecls(variableType.getQualifier());
+
+ // EQUAL assignment_expression
+ TIntermTyped* expressionNode = nullptr;
+ if (acceptTokenClass(EHTokAssign)) {
+ if (typedefDecl)
+ parseContext.error(idToken.loc, "can't have an initializer", "typedef", "");
+ if (! acceptAssignmentExpression(expressionNode)) {
+ expected("initializer");
+ return false;
+ }
+ }
+
+ // TODO: things scoped within an annotation need their own name space;
+ // TODO: strings are not yet handled.
+ if (variableType.getBasicType() != EbtString && parseContext.getAnnotationNestingLevel() == 0) {
+ if (typedefDecl)
+ parseContext.declareTypedef(idToken.loc, *fullName, variableType);
+ else if (variableType.getBasicType() == EbtBlock) {
+ if (expressionNode)
+ parseContext.error(idToken.loc, "buffer aliasing not yet supported", "block initializer", "");
+ parseContext.declareBlock(idToken.loc, variableType, fullName);
+ parseContext.declareStructBufferCounter(idToken.loc, variableType, *fullName);
+ } else {
+ if (variableType.getQualifier().storage == EvqUniform && ! variableType.containsOpaque()) {
+ // this isn't really an individual variable, but a member of the $Global buffer
+ parseContext.growGlobalUniformBlock(idToken.loc, variableType, *fullName);
+ } else {
+ // Declare the variable and add any initializer code to the AST.
+ // The top-level node is always made into an aggregate, as that's
+ // historically how the AST has been.
+ initializers = intermediate.growAggregate(initializers,
+ parseContext.declareVariable(idToken.loc, *fullName, variableType, expressionNode),
+ idToken.loc);
+ }
+ }
+ }
+ }
+
+ // COMMA
+ if (acceptTokenClass(EHTokComma))
+ declarator_list = true;
+ }
+
+ // The top-level initializer node is a sequence.
+ if (initializers != nullptr)
+ initializers->setOperator(EOpSequence);
+
+ // if we have a locally scoped static, it needs a globally scoped initializer
+ if (declaredType.getQualifier().storage == EvqGlobal && !parseContext.symbolTable.atGlobalLevel()) {
+ unitNode = intermediate.growAggregate(unitNode, initializers, idToken.loc);
+ } else {
+ // Add the initializers' aggregate to the nodeList we were handed.
+ if (nodeList)
+ nodeList = intermediate.growAggregate(nodeList, initializers);
+ else
+ nodeList = initializers;
+ }
+
+ // SEMICOLON
+ if (! acceptTokenClass(EHTokSemicolon)) {
+ // This may have been a false detection of what appeared to be a declaration, but
+ // was actually an assignment such as "float = 4", where "float" is an identifier.
+ // We put the token back to let further parsing happen for cases where that may
+ // happen. This errors on the side of caution, and mostly triggers the error.
+ if (peek() == EHTokAssign || peek() == EHTokLeftBracket || peek() == EHTokDot || peek() == EHTokComma)
+ recedeToken();
+ else
+ expected(";");
+ return false;
+ }
+
+ return true;
+}
+
+// control_declaration
+// : fully_specified_type identifier EQUAL expression
+//
+bool HlslGrammar::acceptControlDeclaration(TIntermNode*& node)
+{
+ node = nullptr;
+ TAttributes attributes;
+
+ // fully_specified_type
+ TType type;
+ if (! acceptFullySpecifiedType(type, attributes))
+ return false;
+
+ if (attributes.size() > 0)
+ parseContext.warn(token.loc, "attributes don't apply to control declaration", "", "");
+
+ // filter out type casts
+ if (peekTokenClass(EHTokLeftParen)) {
+ recedeToken();
+ return false;
+ }
+
+ // identifier
+ HlslToken idToken;
+ if (! acceptIdentifier(idToken)) {
+ expected("identifier");
+ return false;
+ }
+
+ // EQUAL
+ TIntermTyped* expressionNode = nullptr;
+ if (! acceptTokenClass(EHTokAssign)) {
+ expected("=");
+ return false;
+ }
+
+ // expression
+ if (! acceptExpression(expressionNode)) {
+ expected("initializer");
+ return false;
+ }
+
+ node = parseContext.declareVariable(idToken.loc, *idToken.string, type, expressionNode);
+
+ return true;
+}
+
+// fully_specified_type
+// : type_specifier
+// | type_qualifier type_specifier
+//
+bool HlslGrammar::acceptFullySpecifiedType(TType& type, const TAttributes& attributes)
+{
+ TIntermNode* nodeList = nullptr;
+ return acceptFullySpecifiedType(type, nodeList, attributes);
+}
+bool HlslGrammar::acceptFullySpecifiedType(TType& type, TIntermNode*& nodeList, const TAttributes& attributes, bool forbidDeclarators)
+{
+ // type_qualifier
+ TQualifier qualifier;
+ qualifier.clear();
+ if (! acceptQualifier(qualifier))
+ return false;
+ TSourceLoc loc = token.loc;
+
+ // type_specifier
+ if (! acceptType(type, nodeList)) {
+ // If this is not a type, we may have inadvertently gone down a wrong path
+ // by parsing "sample", which can be treated like either an identifier or a
+ // qualifier. Back it out, if we did.
+ if (qualifier.sample)
+ recedeToken();
+
+ return false;
+ }
+
+ if (type.getBasicType() == EbtBlock) {
+ // the type was a block, which set some parts of the qualifier
+ parseContext.mergeQualifiers(type.getQualifier(), qualifier);
+
+ // merge in the attributes
+ parseContext.transferTypeAttributes(token.loc, attributes, type);
+
+ // further, it can create an anonymous instance of the block
+ // (cbuffer and tbuffer don't consume the next identifier, and
+ // should set forbidDeclarators)
+ if (forbidDeclarators || peek() != EHTokIdentifier)
+ parseContext.declareBlock(loc, type);
+ } else {
+ // Some qualifiers are set when parsing the type. Merge those with
+ // whatever comes from acceptQualifier.
+ assert(qualifier.layoutFormat == ElfNone);
+
+ qualifier.layoutFormat = type.getQualifier().layoutFormat;
+ qualifier.precision = type.getQualifier().precision;
+
+ if (type.getQualifier().storage == EvqOut ||
+ type.getQualifier().storage == EvqBuffer) {
+ qualifier.storage = type.getQualifier().storage;
+ qualifier.readonly = type.getQualifier().readonly;
+ }
+
+ if (type.isBuiltIn())
+ qualifier.builtIn = type.getQualifier().builtIn;
+
+ type.getQualifier() = qualifier;
+ }
+
+ return true;
+}
+
+// type_qualifier
+// : qualifier qualifier ...
+//
+// Zero or more of these, so this can't return false.
+//
+bool HlslGrammar::acceptQualifier(TQualifier& qualifier)
+{
+ do {
+ switch (peek()) {
+ case EHTokStatic:
+ qualifier.storage = EvqGlobal;
+ break;
+ case EHTokExtern:
+ // TODO: no meaning in glslang?
+ break;
+ case EHTokShared:
+ // TODO: hint
+ break;
+ case EHTokGroupShared:
+ qualifier.storage = EvqShared;
+ break;
+ case EHTokUniform:
+ qualifier.storage = EvqUniform;
+ break;
+ case EHTokConst:
+ qualifier.storage = EvqConst;
+ break;
+ case EHTokVolatile:
+ qualifier.volatil = true;
+ break;
+ case EHTokLinear:
+ qualifier.smooth = true;
+ break;
+ case EHTokCentroid:
+ qualifier.centroid = true;
+ break;
+ case EHTokNointerpolation:
+ qualifier.flat = true;
+ break;
+ case EHTokNoperspective:
+ qualifier.nopersp = true;
+ break;
+ case EHTokSample:
+ qualifier.sample = true;
+ break;
+ case EHTokRowMajor:
+ qualifier.layoutMatrix = ElmColumnMajor;
+ break;
+ case EHTokColumnMajor:
+ qualifier.layoutMatrix = ElmRowMajor;
+ break;
+ case EHTokPrecise:
+ qualifier.noContraction = true;
+ break;
+ case EHTokIn:
+ qualifier.storage = (qualifier.storage == EvqOut) ? EvqInOut : EvqIn;
+ break;
+ case EHTokOut:
+ qualifier.storage = (qualifier.storage == EvqIn) ? EvqInOut : EvqOut;
+ break;
+ case EHTokInOut:
+ qualifier.storage = EvqInOut;
+ break;
+ case EHTokLayout:
+ if (! acceptLayoutQualifierList(qualifier))
+ return false;
+ continue;
+ case EHTokGloballyCoherent:
+ qualifier.coherent = true;
+ break;
+ case EHTokInline:
+ // TODO: map this to SPIR-V function control
+ break;
+
+ // GS geometries: these are specified on stage input variables, and are an error (not verified here)
+ // for output variables.
+ case EHTokPoint:
+ qualifier.storage = EvqIn;
+ if (!parseContext.handleInputGeometry(token.loc, ElgPoints))
+ return false;
+ break;
+ case EHTokLine:
+ qualifier.storage = EvqIn;
+ if (!parseContext.handleInputGeometry(token.loc, ElgLines))
+ return false;
+ break;
+ case EHTokTriangle:
+ qualifier.storage = EvqIn;
+ if (!parseContext.handleInputGeometry(token.loc, ElgTriangles))
+ return false;
+ break;
+ case EHTokLineAdj:
+ qualifier.storage = EvqIn;
+ if (!parseContext.handleInputGeometry(token.loc, ElgLinesAdjacency))
+ return false;
+ break;
+ case EHTokTriangleAdj:
+ qualifier.storage = EvqIn;
+ if (!parseContext.handleInputGeometry(token.loc, ElgTrianglesAdjacency))
+ return false;
+ break;
+
+ default:
+ return true;
+ }
+ advanceToken();
+ } while (true);
+}
+
+// layout_qualifier_list
+// : LAYOUT LEFT_PAREN layout_qualifier COMMA layout_qualifier ... RIGHT_PAREN
+//
+// layout_qualifier
+// : identifier
+// | identifier EQUAL expression
+//
+// Zero or more of these, so this can't return false.
+//
+bool HlslGrammar::acceptLayoutQualifierList(TQualifier& qualifier)
+{
+ if (! acceptTokenClass(EHTokLayout))
+ return false;
+
+ // LEFT_PAREN
+ if (! acceptTokenClass(EHTokLeftParen))
+ return false;
+
+ do {
+ // identifier
+ HlslToken idToken;
+ if (! acceptIdentifier(idToken))
+ break;
+
+ // EQUAL expression
+ if (acceptTokenClass(EHTokAssign)) {
+ TIntermTyped* expr;
+ if (! acceptConditionalExpression(expr)) {
+ expected("expression");
+ return false;
+ }
+ parseContext.setLayoutQualifier(idToken.loc, qualifier, *idToken.string, expr);
+ } else
+ parseContext.setLayoutQualifier(idToken.loc, qualifier, *idToken.string);
+
+ // COMMA
+ if (! acceptTokenClass(EHTokComma))
+ break;
+ } while (true);
+
+ // RIGHT_PAREN
+ if (! acceptTokenClass(EHTokRightParen)) {
+ expected(")");
+ return false;
+ }
+
+ return true;
+}
+
+// template_type
+// : FLOAT
+// | DOUBLE
+// | INT
+// | DWORD
+// | UINT
+// | BOOL
+//
+bool HlslGrammar::acceptTemplateVecMatBasicType(TBasicType& basicType)
+{
+ switch (peek()) {
+ case EHTokFloat:
+ basicType = EbtFloat;
+ break;
+ case EHTokDouble:
+ basicType = EbtDouble;
+ break;
+ case EHTokInt:
+ case EHTokDword:
+ basicType = EbtInt;
+ break;
+ case EHTokUint:
+ basicType = EbtUint;
+ break;
+ case EHTokBool:
+ basicType = EbtBool;
+ break;
+ default:
+ return false;
+ }
+
+ advanceToken();
+
+ return true;
+}
+
+// vector_template_type
+// : VECTOR
+// | VECTOR LEFT_ANGLE template_type COMMA integer_literal RIGHT_ANGLE
+//
+bool HlslGrammar::acceptVectorTemplateType(TType& type)
+{
+ if (! acceptTokenClass(EHTokVector))
+ return false;
+
+ if (! acceptTokenClass(EHTokLeftAngle)) {
+ // in HLSL, 'vector' alone means float4.
+ new(&type) TType(EbtFloat, EvqTemporary, 4);
+ return true;
+ }
+
+ TBasicType basicType;
+ if (! acceptTemplateVecMatBasicType(basicType)) {
+ expected("scalar type");
+ return false;
+ }
+
+ // COMMA
+ if (! acceptTokenClass(EHTokComma)) {
+ expected(",");
+ return false;
+ }
+
+ // integer
+ if (! peekTokenClass(EHTokIntConstant)) {
+ expected("literal integer");
+ return false;
+ }
+
+ TIntermTyped* vecSize;
+ if (! acceptLiteral(vecSize))
+ return false;
+
+ const int vecSizeI = vecSize->getAsConstantUnion()->getConstArray()[0].getIConst();
+
+ new(&type) TType(basicType, EvqTemporary, vecSizeI);
+
+ if (vecSizeI == 1)
+ type.makeVector();
+
+ if (!acceptTokenClass(EHTokRightAngle)) {
+ expected("right angle bracket");
+ return false;
+ }
+
+ return true;
+}
+
+// matrix_template_type
+// : MATRIX
+// | MATRIX LEFT_ANGLE template_type COMMA integer_literal COMMA integer_literal RIGHT_ANGLE
+//
+bool HlslGrammar::acceptMatrixTemplateType(TType& type)
+{
+ if (! acceptTokenClass(EHTokMatrix))
+ return false;
+
+ if (! acceptTokenClass(EHTokLeftAngle)) {
+ // in HLSL, 'matrix' alone means float4x4.
+ new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 4);
+ return true;
+ }
+
+ TBasicType basicType;
+ if (! acceptTemplateVecMatBasicType(basicType)) {
+ expected("scalar type");
+ return false;
+ }
+
+ // COMMA
+ if (! acceptTokenClass(EHTokComma)) {
+ expected(",");
+ return false;
+ }
+
+ // integer rows
+ if (! peekTokenClass(EHTokIntConstant)) {
+ expected("literal integer");
+ return false;
+ }
+
+ TIntermTyped* rows;
+ if (! acceptLiteral(rows))
+ return false;
+
+ // COMMA
+ if (! acceptTokenClass(EHTokComma)) {
+ expected(",");
+ return false;
+ }
+
+ // integer cols
+ if (! peekTokenClass(EHTokIntConstant)) {
+ expected("literal integer");
+ return false;
+ }
+
+ TIntermTyped* cols;
+ if (! acceptLiteral(cols))
+ return false;
+
+ new(&type) TType(basicType, EvqTemporary, 0,
+ rows->getAsConstantUnion()->getConstArray()[0].getIConst(),
+ cols->getAsConstantUnion()->getConstArray()[0].getIConst());
+
+ if (!acceptTokenClass(EHTokRightAngle)) {
+ expected("right angle bracket");
+ return false;
+ }
+
+ return true;
+}
+
+// layout_geometry
+// : LINESTREAM
+// | POINTSTREAM
+// | TRIANGLESTREAM
+//
+bool HlslGrammar::acceptOutputPrimitiveGeometry(TLayoutGeometry& geometry)
+{
+ // read geometry type
+ const EHlslTokenClass geometryType = peek();
+
+ switch (geometryType) {
+ case EHTokPointStream: geometry = ElgPoints; break;
+ case EHTokLineStream: geometry = ElgLineStrip; break;
+ case EHTokTriangleStream: geometry = ElgTriangleStrip; break;
+ default:
+ return false; // not a layout geometry
+ }
+
+ advanceToken(); // consume the layout keyword
+ return true;
+}
+
+// tessellation_decl_type
+// : INPUTPATCH
+// | OUTPUTPATCH
+//
+bool HlslGrammar::acceptTessellationDeclType(TBuiltInVariable& patchType)
+{
+ // read geometry type
+ const EHlslTokenClass tessType = peek();
+
+ switch (tessType) {
+ case EHTokInputPatch: patchType = EbvInputPatch; break;
+ case EHTokOutputPatch: patchType = EbvOutputPatch; break;
+ default:
+ return false; // not a tessellation decl
+ }
+
+ advanceToken(); // consume the keyword
+ return true;
+}
+
+// tessellation_patch_template_type
+// : tessellation_decl_type LEFT_ANGLE type comma integer_literal RIGHT_ANGLE
+//
+bool HlslGrammar::acceptTessellationPatchTemplateType(TType& type)
+{
+ TBuiltInVariable patchType;
+
+ if (! acceptTessellationDeclType(patchType))
+ return false;
+
+ if (! acceptTokenClass(EHTokLeftAngle))
+ return false;
+
+ if (! acceptType(type)) {
+ expected("tessellation patch type");
+ return false;
+ }
+
+ if (! acceptTokenClass(EHTokComma))
+ return false;
+
+ // integer size
+ if (! peekTokenClass(EHTokIntConstant)) {
+ expected("literal integer");
+ return false;
+ }
+
+ TIntermTyped* size;
+ if (! acceptLiteral(size))
+ return false;
+
+ TArraySizes* arraySizes = new TArraySizes;
+ arraySizes->addInnerSize(size->getAsConstantUnion()->getConstArray()[0].getIConst());
+ type.transferArraySizes(arraySizes);
+ type.getQualifier().builtIn = patchType;
+
+ if (! acceptTokenClass(EHTokRightAngle)) {
+ expected("right angle bracket");
+ return false;
+ }
+
+ return true;
+}
+
+// stream_out_template_type
+// : output_primitive_geometry_type LEFT_ANGLE type RIGHT_ANGLE
+//
+bool HlslGrammar::acceptStreamOutTemplateType(TType& type, TLayoutGeometry& geometry)
+{
+ geometry = ElgNone;
+
+ if (! acceptOutputPrimitiveGeometry(geometry))
+ return false;
+
+ if (! acceptTokenClass(EHTokLeftAngle))
+ return false;
+
+ if (! acceptType(type)) {
+ expected("stream output type");
+ return false;
+ }
+
+ type.getQualifier().storage = EvqOut;
+ type.getQualifier().builtIn = EbvGsOutputStream;
+
+ if (! acceptTokenClass(EHTokRightAngle)) {
+ expected("right angle bracket");
+ return false;
+ }
+
+ return true;
+}
+
+// annotations
+// : LEFT_ANGLE declaration SEMI_COLON ... declaration SEMICOLON RIGHT_ANGLE
+//
+bool HlslGrammar::acceptAnnotations(TQualifier&)
+{
+ if (! acceptTokenClass(EHTokLeftAngle))
+ return false;
+
+ // note that we are nesting a name space
+ parseContext.nestAnnotations();
+
+ // declaration SEMI_COLON ... declaration SEMICOLON RIGHT_ANGLE
+ do {
+ // eat any extra SEMI_COLON; don't know if the grammar calls for this or not
+ while (acceptTokenClass(EHTokSemicolon))
+ ;
+
+ if (acceptTokenClass(EHTokRightAngle))
+ break;
+
+ // declaration
+ TIntermNode* node = nullptr;
+ if (! acceptDeclaration(node)) {
+ expected("declaration in annotation");
+ return false;
+ }
+ } while (true);
+
+ parseContext.unnestAnnotations();
+ return true;
+}
+
+// subpass input type
+// : SUBPASSINPUT
+// | SUBPASSINPUT VECTOR LEFT_ANGLE template_type RIGHT_ANGLE
+// | SUBPASSINPUTMS
+// | SUBPASSINPUTMS VECTOR LEFT_ANGLE template_type RIGHT_ANGLE
+bool HlslGrammar::acceptSubpassInputType(TType& type)
+{
+ // read subpass type
+ const EHlslTokenClass subpassInputType = peek();
+
+ bool multisample;
+
+ switch (subpassInputType) {
+ case EHTokSubpassInput: multisample = false; break;
+ case EHTokSubpassInputMS: multisample = true; break;
+ default:
+ return false; // not a subpass input declaration
+ }
+
+ advanceToken(); // consume the sampler type keyword
+
+ TType subpassType(EbtFloat, EvqUniform, 4); // default type is float4
+
+ if (acceptTokenClass(EHTokLeftAngle)) {
+ if (! acceptType(subpassType)) {
+ expected("scalar or vector type");
+ return false;
+ }
+
+ const TBasicType basicRetType = subpassType.getBasicType() ;
+
+ switch (basicRetType) {
+ case EbtFloat:
+ case EbtUint:
+ case EbtInt:
+ case EbtStruct:
+ break;
+ default:
+ unimplemented("basic type in subpass input");
+ return false;
+ }
+
+ if (! acceptTokenClass(EHTokRightAngle)) {
+ expected("right angle bracket");
+ return false;
+ }
+ }
+
+ const TBasicType subpassBasicType = subpassType.isStruct() ? (*subpassType.getStruct())[0].type->getBasicType()
+ : subpassType.getBasicType();
+
+ TSampler sampler;
+ sampler.setSubpass(subpassBasicType, multisample);
+
+ // Remember the declared return type. Function returns false on error.
+ if (!parseContext.setTextureReturnType(sampler, subpassType, token.loc))
+ return false;
+
+ type.shallowCopy(TType(sampler, EvqUniform));
+
+ return true;
+}
+
+// sampler_type for DX9 compatibility
+// : SAMPLER
+// | SAMPLER1D
+// | SAMPLER2D
+// | SAMPLER3D
+// | SAMPLERCUBE
+bool HlslGrammar::acceptSamplerTypeDX9(TType &type)
+{
+ // read sampler type
+ const EHlslTokenClass samplerType = peek();
+
+ TSamplerDim dim = EsdNone;
+ TType txType(EbtFloat, EvqUniform, 4); // default type is float4
+
+ bool isShadow = false;
+
+ switch (samplerType)
+ {
+ case EHTokSampler: dim = Esd2D; break;
+ case EHTokSampler1d: dim = Esd1D; break;
+ case EHTokSampler2d: dim = Esd2D; break;
+ case EHTokSampler3d: dim = Esd3D; break;
+ case EHTokSamplerCube: dim = EsdCube; break;
+ default:
+ return false; // not a dx9 sampler declaration
+ }
+
+ advanceToken(); // consume the sampler type keyword
+
+ TArraySizes *arraySizes = nullptr; // TODO: array
+
+ TSampler sampler;
+ sampler.set(txType.getBasicType(), dim, false, isShadow, false);
+
+ if (!parseContext.setTextureReturnType(sampler, txType, token.loc))
+ return false;
+
+ type.shallowCopy(TType(sampler, EvqUniform, arraySizes));
+ type.getQualifier().layoutFormat = ElfNone;
+
+ return true;
+}
+
+// sampler_type
+// : SAMPLER
+// | SAMPLER1D
+// | SAMPLER2D
+// | SAMPLER3D
+// | SAMPLERCUBE
+// | SAMPLERSTATE
+// | SAMPLERCOMPARISONSTATE
+bool HlslGrammar::acceptSamplerType(TType& type)
+{
+ // read sampler type
+ const EHlslTokenClass samplerType = peek();
+
+ // TODO: for DX9
+ // TSamplerDim dim = EsdNone;
+
+ bool isShadow = false;
+
+ switch (samplerType) {
+ case EHTokSampler: break;
+ case EHTokSampler1d: /*dim = Esd1D*/; break;
+ case EHTokSampler2d: /*dim = Esd2D*/; break;
+ case EHTokSampler3d: /*dim = Esd3D*/; break;
+ case EHTokSamplerCube: /*dim = EsdCube*/; break;
+ case EHTokSamplerState: break;
+ case EHTokSamplerComparisonState: isShadow = true; break;
+ default:
+ return false; // not a sampler declaration
+ }
+
+ advanceToken(); // consume the sampler type keyword
+
+ TArraySizes* arraySizes = nullptr; // TODO: array
+
+ TSampler sampler;
+ sampler.setPureSampler(isShadow);
+
+ type.shallowCopy(TType(sampler, EvqUniform, arraySizes));
+
+ return true;
+}
+
+// texture_type
+// | BUFFER
+// | TEXTURE1D
+// | TEXTURE1DARRAY
+// | TEXTURE2D
+// | TEXTURE2DARRAY
+// | TEXTURE3D
+// | TEXTURECUBE
+// | TEXTURECUBEARRAY
+// | TEXTURE2DMS
+// | TEXTURE2DMSARRAY
+// | RWBUFFER
+// | RWTEXTURE1D
+// | RWTEXTURE1DARRAY
+// | RWTEXTURE2D
+// | RWTEXTURE2DARRAY
+// | RWTEXTURE3D
+
+bool HlslGrammar::acceptTextureType(TType& type)
+{
+ const EHlslTokenClass textureType = peek();
+
+ TSamplerDim dim = EsdNone;
+ bool array = false;
+ bool ms = false;
+ bool image = false;
+ bool combined = true;
+
+ switch (textureType) {
+ case EHTokBuffer: dim = EsdBuffer; combined = false; break;
+ case EHTokTexture1d: dim = Esd1D; break;
+ case EHTokTexture1darray: dim = Esd1D; array = true; break;
+ case EHTokTexture2d: dim = Esd2D; break;
+ case EHTokTexture2darray: dim = Esd2D; array = true; break;
+ case EHTokTexture3d: dim = Esd3D; break;
+ case EHTokTextureCube: dim = EsdCube; break;
+ case EHTokTextureCubearray: dim = EsdCube; array = true; break;
+ case EHTokTexture2DMS: dim = Esd2D; ms = true; break;
+ case EHTokTexture2DMSarray: dim = Esd2D; array = true; ms = true; break;
+ case EHTokRWBuffer: dim = EsdBuffer; image=true; break;
+ case EHTokRWTexture1d: dim = Esd1D; array=false; image=true; break;
+ case EHTokRWTexture1darray: dim = Esd1D; array=true; image=true; break;
+ case EHTokRWTexture2d: dim = Esd2D; array=false; image=true; break;
+ case EHTokRWTexture2darray: dim = Esd2D; array=true; image=true; break;
+ case EHTokRWTexture3d: dim = Esd3D; array=false; image=true; break;
+ default:
+ return false; // not a texture declaration
+ }
+
+ advanceToken(); // consume the texture object keyword
+
+ TType txType(EbtFloat, EvqUniform, 4); // default type is float4
+
+ TIntermTyped* msCount = nullptr;
+
+ // texture type: required for multisample types and RWBuffer/RWTextures!
+ if (acceptTokenClass(EHTokLeftAngle)) {
+ if (! acceptType(txType)) {
+ expected("scalar or vector type");
+ return false;
+ }
+
+ const TBasicType basicRetType = txType.getBasicType() ;
+
+ switch (basicRetType) {
+ case EbtFloat:
+ case EbtUint:
+ case EbtInt:
+ case EbtStruct:
+ break;
+ default:
+ unimplemented("basic type in texture");
+ return false;
+ }
+
+ // Buffers can handle small mats if they fit in 4 components
+ if (dim == EsdBuffer && txType.isMatrix()) {
+ if ((txType.getMatrixCols() * txType.getMatrixRows()) > 4) {
+ expected("components < 4 in matrix buffer type");
+ return false;
+ }
+
+ // TODO: except we don't handle it yet...
+ unimplemented("matrix type in buffer");
+ return false;
+ }
+
+ if (!txType.isScalar() && !txType.isVector() && !txType.isStruct()) {
+ expected("scalar, vector, or struct type");
+ return false;
+ }
+
+ if (ms && acceptTokenClass(EHTokComma)) {
+ // read sample count for multisample types, if given
+ if (! peekTokenClass(EHTokIntConstant)) {
+ expected("multisample count");
+ return false;
+ }
+
+ if (! acceptLiteral(msCount)) // should never fail, since we just found an integer
+ return false;
+ }
+
+ if (! acceptTokenClass(EHTokRightAngle)) {
+ expected("right angle bracket");
+ return false;
+ }
+ } else if (ms) {
+ expected("texture type for multisample");
+ return false;
+ } else if (image) {
+ expected("type for RWTexture/RWBuffer");
+ return false;
+ }
+
+ TArraySizes* arraySizes = nullptr;
+ const bool shadow = false; // declared on the sampler
+
+ TSampler sampler;
+ TLayoutFormat format = ElfNone;
+
+ // Buffer, RWBuffer and RWTexture (images) require a TLayoutFormat. We handle only a limit set.
+ if (image || dim == EsdBuffer)
+ format = parseContext.getLayoutFromTxType(token.loc, txType);
+
+ const TBasicType txBasicType = txType.isStruct() ? (*txType.getStruct())[0].type->getBasicType()
+ : txType.getBasicType();
+
+ // Non-image Buffers are combined
+ if (dim == EsdBuffer && !image) {
+ sampler.set(txType.getBasicType(), dim, array);
+ } else {
+ // DX10 textures are separated. TODO: DX9.
+ if (image) {
+ sampler.setImage(txBasicType, dim, array, shadow, ms);
+ } else {
+ sampler.setTexture(txBasicType, dim, array, shadow, ms);
+ }
+ }
+
+ // Remember the declared return type. Function returns false on error.
+ if (!parseContext.setTextureReturnType(sampler, txType, token.loc))
+ return false;
+
+ // Force uncombined, if necessary
+ if (!combined)
+ sampler.combined = false;
+
+ type.shallowCopy(TType(sampler, EvqUniform, arraySizes));
+ type.getQualifier().layoutFormat = format;
+
+ return true;
+}
+
+// If token is for a type, update 'type' with the type information,
+// and return true and advance.
+// Otherwise, return false, and don't advance
+bool HlslGrammar::acceptType(TType& type)
+{
+ TIntermNode* nodeList = nullptr;
+ return acceptType(type, nodeList);
+}
+bool HlslGrammar::acceptType(TType& type, TIntermNode*& nodeList)
+{
+ // Basic types for min* types, use native halfs if the option allows them.
+ bool enable16BitTypes = parseContext.hlslEnable16BitTypes();
+
+ const TBasicType min16float_bt = enable16BitTypes ? EbtFloat16 : EbtFloat;
+ const TBasicType min10float_bt = enable16BitTypes ? EbtFloat16 : EbtFloat;
+ const TBasicType half_bt = enable16BitTypes ? EbtFloat16 : EbtFloat;
+ const TBasicType min16int_bt = enable16BitTypes ? EbtInt16 : EbtInt;
+ const TBasicType min12int_bt = enable16BitTypes ? EbtInt16 : EbtInt;
+ const TBasicType min16uint_bt = enable16BitTypes ? EbtUint16 : EbtUint;
+
+ // Some types might have turned into identifiers. Take the hit for checking
+ // when this has happened.
+ if (typeIdentifiers) {
+ const char* identifierString = getTypeString(peek());
+ if (identifierString != nullptr) {
+ TString name = identifierString;
+ // if it's an identifier, it's not a type
+ if (parseContext.symbolTable.find(name) != nullptr)
+ return false;
+ }
+ }
+
+ bool isUnorm = false;
+ bool isSnorm = false;
+
+ // Accept snorm and unorm. Presently, this is ignored, save for an error check below.
+ switch (peek()) {
+ case EHTokUnorm:
+ isUnorm = true;
+ advanceToken(); // eat the token
+ break;
+ case EHTokSNorm:
+ isSnorm = true;
+ advanceToken(); // eat the token
+ break;
+ default:
+ break;
+ }
+
+ switch (peek()) {
+ case EHTokVector:
+ return acceptVectorTemplateType(type);
+ break;
+
+ case EHTokMatrix:
+ return acceptMatrixTemplateType(type);
+ break;
+
+ case EHTokPointStream: // fall through
+ case EHTokLineStream: // ...
+ case EHTokTriangleStream: // ...
+ {
+ TLayoutGeometry geometry;
+ if (! acceptStreamOutTemplateType(type, geometry))
+ return false;
+
+ if (! parseContext.handleOutputGeometry(token.loc, geometry))
+ return false;
+
+ return true;
+ }
+
+ case EHTokInputPatch: // fall through
+ case EHTokOutputPatch: // ...
+ {
+ if (! acceptTessellationPatchTemplateType(type))
+ return false;
+
+ return true;
+ }
+
+ case EHTokSampler: // fall through
+ case EHTokSampler1d: // ...
+ case EHTokSampler2d: // ...
+ case EHTokSampler3d: // ...
+ case EHTokSamplerCube: // ...
+ if (parseContext.hlslDX9Compatible())
+ return acceptSamplerTypeDX9(type);
+ else
+ return acceptSamplerType(type);
+ break;
+
+ case EHTokSamplerState: // fall through
+ case EHTokSamplerComparisonState: // ...
+ return acceptSamplerType(type);
+ break;
+
+ case EHTokSubpassInput: // fall through
+ case EHTokSubpassInputMS: // ...
+ return acceptSubpassInputType(type);
+ break;
+
+ case EHTokBuffer: // fall through
+ case EHTokTexture1d: // ...
+ case EHTokTexture1darray: // ...
+ case EHTokTexture2d: // ...
+ case EHTokTexture2darray: // ...
+ case EHTokTexture3d: // ...
+ case EHTokTextureCube: // ...
+ case EHTokTextureCubearray: // ...
+ case EHTokTexture2DMS: // ...
+ case EHTokTexture2DMSarray: // ...
+ case EHTokRWTexture1d: // ...
+ case EHTokRWTexture1darray: // ...
+ case EHTokRWTexture2d: // ...
+ case EHTokRWTexture2darray: // ...
+ case EHTokRWTexture3d: // ...
+ case EHTokRWBuffer: // ...
+ return acceptTextureType(type);
+ break;
+
+ case EHTokAppendStructuredBuffer:
+ case EHTokByteAddressBuffer:
+ case EHTokConsumeStructuredBuffer:
+ case EHTokRWByteAddressBuffer:
+ case EHTokRWStructuredBuffer:
+ case EHTokStructuredBuffer:
+ return acceptStructBufferType(type);
+ break;
+
+ case EHTokTextureBuffer:
+ return acceptTextureBufferType(type);
+ break;
+
+ case EHTokConstantBuffer:
+ return acceptConstantBufferType(type);
+
+ case EHTokClass:
+ case EHTokStruct:
+ case EHTokCBuffer:
+ case EHTokTBuffer:
+ return acceptStruct(type, nodeList);
+
+ case EHTokIdentifier:
+ // An identifier could be for a user-defined type.
+ // Note we cache the symbol table lookup, to save for a later rule
+ // when this is not a type.
+ if (parseContext.lookupUserType(*token.string, type) != nullptr) {
+ advanceToken();
+ return true;
+ } else
+ return false;
+
+ case EHTokVoid:
+ new(&type) TType(EbtVoid);
+ break;
+
+ case EHTokString:
+ new(&type) TType(EbtString);
+ break;
+
+ case EHTokFloat:
+ new(&type) TType(EbtFloat);
+ break;
+ case EHTokFloat1:
+ new(&type) TType(EbtFloat);
+ type.makeVector();
+ break;
+ case EHTokFloat2:
+ new(&type) TType(EbtFloat, EvqTemporary, 2);
+ break;
+ case EHTokFloat3:
+ new(&type) TType(EbtFloat, EvqTemporary, 3);
+ break;
+ case EHTokFloat4:
+ new(&type) TType(EbtFloat, EvqTemporary, 4);
+ break;
+
+ case EHTokDouble:
+ new(&type) TType(EbtDouble);
+ break;
+ case EHTokDouble1:
+ new(&type) TType(EbtDouble);
+ type.makeVector();
+ break;
+ case EHTokDouble2:
+ new(&type) TType(EbtDouble, EvqTemporary, 2);
+ break;
+ case EHTokDouble3:
+ new(&type) TType(EbtDouble, EvqTemporary, 3);
+ break;
+ case EHTokDouble4:
+ new(&type) TType(EbtDouble, EvqTemporary, 4);
+ break;
+
+ case EHTokInt:
+ case EHTokDword:
+ new(&type) TType(EbtInt);
+ break;
+ case EHTokInt1:
+ new(&type) TType(EbtInt);
+ type.makeVector();
+ break;
+ case EHTokInt2:
+ new(&type) TType(EbtInt, EvqTemporary, 2);
+ break;
+ case EHTokInt3:
+ new(&type) TType(EbtInt, EvqTemporary, 3);
+ break;
+ case EHTokInt4:
+ new(&type) TType(EbtInt, EvqTemporary, 4);
+ break;
+
+ case EHTokUint:
+ new(&type) TType(EbtUint);
+ break;
+ case EHTokUint1:
+ new(&type) TType(EbtUint);
+ type.makeVector();
+ break;
+ case EHTokUint2:
+ new(&type) TType(EbtUint, EvqTemporary, 2);
+ break;
+ case EHTokUint3:
+ new(&type) TType(EbtUint, EvqTemporary, 3);
+ break;
+ case EHTokUint4:
+ new(&type) TType(EbtUint, EvqTemporary, 4);
+ break;
+
+ case EHTokUint64:
+ new(&type) TType(EbtUint64);
+ break;
+
+ case EHTokBool:
+ new(&type) TType(EbtBool);
+ break;
+ case EHTokBool1:
+ new(&type) TType(EbtBool);
+ type.makeVector();
+ break;
+ case EHTokBool2:
+ new(&type) TType(EbtBool, EvqTemporary, 2);
+ break;
+ case EHTokBool3:
+ new(&type) TType(EbtBool, EvqTemporary, 3);
+ break;
+ case EHTokBool4:
+ new(&type) TType(EbtBool, EvqTemporary, 4);
+ break;
+
+ case EHTokHalf:
+ new(&type) TType(half_bt, EvqTemporary);
+ break;
+ case EHTokHalf1:
+ new(&type) TType(half_bt, EvqTemporary);
+ type.makeVector();
+ break;
+ case EHTokHalf2:
+ new(&type) TType(half_bt, EvqTemporary, 2);
+ break;
+ case EHTokHalf3:
+ new(&type) TType(half_bt, EvqTemporary, 3);
+ break;
+ case EHTokHalf4:
+ new(&type) TType(half_bt, EvqTemporary, 4);
+ break;
+
+ case EHTokMin16float:
+ new(&type) TType(min16float_bt, EvqTemporary, EpqMedium);
+ break;
+ case EHTokMin16float1:
+ new(&type) TType(min16float_bt, EvqTemporary, EpqMedium);
+ type.makeVector();
+ break;
+ case EHTokMin16float2:
+ new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 2);
+ break;
+ case EHTokMin16float3:
+ new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 3);
+ break;
+ case EHTokMin16float4:
+ new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 4);
+ break;
+
+ case EHTokMin10float:
+ new(&type) TType(min10float_bt, EvqTemporary, EpqMedium);
+ break;
+ case EHTokMin10float1:
+ new(&type) TType(min10float_bt, EvqTemporary, EpqMedium);
+ type.makeVector();
+ break;
+ case EHTokMin10float2:
+ new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 2);
+ break;
+ case EHTokMin10float3:
+ new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 3);
+ break;
+ case EHTokMin10float4:
+ new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 4);
+ break;
+
+ case EHTokMin16int:
+ new(&type) TType(min16int_bt, EvqTemporary, EpqMedium);
+ break;
+ case EHTokMin16int1:
+ new(&type) TType(min16int_bt, EvqTemporary, EpqMedium);
+ type.makeVector();
+ break;
+ case EHTokMin16int2:
+ new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 2);
+ break;
+ case EHTokMin16int3:
+ new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 3);
+ break;
+ case EHTokMin16int4:
+ new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 4);
+ break;
+
+ case EHTokMin12int:
+ new(&type) TType(min12int_bt, EvqTemporary, EpqMedium);
+ break;
+ case EHTokMin12int1:
+ new(&type) TType(min12int_bt, EvqTemporary, EpqMedium);
+ type.makeVector();
+ break;
+ case EHTokMin12int2:
+ new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 2);
+ break;
+ case EHTokMin12int3:
+ new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 3);
+ break;
+ case EHTokMin12int4:
+ new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 4);
+ break;
+
+ case EHTokMin16uint:
+ new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium);
+ break;
+ case EHTokMin16uint1:
+ new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium);
+ type.makeVector();
+ break;
+ case EHTokMin16uint2:
+ new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 2);
+ break;
+ case EHTokMin16uint3:
+ new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 3);
+ break;
+ case EHTokMin16uint4:
+ new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 4);
+ break;
+
+ case EHTokInt1x1:
+ new(&type) TType(EbtInt, EvqTemporary, 0, 1, 1);
+ break;
+ case EHTokInt1x2:
+ new(&type) TType(EbtInt, EvqTemporary, 0, 1, 2);
+ break;
+ case EHTokInt1x3:
+ new(&type) TType(EbtInt, EvqTemporary, 0, 1, 3);
+ break;
+ case EHTokInt1x4:
+ new(&type) TType(EbtInt, EvqTemporary, 0, 1, 4);
+ break;
+ case EHTokInt2x1:
+ new(&type) TType(EbtInt, EvqTemporary, 0, 2, 1);
+ break;
+ case EHTokInt2x2:
+ new(&type) TType(EbtInt, EvqTemporary, 0, 2, 2);
+ break;
+ case EHTokInt2x3:
+ new(&type) TType(EbtInt, EvqTemporary, 0, 2, 3);
+ break;
+ case EHTokInt2x4:
+ new(&type) TType(EbtInt, EvqTemporary, 0, 2, 4);
+ break;
+ case EHTokInt3x1:
+ new(&type) TType(EbtInt, EvqTemporary, 0, 3, 1);
+ break;
+ case EHTokInt3x2:
+ new(&type) TType(EbtInt, EvqTemporary, 0, 3, 2);
+ break;
+ case EHTokInt3x3:
+ new(&type) TType(EbtInt, EvqTemporary, 0, 3, 3);
+ break;
+ case EHTokInt3x4:
+ new(&type) TType(EbtInt, EvqTemporary, 0, 3, 4);
+ break;
+ case EHTokInt4x1:
+ new(&type) TType(EbtInt, EvqTemporary, 0, 4, 1);
+ break;
+ case EHTokInt4x2:
+ new(&type) TType(EbtInt, EvqTemporary, 0, 4, 2);
+ break;
+ case EHTokInt4x3:
+ new(&type) TType(EbtInt, EvqTemporary, 0, 4, 3);
+ break;
+ case EHTokInt4x4:
+ new(&type) TType(EbtInt, EvqTemporary, 0, 4, 4);
+ break;
+
+ case EHTokUint1x1:
+ new(&type) TType(EbtUint, EvqTemporary, 0, 1, 1);
+ break;
+ case EHTokUint1x2:
+ new(&type) TType(EbtUint, EvqTemporary, 0, 1, 2);
+ break;
+ case EHTokUint1x3:
+ new(&type) TType(EbtUint, EvqTemporary, 0, 1, 3);
+ break;
+ case EHTokUint1x4:
+ new(&type) TType(EbtUint, EvqTemporary, 0, 1, 4);
+ break;
+ case EHTokUint2x1:
+ new(&type) TType(EbtUint, EvqTemporary, 0, 2, 1);
+ break;
+ case EHTokUint2x2:
+ new(&type) TType(EbtUint, EvqTemporary, 0, 2, 2);
+ break;
+ case EHTokUint2x3:
+ new(&type) TType(EbtUint, EvqTemporary, 0, 2, 3);
+ break;
+ case EHTokUint2x4:
+ new(&type) TType(EbtUint, EvqTemporary, 0, 2, 4);
+ break;
+ case EHTokUint3x1:
+ new(&type) TType(EbtUint, EvqTemporary, 0, 3, 1);
+ break;
+ case EHTokUint3x2:
+ new(&type) TType(EbtUint, EvqTemporary, 0, 3, 2);
+ break;
+ case EHTokUint3x3:
+ new(&type) TType(EbtUint, EvqTemporary, 0, 3, 3);
+ break;
+ case EHTokUint3x4:
+ new(&type) TType(EbtUint, EvqTemporary, 0, 3, 4);
+ break;
+ case EHTokUint4x1:
+ new(&type) TType(EbtUint, EvqTemporary, 0, 4, 1);
+ break;
+ case EHTokUint4x2:
+ new(&type) TType(EbtUint, EvqTemporary, 0, 4, 2);
+ break;
+ case EHTokUint4x3:
+ new(&type) TType(EbtUint, EvqTemporary, 0, 4, 3);
+ break;
+ case EHTokUint4x4:
+ new(&type) TType(EbtUint, EvqTemporary, 0, 4, 4);
+ break;
+
+ case EHTokBool1x1:
+ new(&type) TType(EbtBool, EvqTemporary, 0, 1, 1);
+ break;
+ case EHTokBool1x2:
+ new(&type) TType(EbtBool, EvqTemporary, 0, 1, 2);
+ break;
+ case EHTokBool1x3:
+ new(&type) TType(EbtBool, EvqTemporary, 0, 1, 3);
+ break;
+ case EHTokBool1x4:
+ new(&type) TType(EbtBool, EvqTemporary, 0, 1, 4);
+ break;
+ case EHTokBool2x1:
+ new(&type) TType(EbtBool, EvqTemporary, 0, 2, 1);
+ break;
+ case EHTokBool2x2:
+ new(&type) TType(EbtBool, EvqTemporary, 0, 2, 2);
+ break;
+ case EHTokBool2x3:
+ new(&type) TType(EbtBool, EvqTemporary, 0, 2, 3);
+ break;
+ case EHTokBool2x4:
+ new(&type) TType(EbtBool, EvqTemporary, 0, 2, 4);
+ break;
+ case EHTokBool3x1:
+ new(&type) TType(EbtBool, EvqTemporary, 0, 3, 1);
+ break;
+ case EHTokBool3x2:
+ new(&type) TType(EbtBool, EvqTemporary, 0, 3, 2);
+ break;
+ case EHTokBool3x3:
+ new(&type) TType(EbtBool, EvqTemporary, 0, 3, 3);
+ break;
+ case EHTokBool3x4:
+ new(&type) TType(EbtBool, EvqTemporary, 0, 3, 4);
+ break;
+ case EHTokBool4x1:
+ new(&type) TType(EbtBool, EvqTemporary, 0, 4, 1);
+ break;
+ case EHTokBool4x2:
+ new(&type) TType(EbtBool, EvqTemporary, 0, 4, 2);
+ break;
+ case EHTokBool4x3:
+ new(&type) TType(EbtBool, EvqTemporary, 0, 4, 3);
+ break;
+ case EHTokBool4x4:
+ new(&type) TType(EbtBool, EvqTemporary, 0, 4, 4);
+ break;
+
+ case EHTokFloat1x1:
+ new(&type) TType(EbtFloat, EvqTemporary, 0, 1, 1);
+ break;
+ case EHTokFloat1x2:
+ new(&type) TType(EbtFloat, EvqTemporary, 0, 1, 2);
+ break;
+ case EHTokFloat1x3:
+ new(&type) TType(EbtFloat, EvqTemporary, 0, 1, 3);
+ break;
+ case EHTokFloat1x4:
+ new(&type) TType(EbtFloat, EvqTemporary, 0, 1, 4);
+ break;
+ case EHTokFloat2x1:
+ new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 1);
+ break;
+ case EHTokFloat2x2:
+ new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 2);
+ break;
+ case EHTokFloat2x3:
+ new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 3);
+ break;
+ case EHTokFloat2x4:
+ new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 4);
+ break;
+ case EHTokFloat3x1:
+ new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 1);
+ break;
+ case EHTokFloat3x2:
+ new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 2);
+ break;
+ case EHTokFloat3x3:
+ new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 3);
+ break;
+ case EHTokFloat3x4:
+ new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 4);
+ break;
+ case EHTokFloat4x1:
+ new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 1);
+ break;
+ case EHTokFloat4x2:
+ new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 2);
+ break;
+ case EHTokFloat4x3:
+ new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 3);
+ break;
+ case EHTokFloat4x4:
+ new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 4);
+ break;
+
+ case EHTokHalf1x1:
+ new(&type) TType(half_bt, EvqTemporary, 0, 1, 1);
+ break;
+ case EHTokHalf1x2:
+ new(&type) TType(half_bt, EvqTemporary, 0, 1, 2);
+ break;
+ case EHTokHalf1x3:
+ new(&type) TType(half_bt, EvqTemporary, 0, 1, 3);
+ break;
+ case EHTokHalf1x4:
+ new(&type) TType(half_bt, EvqTemporary, 0, 1, 4);
+ break;
+ case EHTokHalf2x1:
+ new(&type) TType(half_bt, EvqTemporary, 0, 2, 1);
+ break;
+ case EHTokHalf2x2:
+ new(&type) TType(half_bt, EvqTemporary, 0, 2, 2);
+ break;
+ case EHTokHalf2x3:
+ new(&type) TType(half_bt, EvqTemporary, 0, 2, 3);
+ break;
+ case EHTokHalf2x4:
+ new(&type) TType(half_bt, EvqTemporary, 0, 2, 4);
+ break;
+ case EHTokHalf3x1:
+ new(&type) TType(half_bt, EvqTemporary, 0, 3, 1);
+ break;
+ case EHTokHalf3x2:
+ new(&type) TType(half_bt, EvqTemporary, 0, 3, 2);
+ break;
+ case EHTokHalf3x3:
+ new(&type) TType(half_bt, EvqTemporary, 0, 3, 3);
+ break;
+ case EHTokHalf3x4:
+ new(&type) TType(half_bt, EvqTemporary, 0, 3, 4);
+ break;
+ case EHTokHalf4x1:
+ new(&type) TType(half_bt, EvqTemporary, 0, 4, 1);
+ break;
+ case EHTokHalf4x2:
+ new(&type) TType(half_bt, EvqTemporary, 0, 4, 2);
+ break;
+ case EHTokHalf4x3:
+ new(&type) TType(half_bt, EvqTemporary, 0, 4, 3);
+ break;
+ case EHTokHalf4x4:
+ new(&type) TType(half_bt, EvqTemporary, 0, 4, 4);
+ break;
+
+ case EHTokDouble1x1:
+ new(&type) TType(EbtDouble, EvqTemporary, 0, 1, 1);
+ break;
+ case EHTokDouble1x2:
+ new(&type) TType(EbtDouble, EvqTemporary, 0, 1, 2);
+ break;
+ case EHTokDouble1x3:
+ new(&type) TType(EbtDouble, EvqTemporary, 0, 1, 3);
+ break;
+ case EHTokDouble1x4:
+ new(&type) TType(EbtDouble, EvqTemporary, 0, 1, 4);
+ break;
+ case EHTokDouble2x1:
+ new(&type) TType(EbtDouble, EvqTemporary, 0, 2, 1);
+ break;
+ case EHTokDouble2x2:
+ new(&type) TType(EbtDouble, EvqTemporary, 0, 2, 2);
+ break;
+ case EHTokDouble2x3:
+ new(&type) TType(EbtDouble, EvqTemporary, 0, 2, 3);
+ break;
+ case EHTokDouble2x4:
+ new(&type) TType(EbtDouble, EvqTemporary, 0, 2, 4);
+ break;
+ case EHTokDouble3x1:
+ new(&type) TType(EbtDouble, EvqTemporary, 0, 3, 1);
+ break;
+ case EHTokDouble3x2:
+ new(&type) TType(EbtDouble, EvqTemporary, 0, 3, 2);
+ break;
+ case EHTokDouble3x3:
+ new(&type) TType(EbtDouble, EvqTemporary, 0, 3, 3);
+ break;
+ case EHTokDouble3x4:
+ new(&type) TType(EbtDouble, EvqTemporary, 0, 3, 4);
+ break;
+ case EHTokDouble4x1:
+ new(&type) TType(EbtDouble, EvqTemporary, 0, 4, 1);
+ break;
+ case EHTokDouble4x2:
+ new(&type) TType(EbtDouble, EvqTemporary, 0, 4, 2);
+ break;
+ case EHTokDouble4x3:
+ new(&type) TType(EbtDouble, EvqTemporary, 0, 4, 3);
+ break;
+ case EHTokDouble4x4:
+ new(&type) TType(EbtDouble, EvqTemporary, 0, 4, 4);
+ break;
+
+ default:
+ return false;
+ }
+
+ advanceToken();
+
+ if ((isUnorm || isSnorm) && !type.isFloatingDomain()) {
+ parseContext.error(token.loc, "unorm and snorm only valid in floating point domain", "", "");
+ return false;
+ }
+
+ return true;
+}
+
+// struct
+// : struct_type IDENTIFIER post_decls LEFT_BRACE struct_declaration_list RIGHT_BRACE
+// | struct_type post_decls LEFT_BRACE struct_declaration_list RIGHT_BRACE
+// | struct_type IDENTIFIER // use of previously declared struct type
+//
+// struct_type
+// : STRUCT
+// | CLASS
+// | CBUFFER
+// | TBUFFER
+//
+bool HlslGrammar::acceptStruct(TType& type, TIntermNode*& nodeList)
+{
+ // This storage qualifier will tell us whether it's an AST
+ // block type or just a generic structure type.
+ TStorageQualifier storageQualifier = EvqTemporary;
+ bool readonly = false;
+
+ if (acceptTokenClass(EHTokCBuffer)) {
+ // CBUFFER
+ storageQualifier = EvqUniform;
+ } else if (acceptTokenClass(EHTokTBuffer)) {
+ // TBUFFER
+ storageQualifier = EvqBuffer;
+ readonly = true;
+ } else if (! acceptTokenClass(EHTokClass) && ! acceptTokenClass(EHTokStruct)) {
+ // Neither CLASS nor STRUCT
+ return false;
+ }
+
+ // Now known to be one of CBUFFER, TBUFFER, CLASS, or STRUCT
+
+
+ // IDENTIFIER. It might also be a keyword which can double as an identifier.
+ // For example: 'cbuffer ConstantBuffer' or 'struct ConstantBuffer' is legal.
+ // 'cbuffer int' is also legal, and 'struct int' appears rejected only because
+ // it attempts to redefine the 'int' type.
+ const char* idString = getTypeString(peek());
+ TString structName = "";
+ if (peekTokenClass(EHTokIdentifier) || idString != nullptr) {
+ if (idString != nullptr)
+ structName = *idString;
+ else
+ structName = *token.string;
+ advanceToken();
+ }
+
+ // post_decls
+ TQualifier postDeclQualifier;
+ postDeclQualifier.clear();
+ bool postDeclsFound = acceptPostDecls(postDeclQualifier);
+
+ // LEFT_BRACE, or
+ // struct_type IDENTIFIER
+ if (! acceptTokenClass(EHTokLeftBrace)) {
+ if (structName.size() > 0 && !postDeclsFound && parseContext.lookupUserType(structName, type) != nullptr) {
+ // struct_type IDENTIFIER
+ return true;
+ } else {
+ expected("{");
+ return false;
+ }
+ }
+
+
+ // struct_declaration_list
+ TTypeList* typeList;
+ // Save each member function so they can be processed after we have a fully formed 'this'.
+ TVector<TFunctionDeclarator> functionDeclarators;
+
+ parseContext.pushNamespace(structName);
+ bool acceptedList = acceptStructDeclarationList(typeList, nodeList, functionDeclarators);
+ parseContext.popNamespace();
+
+ if (! acceptedList) {
+ expected("struct member declarations");
+ return false;
+ }
+
+ // RIGHT_BRACE
+ if (! acceptTokenClass(EHTokRightBrace)) {
+ expected("}");
+ return false;
+ }
+
+ // create the user-defined type
+ if (storageQualifier == EvqTemporary)
+ new(&type) TType(typeList, structName);
+ else {
+ postDeclQualifier.storage = storageQualifier;
+ postDeclQualifier.readonly = readonly;
+ new(&type) TType(typeList, structName, postDeclQualifier); // sets EbtBlock
+ }
+
+ parseContext.declareStruct(token.loc, structName, type);
+
+ // For member functions: now that we know the type of 'this', go back and
+ // - add their implicit argument with 'this' (not to the mangling, just the argument list)
+ // - parse the functions, their tokens were saved for deferred parsing (now)
+ for (int b = 0; b < (int)functionDeclarators.size(); ++b) {
+ // update signature
+ if (functionDeclarators[b].function->hasImplicitThis())
+ functionDeclarators[b].function->addThisParameter(type, intermediate.implicitThisName);
+ }
+
+ // All member functions get parsed inside the class/struct namespace and with the
+ // class/struct members in a symbol-table level.
+ parseContext.pushNamespace(structName);
+ parseContext.pushThisScope(type, functionDeclarators);
+ bool deferredSuccess = true;
+ for (int b = 0; b < (int)functionDeclarators.size() && deferredSuccess; ++b) {
+ // parse body
+ pushTokenStream(functionDeclarators[b].body);
+ if (! acceptFunctionBody(functionDeclarators[b], nodeList))
+ deferredSuccess = false;
+ popTokenStream();
+ }
+ parseContext.popThisScope();
+ parseContext.popNamespace();
+
+ return deferredSuccess;
+}
+
+// constantbuffer
+// : CONSTANTBUFFER LEFT_ANGLE type RIGHT_ANGLE
+bool HlslGrammar::acceptConstantBufferType(TType& type)
+{
+ if (! acceptTokenClass(EHTokConstantBuffer))
+ return false;
+
+ if (! acceptTokenClass(EHTokLeftAngle)) {
+ expected("left angle bracket");
+ return false;
+ }
+
+ TType templateType;
+ if (! acceptType(templateType)) {
+ expected("type");
+ return false;
+ }
+
+ if (! acceptTokenClass(EHTokRightAngle)) {
+ expected("right angle bracket");
+ return false;
+ }
+
+ TQualifier postDeclQualifier;
+ postDeclQualifier.clear();
+ postDeclQualifier.storage = EvqUniform;
+
+ if (templateType.isStruct()) {
+ // Make a block from the type parsed as the template argument
+ TTypeList* typeList = templateType.getWritableStruct();
+ new(&type) TType(typeList, "", postDeclQualifier); // sets EbtBlock
+
+ type.getQualifier().storage = EvqUniform;
+
+ return true;
+ } else {
+ parseContext.error(token.loc, "non-structure type in ConstantBuffer", "", "");
+ return false;
+ }
+}
+
+// texture_buffer
+// : TEXTUREBUFFER LEFT_ANGLE type RIGHT_ANGLE
+bool HlslGrammar::acceptTextureBufferType(TType& type)
+{
+ if (! acceptTokenClass(EHTokTextureBuffer))
+ return false;
+
+ if (! acceptTokenClass(EHTokLeftAngle)) {
+ expected("left angle bracket");
+ return false;
+ }
+
+ TType templateType;
+ if (! acceptType(templateType)) {
+ expected("type");
+ return false;
+ }
+
+ if (! acceptTokenClass(EHTokRightAngle)) {
+ expected("right angle bracket");
+ return false;
+ }
+
+ templateType.getQualifier().storage = EvqBuffer;
+ templateType.getQualifier().readonly = true;
+
+ TType blockType(templateType.getWritableStruct(), "", templateType.getQualifier());
+
+ blockType.getQualifier().storage = EvqBuffer;
+ blockType.getQualifier().readonly = true;
+
+ type.shallowCopy(blockType);
+
+ return true;
+}
+
+
+// struct_buffer
+// : APPENDSTRUCTUREDBUFFER
+// | BYTEADDRESSBUFFER
+// | CONSUMESTRUCTUREDBUFFER
+// | RWBYTEADDRESSBUFFER
+// | RWSTRUCTUREDBUFFER
+// | STRUCTUREDBUFFER
+bool HlslGrammar::acceptStructBufferType(TType& type)
+{
+ const EHlslTokenClass structBuffType = peek();
+
+ // TODO: globallycoherent
+ bool hasTemplateType = true;
+ bool readonly = false;
+
+ TStorageQualifier storage = EvqBuffer;
+ TBuiltInVariable builtinType = EbvNone;
+
+ switch (structBuffType) {
+ case EHTokAppendStructuredBuffer:
+ builtinType = EbvAppendConsume;
+ break;
+ case EHTokByteAddressBuffer:
+ hasTemplateType = false;
+ readonly = true;
+ builtinType = EbvByteAddressBuffer;
+ break;
+ case EHTokConsumeStructuredBuffer:
+ builtinType = EbvAppendConsume;
+ break;
+ case EHTokRWByteAddressBuffer:
+ hasTemplateType = false;
+ builtinType = EbvRWByteAddressBuffer;
+ break;
+ case EHTokRWStructuredBuffer:
+ builtinType = EbvRWStructuredBuffer;
+ break;
+ case EHTokStructuredBuffer:
+ builtinType = EbvStructuredBuffer;
+ readonly = true;
+ break;
+ default:
+ return false; // not a structure buffer type
+ }
+
+ advanceToken(); // consume the structure keyword
+
+ // type on which this StructedBuffer is templatized. E.g, StructedBuffer<MyStruct> ==> MyStruct
+ TType* templateType = new TType;
+
+ if (hasTemplateType) {
+ if (! acceptTokenClass(EHTokLeftAngle)) {
+ expected("left angle bracket");
+ return false;
+ }
+
+ if (! acceptType(*templateType)) {
+ expected("type");
+ return false;
+ }
+ if (! acceptTokenClass(EHTokRightAngle)) {
+ expected("right angle bracket");
+ return false;
+ }
+ } else {
+ // byte address buffers have no explicit type.
+ TType uintType(EbtUint, storage);
+ templateType->shallowCopy(uintType);
+ }
+
+ // Create an unsized array out of that type.
+ // TODO: does this work if it's already an array type?
+ TArraySizes* unsizedArray = new TArraySizes;
+ unsizedArray->addInnerSize(UnsizedArraySize);
+ templateType->transferArraySizes(unsizedArray);
+ templateType->getQualifier().storage = storage;
+
+ // field name is canonical for all structbuffers
+ templateType->setFieldName("@data");
+
+ TTypeList* blockStruct = new TTypeList;
+ TTypeLoc member = { templateType, token.loc };
+ blockStruct->push_back(member);
+
+ // This is the type of the buffer block (SSBO)
+ TType blockType(blockStruct, "", templateType->getQualifier());
+
+ blockType.getQualifier().storage = storage;
+ blockType.getQualifier().readonly = readonly;
+ blockType.getQualifier().builtIn = builtinType;
+
+ // We may have created an equivalent type before, in which case we should use its
+ // deep structure.
+ parseContext.shareStructBufferType(blockType);
+
+ type.shallowCopy(blockType);
+
+ return true;
+}
+
+// struct_declaration_list
+// : struct_declaration SEMI_COLON struct_declaration SEMI_COLON ...
+//
+// struct_declaration
+// : attributes fully_specified_type struct_declarator COMMA struct_declarator ...
+// | attributes fully_specified_type IDENTIFIER function_parameters post_decls compound_statement // member-function definition
+//
+// struct_declarator
+// : IDENTIFIER post_decls
+// | IDENTIFIER array_specifier post_decls
+// | IDENTIFIER function_parameters post_decls // member-function prototype
+//
+bool HlslGrammar::acceptStructDeclarationList(TTypeList*& typeList, TIntermNode*& nodeList,
+ TVector<TFunctionDeclarator>& declarators)
+{
+ typeList = new TTypeList();
+ HlslToken idToken;
+
+ do {
+ // success on seeing the RIGHT_BRACE coming up
+ if (peekTokenClass(EHTokRightBrace))
+ break;
+
+ // struct_declaration
+
+ // attributes
+ TAttributes attributes;
+ acceptAttributes(attributes);
+
+ bool declarator_list = false;
+
+ // fully_specified_type
+ TType memberType;
+ if (! acceptFullySpecifiedType(memberType, nodeList, attributes)) {
+ expected("member type");
+ return false;
+ }
+
+ // merge in the attributes
+ parseContext.transferTypeAttributes(token.loc, attributes, memberType);
+
+ // struct_declarator COMMA struct_declarator ...
+ bool functionDefinitionAccepted = false;
+ do {
+ if (! acceptIdentifier(idToken)) {
+ expected("member name");
+ return false;
+ }
+
+ if (peekTokenClass(EHTokLeftParen)) {
+ // function_parameters
+ if (!declarator_list) {
+ declarators.resize(declarators.size() + 1);
+ // request a token stream for deferred processing
+ functionDefinitionAccepted = acceptMemberFunctionDefinition(nodeList, memberType, *idToken.string,
+ declarators.back());
+ if (functionDefinitionAccepted)
+ break;
+ }
+ expected("member-function definition");
+ return false;
+ } else {
+ // add it to the list of members
+ TTypeLoc member = { new TType(EbtVoid), token.loc };
+ member.type->shallowCopy(memberType);
+ member.type->setFieldName(*idToken.string);
+ typeList->push_back(member);
+
+ // array_specifier
+ TArraySizes* arraySizes = nullptr;
+ acceptArraySpecifier(arraySizes);
+ if (arraySizes)
+ typeList->back().type->transferArraySizes(arraySizes);
+
+ acceptPostDecls(member.type->getQualifier());
+
+ // EQUAL assignment_expression
+ if (acceptTokenClass(EHTokAssign)) {
+ parseContext.warn(idToken.loc, "struct-member initializers ignored", "typedef", "");
+ TIntermTyped* expressionNode = nullptr;
+ if (! acceptAssignmentExpression(expressionNode)) {
+ expected("initializer");
+ return false;
+ }
+ }
+ }
+ // success on seeing the SEMICOLON coming up
+ if (peekTokenClass(EHTokSemicolon))
+ break;
+
+ // COMMA
+ if (acceptTokenClass(EHTokComma))
+ declarator_list = true;
+ else {
+ expected(",");
+ return false;
+ }
+
+ } while (true);
+
+ // SEMI_COLON
+ if (! functionDefinitionAccepted && ! acceptTokenClass(EHTokSemicolon)) {
+ expected(";");
+ return false;
+ }
+
+ } while (true);
+
+ return true;
+}
+
+// member_function_definition
+// | function_parameters post_decls compound_statement
+//
+// Expects type to have EvqGlobal for a static member and
+// EvqTemporary for non-static member.
+bool HlslGrammar::acceptMemberFunctionDefinition(TIntermNode*& nodeList, const TType& type, TString& memberName,
+ TFunctionDeclarator& declarator)
+{
+ bool accepted = false;
+
+ TString* functionName = &memberName;
+ parseContext.getFullNamespaceName(functionName);
+ declarator.function = new TFunction(functionName, type);
+ if (type.getQualifier().storage == EvqTemporary)
+ declarator.function->setImplicitThis();
+ else
+ declarator.function->setIllegalImplicitThis();
+
+ // function_parameters
+ if (acceptFunctionParameters(*declarator.function)) {
+ // post_decls
+ acceptPostDecls(declarator.function->getWritableType().getQualifier());
+
+ // compound_statement (function body definition)
+ if (peekTokenClass(EHTokLeftBrace)) {
+ declarator.loc = token.loc;
+ declarator.body = new TVector<HlslToken>;
+ accepted = acceptFunctionDefinition(declarator, nodeList, declarator.body);
+ }
+ } else
+ expected("function parameter list");
+
+ return accepted;
+}
+
+// function_parameters
+// : LEFT_PAREN parameter_declaration COMMA parameter_declaration ... RIGHT_PAREN
+// | LEFT_PAREN VOID RIGHT_PAREN
+//
+bool HlslGrammar::acceptFunctionParameters(TFunction& function)
+{
+ // LEFT_PAREN
+ if (! acceptTokenClass(EHTokLeftParen))
+ return false;
+
+ // VOID RIGHT_PAREN
+ if (! acceptTokenClass(EHTokVoid)) {
+ do {
+ // parameter_declaration
+ if (! acceptParameterDeclaration(function))
+ break;
+
+ // COMMA
+ if (! acceptTokenClass(EHTokComma))
+ break;
+ } while (true);
+ }
+
+ // RIGHT_PAREN
+ if (! acceptTokenClass(EHTokRightParen)) {
+ expected(")");
+ return false;
+ }
+
+ return true;
+}
+
+// default_parameter_declaration
+// : EQUAL conditional_expression
+// : EQUAL initializer
+bool HlslGrammar::acceptDefaultParameterDeclaration(const TType& type, TIntermTyped*& node)
+{
+ node = nullptr;
+
+ // Valid not to have a default_parameter_declaration
+ if (!acceptTokenClass(EHTokAssign))
+ return true;
+
+ if (!acceptConditionalExpression(node)) {
+ if (!acceptInitializer(node))
+ return false;
+
+ // For initializer lists, we have to const-fold into a constructor for the type, so build
+ // that.
+ TFunction* constructor = parseContext.makeConstructorCall(token.loc, type);
+ if (constructor == nullptr) // cannot construct
+ return false;
+
+ TIntermTyped* arguments = nullptr;
+ for (int i = 0; i < int(node->getAsAggregate()->getSequence().size()); i++)
+ parseContext.handleFunctionArgument(constructor, arguments, node->getAsAggregate()->getSequence()[i]->getAsTyped());
+
+ node = parseContext.handleFunctionCall(token.loc, constructor, node);
+ }
+
+ if (node == nullptr)
+ return false;
+
+ // If this is simply a constant, we can use it directly.
+ if (node->getAsConstantUnion())
+ return true;
+
+ // Otherwise, it has to be const-foldable.
+ TIntermTyped* origNode = node;
+
+ node = intermediate.fold(node->getAsAggregate());
+
+ if (node != nullptr && origNode != node)
+ return true;
+
+ parseContext.error(token.loc, "invalid default parameter value", "", "");
+
+ return false;
+}
+
+// parameter_declaration
+// : attributes attributed_declaration
+//
+// attributed_declaration
+// : fully_specified_type post_decls [ = default_parameter_declaration ]
+// | fully_specified_type identifier array_specifier post_decls [ = default_parameter_declaration ]
+//
+bool HlslGrammar::acceptParameterDeclaration(TFunction& function)
+{
+ // attributes
+ TAttributes attributes;
+ acceptAttributes(attributes);
+
+ // fully_specified_type
+ TType* type = new TType;
+ if (! acceptFullySpecifiedType(*type, attributes))
+ return false;
+
+ // merge in the attributes
+ parseContext.transferTypeAttributes(token.loc, attributes, *type);
+
+ // identifier
+ HlslToken idToken;
+ acceptIdentifier(idToken);
+
+ // array_specifier
+ TArraySizes* arraySizes = nullptr;
+ acceptArraySpecifier(arraySizes);
+ if (arraySizes) {
+ if (arraySizes->hasUnsized()) {
+ parseContext.error(token.loc, "function parameter requires array size", "[]", "");
+ return false;
+ }
+
+ type->transferArraySizes(arraySizes);
+ }
+
+ // post_decls
+ acceptPostDecls(type->getQualifier());
+
+ TIntermTyped* defaultValue;
+ if (!acceptDefaultParameterDeclaration(*type, defaultValue))
+ return false;
+
+ parseContext.paramFix(*type);
+
+ // If any prior parameters have default values, all the parameters after that must as well.
+ if (defaultValue == nullptr && function.getDefaultParamCount() > 0) {
+ parseContext.error(idToken.loc, "invalid parameter after default value parameters", idToken.string->c_str(), "");
+ return false;
+ }
+
+ TParameter param = { idToken.string, type, defaultValue };
+ function.addParameter(param);
+
+ return true;
+}
+
+// Do the work to create the function definition in addition to
+// parsing the body (compound_statement).
+//
+// If 'deferredTokens' are passed in, just get the token stream,
+// don't process.
+//
+bool HlslGrammar::acceptFunctionDefinition(TFunctionDeclarator& declarator, TIntermNode*& nodeList,
+ TVector<HlslToken>* deferredTokens)
+{
+ parseContext.handleFunctionDeclarator(declarator.loc, *declarator.function, false /* not prototype */);
+
+ if (deferredTokens)
+ return captureBlockTokens(*deferredTokens);
+ else
+ return acceptFunctionBody(declarator, nodeList);
+}
+
+bool HlslGrammar::acceptFunctionBody(TFunctionDeclarator& declarator, TIntermNode*& nodeList)
+{
+ // we might get back an entry-point
+ TIntermNode* entryPointNode = nullptr;
+
+ // This does a pushScope()
+ TIntermNode* functionNode = parseContext.handleFunctionDefinition(declarator.loc, *declarator.function,
+ declarator.attributes, entryPointNode);
+
+ // compound_statement
+ TIntermNode* functionBody = nullptr;
+ if (! acceptCompoundStatement(functionBody))
+ return false;
+
+ // this does a popScope()
+ parseContext.handleFunctionBody(declarator.loc, *declarator.function, functionBody, functionNode);
+
+ // Hook up the 1 or 2 function definitions.
+ nodeList = intermediate.growAggregate(nodeList, functionNode);
+ nodeList = intermediate.growAggregate(nodeList, entryPointNode);
+
+ return true;
+}
+
+// Accept an expression with parenthesis around it, where
+// the parenthesis ARE NOT expression parenthesis, but the
+// syntactically required ones like in "if ( expression )".
+//
+// Also accepts a declaration expression; "if (int a = expression)".
+//
+// Note this one is not set up to be speculative; as it gives
+// errors if not found.
+//
+bool HlslGrammar::acceptParenExpression(TIntermTyped*& expression)
+{
+ expression = nullptr;
+
+ // LEFT_PAREN
+ if (! acceptTokenClass(EHTokLeftParen))
+ expected("(");
+
+ bool decl = false;
+ TIntermNode* declNode = nullptr;
+ decl = acceptControlDeclaration(declNode);
+ if (decl) {
+ if (declNode == nullptr || declNode->getAsTyped() == nullptr) {
+ expected("initialized declaration");
+ return false;
+ } else
+ expression = declNode->getAsTyped();
+ } else {
+ // no declaration
+ if (! acceptExpression(expression)) {
+ expected("expression");
+ return false;
+ }
+ }
+
+ // RIGHT_PAREN
+ if (! acceptTokenClass(EHTokRightParen))
+ expected(")");
+
+ return true;
+}
+
+// The top-level full expression recognizer.
+//
+// expression
+// : assignment_expression COMMA assignment_expression COMMA assignment_expression ...
+//
+bool HlslGrammar::acceptExpression(TIntermTyped*& node)
+{
+ node = nullptr;
+
+ // assignment_expression
+ if (! acceptAssignmentExpression(node))
+ return false;
+
+ if (! peekTokenClass(EHTokComma))
+ return true;
+
+ do {
+ // ... COMMA
+ TSourceLoc loc = token.loc;
+ advanceToken();
+
+ // ... assignment_expression
+ TIntermTyped* rightNode = nullptr;
+ if (! acceptAssignmentExpression(rightNode)) {
+ expected("assignment expression");
+ return false;
+ }
+
+ node = intermediate.addComma(node, rightNode, loc);
+
+ if (! peekTokenClass(EHTokComma))
+ return true;
+ } while (true);
+}
+
+// initializer
+// : LEFT_BRACE RIGHT_BRACE
+// | LEFT_BRACE initializer_list RIGHT_BRACE
+//
+// initializer_list
+// : assignment_expression COMMA assignment_expression COMMA ...
+//
+bool HlslGrammar::acceptInitializer(TIntermTyped*& node)
+{
+ // LEFT_BRACE
+ if (! acceptTokenClass(EHTokLeftBrace))
+ return false;
+
+ // RIGHT_BRACE
+ TSourceLoc loc = token.loc;
+ if (acceptTokenClass(EHTokRightBrace)) {
+ // a zero-length initializer list
+ node = intermediate.makeAggregate(loc);
+ return true;
+ }
+
+ // initializer_list
+ node = nullptr;
+ do {
+ // assignment_expression
+ TIntermTyped* expr;
+ if (! acceptAssignmentExpression(expr)) {
+ expected("assignment expression in initializer list");
+ return false;
+ }
+
+ const bool firstNode = (node == nullptr);
+
+ node = intermediate.growAggregate(node, expr, loc);
+
+ // If every sub-node in the list has qualifier EvqConst, the returned node becomes
+ // EvqConst. Otherwise, it becomes EvqTemporary. That doesn't happen with e.g.
+ // EvqIn or EvqPosition, since the collection isn't EvqPosition if all the members are.
+ if (firstNode && expr->getQualifier().storage == EvqConst)
+ node->getQualifier().storage = EvqConst;
+ else if (expr->getQualifier().storage != EvqConst)
+ node->getQualifier().storage = EvqTemporary;
+
+ // COMMA
+ if (acceptTokenClass(EHTokComma)) {
+ if (acceptTokenClass(EHTokRightBrace)) // allow trailing comma
+ return true;
+ continue;
+ }
+
+ // RIGHT_BRACE
+ if (acceptTokenClass(EHTokRightBrace))
+ return true;
+
+ expected(", or }");
+ return false;
+ } while (true);
+}
+
+// Accept an assignment expression, where assignment operations
+// associate right-to-left. That is, it is implicit, for example
+//
+// a op (b op (c op d))
+//
+// assigment_expression
+// : initializer
+// | conditional_expression
+// | conditional_expression assign_op conditional_expression assign_op conditional_expression ...
+//
+bool HlslGrammar::acceptAssignmentExpression(TIntermTyped*& node)
+{
+ // initializer
+ if (peekTokenClass(EHTokLeftBrace)) {
+ if (acceptInitializer(node))
+ return true;
+
+ expected("initializer");
+ return false;
+ }
+
+ // conditional_expression
+ if (! acceptConditionalExpression(node))
+ return false;
+
+ // assignment operation?
+ TOperator assignOp = HlslOpMap::assignment(peek());
+ if (assignOp == EOpNull)
+ return true;
+
+ // assign_op
+ TSourceLoc loc = token.loc;
+ advanceToken();
+
+ // conditional_expression assign_op conditional_expression ...
+ // Done by recursing this function, which automatically
+ // gets the right-to-left associativity.
+ TIntermTyped* rightNode = nullptr;
+ if (! acceptAssignmentExpression(rightNode)) {
+ expected("assignment expression");
+ return false;
+ }
+
+ node = parseContext.handleAssign(loc, assignOp, node, rightNode);
+ node = parseContext.handleLvalue(loc, "assign", node);
+
+ if (node == nullptr) {
+ parseContext.error(loc, "could not create assignment", "", "");
+ return false;
+ }
+
+ if (! peekTokenClass(EHTokComma))
+ return true;
+
+ return true;
+}
+
+// Accept a conditional expression, which associates right-to-left,
+// accomplished by the "true" expression calling down to lower
+// precedence levels than this level.
+//
+// conditional_expression
+// : binary_expression
+// | binary_expression QUESTION expression COLON assignment_expression
+//
+bool HlslGrammar::acceptConditionalExpression(TIntermTyped*& node)
+{
+ // binary_expression
+ if (! acceptBinaryExpression(node, PlLogicalOr))
+ return false;
+
+ if (! acceptTokenClass(EHTokQuestion))
+ return true;
+
+ node = parseContext.convertConditionalExpression(token.loc, node, false);
+ if (node == nullptr)
+ return false;
+
+ ++parseContext.controlFlowNestingLevel; // this only needs to work right if no errors
+
+ TIntermTyped* trueNode = nullptr;
+ if (! acceptExpression(trueNode)) {
+ expected("expression after ?");
+ return false;
+ }
+ TSourceLoc loc = token.loc;
+
+ if (! acceptTokenClass(EHTokColon)) {
+ expected(":");
+ return false;
+ }
+
+ TIntermTyped* falseNode = nullptr;
+ if (! acceptAssignmentExpression(falseNode)) {
+ expected("expression after :");
+ return false;
+ }
+
+ --parseContext.controlFlowNestingLevel;
+
+ node = intermediate.addSelection(node, trueNode, falseNode, loc);
+
+ return true;
+}
+
+// Accept a binary expression, for binary operations that
+// associate left-to-right. This is, it is implicit, for example
+//
+// ((a op b) op c) op d
+//
+// binary_expression
+// : expression op expression op expression ...
+//
+// where 'expression' is the next higher level in precedence.
+//
+bool HlslGrammar::acceptBinaryExpression(TIntermTyped*& node, PrecedenceLevel precedenceLevel)
+{
+ if (precedenceLevel > PlMul)
+ return acceptUnaryExpression(node);
+
+ // assignment_expression
+ if (! acceptBinaryExpression(node, (PrecedenceLevel)(precedenceLevel + 1)))
+ return false;
+
+ do {
+ TOperator op = HlslOpMap::binary(peek());
+ PrecedenceLevel tokenLevel = HlslOpMap::precedenceLevel(op);
+ if (tokenLevel < precedenceLevel)
+ return true;
+
+ // ... op
+ TSourceLoc loc = token.loc;
+ advanceToken();
+
+ // ... expression
+ TIntermTyped* rightNode = nullptr;
+ if (! acceptBinaryExpression(rightNode, (PrecedenceLevel)(precedenceLevel + 1))) {
+ expected("expression");
+ return false;
+ }
+
+ node = intermediate.addBinaryMath(op, node, rightNode, loc);
+ if (node == nullptr) {
+ parseContext.error(loc, "Could not perform requested binary operation", "", "");
+ return false;
+ }
+ } while (true);
+}
+
+// unary_expression
+// : (type) unary_expression
+// | + unary_expression
+// | - unary_expression
+// | ! unary_expression
+// | ~ unary_expression
+// | ++ unary_expression
+// | -- unary_expression
+// | postfix_expression
+//
+bool HlslGrammar::acceptUnaryExpression(TIntermTyped*& node)
+{
+ // (type) unary_expression
+ // Have to look two steps ahead, because this could be, e.g., a
+ // postfix_expression instead, since that also starts with at "(".
+ if (acceptTokenClass(EHTokLeftParen)) {
+ TType castType;
+ if (acceptType(castType)) {
+ // recognize any array_specifier as part of the type
+ TArraySizes* arraySizes = nullptr;
+ acceptArraySpecifier(arraySizes);
+ if (arraySizes != nullptr)
+ castType.transferArraySizes(arraySizes);
+ TSourceLoc loc = token.loc;
+ if (acceptTokenClass(EHTokRightParen)) {
+ // We've matched "(type)" now, get the expression to cast
+ if (! acceptUnaryExpression(node))
+ return false;
+
+ // Hook it up like a constructor
+ TFunction* constructorFunction = parseContext.makeConstructorCall(loc, castType);
+ if (constructorFunction == nullptr) {
+ expected("type that can be constructed");
+ return false;
+ }
+ TIntermTyped* arguments = nullptr;
+ parseContext.handleFunctionArgument(constructorFunction, arguments, node);
+ node = parseContext.handleFunctionCall(loc, constructorFunction, arguments);
+
+ return node != nullptr;
+ } else {
+ // This could be a parenthesized constructor, ala (int(3)), and we just accepted
+ // the '(int' part. We must back up twice.
+ recedeToken();
+ recedeToken();
+
+ // Note, there are no array constructors like
+ // (float[2](...))
+ if (arraySizes != nullptr)
+ parseContext.error(loc, "parenthesized array constructor not allowed", "([]())", "", "");
+ }
+ } else {
+ // This isn't a type cast, but it still started "(", so if it is a
+ // unary expression, it can only be a postfix_expression, so try that.
+ // Back it up first.
+ recedeToken();
+ return acceptPostfixExpression(node);
+ }
+ }
+
+ // peek for "op unary_expression"
+ TOperator unaryOp = HlslOpMap::preUnary(peek());
+
+ // postfix_expression (if no unary operator)
+ if (unaryOp == EOpNull)
+ return acceptPostfixExpression(node);
+
+ // op unary_expression
+ TSourceLoc loc = token.loc;
+ advanceToken();
+ if (! acceptUnaryExpression(node))
+ return false;
+
+ // + is a no-op
+ if (unaryOp == EOpAdd)
+ return true;
+
+ node = intermediate.addUnaryMath(unaryOp, node, loc);
+
+ // These unary ops require lvalues
+ if (unaryOp == EOpPreIncrement || unaryOp == EOpPreDecrement)
+ node = parseContext.handleLvalue(loc, "unary operator", node);
+
+ return node != nullptr;
+}
+
+// postfix_expression
+// : LEFT_PAREN expression RIGHT_PAREN
+// | literal
+// | constructor
+// | IDENTIFIER [ COLONCOLON IDENTIFIER [ COLONCOLON IDENTIFIER ... ] ]
+// | function_call
+// | postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET
+// | postfix_expression DOT IDENTIFIER
+// | postfix_expression DOT IDENTIFIER arguments
+// | postfix_expression arguments
+// | postfix_expression INC_OP
+// | postfix_expression DEC_OP
+//
+bool HlslGrammar::acceptPostfixExpression(TIntermTyped*& node)
+{
+ // Not implemented as self-recursive:
+ // The logical "right recursion" is done with a loop at the end
+
+ // idToken will pick up either a variable or a function name in a function call
+ HlslToken idToken;
+
+ // Find something before the postfix operations, as they can't operate
+ // on nothing. So, no "return true", they fall through, only "return false".
+ if (acceptTokenClass(EHTokLeftParen)) {
+ // LEFT_PAREN expression RIGHT_PAREN
+ if (! acceptExpression(node)) {
+ expected("expression");
+ return false;
+ }
+ if (! acceptTokenClass(EHTokRightParen)) {
+ expected(")");
+ return false;
+ }
+ } else if (acceptLiteral(node)) {
+ // literal (nothing else to do yet)
+ } else if (acceptConstructor(node)) {
+ // constructor (nothing else to do yet)
+ } else if (acceptIdentifier(idToken)) {
+ // user-type, namespace name, variable, or function name
+ TString* fullName = idToken.string;
+ while (acceptTokenClass(EHTokColonColon)) {
+ // user-type or namespace name
+ fullName = NewPoolTString(fullName->c_str());
+ fullName->append(parseContext.scopeMangler);
+ if (acceptIdentifier(idToken))
+ fullName->append(*idToken.string);
+ else {
+ expected("identifier after ::");
+ return false;
+ }
+ }
+ if (! peekTokenClass(EHTokLeftParen)) {
+ node = parseContext.handleVariable(idToken.loc, fullName);
+ if (node == nullptr)
+ return false;
+ } else if (acceptFunctionCall(idToken.loc, *fullName, node, nullptr)) {
+ // function_call (nothing else to do yet)
+ } else {
+ expected("function call arguments");
+ return false;
+ }
+ } else {
+ // nothing found, can't post operate
+ return false;
+ }
+
+ // Something was found, chain as many postfix operations as exist.
+ do {
+ TSourceLoc loc = token.loc;
+ TOperator postOp = HlslOpMap::postUnary(peek());
+
+ // Consume only a valid post-unary operator, otherwise we are done.
+ switch (postOp) {
+ case EOpIndexDirectStruct:
+ case EOpIndexIndirect:
+ case EOpPostIncrement:
+ case EOpPostDecrement:
+ case EOpScoping:
+ advanceToken();
+ break;
+ default:
+ return true;
+ }
+
+ // We have a valid post-unary operator, process it.
+ switch (postOp) {
+ case EOpScoping:
+ case EOpIndexDirectStruct:
+ {
+ // DOT IDENTIFIER
+ // includes swizzles, member variables, and member functions
+ HlslToken field;
+ if (! acceptIdentifier(field)) {
+ expected("swizzle or member");
+ return false;
+ }
+
+ if (peekTokenClass(EHTokLeftParen)) {
+ // member function
+ TIntermTyped* thisNode = node;
+
+ // arguments
+ if (! acceptFunctionCall(field.loc, *field.string, node, thisNode)) {
+ expected("function parameters");
+ return false;
+ }
+ } else
+ node = parseContext.handleDotDereference(field.loc, node, *field.string);
+
+ break;
+ }
+ case EOpIndexIndirect:
+ {
+ // LEFT_BRACKET integer_expression RIGHT_BRACKET
+ TIntermTyped* indexNode = nullptr;
+ if (! acceptExpression(indexNode) ||
+ ! peekTokenClass(EHTokRightBracket)) {
+ expected("expression followed by ']'");
+ return false;
+ }
+ advanceToken();
+ node = parseContext.handleBracketDereference(indexNode->getLoc(), node, indexNode);
+ if (node == nullptr)
+ return false;
+ break;
+ }
+ case EOpPostIncrement:
+ // INC_OP
+ // fall through
+ case EOpPostDecrement:
+ // DEC_OP
+ node = intermediate.addUnaryMath(postOp, node, loc);
+ node = parseContext.handleLvalue(loc, "unary operator", node);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ } while (true);
+}
+
+// constructor
+// : type argument_list
+//
+bool HlslGrammar::acceptConstructor(TIntermTyped*& node)
+{
+ // type
+ TType type;
+ if (acceptType(type)) {
+ TFunction* constructorFunction = parseContext.makeConstructorCall(token.loc, type);
+ if (constructorFunction == nullptr)
+ return false;
+
+ // arguments
+ TIntermTyped* arguments = nullptr;
+ if (! acceptArguments(constructorFunction, arguments)) {
+ // It's possible this is a type keyword used as an identifier. Put the token back
+ // for later use.
+ recedeToken();
+ return false;
+ }
+
+ // hook it up
+ node = parseContext.handleFunctionCall(arguments->getLoc(), constructorFunction, arguments);
+
+ return node != nullptr;
+ }
+
+ return false;
+}
+
+// The function_call identifier was already recognized, and passed in as idToken.
+//
+// function_call
+// : [idToken] arguments
+//
+bool HlslGrammar::acceptFunctionCall(const TSourceLoc& loc, TString& name, TIntermTyped*& node, TIntermTyped* baseObject)
+{
+ // name
+ TString* functionName = nullptr;
+ if (baseObject == nullptr) {
+ functionName = &name;
+ } else if (parseContext.isBuiltInMethod(loc, baseObject, name)) {
+ // Built-in methods are not in the symbol table as methods, but as global functions
+ // taking an explicit 'this' as the first argument.
+ functionName = NewPoolTString(BUILTIN_PREFIX);
+ functionName->append(name);
+ } else {
+ if (! baseObject->getType().isStruct()) {
+ expected("structure");
+ return false;
+ }
+ functionName = NewPoolTString("");
+ functionName->append(baseObject->getType().getTypeName());
+ parseContext.addScopeMangler(*functionName);
+ functionName->append(name);
+ }
+
+ // function
+ TFunction* function = new TFunction(functionName, TType(EbtVoid));
+
+ // arguments
+ TIntermTyped* arguments = nullptr;
+ if (baseObject != nullptr) {
+ // Non-static member functions have an implicit first argument of the base object.
+ parseContext.handleFunctionArgument(function, arguments, baseObject);
+ }
+ if (! acceptArguments(function, arguments))
+ return false;
+
+ // call
+ node = parseContext.handleFunctionCall(loc, function, arguments);
+
+ return node != nullptr;
+}
+
+// arguments
+// : LEFT_PAREN expression COMMA expression COMMA ... RIGHT_PAREN
+//
+// The arguments are pushed onto the 'function' argument list and
+// onto the 'arguments' aggregate.
+//
+bool HlslGrammar::acceptArguments(TFunction* function, TIntermTyped*& arguments)
+{
+ // LEFT_PAREN
+ if (! acceptTokenClass(EHTokLeftParen))
+ return false;
+
+ // RIGHT_PAREN
+ if (acceptTokenClass(EHTokRightParen))
+ return true;
+
+ // must now be at least one expression...
+ do {
+ // expression
+ TIntermTyped* arg;
+ if (! acceptAssignmentExpression(arg))
+ return false;
+
+ // hook it up
+ parseContext.handleFunctionArgument(function, arguments, arg);
+
+ // COMMA
+ if (! acceptTokenClass(EHTokComma))
+ break;
+ } while (true);
+
+ // RIGHT_PAREN
+ if (! acceptTokenClass(EHTokRightParen)) {
+ expected(")");
+ return false;
+ }
+
+ return true;
+}
+
+bool HlslGrammar::acceptLiteral(TIntermTyped*& node)
+{
+ switch (token.tokenClass) {
+ case EHTokIntConstant:
+ node = intermediate.addConstantUnion(token.i, token.loc, true);
+ break;
+ case EHTokUintConstant:
+ node = intermediate.addConstantUnion(token.u, token.loc, true);
+ break;
+ case EHTokFloat16Constant:
+ node = intermediate.addConstantUnion(token.d, EbtFloat16, token.loc, true);
+ break;
+ case EHTokFloatConstant:
+ node = intermediate.addConstantUnion(token.d, EbtFloat, token.loc, true);
+ break;
+ case EHTokDoubleConstant:
+ node = intermediate.addConstantUnion(token.d, EbtDouble, token.loc, true);
+ break;
+ case EHTokBoolConstant:
+ node = intermediate.addConstantUnion(token.b, token.loc, true);
+ break;
+ case EHTokStringConstant:
+ node = intermediate.addConstantUnion(token.string, token.loc, true);
+ break;
+
+ default:
+ return false;
+ }
+
+ advanceToken();
+
+ return true;
+}
+
+// simple_statement
+// : SEMICOLON
+// | declaration_statement
+// | expression SEMICOLON
+//
+bool HlslGrammar::acceptSimpleStatement(TIntermNode*& statement)
+{
+ // SEMICOLON
+ if (acceptTokenClass(EHTokSemicolon))
+ return true;
+
+ // declaration
+ if (acceptDeclaration(statement))
+ return true;
+
+ // expression
+ TIntermTyped* node;
+ if (acceptExpression(node))
+ statement = node;
+ else
+ return false;
+
+ // SEMICOLON (following an expression)
+ if (acceptTokenClass(EHTokSemicolon))
+ return true;
+ else {
+ expected(";");
+ return false;
+ }
+}
+
+// compound_statement
+// : LEFT_CURLY statement statement ... RIGHT_CURLY
+//
+bool HlslGrammar::acceptCompoundStatement(TIntermNode*& retStatement)
+{
+ TIntermAggregate* compoundStatement = nullptr;
+
+ // LEFT_CURLY
+ if (! acceptTokenClass(EHTokLeftBrace))
+ return false;
+
+ // statement statement ...
+ TIntermNode* statement = nullptr;
+ while (acceptStatement(statement)) {
+ TIntermBranch* branch = statement ? statement->getAsBranchNode() : nullptr;
+ if (branch != nullptr && (branch->getFlowOp() == EOpCase ||
+ branch->getFlowOp() == EOpDefault)) {
+ // hook up individual subsequences within a switch statement
+ parseContext.wrapupSwitchSubsequence(compoundStatement, statement);
+ compoundStatement = nullptr;
+ } else {
+ // hook it up to the growing compound statement
+ compoundStatement = intermediate.growAggregate(compoundStatement, statement);
+ }
+ }
+ if (compoundStatement)
+ compoundStatement->setOperator(EOpSequence);
+
+ retStatement = compoundStatement;
+
+ // RIGHT_CURLY
+ return acceptTokenClass(EHTokRightBrace);
+}
+
+bool HlslGrammar::acceptScopedStatement(TIntermNode*& statement)
+{
+ parseContext.pushScope();
+ bool result = acceptStatement(statement);
+ parseContext.popScope();
+
+ return result;
+}
+
+bool HlslGrammar::acceptScopedCompoundStatement(TIntermNode*& statement)
+{
+ parseContext.pushScope();
+ bool result = acceptCompoundStatement(statement);
+ parseContext.popScope();
+
+ return result;
+}
+
+// statement
+// : attributes attributed_statement
+//
+// attributed_statement
+// : compound_statement
+// | simple_statement
+// | selection_statement
+// | switch_statement
+// | case_label
+// | default_label
+// | iteration_statement
+// | jump_statement
+//
+bool HlslGrammar::acceptStatement(TIntermNode*& statement)
+{
+ statement = nullptr;
+
+ // attributes
+ TAttributes attributes;
+ acceptAttributes(attributes);
+
+ // attributed_statement
+ switch (peek()) {
+ case EHTokLeftBrace:
+ return acceptScopedCompoundStatement(statement);
+
+ case EHTokIf:
+ return acceptSelectionStatement(statement, attributes);
+
+ case EHTokSwitch:
+ return acceptSwitchStatement(statement, attributes);
+
+ case EHTokFor:
+ case EHTokDo:
+ case EHTokWhile:
+ return acceptIterationStatement(statement, attributes);
+
+ case EHTokContinue:
+ case EHTokBreak:
+ case EHTokDiscard:
+ case EHTokReturn:
+ return acceptJumpStatement(statement);
+
+ case EHTokCase:
+ return acceptCaseLabel(statement);
+ case EHTokDefault:
+ return acceptDefaultLabel(statement);
+
+ case EHTokRightBrace:
+ // Performance: not strictly necessary, but stops a bunch of hunting early,
+ // and is how sequences of statements end.
+ return false;
+
+ default:
+ return acceptSimpleStatement(statement);
+ }
+
+ return true;
+}
+
+// attributes
+// : [zero or more:] bracketed-attribute
+//
+// bracketed-attribute:
+// : LEFT_BRACKET scoped-attribute RIGHT_BRACKET
+// : LEFT_BRACKET LEFT_BRACKET scoped-attribute RIGHT_BRACKET RIGHT_BRACKET
+//
+// scoped-attribute:
+// : attribute
+// | namespace COLON COLON attribute
+//
+// attribute:
+// : UNROLL
+// | UNROLL LEFT_PAREN literal RIGHT_PAREN
+// | FASTOPT
+// | ALLOW_UAV_CONDITION
+// | BRANCH
+// | FLATTEN
+// | FORCECASE
+// | CALL
+// | DOMAIN
+// | EARLYDEPTHSTENCIL
+// | INSTANCE
+// | MAXTESSFACTOR
+// | OUTPUTCONTROLPOINTS
+// | OUTPUTTOPOLOGY
+// | PARTITIONING
+// | PATCHCONSTANTFUNC
+// | NUMTHREADS LEFT_PAREN x_size, y_size,z z_size RIGHT_PAREN
+//
+void HlslGrammar::acceptAttributes(TAttributes& attributes)
+{
+ // For now, accept the [ XXX(X) ] syntax, but drop all but
+ // numthreads, which is used to set the CS local size.
+ // TODO: subset to correct set? Pass on?
+ do {
+ HlslToken attributeToken;
+
+ // LEFT_BRACKET?
+ if (! acceptTokenClass(EHTokLeftBracket))
+ return;
+ // another LEFT_BRACKET?
+ bool doubleBrackets = false;
+ if (acceptTokenClass(EHTokLeftBracket))
+ doubleBrackets = true;
+
+ // attribute? (could be namespace; will adjust later)
+ if (!acceptIdentifier(attributeToken)) {
+ if (!peekTokenClass(EHTokRightBracket)) {
+ expected("namespace or attribute identifier");
+ advanceToken();
+ }
+ }
+
+ TString nameSpace;
+ if (acceptTokenClass(EHTokColonColon)) {
+ // namespace COLON COLON
+ nameSpace = *attributeToken.string;
+ // attribute
+ if (!acceptIdentifier(attributeToken)) {
+ expected("attribute identifier");
+ return;
+ }
+ }
+
+ TIntermAggregate* expressions = nullptr;
+
+ // (x, ...)
+ if (acceptTokenClass(EHTokLeftParen)) {
+ expressions = new TIntermAggregate;
+
+ TIntermTyped* node;
+ bool expectingExpression = false;
+
+ while (acceptAssignmentExpression(node)) {
+ expectingExpression = false;
+ expressions->getSequence().push_back(node);
+ if (acceptTokenClass(EHTokComma))
+ expectingExpression = true;
+ }
+
+ // 'expressions' is an aggregate with the expressions in it
+ if (! acceptTokenClass(EHTokRightParen))
+ expected(")");
+
+ // Error for partial or missing expression
+ if (expectingExpression || expressions->getSequence().empty())
+ expected("expression");
+ }
+
+ // RIGHT_BRACKET
+ if (!acceptTokenClass(EHTokRightBracket)) {
+ expected("]");
+ return;
+ }
+ // another RIGHT_BRACKET?
+ if (doubleBrackets && !acceptTokenClass(EHTokRightBracket)) {
+ expected("]]");
+ return;
+ }
+
+ // Add any values we found into the attribute map.
+ if (attributeToken.string != nullptr) {
+ TAttributeType attributeType = parseContext.attributeFromName(nameSpace, *attributeToken.string);
+ if (attributeType == EatNone)
+ parseContext.warn(attributeToken.loc, "unrecognized attribute", attributeToken.string->c_str(), "");
+ else {
+ TAttributeArgs attributeArgs = { attributeType, expressions };
+ attributes.push_back(attributeArgs);
+ }
+ }
+ } while (true);
+}
+
+// selection_statement
+// : IF LEFT_PAREN expression RIGHT_PAREN statement
+// : IF LEFT_PAREN expression RIGHT_PAREN statement ELSE statement
+//
+bool HlslGrammar::acceptSelectionStatement(TIntermNode*& statement, const TAttributes& attributes)
+{
+ TSourceLoc loc = token.loc;
+
+ // IF
+ if (! acceptTokenClass(EHTokIf))
+ return false;
+
+ // so that something declared in the condition is scoped to the lifetimes
+ // of the then-else statements
+ parseContext.pushScope();
+
+ // LEFT_PAREN expression RIGHT_PAREN
+ TIntermTyped* condition;
+ if (! acceptParenExpression(condition))
+ return false;
+ condition = parseContext.convertConditionalExpression(loc, condition);
+ if (condition == nullptr)
+ return false;
+
+ // create the child statements
+ TIntermNodePair thenElse = { nullptr, nullptr };
+
+ ++parseContext.controlFlowNestingLevel; // this only needs to work right if no errors
+
+ // then statement
+ if (! acceptScopedStatement(thenElse.node1)) {
+ expected("then statement");
+ return false;
+ }
+
+ // ELSE
+ if (acceptTokenClass(EHTokElse)) {
+ // else statement
+ if (! acceptScopedStatement(thenElse.node2)) {
+ expected("else statement");
+ return false;
+ }
+ }
+
+ // Put the pieces together
+ statement = intermediate.addSelection(condition, thenElse, loc);
+ parseContext.handleSelectionAttributes(loc, statement->getAsSelectionNode(), attributes);
+
+ parseContext.popScope();
+ --parseContext.controlFlowNestingLevel;
+
+ return true;
+}
+
+// switch_statement
+// : SWITCH LEFT_PAREN expression RIGHT_PAREN compound_statement
+//
+bool HlslGrammar::acceptSwitchStatement(TIntermNode*& statement, const TAttributes& attributes)
+{
+ // SWITCH
+ TSourceLoc loc = token.loc;
+
+ if (! acceptTokenClass(EHTokSwitch))
+ return false;
+
+ // LEFT_PAREN expression RIGHT_PAREN
+ parseContext.pushScope();
+ TIntermTyped* switchExpression;
+ if (! acceptParenExpression(switchExpression)) {
+ parseContext.popScope();
+ return false;
+ }
+
+ // compound_statement
+ parseContext.pushSwitchSequence(new TIntermSequence);
+
+ ++parseContext.controlFlowNestingLevel;
+ bool statementOkay = acceptCompoundStatement(statement);
+ --parseContext.controlFlowNestingLevel;
+
+ if (statementOkay)
+ statement = parseContext.addSwitch(loc, switchExpression, statement ? statement->getAsAggregate() : nullptr,
+ attributes);
+
+ parseContext.popSwitchSequence();
+ parseContext.popScope();
+
+ return statementOkay;
+}
+
+// iteration_statement
+// : WHILE LEFT_PAREN condition RIGHT_PAREN statement
+// | DO LEFT_BRACE statement RIGHT_BRACE WHILE LEFT_PAREN expression RIGHT_PAREN SEMICOLON
+// | FOR LEFT_PAREN for_init_statement for_rest_statement RIGHT_PAREN statement
+//
+// Non-speculative, only call if it needs to be found; WHILE or DO or FOR already seen.
+bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement, const TAttributes& attributes)
+{
+ TSourceLoc loc = token.loc;
+ TIntermTyped* condition = nullptr;
+
+ EHlslTokenClass loop = peek();
+ assert(loop == EHTokDo || loop == EHTokFor || loop == EHTokWhile);
+
+ // WHILE or DO or FOR
+ advanceToken();
+
+ TIntermLoop* loopNode = nullptr;
+ switch (loop) {
+ case EHTokWhile:
+ // so that something declared in the condition is scoped to the lifetime
+ // of the while sub-statement
+ parseContext.pushScope(); // this only needs to work right if no errors
+ parseContext.nestLooping();
+ ++parseContext.controlFlowNestingLevel;
+
+ // LEFT_PAREN condition RIGHT_PAREN
+ if (! acceptParenExpression(condition))
+ return false;
+ condition = parseContext.convertConditionalExpression(loc, condition);
+ if (condition == nullptr)
+ return false;
+
+ // statement
+ if (! acceptScopedStatement(statement)) {
+ expected("while sub-statement");
+ return false;
+ }
+
+ parseContext.unnestLooping();
+ parseContext.popScope();
+ --parseContext.controlFlowNestingLevel;
+
+ loopNode = intermediate.addLoop(statement, condition, nullptr, true, loc);
+ statement = loopNode;
+ break;
+
+ case EHTokDo:
+ parseContext.nestLooping(); // this only needs to work right if no errors
+ ++parseContext.controlFlowNestingLevel;
+
+ // statement
+ if (! acceptScopedStatement(statement)) {
+ expected("do sub-statement");
+ return false;
+ }
+
+ // WHILE
+ if (! acceptTokenClass(EHTokWhile)) {
+ expected("while");
+ return false;
+ }
+
+ // LEFT_PAREN condition RIGHT_PAREN
+ if (! acceptParenExpression(condition))
+ return false;
+ condition = parseContext.convertConditionalExpression(loc, condition);
+ if (condition == nullptr)
+ return false;
+
+ if (! acceptTokenClass(EHTokSemicolon))
+ expected(";");
+
+ parseContext.unnestLooping();
+ --parseContext.controlFlowNestingLevel;
+
+ loopNode = intermediate.addLoop(statement, condition, 0, false, loc);
+ statement = loopNode;
+ break;
+
+ case EHTokFor:
+ {
+ // LEFT_PAREN
+ if (! acceptTokenClass(EHTokLeftParen))
+ expected("(");
+
+ // so that something declared in the condition is scoped to the lifetime
+ // of the for sub-statement
+ parseContext.pushScope();
+
+ // initializer
+ TIntermNode* initNode = nullptr;
+ if (! acceptSimpleStatement(initNode))
+ expected("for-loop initializer statement");
+
+ parseContext.nestLooping(); // this only needs to work right if no errors
+ ++parseContext.controlFlowNestingLevel;
+
+ // condition SEMI_COLON
+ acceptExpression(condition);
+ if (! acceptTokenClass(EHTokSemicolon))
+ expected(";");
+ if (condition != nullptr) {
+ condition = parseContext.convertConditionalExpression(loc, condition);
+ if (condition == nullptr)
+ return false;
+ }
+
+ // iterator SEMI_COLON
+ TIntermTyped* iterator = nullptr;
+ acceptExpression(iterator);
+ if (! acceptTokenClass(EHTokRightParen))
+ expected(")");
+
+ // statement
+ if (! acceptScopedStatement(statement)) {
+ expected("for sub-statement");
+ return false;
+ }
+
+ statement = intermediate.addForLoop(statement, initNode, condition, iterator, true, loc, loopNode);
+
+ parseContext.popScope();
+ parseContext.unnestLooping();
+ --parseContext.controlFlowNestingLevel;
+
+ break;
+ }
+
+ default:
+ return false;
+ }
+
+ parseContext.handleLoopAttributes(loc, loopNode, attributes);
+ return true;
+}
+
+// jump_statement
+// : CONTINUE SEMICOLON
+// | BREAK SEMICOLON
+// | DISCARD SEMICOLON
+// | RETURN SEMICOLON
+// | RETURN expression SEMICOLON
+//
+bool HlslGrammar::acceptJumpStatement(TIntermNode*& statement)
+{
+ EHlslTokenClass jump = peek();
+ switch (jump) {
+ case EHTokContinue:
+ case EHTokBreak:
+ case EHTokDiscard:
+ case EHTokReturn:
+ advanceToken();
+ break;
+ default:
+ // not something we handle in this function
+ return false;
+ }
+
+ switch (jump) {
+ case EHTokContinue:
+ statement = intermediate.addBranch(EOpContinue, token.loc);
+ if (parseContext.loopNestingLevel == 0) {
+ expected("loop");
+ return false;
+ }
+ break;
+ case EHTokBreak:
+ statement = intermediate.addBranch(EOpBreak, token.loc);
+ if (parseContext.loopNestingLevel == 0 && parseContext.switchSequenceStack.size() == 0) {
+ expected("loop or switch");
+ return false;
+ }
+ break;
+ case EHTokDiscard:
+ statement = intermediate.addBranch(EOpKill, token.loc);
+ break;
+
+ case EHTokReturn:
+ {
+ // expression
+ TIntermTyped* node;
+ if (acceptExpression(node)) {
+ // hook it up
+ statement = parseContext.handleReturnValue(token.loc, node);
+ } else
+ statement = intermediate.addBranch(EOpReturn, token.loc);
+ break;
+ }
+
+ default:
+ assert(0);
+ return false;
+ }
+
+ // SEMICOLON
+ if (! acceptTokenClass(EHTokSemicolon))
+ expected(";");
+
+ return true;
+}
+
+// case_label
+// : CASE expression COLON
+//
+bool HlslGrammar::acceptCaseLabel(TIntermNode*& statement)
+{
+ TSourceLoc loc = token.loc;
+ if (! acceptTokenClass(EHTokCase))
+ return false;
+
+ TIntermTyped* expression;
+ if (! acceptExpression(expression)) {
+ expected("case expression");
+ return false;
+ }
+
+ if (! acceptTokenClass(EHTokColon)) {
+ expected(":");
+ return false;
+ }
+
+ statement = parseContext.intermediate.addBranch(EOpCase, expression, loc);
+
+ return true;
+}
+
+// default_label
+// : DEFAULT COLON
+//
+bool HlslGrammar::acceptDefaultLabel(TIntermNode*& statement)
+{
+ TSourceLoc loc = token.loc;
+ if (! acceptTokenClass(EHTokDefault))
+ return false;
+
+ if (! acceptTokenClass(EHTokColon)) {
+ expected(":");
+ return false;
+ }
+
+ statement = parseContext.intermediate.addBranch(EOpDefault, loc);
+
+ return true;
+}
+
+// array_specifier
+// : LEFT_BRACKET integer_expression RGHT_BRACKET ... // optional
+// : LEFT_BRACKET RGHT_BRACKET // optional
+//
+void HlslGrammar::acceptArraySpecifier(TArraySizes*& arraySizes)
+{
+ arraySizes = nullptr;
+
+ // Early-out if there aren't any array dimensions
+ if (!peekTokenClass(EHTokLeftBracket))
+ return;
+
+ // If we get here, we have at least one array dimension. This will track the sizes we find.
+ arraySizes = new TArraySizes;
+
+ // Collect each array dimension.
+ while (acceptTokenClass(EHTokLeftBracket)) {
+ TSourceLoc loc = token.loc;
+ TIntermTyped* sizeExpr = nullptr;
+
+ // Array sizing expression is optional. If omitted, array will be later sized by initializer list.
+ const bool hasArraySize = acceptAssignmentExpression(sizeExpr);
+
+ if (! acceptTokenClass(EHTokRightBracket)) {
+ expected("]");
+ return;
+ }
+
+ if (hasArraySize) {
+ TArraySize arraySize;
+ parseContext.arraySizeCheck(loc, sizeExpr, arraySize);
+ arraySizes->addInnerSize(arraySize);
+ } else {
+ arraySizes->addInnerSize(0); // sized by initializers.
+ }
+ }
+}
+
+// post_decls
+// : COLON semantic // optional
+// COLON PACKOFFSET LEFT_PAREN c[Subcomponent][.component] RIGHT_PAREN // optional
+// COLON REGISTER LEFT_PAREN [shader_profile,] Type#[subcomp]opt (COMMA SPACEN)opt RIGHT_PAREN // optional
+// COLON LAYOUT layout_qualifier_list
+// annotations // optional
+//
+// Return true if any tokens were accepted. That is,
+// false can be returned on successfully recognizing nothing,
+// not necessarily meaning bad syntax.
+//
+bool HlslGrammar::acceptPostDecls(TQualifier& qualifier)
+{
+ bool found = false;
+
+ do {
+ // COLON
+ if (acceptTokenClass(EHTokColon)) {
+ found = true;
+ HlslToken idToken;
+ if (peekTokenClass(EHTokLayout))
+ acceptLayoutQualifierList(qualifier);
+ else if (acceptTokenClass(EHTokPackOffset)) {
+ // PACKOFFSET LEFT_PAREN c[Subcomponent][.component] RIGHT_PAREN
+ if (! acceptTokenClass(EHTokLeftParen)) {
+ expected("(");
+ return false;
+ }
+ HlslToken locationToken;
+ if (! acceptIdentifier(locationToken)) {
+ expected("c[subcomponent][.component]");
+ return false;
+ }
+ HlslToken componentToken;
+ if (acceptTokenClass(EHTokDot)) {
+ if (! acceptIdentifier(componentToken)) {
+ expected("component");
+ return false;
+ }
+ }
+ if (! acceptTokenClass(EHTokRightParen)) {
+ expected(")");
+ break;
+ }
+ parseContext.handlePackOffset(locationToken.loc, qualifier, *locationToken.string, componentToken.string);
+ } else if (! acceptIdentifier(idToken)) {
+ expected("layout, semantic, packoffset, or register");
+ return false;
+ } else if (*idToken.string == "register") {
+ // REGISTER LEFT_PAREN [shader_profile,] Type#[subcomp]opt (COMMA SPACEN)opt RIGHT_PAREN
+ // LEFT_PAREN
+ if (! acceptTokenClass(EHTokLeftParen)) {
+ expected("(");
+ return false;
+ }
+ HlslToken registerDesc; // for Type#
+ HlslToken profile;
+ if (! acceptIdentifier(registerDesc)) {
+ expected("register number description");
+ return false;
+ }
+ if (registerDesc.string->size() > 1 && !isdigit((*registerDesc.string)[1]) &&
+ acceptTokenClass(EHTokComma)) {
+ // Then we didn't really see the registerDesc yet, it was
+ // actually the profile. Adjust...
+ profile = registerDesc;
+ if (! acceptIdentifier(registerDesc)) {
+ expected("register number description");
+ return false;
+ }
+ }
+ int subComponent = 0;
+ if (acceptTokenClass(EHTokLeftBracket)) {
+ // LEFT_BRACKET subcomponent RIGHT_BRACKET
+ if (! peekTokenClass(EHTokIntConstant)) {
+ expected("literal integer");
+ return false;
+ }
+ subComponent = token.i;
+ advanceToken();
+ if (! acceptTokenClass(EHTokRightBracket)) {
+ expected("]");
+ break;
+ }
+ }
+ // (COMMA SPACEN)opt
+ HlslToken spaceDesc;
+ if (acceptTokenClass(EHTokComma)) {
+ if (! acceptIdentifier(spaceDesc)) {
+ expected ("space identifier");
+ return false;
+ }
+ }
+ // RIGHT_PAREN
+ if (! acceptTokenClass(EHTokRightParen)) {
+ expected(")");
+ break;
+ }
+ parseContext.handleRegister(registerDesc.loc, qualifier, profile.string, *registerDesc.string, subComponent, spaceDesc.string);
+ } else {
+ // semantic, in idToken.string
+ TString semanticUpperCase = *idToken.string;
+ std::transform(semanticUpperCase.begin(), semanticUpperCase.end(), semanticUpperCase.begin(), ::toupper);
+ parseContext.handleSemantic(idToken.loc, qualifier, mapSemantic(semanticUpperCase.c_str()), semanticUpperCase);
+ }
+ } else if (peekTokenClass(EHTokLeftAngle)) {
+ found = true;
+ acceptAnnotations(qualifier);
+ } else
+ break;
+
+ } while (true);
+
+ return found;
+}
+
+//
+// Get the stream of tokens from the scanner, but skip all syntactic/semantic
+// processing.
+//
+bool HlslGrammar::captureBlockTokens(TVector<HlslToken>& tokens)
+{
+ if (! peekTokenClass(EHTokLeftBrace))
+ return false;
+
+ int braceCount = 0;
+
+ do {
+ switch (peek()) {
+ case EHTokLeftBrace:
+ ++braceCount;
+ break;
+ case EHTokRightBrace:
+ --braceCount;
+ break;
+ case EHTokNone:
+ // End of input before balance { } is bad...
+ return false;
+ default:
+ break;
+ }
+
+ tokens.push_back(token);
+ advanceToken();
+ } while (braceCount > 0);
+
+ return true;
+}
+
+// Return a string for just the types that can also be declared as an identifier.
+const char* HlslGrammar::getTypeString(EHlslTokenClass tokenClass) const
+{
+ switch (tokenClass) {
+ case EHTokSample: return "sample";
+ case EHTokHalf: return "half";
+ case EHTokHalf1x1: return "half1x1";
+ case EHTokHalf1x2: return "half1x2";
+ case EHTokHalf1x3: return "half1x3";
+ case EHTokHalf1x4: return "half1x4";
+ case EHTokHalf2x1: return "half2x1";
+ case EHTokHalf2x2: return "half2x2";
+ case EHTokHalf2x3: return "half2x3";
+ case EHTokHalf2x4: return "half2x4";
+ case EHTokHalf3x1: return "half3x1";
+ case EHTokHalf3x2: return "half3x2";
+ case EHTokHalf3x3: return "half3x3";
+ case EHTokHalf3x4: return "half3x4";
+ case EHTokHalf4x1: return "half4x1";
+ case EHTokHalf4x2: return "half4x2";
+ case EHTokHalf4x3: return "half4x3";
+ case EHTokHalf4x4: return "half4x4";
+ case EHTokBool: return "bool";
+ case EHTokFloat: return "float";
+ case EHTokDouble: return "double";
+ case EHTokInt: return "int";
+ case EHTokUint: return "uint";
+ case EHTokMin16float: return "min16float";
+ case EHTokMin10float: return "min10float";
+ case EHTokMin16int: return "min16int";
+ case EHTokMin12int: return "min12int";
+ case EHTokConstantBuffer: return "ConstantBuffer";
+ case EHTokLayout: return "layout";
+ default:
+ return nullptr;
+ }
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/hlsl/hlslGrammar.h b/src/3rdparty/glslang/hlsl/hlslGrammar.h
new file mode 100644
index 0000000..27706b2
--- /dev/null
+++ b/src/3rdparty/glslang/hlsl/hlslGrammar.h
@@ -0,0 +1,142 @@
+//
+// Copyright (C) 2016-2018 Google, Inc.
+// Copyright (C) 2016 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of Google, Inc., nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef HLSLGRAMMAR_H_
+#define HLSLGRAMMAR_H_
+
+#include "hlslParseHelper.h"
+#include "hlslOpMap.h"
+#include "hlslTokenStream.h"
+
+namespace glslang {
+
+ class TFunctionDeclarator;
+
+ // Should just be the grammar aspect of HLSL.
+ // Described in more detail in hlslGrammar.cpp.
+
+ class HlslGrammar : public HlslTokenStream {
+ public:
+ HlslGrammar(HlslScanContext& scanner, HlslParseContext& parseContext)
+ : HlslTokenStream(scanner), parseContext(parseContext), intermediate(parseContext.intermediate),
+ typeIdentifiers(false), unitNode(nullptr) { }
+ virtual ~HlslGrammar() { }
+
+ bool parse();
+
+ protected:
+ HlslGrammar();
+ HlslGrammar& operator=(const HlslGrammar&);
+
+ void expected(const char*);
+ void unimplemented(const char*);
+ bool acceptIdentifier(HlslToken&);
+ bool acceptCompilationUnit();
+ bool acceptDeclarationList(TIntermNode*&);
+ bool acceptDeclaration(TIntermNode*&);
+ bool acceptControlDeclaration(TIntermNode*& node);
+ bool acceptSamplerDeclarationDX9(TType&);
+ bool acceptSamplerState();
+ bool acceptFullySpecifiedType(TType&, const TAttributes&);
+ bool acceptFullySpecifiedType(TType&, TIntermNode*& nodeList, const TAttributes&, bool forbidDeclarators = false);
+ bool acceptQualifier(TQualifier&);
+ bool acceptLayoutQualifierList(TQualifier&);
+ bool acceptType(TType&);
+ bool acceptType(TType&, TIntermNode*& nodeList);
+ bool acceptTemplateVecMatBasicType(TBasicType&);
+ bool acceptVectorTemplateType(TType&);
+ bool acceptMatrixTemplateType(TType&);
+ bool acceptTessellationDeclType(TBuiltInVariable&);
+ bool acceptTessellationPatchTemplateType(TType&);
+ bool acceptStreamOutTemplateType(TType&, TLayoutGeometry&);
+ bool acceptOutputPrimitiveGeometry(TLayoutGeometry&);
+ bool acceptAnnotations(TQualifier&);
+ bool acceptSamplerTypeDX9(TType &);
+ bool acceptSamplerType(TType&);
+ bool acceptTextureType(TType&);
+ bool acceptSubpassInputType(TType&);
+ bool acceptStructBufferType(TType&);
+ bool acceptTextureBufferType(TType&);
+ bool acceptConstantBufferType(TType&);
+ bool acceptStruct(TType&, TIntermNode*& nodeList);
+ bool acceptStructDeclarationList(TTypeList*&, TIntermNode*& nodeList, TVector<TFunctionDeclarator>&);
+ bool acceptMemberFunctionDefinition(TIntermNode*& nodeList, const TType&, TString& memberName,
+ TFunctionDeclarator&);
+ bool acceptFunctionParameters(TFunction&);
+ bool acceptParameterDeclaration(TFunction&);
+ bool acceptFunctionDefinition(TFunctionDeclarator&, TIntermNode*& nodeList, TVector<HlslToken>* deferredTokens);
+ bool acceptFunctionBody(TFunctionDeclarator& declarator, TIntermNode*& nodeList);
+ bool acceptParenExpression(TIntermTyped*&);
+ bool acceptExpression(TIntermTyped*&);
+ bool acceptInitializer(TIntermTyped*&);
+ bool acceptAssignmentExpression(TIntermTyped*&);
+ bool acceptConditionalExpression(TIntermTyped*&);
+ bool acceptBinaryExpression(TIntermTyped*&, PrecedenceLevel);
+ bool acceptUnaryExpression(TIntermTyped*&);
+ bool acceptPostfixExpression(TIntermTyped*&);
+ bool acceptConstructor(TIntermTyped*&);
+ bool acceptFunctionCall(const TSourceLoc&, TString& name, TIntermTyped*&, TIntermTyped* objectBase);
+ bool acceptArguments(TFunction*, TIntermTyped*&);
+ bool acceptLiteral(TIntermTyped*&);
+ bool acceptSimpleStatement(TIntermNode*&);
+ bool acceptCompoundStatement(TIntermNode*&);
+ bool acceptScopedStatement(TIntermNode*&);
+ bool acceptScopedCompoundStatement(TIntermNode*&);
+ bool acceptStatement(TIntermNode*&);
+ bool acceptNestedStatement(TIntermNode*&);
+ void acceptAttributes(TAttributes&);
+ bool acceptSelectionStatement(TIntermNode*&, const TAttributes&);
+ bool acceptSwitchStatement(TIntermNode*&, const TAttributes&);
+ bool acceptIterationStatement(TIntermNode*&, const TAttributes&);
+ bool acceptJumpStatement(TIntermNode*&);
+ bool acceptCaseLabel(TIntermNode*&);
+ bool acceptDefaultLabel(TIntermNode*&);
+ void acceptArraySpecifier(TArraySizes*&);
+ bool acceptPostDecls(TQualifier&);
+ bool acceptDefaultParameterDeclaration(const TType&, TIntermTyped*&);
+
+ bool captureBlockTokens(TVector<HlslToken>& tokens);
+ const char* getTypeString(EHlslTokenClass tokenClass) const;
+
+ HlslParseContext& parseContext; // state of parsing and helper functions for building the intermediate
+ TIntermediate& intermediate; // the final product, the intermediate representation, includes the AST
+ bool typeIdentifiers; // shader uses some types as identifiers
+ TIntermNode* unitNode;
+ };
+
+} // end namespace glslang
+
+#endif // HLSLGRAMMAR_H_
diff --git a/src/3rdparty/glslang/hlsl/hlslOpMap.cpp b/src/3rdparty/glslang/hlsl/hlslOpMap.cpp
new file mode 100644
index 0000000..ebe6fbd
--- /dev/null
+++ b/src/3rdparty/glslang/hlsl/hlslOpMap.cpp
@@ -0,0 +1,173 @@
+//
+// Copyright (C) 2016 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of Google, Inc., nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+// Map from physical token form (e.g. '-') to logical operator
+// form (e.g., binary subtract or unary negate).
+
+#include "hlslOpMap.h"
+
+namespace glslang {
+
+// Map parsing tokens that could be assignments into assignment operators.
+TOperator HlslOpMap::assignment(EHlslTokenClass op)
+{
+ switch (op) {
+ case EHTokAssign: return EOpAssign;
+ case EHTokMulAssign: return EOpMulAssign;
+ case EHTokDivAssign: return EOpDivAssign;
+ case EHTokAddAssign: return EOpAddAssign;
+ case EHTokModAssign: return EOpModAssign;
+ case EHTokLeftAssign: return EOpLeftShiftAssign;
+ case EHTokRightAssign: return EOpRightShiftAssign;
+ case EHTokAndAssign: return EOpAndAssign;
+ case EHTokXorAssign: return EOpExclusiveOrAssign;
+ case EHTokOrAssign: return EOpInclusiveOrAssign;
+ case EHTokSubAssign: return EOpSubAssign;
+
+ default:
+ return EOpNull;
+ }
+}
+
+// Map parsing tokens that could be binary operations into binary operators.
+TOperator HlslOpMap::binary(EHlslTokenClass op)
+{
+ switch (op) {
+ case EHTokPlus: return EOpAdd;
+ case EHTokDash: return EOpSub;
+ case EHTokStar: return EOpMul;
+ case EHTokSlash: return EOpDiv;
+ case EHTokPercent: return EOpMod;
+ case EHTokRightOp: return EOpRightShift;
+ case EHTokLeftOp: return EOpLeftShift;
+ case EHTokAmpersand: return EOpAnd;
+ case EHTokVerticalBar: return EOpInclusiveOr;
+ case EHTokCaret: return EOpExclusiveOr;
+ case EHTokEqOp: return EOpEqual;
+ case EHTokNeOp: return EOpNotEqual;
+ case EHTokLeftAngle: return EOpLessThan;
+ case EHTokRightAngle: return EOpGreaterThan;
+ case EHTokLeOp: return EOpLessThanEqual;
+ case EHTokGeOp: return EOpGreaterThanEqual;
+ case EHTokOrOp: return EOpLogicalOr;
+ case EHTokXorOp: return EOpLogicalXor;
+ case EHTokAndOp: return EOpLogicalAnd;
+
+ default:
+ return EOpNull;
+ }
+}
+
+// Map parsing tokens that could be unary operations into unary operators.
+// These are just the ones that can appear in front of its operand.
+TOperator HlslOpMap::preUnary(EHlslTokenClass op)
+{
+ switch (op) {
+ case EHTokPlus: return EOpAdd; // means no-op, but still a unary op was present
+ case EHTokDash: return EOpNegative;
+ case EHTokBang: return EOpLogicalNot;
+ case EHTokTilde: return EOpBitwiseNot;
+
+ case EHTokIncOp: return EOpPreIncrement;
+ case EHTokDecOp: return EOpPreDecrement;
+
+ default: return EOpNull; // means not a pre-unary op
+ }
+}
+
+// Map parsing tokens that could be unary operations into unary operators.
+// These are just the ones that can appear behind its operand.
+TOperator HlslOpMap::postUnary(EHlslTokenClass op)
+{
+ switch (op) {
+ case EHTokDot: return EOpIndexDirectStruct;
+ case EHTokLeftBracket: return EOpIndexIndirect;
+
+ case EHTokIncOp: return EOpPostIncrement;
+ case EHTokDecOp: return EOpPostDecrement;
+
+ case EHTokColonColon: return EOpScoping;
+
+ default: return EOpNull; // means not a post-unary op
+ }
+}
+
+// Map operators into their level of precedence.
+PrecedenceLevel HlslOpMap::precedenceLevel(TOperator op)
+{
+ switch (op) {
+ case EOpLogicalOr:
+ return PlLogicalOr;
+ case EOpLogicalXor:
+ return PlLogicalXor;
+ case EOpLogicalAnd:
+ return PlLogicalAnd;
+
+ case EOpInclusiveOr:
+ return PlBitwiseOr;
+ case EOpExclusiveOr:
+ return PlBitwiseXor;
+ case EOpAnd:
+ return PlBitwiseAnd;
+
+ case EOpEqual:
+ case EOpNotEqual:
+ return PlEquality;
+
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+ return PlRelational;
+
+ case EOpRightShift:
+ case EOpLeftShift:
+ return PlShift;
+
+ case EOpAdd:
+ case EOpSub:
+ return PlAdd;
+
+ case EOpMul:
+ case EOpDiv:
+ case EOpMod:
+ return PlMul;
+
+ default:
+ return PlBad;
+ }
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/hlsl/hlslOpMap.h b/src/3rdparty/glslang/hlsl/hlslOpMap.h
new file mode 100644
index 0000000..4e783f3
--- /dev/null
+++ b/src/3rdparty/glslang/hlsl/hlslOpMap.h
@@ -0,0 +1,69 @@
+//
+// Copyright (C) 2016 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of Google, Inc., nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef HLSLOPMAP_H_
+#define HLSLOPMAP_H_
+
+#include "hlslScanContext.h"
+
+namespace glslang {
+
+ enum PrecedenceLevel {
+ PlBad,
+ PlLogicalOr,
+ PlLogicalXor,
+ PlLogicalAnd,
+ PlBitwiseOr,
+ PlBitwiseXor,
+ PlBitwiseAnd,
+ PlEquality,
+ PlRelational,
+ PlShift,
+ PlAdd,
+ PlMul
+ };
+
+ class HlslOpMap {
+ public:
+ static TOperator assignment(EHlslTokenClass op);
+ static TOperator binary(EHlslTokenClass op);
+ static TOperator preUnary(EHlslTokenClass op);
+ static TOperator postUnary(EHlslTokenClass op);
+ static PrecedenceLevel precedenceLevel(TOperator);
+ };
+
+} // end namespace glslang
+
+#endif // HLSLOPMAP_H_
diff --git a/src/3rdparty/glslang/hlsl/hlslParseHelper.cpp b/src/3rdparty/glslang/hlsl/hlslParseHelper.cpp
new file mode 100644
index 0000000..213f236
--- /dev/null
+++ b/src/3rdparty/glslang/hlsl/hlslParseHelper.cpp
@@ -0,0 +1,9984 @@
+//
+// Copyright (C) 2017-2018 Google, Inc.
+// Copyright (C) 2017 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "hlslParseHelper.h"
+#include "hlslScanContext.h"
+#include "hlslGrammar.h"
+#include "hlslAttributes.h"
+
+#include "../glslang/Include/Common.h"
+#include "../glslang/MachineIndependent/Scan.h"
+#include "../glslang/MachineIndependent/preprocessor/PpContext.h"
+
+#include "../glslang/OSDependent/osinclude.h"
+
+#include <algorithm>
+#include <functional>
+#include <cctype>
+#include <array>
+#include <set>
+
+namespace glslang {
+
+HlslParseContext::HlslParseContext(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins,
+ int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language,
+ TInfoSink& infoSink,
+ const TString sourceEntryPointName,
+ bool forwardCompatible, EShMessages messages) :
+ TParseContextBase(symbolTable, interm, parsingBuiltins, version, profile, spvVersion, language, infoSink,
+ forwardCompatible, messages, &sourceEntryPointName),
+ annotationNestingLevel(0),
+ inputPatch(nullptr),
+ nextInLocation(0), nextOutLocation(0),
+ entryPointFunction(nullptr),
+ entryPointFunctionBody(nullptr),
+ gsStreamOutput(nullptr),
+ clipDistanceOutput(nullptr),
+ cullDistanceOutput(nullptr),
+ clipDistanceInput(nullptr),
+ cullDistanceInput(nullptr)
+{
+ globalUniformDefaults.clear();
+ globalUniformDefaults.layoutMatrix = ElmRowMajor;
+ globalUniformDefaults.layoutPacking = ElpStd140;
+
+ globalBufferDefaults.clear();
+ globalBufferDefaults.layoutMatrix = ElmRowMajor;
+ globalBufferDefaults.layoutPacking = ElpStd430;
+
+ globalInputDefaults.clear();
+ globalOutputDefaults.clear();
+
+ clipSemanticNSizeIn.fill(0);
+ cullSemanticNSizeIn.fill(0);
+ clipSemanticNSizeOut.fill(0);
+ cullSemanticNSizeOut.fill(0);
+
+ // "Shaders in the transform
+ // feedback capturing mode have an initial global default of
+ // layout(xfb_buffer = 0) out;"
+ if (language == EShLangVertex ||
+ language == EShLangTessControl ||
+ language == EShLangTessEvaluation ||
+ language == EShLangGeometry)
+ globalOutputDefaults.layoutXfbBuffer = 0;
+
+ if (language == EShLangGeometry)
+ globalOutputDefaults.layoutStream = 0;
+}
+
+HlslParseContext::~HlslParseContext()
+{
+}
+
+void HlslParseContext::initializeExtensionBehavior()
+{
+ TParseContextBase::initializeExtensionBehavior();
+
+ // HLSL allows #line by default.
+ extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhEnable;
+}
+
+void HlslParseContext::setLimits(const TBuiltInResource& r)
+{
+ resources = r;
+ intermediate.setLimits(resources);
+}
+
+//
+// Parse an array of strings using the parser in HlslRules.
+//
+// Returns true for successful acceptance of the shader, false if any errors.
+//
+bool HlslParseContext::parseShaderStrings(TPpContext& ppContext, TInputScanner& input, bool versionWillBeError)
+{
+ currentScanner = &input;
+ ppContext.setInput(input, versionWillBeError);
+
+ HlslScanContext scanContext(*this, ppContext);
+ HlslGrammar grammar(scanContext, *this);
+ if (!grammar.parse()) {
+ // Print a message formated such that if you click on the message it will take you right to
+ // the line through most UIs.
+ const glslang::TSourceLoc& sourceLoc = input.getSourceLoc();
+ infoSink.info << sourceLoc.getFilenameStr() << "(" << sourceLoc.line << "): error at column " << sourceLoc.column
+ << ", HLSL parsing failed.\n";
+ ++numErrors;
+ return false;
+ }
+
+ finish();
+
+ return numErrors == 0;
+}
+
+//
+// Return true if this l-value node should be converted in some manner.
+// For instance: turning a load aggregate into a store in an l-value.
+//
+bool HlslParseContext::shouldConvertLValue(const TIntermNode* node) const
+{
+ if (node == nullptr || node->getAsTyped() == nullptr)
+ return false;
+
+ const TIntermAggregate* lhsAsAggregate = node->getAsAggregate();
+ const TIntermBinary* lhsAsBinary = node->getAsBinaryNode();
+
+ // If it's a swizzled/indexed aggregate, look at the left node instead.
+ if (lhsAsBinary != nullptr &&
+ (lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect))
+ lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate();
+ if (lhsAsAggregate != nullptr && lhsAsAggregate->getOp() == EOpImageLoad)
+ return true;
+
+ return false;
+}
+
+void HlslParseContext::growGlobalUniformBlock(const TSourceLoc& loc, TType& memberType, const TString& memberName,
+ TTypeList* newTypeList)
+{
+ newTypeList = nullptr;
+ correctUniform(memberType.getQualifier());
+ if (memberType.isStruct()) {
+ auto it = ioTypeMap.find(memberType.getStruct());
+ if (it != ioTypeMap.end() && it->second.uniform)
+ newTypeList = it->second.uniform;
+ }
+ TParseContextBase::growGlobalUniformBlock(loc, memberType, memberName, newTypeList);
+}
+
+//
+// Return a TLayoutFormat corresponding to the given texture type.
+//
+TLayoutFormat HlslParseContext::getLayoutFromTxType(const TSourceLoc& loc, const TType& txType)
+{
+ if (txType.isStruct()) {
+ // TODO: implement.
+ error(loc, "unimplemented: structure type in image or buffer", "", "");
+ return ElfNone;
+ }
+
+ const int components = txType.getVectorSize();
+ const TBasicType txBasicType = txType.getBasicType();
+
+ const auto selectFormat = [this,&components](TLayoutFormat v1, TLayoutFormat v2, TLayoutFormat v4) -> TLayoutFormat {
+ if (intermediate.getNoStorageFormat())
+ return ElfNone;
+
+ return components == 1 ? v1 :
+ components == 2 ? v2 : v4;
+ };
+
+ switch (txBasicType) {
+ case EbtFloat: return selectFormat(ElfR32f, ElfRg32f, ElfRgba32f);
+ case EbtInt: return selectFormat(ElfR32i, ElfRg32i, ElfRgba32i);
+ case EbtUint: return selectFormat(ElfR32ui, ElfRg32ui, ElfRgba32ui);
+ default:
+ error(loc, "unknown basic type in image format", "", "");
+ return ElfNone;
+ }
+}
+
+//
+// Both test and if necessary, spit out an error, to see if the node is really
+// an l-value that can be operated on this way.
+//
+// Returns true if there was an error.
+//
+bool HlslParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node)
+{
+ if (shouldConvertLValue(node)) {
+ // if we're writing to a texture, it must be an RW form.
+
+ TIntermAggregate* lhsAsAggregate = node->getAsAggregate();
+ TIntermTyped* object = lhsAsAggregate->getSequence()[0]->getAsTyped();
+
+ if (!object->getType().getSampler().isImage()) {
+ error(loc, "operator[] on a non-RW texture must be an r-value", "", "");
+ return true;
+ }
+ }
+
+ // We tolerate samplers as l-values, even though they are nominally
+ // illegal, because we expect a later optimization to eliminate them.
+ if (node->getType().getBasicType() == EbtSampler) {
+ intermediate.setNeedsLegalization();
+ return false;
+ }
+
+ // Let the base class check errors
+ return TParseContextBase::lValueErrorCheck(loc, op, node);
+}
+
+//
+// This function handles l-value conversions and verifications. It uses, but is not synonymous
+// with lValueErrorCheck. That function accepts an l-value directly, while this one must be
+// given the surrounding tree - e.g, with an assignment, so we can convert the assign into a
+// series of other image operations.
+//
+// Most things are passed through unmodified, except for error checking.
+//
+TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char* op, TIntermTyped*& node)
+{
+ if (node == nullptr)
+ return nullptr;
+
+ TIntermBinary* nodeAsBinary = node->getAsBinaryNode();
+ TIntermUnary* nodeAsUnary = node->getAsUnaryNode();
+ TIntermAggregate* sequence = nullptr;
+
+ TIntermTyped* lhs = nodeAsUnary ? nodeAsUnary->getOperand() :
+ nodeAsBinary ? nodeAsBinary->getLeft() :
+ nullptr;
+
+ // Early bail out if there is no conversion to apply
+ if (!shouldConvertLValue(lhs)) {
+ if (lhs != nullptr)
+ if (lValueErrorCheck(loc, op, lhs))
+ return nullptr;
+ return node;
+ }
+
+ // *** If we get here, we're going to apply some conversion to an l-value.
+
+ // Helper to create a load.
+ const auto makeLoad = [&](TIntermSymbol* rhsTmp, TIntermTyped* object, TIntermTyped* coord, const TType& derefType) {
+ TIntermAggregate* loadOp = new TIntermAggregate(EOpImageLoad);
+ loadOp->setLoc(loc);
+ loadOp->getSequence().push_back(object);
+ loadOp->getSequence().push_back(intermediate.addSymbol(*coord->getAsSymbolNode()));
+ loadOp->setType(derefType);
+
+ sequence = intermediate.growAggregate(sequence,
+ intermediate.addAssign(EOpAssign, rhsTmp, loadOp, loc),
+ loc);
+ };
+
+ // Helper to create a store.
+ const auto makeStore = [&](TIntermTyped* object, TIntermTyped* coord, TIntermSymbol* rhsTmp) {
+ TIntermAggregate* storeOp = new TIntermAggregate(EOpImageStore);
+ storeOp->getSequence().push_back(object);
+ storeOp->getSequence().push_back(coord);
+ storeOp->getSequence().push_back(intermediate.addSymbol(*rhsTmp));
+ storeOp->setLoc(loc);
+ storeOp->setType(TType(EbtVoid));
+
+ sequence = intermediate.growAggregate(sequence, storeOp);
+ };
+
+ // Helper to create an assign.
+ const auto makeBinary = [&](TOperator op, TIntermTyped* lhs, TIntermTyped* rhs) {
+ sequence = intermediate.growAggregate(sequence,
+ intermediate.addBinaryNode(op, lhs, rhs, loc, lhs->getType()),
+ loc);
+ };
+
+ // Helper to complete sequence by adding trailing variable, so we evaluate to the right value.
+ const auto finishSequence = [&](TIntermSymbol* rhsTmp, const TType& derefType) -> TIntermAggregate* {
+ // Add a trailing use of the temp, so the sequence returns the proper value.
+ sequence = intermediate.growAggregate(sequence, intermediate.addSymbol(*rhsTmp));
+ sequence->setOperator(EOpSequence);
+ sequence->setLoc(loc);
+ sequence->setType(derefType);
+
+ return sequence;
+ };
+
+ // Helper to add unary op
+ const auto makeUnary = [&](TOperator op, TIntermSymbol* rhsTmp) {
+ sequence = intermediate.growAggregate(sequence,
+ intermediate.addUnaryNode(op, intermediate.addSymbol(*rhsTmp), loc,
+ rhsTmp->getType()),
+ loc);
+ };
+
+ // Return true if swizzle or index writes all components of the given variable.
+ const auto writesAllComponents = [&](TIntermSymbol* var, TIntermBinary* swizzle) -> bool {
+ if (swizzle == nullptr) // not a swizzle or index
+ return true;
+
+ // Track which components are being set.
+ std::array<bool, 4> compIsSet;
+ compIsSet.fill(false);
+
+ const TIntermConstantUnion* asConst = swizzle->getRight()->getAsConstantUnion();
+ const TIntermAggregate* asAggregate = swizzle->getRight()->getAsAggregate();
+
+ // This could be either a direct index, or a swizzle.
+ if (asConst) {
+ compIsSet[asConst->getConstArray()[0].getIConst()] = true;
+ } else if (asAggregate) {
+ const TIntermSequence& seq = asAggregate->getSequence();
+ for (int comp=0; comp<int(seq.size()); ++comp)
+ compIsSet[seq[comp]->getAsConstantUnion()->getConstArray()[0].getIConst()] = true;
+ } else {
+ assert(0);
+ }
+
+ // Return true if all components are being set by the index or swizzle
+ return std::all_of(compIsSet.begin(), compIsSet.begin() + var->getType().getVectorSize(),
+ [](bool isSet) { return isSet; } );
+ };
+
+ // Create swizzle matching input swizzle
+ const auto addSwizzle = [&](TIntermSymbol* var, TIntermBinary* swizzle) -> TIntermTyped* {
+ if (swizzle)
+ return intermediate.addBinaryNode(swizzle->getOp(), var, swizzle->getRight(), loc, swizzle->getType());
+ else
+ return var;
+ };
+
+ TIntermBinary* lhsAsBinary = lhs->getAsBinaryNode();
+ TIntermAggregate* lhsAsAggregate = lhs->getAsAggregate();
+ bool lhsIsSwizzle = false;
+
+ // If it's a swizzled L-value, remember the swizzle, and use the LHS.
+ if (lhsAsBinary != nullptr && (lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect)) {
+ lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate();
+ lhsIsSwizzle = true;
+ }
+
+ TIntermTyped* object = lhsAsAggregate->getSequence()[0]->getAsTyped();
+ TIntermTyped* coord = lhsAsAggregate->getSequence()[1]->getAsTyped();
+
+ const TSampler& texSampler = object->getType().getSampler();
+
+ TType objDerefType;
+ getTextureReturnType(texSampler, objDerefType);
+
+ if (nodeAsBinary) {
+ TIntermTyped* rhs = nodeAsBinary->getRight();
+ const TOperator assignOp = nodeAsBinary->getOp();
+
+ bool isModifyOp = false;
+
+ switch (assignOp) {
+ case EOpAddAssign:
+ case EOpSubAssign:
+ case EOpMulAssign:
+ case EOpVectorTimesMatrixAssign:
+ case EOpVectorTimesScalarAssign:
+ case EOpMatrixTimesScalarAssign:
+ case EOpMatrixTimesMatrixAssign:
+ case EOpDivAssign:
+ case EOpModAssign:
+ case EOpAndAssign:
+ case EOpInclusiveOrAssign:
+ case EOpExclusiveOrAssign:
+ case EOpLeftShiftAssign:
+ case EOpRightShiftAssign:
+ isModifyOp = true;
+ // fall through...
+ case EOpAssign:
+ {
+ // Since this is an lvalue, we'll convert an image load to a sequence like this
+ // (to still provide the value):
+ // OpSequence
+ // OpImageStore(object, lhs, rhs)
+ // rhs
+ // But if it's not a simple symbol RHS (say, a fn call), we don't want to duplicate the RHS,
+ // so we'll convert instead to this:
+ // OpSequence
+ // rhsTmp = rhs
+ // OpImageStore(object, coord, rhsTmp)
+ // rhsTmp
+ // If this is a read-modify-write op, like +=, we issue:
+ // OpSequence
+ // coordtmp = load's param1
+ // rhsTmp = OpImageLoad(object, coordTmp)
+ // rhsTmp op= rhs
+ // OpImageStore(object, coordTmp, rhsTmp)
+ // rhsTmp
+ //
+ // If the lvalue is swizzled, we apply that when writing the temp variable, like so:
+ // ...
+ // rhsTmp.some_swizzle = ...
+ // For partial writes, an error is generated.
+
+ TIntermSymbol* rhsTmp = rhs->getAsSymbolNode();
+ TIntermTyped* coordTmp = coord;
+
+ if (rhsTmp == nullptr || isModifyOp || lhsIsSwizzle) {
+ rhsTmp = makeInternalVariableNode(loc, "storeTemp", objDerefType);
+
+ // Partial updates not yet supported
+ if (!writesAllComponents(rhsTmp, lhsAsBinary)) {
+ error(loc, "unimplemented: partial image updates", "", "");
+ }
+
+ // Assign storeTemp = rhs
+ if (isModifyOp) {
+ // We have to make a temp var for the coordinate, to avoid evaluating it twice.
+ coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType());
+ makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1]
+ makeLoad(rhsTmp, object, coordTmp, objDerefType); // rhsTmp = OpImageLoad(object, coordTmp)
+ }
+
+ // rhsTmp op= rhs.
+ makeBinary(assignOp, addSwizzle(intermediate.addSymbol(*rhsTmp), lhsAsBinary), rhs);
+ }
+
+ makeStore(object, coordTmp, rhsTmp); // add a store
+ return finishSequence(rhsTmp, objDerefType); // return rhsTmp from sequence
+ }
+
+ default:
+ break;
+ }
+ }
+
+ if (nodeAsUnary) {
+ const TOperator assignOp = nodeAsUnary->getOp();
+
+ switch (assignOp) {
+ case EOpPreIncrement:
+ case EOpPreDecrement:
+ {
+ // We turn this into:
+ // OpSequence
+ // coordtmp = load's param1
+ // rhsTmp = OpImageLoad(object, coordTmp)
+ // rhsTmp op
+ // OpImageStore(object, coordTmp, rhsTmp)
+ // rhsTmp
+
+ TIntermSymbol* rhsTmp = makeInternalVariableNode(loc, "storeTemp", objDerefType);
+ TIntermTyped* coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType());
+
+ makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1]
+ makeLoad(rhsTmp, object, coordTmp, objDerefType); // rhsTmp = OpImageLoad(object, coordTmp)
+ makeUnary(assignOp, rhsTmp); // op rhsTmp
+ makeStore(object, coordTmp, rhsTmp); // OpImageStore(object, coordTmp, rhsTmp)
+ return finishSequence(rhsTmp, objDerefType); // return rhsTmp from sequence
+ }
+
+ case EOpPostIncrement:
+ case EOpPostDecrement:
+ {
+ // We turn this into:
+ // OpSequence
+ // coordtmp = load's param1
+ // rhsTmp1 = OpImageLoad(object, coordTmp)
+ // rhsTmp2 = rhsTmp1
+ // rhsTmp2 op
+ // OpImageStore(object, coordTmp, rhsTmp2)
+ // rhsTmp1 (pre-op value)
+ TIntermSymbol* rhsTmp1 = makeInternalVariableNode(loc, "storeTempPre", objDerefType);
+ TIntermSymbol* rhsTmp2 = makeInternalVariableNode(loc, "storeTempPost", objDerefType);
+ TIntermTyped* coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType());
+
+ makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1]
+ makeLoad(rhsTmp1, object, coordTmp, objDerefType); // rhsTmp1 = OpImageLoad(object, coordTmp)
+ makeBinary(EOpAssign, rhsTmp2, rhsTmp1); // rhsTmp2 = rhsTmp1
+ makeUnary(assignOp, rhsTmp2); // rhsTmp op
+ makeStore(object, coordTmp, rhsTmp2); // OpImageStore(object, coordTmp, rhsTmp2)
+ return finishSequence(rhsTmp1, objDerefType); // return rhsTmp from sequence
+ }
+
+ default:
+ break;
+ }
+ }
+
+ if (lhs)
+ if (lValueErrorCheck(loc, op, lhs))
+ return nullptr;
+
+ return node;
+}
+
+void HlslParseContext::handlePragma(const TSourceLoc& loc, const TVector<TString>& tokens)
+{
+ if (pragmaCallback)
+ pragmaCallback(loc.line, tokens);
+
+ if (tokens.size() == 0)
+ return;
+
+ // These pragmas are case insensitive in HLSL, so we'll compare in lower case.
+ TVector<TString> lowerTokens = tokens;
+
+ for (auto it = lowerTokens.begin(); it != lowerTokens.end(); ++it)
+ std::transform(it->begin(), it->end(), it->begin(), ::tolower);
+
+ // Handle pack_matrix
+ if (tokens.size() == 4 && lowerTokens[0] == "pack_matrix" && tokens[1] == "(" && tokens[3] == ")") {
+ // Note that HLSL semantic order is Mrc, not Mcr like SPIR-V, so we reverse the sense.
+ // Row major becomes column major and vice versa.
+
+ if (lowerTokens[2] == "row_major") {
+ globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmColumnMajor;
+ } else if (lowerTokens[2] == "column_major") {
+ globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmRowMajor;
+ } else {
+ // unknown majorness strings are treated as (HLSL column major)==(SPIR-V row major)
+ warn(loc, "unknown pack_matrix pragma value", tokens[2].c_str(), "");
+ globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmRowMajor;
+ }
+ return;
+ }
+
+ // Handle once
+ if (lowerTokens[0] == "once") {
+ warn(loc, "not implemented", "#pragma once", "");
+ return;
+ }
+}
+
+//
+// Look at a '.' matrix selector string and change it into components
+// for a matrix. There are two types:
+//
+// _21 second row, first column (one based)
+// _m21 third row, second column (zero based)
+//
+// Returns true if there is no error.
+//
+bool HlslParseContext::parseMatrixSwizzleSelector(const TSourceLoc& loc, const TString& fields, int cols, int rows,
+ TSwizzleSelectors<TMatrixSelector>& components)
+{
+ int startPos[MaxSwizzleSelectors];
+ int numComps = 0;
+ TString compString = fields;
+
+ // Find where each component starts,
+ // recording the first character position after the '_'.
+ for (size_t c = 0; c < compString.size(); ++c) {
+ if (compString[c] == '_') {
+ if (numComps >= MaxSwizzleSelectors) {
+ error(loc, "matrix component swizzle has too many components", compString.c_str(), "");
+ return false;
+ }
+ if (c > compString.size() - 3 ||
+ ((compString[c+1] == 'm' || compString[c+1] == 'M') && c > compString.size() - 4)) {
+ error(loc, "matrix component swizzle missing", compString.c_str(), "");
+ return false;
+ }
+ startPos[numComps++] = (int)c + 1;
+ }
+ }
+
+ // Process each component
+ for (int i = 0; i < numComps; ++i) {
+ int pos = startPos[i];
+ int bias = -1;
+ if (compString[pos] == 'm' || compString[pos] == 'M') {
+ bias = 0;
+ ++pos;
+ }
+ TMatrixSelector comp;
+ comp.coord1 = compString[pos+0] - '0' + bias;
+ comp.coord2 = compString[pos+1] - '0' + bias;
+ if (comp.coord1 < 0 || comp.coord1 >= cols) {
+ error(loc, "matrix row component out of range", compString.c_str(), "");
+ return false;
+ }
+ if (comp.coord2 < 0 || comp.coord2 >= rows) {
+ error(loc, "matrix column component out of range", compString.c_str(), "");
+ return false;
+ }
+ components.push_back(comp);
+ }
+
+ return true;
+}
+
+// If the 'comps' express a column of a matrix,
+// return the column. Column means the first coords all match.
+//
+// Otherwise, return -1.
+//
+int HlslParseContext::getMatrixComponentsColumn(int rows, const TSwizzleSelectors<TMatrixSelector>& selector)
+{
+ int col = -1;
+
+ // right number of comps?
+ if (selector.size() != rows)
+ return -1;
+
+ // all comps in the same column?
+ // rows in order?
+ col = selector[0].coord1;
+ for (int i = 0; i < rows; ++i) {
+ if (col != selector[i].coord1)
+ return -1;
+ if (i != selector[i].coord2)
+ return -1;
+ }
+
+ return col;
+}
+
+//
+// Handle seeing a variable identifier in the grammar.
+//
+TIntermTyped* HlslParseContext::handleVariable(const TSourceLoc& loc, const TString* string)
+{
+ int thisDepth;
+ TSymbol* symbol = symbolTable.find(*string, thisDepth);
+ if (symbol && symbol->getAsVariable() && symbol->getAsVariable()->isUserType()) {
+ error(loc, "expected symbol, not user-defined type", string->c_str(), "");
+ return nullptr;
+ }
+
+ const TVariable* variable = nullptr;
+ const TAnonMember* anon = symbol ? symbol->getAsAnonMember() : nullptr;
+ TIntermTyped* node = nullptr;
+ if (anon) {
+ // It was a member of an anonymous container, which could be a 'this' structure.
+
+ // Create a subtree for its dereference.
+ if (thisDepth > 0) {
+ variable = getImplicitThis(thisDepth);
+ if (variable == nullptr)
+ error(loc, "cannot access member variables (static member function?)", "this", "");
+ }
+ if (variable == nullptr)
+ variable = anon->getAnonContainer().getAsVariable();
+
+ TIntermTyped* container = intermediate.addSymbol(*variable, loc);
+ TIntermTyped* constNode = intermediate.addConstantUnion(anon->getMemberNumber(), loc);
+ node = intermediate.addIndex(EOpIndexDirectStruct, container, constNode, loc);
+
+ node->setType(*(*variable->getType().getStruct())[anon->getMemberNumber()].type);
+ if (node->getType().hiddenMember())
+ error(loc, "member of nameless block was not redeclared", string->c_str(), "");
+ } else {
+ // Not a member of an anonymous container.
+
+ // The symbol table search was done in the lexical phase.
+ // See if it was a variable.
+ variable = symbol ? symbol->getAsVariable() : nullptr;
+ if (variable) {
+ if ((variable->getType().getBasicType() == EbtBlock ||
+ variable->getType().getBasicType() == EbtStruct) && variable->getType().getStruct() == nullptr) {
+ error(loc, "cannot be used (maybe an instance name is needed)", string->c_str(), "");
+ variable = nullptr;
+ }
+ } else {
+ if (symbol)
+ error(loc, "variable name expected", string->c_str(), "");
+ }
+
+ // Recovery, if it wasn't found or was not a variable.
+ if (variable == nullptr) {
+ error(loc, "unknown variable", string->c_str(), "");
+ variable = new TVariable(string, TType(EbtVoid));
+ }
+
+ if (variable->getType().getQualifier().isFrontEndConstant())
+ node = intermediate.addConstantUnion(variable->getConstArray(), variable->getType(), loc);
+ else
+ node = intermediate.addSymbol(*variable, loc);
+ }
+
+ if (variable->getType().getQualifier().isIo())
+ intermediate.addIoAccessed(*string);
+
+ return node;
+}
+
+//
+// Handle operator[] on any objects it applies to. Currently:
+// Textures
+// Buffers
+//
+TIntermTyped* HlslParseContext::handleBracketOperator(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index)
+{
+ // handle r-value operator[] on textures and images. l-values will be processed later.
+ if (base->getType().getBasicType() == EbtSampler && !base->isArray()) {
+ const TSampler& sampler = base->getType().getSampler();
+ if (sampler.isImage() || sampler.isTexture()) {
+ if (! mipsOperatorMipArg.empty() && mipsOperatorMipArg.back().mipLevel == nullptr) {
+ // The first operator[] to a .mips[] sequence is the mip level. We'll remember it.
+ mipsOperatorMipArg.back().mipLevel = index;
+ return base; // next [] index is to the same base.
+ } else {
+ TIntermAggregate* load = new TIntermAggregate(sampler.isImage() ? EOpImageLoad : EOpTextureFetch);
+
+ TType sampReturnType;
+ getTextureReturnType(sampler, sampReturnType);
+
+ load->setType(sampReturnType);
+ load->setLoc(loc);
+ load->getSequence().push_back(base);
+ load->getSequence().push_back(index);
+
+ // Textures need a MIP. If we saw one go by, use it. Otherwise, use zero.
+ if (sampler.isTexture()) {
+ if (! mipsOperatorMipArg.empty()) {
+ load->getSequence().push_back(mipsOperatorMipArg.back().mipLevel);
+ mipsOperatorMipArg.pop_back();
+ } else {
+ load->getSequence().push_back(intermediate.addConstantUnion(0, loc, true));
+ }
+ }
+
+ return load;
+ }
+ }
+ }
+
+ // Handle operator[] on structured buffers: this indexes into the array element of the buffer.
+ // indexStructBufferContent returns nullptr if it isn't a structuredbuffer (SSBO).
+ TIntermTyped* sbArray = indexStructBufferContent(loc, base);
+ if (sbArray != nullptr) {
+ if (sbArray == nullptr)
+ return nullptr;
+
+ // Now we'll apply the [] index to that array
+ const TOperator idxOp = (index->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
+
+ TIntermTyped* element = intermediate.addIndex(idxOp, sbArray, index, loc);
+ const TType derefType(sbArray->getType(), 0);
+ element->setType(derefType);
+ return element;
+ }
+
+ return nullptr;
+}
+
+//
+// Cast index value to a uint if it isn't already (for operator[], load indexes, etc)
+TIntermTyped* HlslParseContext::makeIntegerIndex(TIntermTyped* index)
+{
+ const TBasicType indexBasicType = index->getType().getBasicType();
+ const int vecSize = index->getType().getVectorSize();
+
+ // We can use int types directly as the index
+ if (indexBasicType == EbtInt || indexBasicType == EbtUint ||
+ indexBasicType == EbtInt64 || indexBasicType == EbtUint64)
+ return index;
+
+ // Cast index to unsigned integer if it isn't one.
+ return intermediate.addConversion(EOpConstructUint, TType(EbtUint, EvqTemporary, vecSize), index);
+}
+
+//
+// Handle seeing a base[index] dereference in the grammar.
+//
+TIntermTyped* HlslParseContext::handleBracketDereference(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index)
+{
+ index = makeIntegerIndex(index);
+
+ if (index == nullptr) {
+ error(loc, " unknown index type ", "", "");
+ return nullptr;
+ }
+
+ TIntermTyped* result = handleBracketOperator(loc, base, index);
+
+ if (result != nullptr)
+ return result; // it was handled as an operator[]
+
+ bool flattened = false;
+ int indexValue = 0;
+ if (index->getQualifier().isFrontEndConstant())
+ indexValue = index->getAsConstantUnion()->getConstArray()[0].getIConst();
+
+ variableCheck(base);
+ if (! base->isArray() && ! base->isMatrix() && ! base->isVector()) {
+ if (base->getAsSymbolNode())
+ error(loc, " left of '[' is not of type array, matrix, or vector ",
+ base->getAsSymbolNode()->getName().c_str(), "");
+ else
+ error(loc, " left of '[' is not of type array, matrix, or vector ", "expression", "");
+ } else if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst) {
+ // both base and index are front-end constants
+ checkIndex(loc, base->getType(), indexValue);
+ return intermediate.foldDereference(base, indexValue, loc);
+ } else {
+ // at least one of base and index is variable...
+
+ if (index->getQualifier().isFrontEndConstant())
+ checkIndex(loc, base->getType(), indexValue);
+
+ if (base->getType().isScalarOrVec1())
+ result = base;
+ else if (base->getAsSymbolNode() && wasFlattened(base)) {
+ if (index->getQualifier().storage != EvqConst)
+ error(loc, "Invalid variable index to flattened array", base->getAsSymbolNode()->getName().c_str(), "");
+
+ result = flattenAccess(base, indexValue);
+ flattened = (result != base);
+ } else {
+ if (index->getQualifier().isFrontEndConstant()) {
+ if (base->getType().isUnsizedArray())
+ base->getWritableType().updateImplicitArraySize(indexValue + 1);
+ else
+ checkIndex(loc, base->getType(), indexValue);
+ result = intermediate.addIndex(EOpIndexDirect, base, index, loc);
+ } else
+ result = intermediate.addIndex(EOpIndexIndirect, base, index, loc);
+ }
+ }
+
+ if (result == nullptr) {
+ // Insert dummy error-recovery result
+ result = intermediate.addConstantUnion(0.0, EbtFloat, loc);
+ } else {
+ // If the array reference was flattened, it has the correct type. E.g, if it was
+ // a uniform array, it was flattened INTO a set of scalar uniforms, not scalar temps.
+ // In that case, we preserve the qualifiers.
+ if (!flattened) {
+ // Insert valid dereferenced result
+ TType newType(base->getType(), 0); // dereferenced type
+ if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst)
+ newType.getQualifier().storage = EvqConst;
+ else
+ newType.getQualifier().storage = EvqTemporary;
+ result->setType(newType);
+ }
+ }
+
+ return result;
+}
+
+// Handle seeing a binary node with a math operation.
+TIntermTyped* HlslParseContext::handleBinaryMath(const TSourceLoc& loc, const char* str, TOperator op,
+ TIntermTyped* left, TIntermTyped* right)
+{
+ TIntermTyped* result = intermediate.addBinaryMath(op, left, right, loc);
+ if (result == nullptr)
+ binaryOpError(loc, str, left->getCompleteString(), right->getCompleteString());
+
+ return result;
+}
+
+// Handle seeing a unary node with a math operation.
+TIntermTyped* HlslParseContext::handleUnaryMath(const TSourceLoc& loc, const char* str, TOperator op,
+ TIntermTyped* childNode)
+{
+ TIntermTyped* result = intermediate.addUnaryMath(op, childNode, loc);
+
+ if (result)
+ return result;
+ else
+ unaryOpError(loc, str, childNode->getCompleteString());
+
+ return childNode;
+}
+//
+// Return true if the name is a struct buffer method
+//
+bool HlslParseContext::isStructBufferMethod(const TString& name) const
+{
+ return
+ name == "GetDimensions" ||
+ name == "Load" ||
+ name == "Load2" ||
+ name == "Load3" ||
+ name == "Load4" ||
+ name == "Store" ||
+ name == "Store2" ||
+ name == "Store3" ||
+ name == "Store4" ||
+ name == "InterlockedAdd" ||
+ name == "InterlockedAnd" ||
+ name == "InterlockedCompareExchange" ||
+ name == "InterlockedCompareStore" ||
+ name == "InterlockedExchange" ||
+ name == "InterlockedMax" ||
+ name == "InterlockedMin" ||
+ name == "InterlockedOr" ||
+ name == "InterlockedXor" ||
+ name == "IncrementCounter" ||
+ name == "DecrementCounter" ||
+ name == "Append" ||
+ name == "Consume";
+}
+
+//
+// Handle seeing a base.field dereference in the grammar, where 'field' is a
+// swizzle or member variable.
+//
+TIntermTyped* HlslParseContext::handleDotDereference(const TSourceLoc& loc, TIntermTyped* base, const TString& field)
+{
+ variableCheck(base);
+
+ if (base->isArray()) {
+ error(loc, "cannot apply to an array:", ".", field.c_str());
+ return base;
+ }
+
+ TIntermTyped* result = base;
+
+ if (base->getType().getBasicType() == EbtSampler) {
+ // Handle .mips[mipid][pos] operation on textures
+ const TSampler& sampler = base->getType().getSampler();
+ if (sampler.isTexture() && field == "mips") {
+ // Push a null to signify that we expect a mip level under operator[] next.
+ mipsOperatorMipArg.push_back(tMipsOperatorData(loc, nullptr));
+ // Keep 'result' pointing to 'base', since we expect an operator[] to go by next.
+ } else {
+ if (field == "mips")
+ error(loc, "unexpected texture type for .mips[][] operator:",
+ base->getType().getCompleteString().c_str(), "");
+ else
+ error(loc, "unexpected operator on texture type:", field.c_str(),
+ base->getType().getCompleteString().c_str());
+ }
+ } else if (base->isVector() || base->isScalar()) {
+ TSwizzleSelectors<TVectorSelector> selectors;
+ parseSwizzleSelector(loc, field, base->getVectorSize(), selectors);
+
+ if (base->isScalar()) {
+ if (selectors.size() == 1)
+ return result;
+ else {
+ TType type(base->getBasicType(), EvqTemporary, selectors.size());
+ return addConstructor(loc, base, type);
+ }
+ }
+ if (base->getVectorSize() == 1) {
+ TType scalarType(base->getBasicType(), EvqTemporary, 1);
+ if (selectors.size() == 1)
+ return addConstructor(loc, base, scalarType);
+ else {
+ TType vectorType(base->getBasicType(), EvqTemporary, selectors.size());
+ return addConstructor(loc, addConstructor(loc, base, scalarType), vectorType);
+ }
+ }
+
+ if (base->getType().getQualifier().isFrontEndConstant())
+ result = intermediate.foldSwizzle(base, selectors, loc);
+ else {
+ if (selectors.size() == 1) {
+ TIntermTyped* index = intermediate.addConstantUnion(selectors[0], loc);
+ result = intermediate.addIndex(EOpIndexDirect, base, index, loc);
+ result->setType(TType(base->getBasicType(), EvqTemporary));
+ } else {
+ TIntermTyped* index = intermediate.addSwizzle(selectors, loc);
+ result = intermediate.addIndex(EOpVectorSwizzle, base, index, loc);
+ result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision,
+ selectors.size()));
+ }
+ }
+ } else if (base->isMatrix()) {
+ TSwizzleSelectors<TMatrixSelector> selectors;
+ if (! parseMatrixSwizzleSelector(loc, field, base->getMatrixCols(), base->getMatrixRows(), selectors))
+ return result;
+
+ if (selectors.size() == 1) {
+ // Representable by m[c][r]
+ if (base->getType().getQualifier().isFrontEndConstant()) {
+ result = intermediate.foldDereference(base, selectors[0].coord1, loc);
+ result = intermediate.foldDereference(result, selectors[0].coord2, loc);
+ } else {
+ result = intermediate.addIndex(EOpIndexDirect, base,
+ intermediate.addConstantUnion(selectors[0].coord1, loc),
+ loc);
+ TType dereferencedCol(base->getType(), 0);
+ result->setType(dereferencedCol);
+ result = intermediate.addIndex(EOpIndexDirect, result,
+ intermediate.addConstantUnion(selectors[0].coord2, loc),
+ loc);
+ TType dereferenced(dereferencedCol, 0);
+ result->setType(dereferenced);
+ }
+ } else {
+ int column = getMatrixComponentsColumn(base->getMatrixRows(), selectors);
+ if (column >= 0) {
+ // Representable by m[c]
+ if (base->getType().getQualifier().isFrontEndConstant())
+ result = intermediate.foldDereference(base, column, loc);
+ else {
+ result = intermediate.addIndex(EOpIndexDirect, base, intermediate.addConstantUnion(column, loc),
+ loc);
+ TType dereferenced(base->getType(), 0);
+ result->setType(dereferenced);
+ }
+ } else {
+ // general case, not a column, not a single component
+ TIntermTyped* index = intermediate.addSwizzle(selectors, loc);
+ result = intermediate.addIndex(EOpMatrixSwizzle, base, index, loc);
+ result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision,
+ selectors.size()));
+ }
+ }
+ } else if (base->getBasicType() == EbtStruct || base->getBasicType() == EbtBlock) {
+ const TTypeList* fields = base->getType().getStruct();
+ bool fieldFound = false;
+ int member;
+ for (member = 0; member < (int)fields->size(); ++member) {
+ if ((*fields)[member].type->getFieldName() == field) {
+ fieldFound = true;
+ break;
+ }
+ }
+ if (fieldFound) {
+ if (base->getAsSymbolNode() && wasFlattened(base)) {
+ result = flattenAccess(base, member);
+ } else {
+ if (base->getType().getQualifier().storage == EvqConst)
+ result = intermediate.foldDereference(base, member, loc);
+ else {
+ TIntermTyped* index = intermediate.addConstantUnion(member, loc);
+ result = intermediate.addIndex(EOpIndexDirectStruct, base, index, loc);
+ result->setType(*(*fields)[member].type);
+ }
+ }
+ } else
+ error(loc, "no such field in structure", field.c_str(), "");
+ } else
+ error(loc, "does not apply to this type:", field.c_str(), base->getType().getCompleteString().c_str());
+
+ return result;
+}
+
+//
+// Return true if the field should be treated as a built-in method.
+// Return false otherwise.
+//
+bool HlslParseContext::isBuiltInMethod(const TSourceLoc&, TIntermTyped* base, const TString& field)
+{
+ if (base == nullptr)
+ return false;
+
+ variableCheck(base);
+
+ if (base->getType().getBasicType() == EbtSampler) {
+ return true;
+ } else if (isStructBufferType(base->getType()) && isStructBufferMethod(field)) {
+ return true;
+ } else if (field == "Append" ||
+ field == "RestartStrip") {
+ // We cannot check the type here: it may be sanitized if we're not compiling a geometry shader, but
+ // the code is around in the shader source.
+ return true;
+ } else
+ return false;
+}
+
+// Independently establish a built-in that is a member of a structure.
+// 'arraySizes' are what's desired for the independent built-in, whatever
+// the higher-level source/expression of them was.
+void HlslParseContext::splitBuiltIn(const TString& baseName, const TType& memberType, const TArraySizes* arraySizes,
+ const TQualifier& outerQualifier)
+{
+ // Because of arrays of structs, we might be asked more than once,
+ // but the arraySizes passed in should have captured the whole thing
+ // the first time.
+ // However, clip/cull rely on multiple updates.
+ if (!isClipOrCullDistance(memberType))
+ if (splitBuiltIns.find(tInterstageIoData(memberType.getQualifier().builtIn, outerQualifier.storage)) !=
+ splitBuiltIns.end())
+ return;
+
+ TVariable* ioVar = makeInternalVariable(baseName + "." + memberType.getFieldName(), memberType);
+
+ if (arraySizes != nullptr && !memberType.isArray())
+ ioVar->getWritableType().copyArraySizes(*arraySizes);
+
+ splitBuiltIns[tInterstageIoData(memberType.getQualifier().builtIn, outerQualifier.storage)] = ioVar;
+ if (!isClipOrCullDistance(ioVar->getType()))
+ trackLinkage(*ioVar);
+
+ // Merge qualifier from the user structure
+ mergeQualifiers(ioVar->getWritableType().getQualifier(), outerQualifier);
+
+ // Fix the builtin type if needed (e.g, some types require fixed array sizes, no matter how the
+ // shader declared them). This is done after mergeQualifiers(), in case fixBuiltInIoType looks
+ // at the qualifier to determine e.g, in or out qualifications.
+ fixBuiltInIoType(ioVar->getWritableType());
+
+ // But, not location, we're losing that
+ ioVar->getWritableType().getQualifier().layoutLocation = TQualifier::layoutLocationEnd;
+}
+
+// Split a type into
+// 1. a struct of non-I/O members
+// 2. a collection of independent I/O variables
+void HlslParseContext::split(const TVariable& variable)
+{
+ // Create a new variable:
+ const TType& clonedType = *variable.getType().clone();
+ const TType& splitType = split(clonedType, variable.getName(), clonedType.getQualifier());
+ splitNonIoVars[variable.getUniqueId()] = makeInternalVariable(variable.getName(), splitType);
+}
+
+// Recursive implementation of split().
+// Returns reference to the modified type.
+const TType& HlslParseContext::split(const TType& type, const TString& name, const TQualifier& outerQualifier)
+{
+ if (type.isStruct()) {
+ TTypeList* userStructure = type.getWritableStruct();
+ for (auto ioType = userStructure->begin(); ioType != userStructure->end(); ) {
+ if (ioType->type->isBuiltIn()) {
+ // move out the built-in
+ splitBuiltIn(name, *ioType->type, type.getArraySizes(), outerQualifier);
+ ioType = userStructure->erase(ioType);
+ } else {
+ split(*ioType->type, name + "." + ioType->type->getFieldName(), outerQualifier);
+ ++ioType;
+ }
+ }
+ }
+
+ return type;
+}
+
+// Is this an aggregate that should be flattened?
+// Can be applied to intermediate levels of type in a hierarchy.
+// Some things like flattening uniform arrays are only about the top level
+// of the aggregate, triggered on 'topLevel'.
+bool HlslParseContext::shouldFlatten(const TType& type, TStorageQualifier qualifier, bool topLevel) const
+{
+ switch (qualifier) {
+ case EvqVaryingIn:
+ case EvqVaryingOut:
+ return type.isStruct() || type.isArray();
+ case EvqUniform:
+ return (type.isArray() && intermediate.getFlattenUniformArrays() && topLevel) ||
+ (type.isStruct() && type.containsOpaque());
+ default:
+ return false;
+ };
+}
+
+// Top level variable flattening: construct data
+void HlslParseContext::flatten(const TVariable& variable, bool linkage)
+{
+ const TType& type = variable.getType();
+
+ // If it's a standalone built-in, there is nothing to flatten
+ if (type.isBuiltIn() && !type.isStruct())
+ return;
+
+ auto entry = flattenMap.insert(std::make_pair(variable.getUniqueId(),
+ TFlattenData(type.getQualifier().layoutBinding,
+ type.getQualifier().layoutLocation)));
+
+ // the item is a map pair, so first->second is the TFlattenData itself.
+ flatten(variable, type, entry.first->second, variable.getName(), linkage, type.getQualifier(), nullptr);
+}
+
+// Recursively flatten the given variable at the provided type, building the flattenData as we go.
+//
+// This is mutually recursive with flattenStruct and flattenArray.
+// We are going to flatten an arbitrarily nested composite structure into a linear sequence of
+// members, and later on, we want to turn a path through the tree structure into a final
+// location in this linear sequence.
+//
+// If the tree was N-ary, that can be directly calculated. However, we are dealing with
+// arbitrary numbers - perhaps a struct of 7 members containing an array of 3. Thus, we must
+// build a data structure to allow the sequence of bracket and dot operators on arrays and
+// structs to arrive at the proper member.
+//
+// To avoid storing a tree with pointers, we are going to flatten the tree into a vector of integers.
+// The leaves are the indexes into the flattened member array.
+// Each level will have the next location for the Nth item stored sequentially, so for instance:
+//
+// struct { float2 a[2]; int b; float4 c[3] };
+//
+// This will produce the following flattened tree:
+// Pos: 0 1 2 3 4 5 6 7 8 9 10 11 12 13
+// (3, 7, 8, 5, 6, 0, 1, 2, 11, 12, 13, 3, 4, 5}
+//
+// Given a reference to mystruct.c[1], the access chain is (2,1), so we traverse:
+// (0+2) = 8 --> (8+1) = 12 --> 12 = 4
+//
+// so the 4th flattened member in traversal order is ours.
+//
+int HlslParseContext::flatten(const TVariable& variable, const TType& type,
+ TFlattenData& flattenData, TString name, bool linkage,
+ const TQualifier& outerQualifier,
+ const TArraySizes* builtInArraySizes)
+{
+ // If something is an arrayed struct, the array flattener will recursively call flatten()
+ // to then flatten the struct, so this is an "if else": we don't do both.
+ if (type.isArray())
+ return flattenArray(variable, type, flattenData, name, linkage, outerQualifier);
+ else if (type.isStruct())
+ return flattenStruct(variable, type, flattenData, name, linkage, outerQualifier, builtInArraySizes);
+ else {
+ assert(0); // should never happen
+ return -1;
+ }
+}
+
+// Add a single flattened member to the flattened data being tracked for the composite
+// Returns true for the final flattening level.
+int HlslParseContext::addFlattenedMember(const TVariable& variable, const TType& type, TFlattenData& flattenData,
+ const TString& memberName, bool linkage,
+ const TQualifier& outerQualifier,
+ const TArraySizes* builtInArraySizes)
+{
+ if (!shouldFlatten(type, outerQualifier.storage, false)) {
+ // This is as far as we flatten. Insert the variable.
+ TVariable* memberVariable = makeInternalVariable(memberName, type);
+ mergeQualifiers(memberVariable->getWritableType().getQualifier(), variable.getType().getQualifier());
+
+ if (flattenData.nextBinding != TQualifier::layoutBindingEnd)
+ memberVariable->getWritableType().getQualifier().layoutBinding = flattenData.nextBinding++;
+
+ if (memberVariable->getType().isBuiltIn()) {
+ // inherited locations are nonsensical for built-ins (TODO: what if semantic had a number)
+ memberVariable->getWritableType().getQualifier().layoutLocation = TQualifier::layoutLocationEnd;
+ } else {
+ // inherited locations must be auto bumped, not replicated
+ if (flattenData.nextLocation != TQualifier::layoutLocationEnd) {
+ memberVariable->getWritableType().getQualifier().layoutLocation = flattenData.nextLocation;
+ flattenData.nextLocation += intermediate.computeTypeLocationSize(memberVariable->getType(), language);
+ nextOutLocation = std::max(nextOutLocation, flattenData.nextLocation);
+ }
+ }
+
+ flattenData.offsets.push_back(static_cast<int>(flattenData.members.size()));
+ flattenData.members.push_back(memberVariable);
+
+ if (linkage)
+ trackLinkage(*memberVariable);
+
+ return static_cast<int>(flattenData.offsets.size()) - 1; // location of the member reference
+ } else {
+ // Further recursion required
+ return flatten(variable, type, flattenData, memberName, linkage, outerQualifier, builtInArraySizes);
+ }
+}
+
+// Figure out the mapping between an aggregate's top members and an
+// equivalent set of individual variables.
+//
+// Assumes shouldFlatten() or equivalent was called first.
+int HlslParseContext::flattenStruct(const TVariable& variable, const TType& type,
+ TFlattenData& flattenData, TString name, bool linkage,
+ const TQualifier& outerQualifier,
+ const TArraySizes* builtInArraySizes)
+{
+ assert(type.isStruct());
+
+ auto members = *type.getStruct();
+
+ // Reserve space for this tree level.
+ int start = static_cast<int>(flattenData.offsets.size());
+ int pos = start;
+ flattenData.offsets.resize(int(pos + members.size()), -1);
+
+ for (int member = 0; member < (int)members.size(); ++member) {
+ TType& dereferencedType = *members[member].type;
+ if (dereferencedType.isBuiltIn())
+ splitBuiltIn(variable.getName(), dereferencedType, builtInArraySizes, outerQualifier);
+ else {
+ const int mpos = addFlattenedMember(variable, dereferencedType, flattenData,
+ name + "." + dereferencedType.getFieldName(),
+ linkage, outerQualifier,
+ builtInArraySizes == nullptr && dereferencedType.isArray()
+ ? dereferencedType.getArraySizes()
+ : builtInArraySizes);
+ flattenData.offsets[pos++] = mpos;
+ }
+ }
+
+ return start;
+}
+
+// Figure out mapping between an array's members and an
+// equivalent set of individual variables.
+//
+// Assumes shouldFlatten() or equivalent was called first.
+int HlslParseContext::flattenArray(const TVariable& variable, const TType& type,
+ TFlattenData& flattenData, TString name, bool linkage,
+ const TQualifier& outerQualifier)
+{
+ assert(type.isSizedArray());
+
+ const int size = type.getOuterArraySize();
+ const TType dereferencedType(type, 0);
+
+ if (name.empty())
+ name = variable.getName();
+
+ // Reserve space for this tree level.
+ int start = static_cast<int>(flattenData.offsets.size());
+ int pos = start;
+ flattenData.offsets.resize(int(pos + size), -1);
+
+ for (int element=0; element < size; ++element) {
+ char elementNumBuf[20]; // sufficient for MAXINT
+ snprintf(elementNumBuf, sizeof(elementNumBuf)-1, "[%d]", element);
+ const int mpos = addFlattenedMember(variable, dereferencedType, flattenData,
+ name + elementNumBuf, linkage, outerQualifier,
+ type.getArraySizes());
+
+ flattenData.offsets[pos++] = mpos;
+ }
+
+ return start;
+}
+
+// Return true if we have flattened this node.
+bool HlslParseContext::wasFlattened(const TIntermTyped* node) const
+{
+ return node != nullptr && node->getAsSymbolNode() != nullptr &&
+ wasFlattened(node->getAsSymbolNode()->getId());
+}
+
+// Return true if we have split this structure
+bool HlslParseContext::wasSplit(const TIntermTyped* node) const
+{
+ return node != nullptr && node->getAsSymbolNode() != nullptr &&
+ wasSplit(node->getAsSymbolNode()->getId());
+}
+
+// Turn an access into an aggregate that was flattened to instead be
+// an access to the individual variable the member was flattened to.
+// Assumes wasFlattened() or equivalent was called first.
+TIntermTyped* HlslParseContext::flattenAccess(TIntermTyped* base, int member)
+{
+ const TType dereferencedType(base->getType(), member); // dereferenced type
+ const TIntermSymbol& symbolNode = *base->getAsSymbolNode();
+ TIntermTyped* flattened = flattenAccess(symbolNode.getId(), member, base->getQualifier().storage,
+ dereferencedType, symbolNode.getFlattenSubset());
+
+ return flattened ? flattened : base;
+}
+TIntermTyped* HlslParseContext::flattenAccess(int uniqueId, int member, TStorageQualifier outerStorage,
+ const TType& dereferencedType, int subset)
+{
+ const auto flattenData = flattenMap.find(uniqueId);
+
+ if (flattenData == flattenMap.end())
+ return nullptr;
+
+ // Calculate new cumulative offset from the packed tree
+ int newSubset = flattenData->second.offsets[subset >= 0 ? subset + member : member];
+
+ TIntermSymbol* subsetSymbol;
+ if (!shouldFlatten(dereferencedType, outerStorage, false)) {
+ // Finished flattening: create symbol for variable
+ member = flattenData->second.offsets[newSubset];
+ const TVariable* memberVariable = flattenData->second.members[member];
+ subsetSymbol = intermediate.addSymbol(*memberVariable);
+ subsetSymbol->setFlattenSubset(-1);
+ } else {
+
+ // If this is not the final flattening, accumulate the position and return
+ // an object of the partially dereferenced type.
+ subsetSymbol = new TIntermSymbol(uniqueId, "flattenShadow", dereferencedType);
+ subsetSymbol->setFlattenSubset(newSubset);
+ }
+
+ return subsetSymbol;
+}
+
+// For finding where the first leaf is in a subtree of a multi-level aggregate
+// that is just getting a subset assigned. Follows the same logic as flattenAccess,
+// but logically going down the "left-most" tree branch each step of the way.
+//
+// Returns the offset into the first leaf of the subset.
+int HlslParseContext::findSubtreeOffset(const TIntermNode& node) const
+{
+ const TIntermSymbol* sym = node.getAsSymbolNode();
+ if (sym == nullptr)
+ return 0;
+ if (!sym->isArray() && !sym->isStruct())
+ return 0;
+ int subset = sym->getFlattenSubset();
+ if (subset == -1)
+ return 0;
+
+ // Getting this far means a partial aggregate is identified by the flatten subset.
+ // Find the first leaf of the subset.
+
+ const auto flattenData = flattenMap.find(sym->getId());
+ if (flattenData == flattenMap.end())
+ return 0;
+
+ return findSubtreeOffset(sym->getType(), subset, flattenData->second.offsets);
+
+ do {
+ subset = flattenData->second.offsets[subset];
+ } while (true);
+}
+// Recursively do the desent
+int HlslParseContext::findSubtreeOffset(const TType& type, int subset, const TVector<int>& offsets) const
+{
+ if (!type.isArray() && !type.isStruct())
+ return offsets[subset];
+ TType derefType(type, 0);
+ return findSubtreeOffset(derefType, offsets[subset], offsets);
+};
+
+// Find and return the split IO TVariable for id, or nullptr if none.
+TVariable* HlslParseContext::getSplitNonIoVar(int id) const
+{
+ const auto splitNonIoVar = splitNonIoVars.find(id);
+ if (splitNonIoVar == splitNonIoVars.end())
+ return nullptr;
+
+ return splitNonIoVar->second;
+}
+
+// Pass through to base class after remembering built-in mappings.
+void HlslParseContext::trackLinkage(TSymbol& symbol)
+{
+ TBuiltInVariable biType = symbol.getType().getQualifier().builtIn;
+
+ if (biType != EbvNone)
+ builtInTessLinkageSymbols[biType] = symbol.clone();
+
+ TParseContextBase::trackLinkage(symbol);
+}
+
+
+// Returns true if the built-in is a clip or cull distance variable.
+bool HlslParseContext::isClipOrCullDistance(TBuiltInVariable builtIn)
+{
+ return builtIn == EbvClipDistance || builtIn == EbvCullDistance;
+}
+
+// Some types require fixed array sizes in SPIR-V, but can be scalars or
+// arrays of sizes SPIR-V doesn't allow. For example, tessellation factors.
+// This creates the right size. A conversion is performed when the internal
+// type is copied to or from the external type. This corrects the externally
+// facing input or output type to abide downstream semantics.
+void HlslParseContext::fixBuiltInIoType(TType& type)
+{
+ int requiredArraySize = 0;
+ int requiredVectorSize = 0;
+
+ switch (type.getQualifier().builtIn) {
+ case EbvTessLevelOuter: requiredArraySize = 4; break;
+ case EbvTessLevelInner: requiredArraySize = 2; break;
+
+ case EbvSampleMask:
+ {
+ // Promote scalar to array of size 1. Leave existing arrays alone.
+ if (!type.isArray())
+ requiredArraySize = 1;
+ break;
+ }
+
+ case EbvWorkGroupId: requiredVectorSize = 3; break;
+ case EbvGlobalInvocationId: requiredVectorSize = 3; break;
+ case EbvLocalInvocationId: requiredVectorSize = 3; break;
+ case EbvTessCoord: requiredVectorSize = 3; break;
+
+ default:
+ if (isClipOrCullDistance(type)) {
+ const int loc = type.getQualifier().layoutLocation;
+
+ if (type.getQualifier().builtIn == EbvClipDistance) {
+ if (type.getQualifier().storage == EvqVaryingIn)
+ clipSemanticNSizeIn[loc] = type.getVectorSize();
+ else
+ clipSemanticNSizeOut[loc] = type.getVectorSize();
+ } else {
+ if (type.getQualifier().storage == EvqVaryingIn)
+ cullSemanticNSizeIn[loc] = type.getVectorSize();
+ else
+ cullSemanticNSizeOut[loc] = type.getVectorSize();
+ }
+ }
+
+ return;
+ }
+
+ // Alter or set vector size as needed.
+ if (requiredVectorSize > 0) {
+ TType newType(type.getBasicType(), type.getQualifier().storage, requiredVectorSize);
+ newType.getQualifier() = type.getQualifier();
+
+ type.shallowCopy(newType);
+ }
+
+ // Alter or set array size as needed.
+ if (requiredArraySize > 0) {
+ if (!type.isArray() || type.getOuterArraySize() != requiredArraySize) {
+ TArraySizes* arraySizes = new TArraySizes;
+ arraySizes->addInnerSize(requiredArraySize);
+ type.transferArraySizes(arraySizes);
+ }
+ }
+}
+
+// Variables that correspond to the user-interface in and out of a stage
+// (not the built-in interface) are
+// - assigned locations
+// - registered as a linkage node (part of the stage's external interface).
+// Assumes it is called in the order in which locations should be assigned.
+void HlslParseContext::assignToInterface(TVariable& variable)
+{
+ const auto assignLocation = [&](TVariable& variable) {
+ TType& type = variable.getWritableType();
+ if (!type.isStruct() || type.getStruct()->size() > 0) {
+ TQualifier& qualifier = type.getQualifier();
+ if (qualifier.storage == EvqVaryingIn || qualifier.storage == EvqVaryingOut) {
+ if (qualifier.builtIn == EbvNone && !qualifier.hasLocation()) {
+ // Strip off the outer array dimension for those having an extra one.
+ int size;
+ if (type.isArray() && qualifier.isArrayedIo(language)) {
+ TType elementType(type, 0);
+ size = intermediate.computeTypeLocationSize(elementType, language);
+ } else
+ size = intermediate.computeTypeLocationSize(type, language);
+
+ if (qualifier.storage == EvqVaryingIn) {
+ variable.getWritableType().getQualifier().layoutLocation = nextInLocation;
+ nextInLocation += size;
+ } else {
+ variable.getWritableType().getQualifier().layoutLocation = nextOutLocation;
+ nextOutLocation += size;
+ }
+ }
+ trackLinkage(variable);
+ }
+ }
+ };
+
+ if (wasFlattened(variable.getUniqueId())) {
+ auto& memberList = flattenMap[variable.getUniqueId()].members;
+ for (auto member = memberList.begin(); member != memberList.end(); ++member)
+ assignLocation(**member);
+ } else if (wasSplit(variable.getUniqueId())) {
+ TVariable* splitIoVar = getSplitNonIoVar(variable.getUniqueId());
+ assignLocation(*splitIoVar);
+ } else {
+ assignLocation(variable);
+ }
+}
+
+//
+// Handle seeing a function declarator in the grammar. This is the precursor
+// to recognizing a function prototype or function definition.
+//
+void HlslParseContext::handleFunctionDeclarator(const TSourceLoc& loc, TFunction& function, bool prototype)
+{
+ //
+ // Multiple declarations of the same function name are allowed.
+ //
+ // If this is a definition, the definition production code will check for redefinitions
+ // (we don't know at this point if it's a definition or not).
+ //
+ bool builtIn;
+ TSymbol* symbol = symbolTable.find(function.getMangledName(), &builtIn);
+ const TFunction* prevDec = symbol ? symbol->getAsFunction() : 0;
+
+ if (prototype) {
+ // All built-in functions are defined, even though they don't have a body.
+ // Count their prototype as a definition instead.
+ if (symbolTable.atBuiltInLevel())
+ function.setDefined();
+ else {
+ if (prevDec && ! builtIn)
+ symbol->getAsFunction()->setPrototyped(); // need a writable one, but like having prevDec as a const
+ function.setPrototyped();
+ }
+ }
+
+ // This insert won't actually insert it if it's a duplicate signature, but it will still check for
+ // other forms of name collisions.
+ if (! symbolTable.insert(function))
+ error(loc, "function name is redeclaration of existing name", function.getName().c_str(), "");
+}
+
+// For struct buffers with counters, we must pass the counter buffer as hidden parameter.
+// This adds the hidden parameter to the parameter list in 'paramNodes' if needed.
+// Otherwise, it's a no-op
+void HlslParseContext::addStructBufferHiddenCounterParam(const TSourceLoc& loc, TParameter& param,
+ TIntermAggregate*& paramNodes)
+{
+ if (! hasStructBuffCounter(*param.type))
+ return;
+
+ const TString counterBlockName(intermediate.addCounterBufferName(*param.name));
+
+ TType counterType;
+ counterBufferType(loc, counterType);
+ TVariable *variable = makeInternalVariable(counterBlockName, counterType);
+
+ if (! symbolTable.insert(*variable))
+ error(loc, "redefinition", variable->getName().c_str(), "");
+
+ paramNodes = intermediate.growAggregate(paramNodes,
+ intermediate.addSymbol(*variable, loc),
+ loc);
+}
+
+//
+// Handle seeing the function prototype in front of a function definition in the grammar.
+// The body is handled after this function returns.
+//
+// Returns an aggregate of parameter-symbol nodes.
+//
+TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function,
+ const TAttributes& attributes,
+ TIntermNode*& entryPointTree)
+{
+ currentCaller = function.getMangledName();
+ TSymbol* symbol = symbolTable.find(function.getMangledName());
+ TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr;
+
+ if (prevDec == nullptr)
+ error(loc, "can't find function", function.getName().c_str(), "");
+ // Note: 'prevDec' could be 'function' if this is the first time we've seen function
+ // as it would have just been put in the symbol table. Otherwise, we're looking up
+ // an earlier occurrence.
+
+ if (prevDec && prevDec->isDefined()) {
+ // Then this function already has a body.
+ error(loc, "function already has a body", function.getName().c_str(), "");
+ }
+ if (prevDec && ! prevDec->isDefined()) {
+ prevDec->setDefined();
+
+ // Remember the return type for later checking for RETURN statements.
+ currentFunctionType = &(prevDec->getType());
+ } else
+ currentFunctionType = new TType(EbtVoid);
+ functionReturnsValue = false;
+
+ // Entry points need different I/O and other handling, transform it so the
+ // rest of this function doesn't care.
+ entryPointTree = transformEntryPoint(loc, function, attributes);
+
+ //
+ // New symbol table scope for body of function plus its arguments
+ //
+ pushScope();
+
+ //
+ // Insert parameters into the symbol table.
+ // If the parameter has no name, it's not an error, just don't insert it
+ // (could be used for unused args).
+ //
+ // Also, accumulate the list of parameters into the AST, so lower level code
+ // knows where to find parameters.
+ //
+ TIntermAggregate* paramNodes = new TIntermAggregate;
+ for (int i = 0; i < function.getParamCount(); i++) {
+ TParameter& param = function[i];
+ if (param.name != nullptr) {
+ TVariable *variable = new TVariable(param.name, *param.type);
+
+ if (i == 0 && function.hasImplicitThis()) {
+ // Anonymous 'this' members are already in a symbol-table level,
+ // and we need to know what function parameter to map them to.
+ symbolTable.makeInternalVariable(*variable);
+ pushImplicitThis(variable);
+ }
+
+ // Insert the parameters with name in the symbol table.
+ if (! symbolTable.insert(*variable))
+ error(loc, "redefinition", variable->getName().c_str(), "");
+
+ // Add parameters to the AST list.
+ if (shouldFlatten(variable->getType(), variable->getType().getQualifier().storage, true)) {
+ // Expand the AST parameter nodes (but not the name mangling or symbol table view)
+ // for structures that need to be flattened.
+ flatten(*variable, false);
+ const TTypeList* structure = variable->getType().getStruct();
+ for (int mem = 0; mem < (int)structure->size(); ++mem) {
+ paramNodes = intermediate.growAggregate(paramNodes,
+ flattenAccess(variable->getUniqueId(), mem,
+ variable->getType().getQualifier().storage,
+ *(*structure)[mem].type),
+ loc);
+ }
+ } else {
+ // Add the parameter to the AST
+ paramNodes = intermediate.growAggregate(paramNodes,
+ intermediate.addSymbol(*variable, loc),
+ loc);
+ }
+
+ // Add hidden AST parameter for struct buffer counters, if needed.
+ addStructBufferHiddenCounterParam(loc, param, paramNodes);
+ } else
+ paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(*param.type, loc), loc);
+ }
+ if (function.hasIllegalImplicitThis())
+ pushImplicitThis(nullptr);
+
+ intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), loc);
+ loopNestingLevel = 0;
+ controlFlowNestingLevel = 0;
+ postEntryPointReturn = false;
+
+ return paramNodes;
+}
+
+// Handle all [attrib] attribute for the shader entry point
+void HlslParseContext::handleEntryPointAttributes(const TSourceLoc& loc, const TAttributes& attributes)
+{
+ for (auto it = attributes.begin(); it != attributes.end(); ++it) {
+ switch (it->name) {
+ case EatNumThreads:
+ {
+ const TIntermSequence& sequence = it->args->getSequence();
+ for (int lid = 0; lid < int(sequence.size()); ++lid)
+ intermediate.setLocalSize(lid, sequence[lid]->getAsConstantUnion()->getConstArray()[0].getIConst());
+ break;
+ }
+ case EatMaxVertexCount:
+ {
+ int maxVertexCount;
+
+ if (! it->getInt(maxVertexCount)) {
+ error(loc, "invalid maxvertexcount", "", "");
+ } else {
+ if (! intermediate.setVertices(maxVertexCount))
+ error(loc, "cannot change previously set maxvertexcount attribute", "", "");
+ }
+ break;
+ }
+ case EatPatchConstantFunc:
+ {
+ TString pcfName;
+ if (! it->getString(pcfName, 0, false)) {
+ error(loc, "invalid patch constant function", "", "");
+ } else {
+ patchConstantFunctionName = pcfName;
+ }
+ break;
+ }
+ case EatDomain:
+ {
+ // Handle [domain("...")]
+ TString domainStr;
+ if (! it->getString(domainStr)) {
+ error(loc, "invalid domain", "", "");
+ } else {
+ TLayoutGeometry domain = ElgNone;
+
+ if (domainStr == "tri") {
+ domain = ElgTriangles;
+ } else if (domainStr == "quad") {
+ domain = ElgQuads;
+ } else if (domainStr == "isoline") {
+ domain = ElgIsolines;
+ } else {
+ error(loc, "unsupported domain type", domainStr.c_str(), "");
+ }
+
+ if (language == EShLangTessEvaluation) {
+ if (! intermediate.setInputPrimitive(domain))
+ error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), "");
+ } else {
+ if (! intermediate.setOutputPrimitive(domain))
+ error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), "");
+ }
+ }
+ break;
+ }
+ case EatOutputTopology:
+ {
+ // Handle [outputtopology("...")]
+ TString topologyStr;
+ if (! it->getString(topologyStr)) {
+ error(loc, "invalid outputtopology", "", "");
+ } else {
+ TVertexOrder vertexOrder = EvoNone;
+ TLayoutGeometry primitive = ElgNone;
+
+ if (topologyStr == "point") {
+ intermediate.setPointMode();
+ } else if (topologyStr == "line") {
+ primitive = ElgIsolines;
+ } else if (topologyStr == "triangle_cw") {
+ vertexOrder = EvoCw;
+ primitive = ElgTriangles;
+ } else if (topologyStr == "triangle_ccw") {
+ vertexOrder = EvoCcw;
+ primitive = ElgTriangles;
+ } else {
+ error(loc, "unsupported outputtopology type", topologyStr.c_str(), "");
+ }
+
+ if (vertexOrder != EvoNone) {
+ if (! intermediate.setVertexOrder(vertexOrder)) {
+ error(loc, "cannot change previously set outputtopology",
+ TQualifier::getVertexOrderString(vertexOrder), "");
+ }
+ }
+ if (primitive != ElgNone)
+ intermediate.setOutputPrimitive(primitive);
+ }
+ break;
+ }
+ case EatPartitioning:
+ {
+ // Handle [partitioning("...")]
+ TString partitionStr;
+ if (! it->getString(partitionStr)) {
+ error(loc, "invalid partitioning", "", "");
+ } else {
+ TVertexSpacing partitioning = EvsNone;
+
+ if (partitionStr == "integer") {
+ partitioning = EvsEqual;
+ } else if (partitionStr == "fractional_even") {
+ partitioning = EvsFractionalEven;
+ } else if (partitionStr == "fractional_odd") {
+ partitioning = EvsFractionalOdd;
+ //} else if (partition == "pow2") { // TODO: currently nothing to map this to.
+ } else {
+ error(loc, "unsupported partitioning type", partitionStr.c_str(), "");
+ }
+
+ if (! intermediate.setVertexSpacing(partitioning))
+ error(loc, "cannot change previously set partitioning",
+ TQualifier::getVertexSpacingString(partitioning), "");
+ }
+ break;
+ }
+ case EatOutputControlPoints:
+ {
+ // Handle [outputcontrolpoints("...")]
+ int ctrlPoints;
+ if (! it->getInt(ctrlPoints)) {
+ error(loc, "invalid outputcontrolpoints", "", "");
+ } else {
+ if (! intermediate.setVertices(ctrlPoints)) {
+ error(loc, "cannot change previously set outputcontrolpoints attribute", "", "");
+ }
+ }
+ break;
+ }
+ case EatEarlyDepthStencil:
+ intermediate.setEarlyFragmentTests();
+ break;
+ case EatBuiltIn:
+ case EatLocation:
+ // tolerate these because of dual use of entrypoint and type attributes
+ break;
+ default:
+ warn(loc, "attribute does not apply to entry point", "", "");
+ break;
+ }
+ }
+}
+
+// Update the given type with any type-like attribute information in the
+// attributes.
+void HlslParseContext::transferTypeAttributes(const TSourceLoc& loc, const TAttributes& attributes, TType& type,
+ bool allowEntry)
+{
+ if (attributes.size() == 0)
+ return;
+
+ int value;
+ TString builtInString;
+ for (auto it = attributes.begin(); it != attributes.end(); ++it) {
+ switch (it->name) {
+ case EatLocation:
+ // location
+ if (it->getInt(value))
+ type.getQualifier().layoutLocation = value;
+ break;
+ case EatBinding:
+ // binding
+ if (it->getInt(value)) {
+ type.getQualifier().layoutBinding = value;
+ type.getQualifier().layoutSet = 0;
+ }
+ // set
+ if (it->getInt(value, 1))
+ type.getQualifier().layoutSet = value;
+ break;
+ case EatGlobalBinding:
+ // global cbuffer binding
+ if (it->getInt(value))
+ globalUniformBinding = value;
+ // global cbuffer binding
+ if (it->getInt(value, 1))
+ globalUniformSet = value;
+ break;
+ case EatInputAttachment:
+ // input attachment
+ if (it->getInt(value))
+ type.getQualifier().layoutAttachment = value;
+ break;
+ case EatBuiltIn:
+ // PointSize built-in
+ if (it->getString(builtInString, 0, false)) {
+ if (builtInString == "PointSize")
+ type.getQualifier().builtIn = EbvPointSize;
+ }
+ break;
+ case EatPushConstant:
+ // push_constant
+ type.getQualifier().layoutPushConstant = true;
+ break;
+ case EatConstantId:
+ // specialization constant
+ if (it->getInt(value)) {
+ TSourceLoc loc;
+ loc.init();
+ setSpecConstantId(loc, type.getQualifier(), value);
+ }
+ break;
+ default:
+ if (! allowEntry)
+ warn(loc, "attribute does not apply to a type", "", "");
+ break;
+ }
+ }
+}
+
+//
+// Do all special handling for the entry point, including wrapping
+// the shader's entry point with the official entry point that will call it.
+//
+// The following:
+//
+// retType shaderEntryPoint(args...) // shader declared entry point
+// { body }
+//
+// Becomes
+//
+// out retType ret;
+// in iargs<that are input>...;
+// out oargs<that are output> ...;
+//
+// void shaderEntryPoint() // synthesized, but official, entry point
+// {
+// args<that are input> = iargs...;
+// ret = @shaderEntryPoint(args...);
+// oargs = args<that are output>...;
+// }
+// retType @shaderEntryPoint(args...)
+// { body }
+//
+// The symbol table will still map the original entry point name to the
+// the modified function and its new name:
+//
+// symbol table: shaderEntryPoint -> @shaderEntryPoint
+//
+// Returns nullptr if no entry-point tree was built, otherwise, returns
+// a subtree that creates the entry point.
+//
+TIntermNode* HlslParseContext::transformEntryPoint(const TSourceLoc& loc, TFunction& userFunction,
+ const TAttributes& attributes)
+{
+ // Return true if this is a tessellation patch constant function input to a domain shader.
+ const auto isDsPcfInput = [this](const TType& type) {
+ return language == EShLangTessEvaluation &&
+ type.contains([](const TType* t) {
+ return t->getQualifier().builtIn == EbvTessLevelOuter ||
+ t->getQualifier().builtIn == EbvTessLevelInner;
+ });
+ };
+
+ // if we aren't in the entry point, fix the IO as such and exit
+ if (userFunction.getName().compare(intermediate.getEntryPointName().c_str()) != 0) {
+ remapNonEntryPointIO(userFunction);
+ return nullptr;
+ }
+
+ entryPointFunction = &userFunction; // needed in finish()
+
+ // Handle entry point attributes
+ handleEntryPointAttributes(loc, attributes);
+
+ // entry point logic...
+
+ // Move parameters and return value to shader in/out
+ TVariable* entryPointOutput; // gets created in remapEntryPointIO
+ TVector<TVariable*> inputs;
+ TVector<TVariable*> outputs;
+ remapEntryPointIO(userFunction, entryPointOutput, inputs, outputs);
+
+ // Further this return/in/out transform by flattening, splitting, and assigning locations
+ const auto makeVariableInOut = [&](TVariable& variable) {
+ if (variable.getType().isStruct()) {
+ if (variable.getType().getQualifier().isArrayedIo(language)) {
+ if (variable.getType().containsBuiltIn())
+ split(variable);
+ } else if (shouldFlatten(variable.getType(), EvqVaryingIn /* not assigned yet, but close enough */, true))
+ flatten(variable, false /* don't track linkage here, it will be tracked in assignToInterface() */);
+ }
+ // TODO: flatten arrays too
+ // TODO: flatten everything in I/O
+ // TODO: replace all split with flatten, make all paths can create flattened I/O, then split code can be removed
+
+ // For clip and cull distance, multiple output variables potentially get merged
+ // into one in assignClipCullDistance. That code in assignClipCullDistance
+ // handles the interface logic, so we avoid it here in that case.
+ if (!isClipOrCullDistance(variable.getType()))
+ assignToInterface(variable);
+ };
+ if (entryPointOutput != nullptr)
+ makeVariableInOut(*entryPointOutput);
+ for (auto it = inputs.begin(); it != inputs.end(); ++it)
+ if (!isDsPcfInput((*it)->getType())) // wait until the end for PCF input (see comment below)
+ makeVariableInOut(*(*it));
+ for (auto it = outputs.begin(); it != outputs.end(); ++it)
+ makeVariableInOut(*(*it));
+
+ // In the domain shader, PCF input must be at the end of the linkage. That's because in the
+ // hull shader there is no ordering: the output comes from the separate PCF, which does not
+ // participate in the argument list. That is always put at the end of the HS linkage, so the
+ // input side of the DS must match. The argument may be in any position in the DS argument list
+ // however, so this ensures the linkage is built in the correct order regardless of argument order.
+ if (language == EShLangTessEvaluation) {
+ for (auto it = inputs.begin(); it != inputs.end(); ++it)
+ if (isDsPcfInput((*it)->getType()))
+ makeVariableInOut(*(*it));
+ }
+
+ // Synthesize the call
+
+ pushScope(); // matches the one in handleFunctionBody()
+
+ // new signature
+ TType voidType(EbtVoid);
+ TFunction synthEntryPoint(&userFunction.getName(), voidType);
+ TIntermAggregate* synthParams = new TIntermAggregate();
+ intermediate.setAggregateOperator(synthParams, EOpParameters, voidType, loc);
+ intermediate.setEntryPointMangledName(synthEntryPoint.getMangledName().c_str());
+ intermediate.incrementEntryPointCount();
+ TFunction callee(&userFunction.getName(), voidType); // call based on old name, which is still in the symbol table
+
+ // change original name
+ userFunction.addPrefix("@"); // change the name in the function, but not in the symbol table
+
+ // Copy inputs (shader-in -> calling arg), while building up the call node
+ TVector<TVariable*> argVars;
+ TIntermAggregate* synthBody = new TIntermAggregate();
+ auto inputIt = inputs.begin();
+ TIntermTyped* callingArgs = nullptr;
+
+ for (int i = 0; i < userFunction.getParamCount(); i++) {
+ TParameter& param = userFunction[i];
+ argVars.push_back(makeInternalVariable(*param.name, *param.type));
+ argVars.back()->getWritableType().getQualifier().makeTemporary();
+
+ // Track the input patch, which is the only non-builtin supported by hull shader PCF.
+ if (param.getDeclaredBuiltIn() == EbvInputPatch)
+ inputPatch = argVars.back();
+
+ TIntermSymbol* arg = intermediate.addSymbol(*argVars.back());
+ handleFunctionArgument(&callee, callingArgs, arg);
+ if (param.type->getQualifier().isParamInput()) {
+ intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg,
+ intermediate.addSymbol(**inputIt)));
+ inputIt++;
+ }
+ }
+
+ // Call
+ currentCaller = synthEntryPoint.getMangledName();
+ TIntermTyped* callReturn = handleFunctionCall(loc, &callee, callingArgs);
+ currentCaller = userFunction.getMangledName();
+
+ // Return value
+ if (entryPointOutput) {
+ TIntermTyped* returnAssign;
+
+ // For hull shaders, the wrapped entry point return value is written to
+ // an array element as indexed by invocation ID, which we might have to make up.
+ // This is required to match SPIR-V semantics.
+ if (language == EShLangTessControl) {
+ TIntermSymbol* invocationIdSym = findTessLinkageSymbol(EbvInvocationId);
+
+ // If there is no user declared invocation ID, we must make one.
+ if (invocationIdSym == nullptr) {
+ TType invocationIdType(EbtUint, EvqIn, 1);
+ TString* invocationIdName = NewPoolTString("InvocationId");
+ invocationIdType.getQualifier().builtIn = EbvInvocationId;
+
+ TVariable* variable = makeInternalVariable(*invocationIdName, invocationIdType);
+
+ globalQualifierFix(loc, variable->getWritableType().getQualifier());
+ trackLinkage(*variable);
+
+ invocationIdSym = intermediate.addSymbol(*variable);
+ }
+
+ TIntermTyped* element = intermediate.addIndex(EOpIndexIndirect, intermediate.addSymbol(*entryPointOutput),
+ invocationIdSym, loc);
+
+ // Set the type of the array element being dereferenced
+ const TType derefElementType(entryPointOutput->getType(), 0);
+ element->setType(derefElementType);
+
+ returnAssign = handleAssign(loc, EOpAssign, element, callReturn);
+ } else {
+ returnAssign = handleAssign(loc, EOpAssign, intermediate.addSymbol(*entryPointOutput), callReturn);
+ }
+ intermediate.growAggregate(synthBody, returnAssign);
+ } else
+ intermediate.growAggregate(synthBody, callReturn);
+
+ // Output copies
+ auto outputIt = outputs.begin();
+ for (int i = 0; i < userFunction.getParamCount(); i++) {
+ TParameter& param = userFunction[i];
+
+ // GS outputs are via emit, so we do not copy them here.
+ if (param.type->getQualifier().isParamOutput()) {
+ if (param.getDeclaredBuiltIn() == EbvGsOutputStream) {
+ // GS output stream does not assign outputs here: it's the Append() method
+ // which writes to the output, probably multiple times separated by Emit.
+ // We merely remember the output to use, here.
+ gsStreamOutput = *outputIt;
+ } else {
+ intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign,
+ intermediate.addSymbol(**outputIt),
+ intermediate.addSymbol(*argVars[i])));
+ }
+
+ outputIt++;
+ }
+ }
+
+ // Put the pieces together to form a full function subtree
+ // for the synthesized entry point.
+ synthBody->setOperator(EOpSequence);
+ TIntermNode* synthFunctionDef = synthParams;
+ handleFunctionBody(loc, synthEntryPoint, synthBody, synthFunctionDef);
+
+ entryPointFunctionBody = synthBody;
+
+ return synthFunctionDef;
+}
+
+void HlslParseContext::handleFunctionBody(const TSourceLoc& loc, TFunction& function, TIntermNode* functionBody,
+ TIntermNode*& node)
+{
+ node = intermediate.growAggregate(node, functionBody);
+ intermediate.setAggregateOperator(node, EOpFunction, function.getType(), loc);
+ node->getAsAggregate()->setName(function.getMangledName().c_str());
+
+ popScope();
+ if (function.hasImplicitThis())
+ popImplicitThis();
+
+ if (function.getType().getBasicType() != EbtVoid && ! functionReturnsValue)
+ error(loc, "function does not return a value:", "", function.getName().c_str());
+}
+
+// AST I/O is done through shader globals declared in the 'in' or 'out'
+// storage class. An HLSL entry point has a return value, input parameters
+// and output parameters. These need to get remapped to the AST I/O.
+void HlslParseContext::remapEntryPointIO(TFunction& function, TVariable*& returnValue,
+ TVector<TVariable*>& inputs, TVector<TVariable*>& outputs)
+{
+ // We might have in input structure type with no decorations that caused it
+ // to look like an input type, yet it has (e.g.) interpolation types that
+ // must be modified that turn it into an input type.
+ // Hence, a missing ioTypeMap for 'input' might need to be synthesized.
+ const auto synthesizeEditedInput = [this](TType& type) {
+ // True if a type needs to be 'flat'
+ const auto needsFlat = [](const TType& type) {
+ return type.containsBasicType(EbtInt) ||
+ type.containsBasicType(EbtUint) ||
+ type.containsBasicType(EbtInt64) ||
+ type.containsBasicType(EbtUint64) ||
+ type.containsBasicType(EbtBool) ||
+ type.containsBasicType(EbtDouble);
+ };
+
+ if (language == EShLangFragment && needsFlat(type)) {
+ if (type.isStruct()) {
+ TTypeList* finalList = nullptr;
+ auto it = ioTypeMap.find(type.getStruct());
+ if (it == ioTypeMap.end() || it->second.input == nullptr) {
+ // Getting here means we have no input struct, but we need one.
+ auto list = new TTypeList;
+ for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) {
+ TType* newType = new TType;
+ newType->shallowCopy(*member->type);
+ TTypeLoc typeLoc = { newType, member->loc };
+ list->push_back(typeLoc);
+ }
+ // install the new input type
+ if (it == ioTypeMap.end()) {
+ tIoKinds newLists = { list, nullptr, nullptr };
+ ioTypeMap[type.getStruct()] = newLists;
+ } else
+ it->second.input = list;
+ finalList = list;
+ } else
+ finalList = it->second.input;
+ // edit for 'flat'
+ for (auto member = finalList->begin(); member != finalList->end(); ++member) {
+ if (needsFlat(*member->type)) {
+ member->type->getQualifier().clearInterpolation();
+ member->type->getQualifier().flat = true;
+ }
+ }
+ } else {
+ type.getQualifier().clearInterpolation();
+ type.getQualifier().flat = true;
+ }
+ }
+ };
+
+ // Do the actual work to make a type be a shader input or output variable,
+ // and clear the original to be non-IO (for use as a normal function parameter/return).
+ const auto makeIoVariable = [this](const char* name, TType& type, TStorageQualifier storage) -> TVariable* {
+ TVariable* ioVariable = makeInternalVariable(name, type);
+ clearUniformInputOutput(type.getQualifier());
+ if (type.isStruct()) {
+ auto newLists = ioTypeMap.find(ioVariable->getType().getStruct());
+ if (newLists != ioTypeMap.end()) {
+ if (storage == EvqVaryingIn && newLists->second.input)
+ ioVariable->getWritableType().setStruct(newLists->second.input);
+ else if (storage == EvqVaryingOut && newLists->second.output)
+ ioVariable->getWritableType().setStruct(newLists->second.output);
+ }
+ }
+ if (storage == EvqVaryingIn) {
+ correctInput(ioVariable->getWritableType().getQualifier());
+ if (language == EShLangTessEvaluation)
+ if (!ioVariable->getType().isArray())
+ ioVariable->getWritableType().getQualifier().patch = true;
+ } else {
+ correctOutput(ioVariable->getWritableType().getQualifier());
+ }
+ ioVariable->getWritableType().getQualifier().storage = storage;
+
+ fixBuiltInIoType(ioVariable->getWritableType());
+
+ return ioVariable;
+ };
+
+ // return value is actually a shader-scoped output (out)
+ if (function.getType().getBasicType() == EbtVoid) {
+ returnValue = nullptr;
+ } else {
+ if (language == EShLangTessControl) {
+ // tessellation evaluation in HLSL writes a per-ctrl-pt value, but it needs to be an
+ // array in SPIR-V semantics. We'll write to it indexed by invocation ID.
+
+ returnValue = makeIoVariable("@entryPointOutput", function.getWritableType(), EvqVaryingOut);
+
+ TType outputType;
+ outputType.shallowCopy(function.getType());
+
+ // vertices has necessarily already been set when handling entry point attributes.
+ TArraySizes* arraySizes = new TArraySizes;
+ arraySizes->addInnerSize(intermediate.getVertices());
+ outputType.transferArraySizes(arraySizes);
+
+ clearUniformInputOutput(function.getWritableType().getQualifier());
+ returnValue = makeIoVariable("@entryPointOutput", outputType, EvqVaryingOut);
+ } else {
+ returnValue = makeIoVariable("@entryPointOutput", function.getWritableType(), EvqVaryingOut);
+ }
+ }
+
+ // parameters are actually shader-scoped inputs and outputs (in or out)
+ for (int i = 0; i < function.getParamCount(); i++) {
+ TType& paramType = *function[i].type;
+ if (paramType.getQualifier().isParamInput()) {
+ synthesizeEditedInput(paramType);
+ TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingIn);
+ inputs.push_back(argAsGlobal);
+ }
+ if (paramType.getQualifier().isParamOutput()) {
+ TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingOut);
+ outputs.push_back(argAsGlobal);
+ }
+ }
+}
+
+// An HLSL function that looks like an entry point, but is not,
+// declares entry point IO built-ins, but these have to be undone.
+void HlslParseContext::remapNonEntryPointIO(TFunction& function)
+{
+ // return value
+ if (function.getType().getBasicType() != EbtVoid)
+ clearUniformInputOutput(function.getWritableType().getQualifier());
+
+ // parameters.
+ // References to structuredbuffer types are left unmodified
+ for (int i = 0; i < function.getParamCount(); i++)
+ if (!isReference(*function[i].type))
+ clearUniformInputOutput(function[i].type->getQualifier());
+}
+
+// Handle function returns, including type conversions to the function return type
+// if necessary.
+TIntermNode* HlslParseContext::handleReturnValue(const TSourceLoc& loc, TIntermTyped* value)
+{
+ functionReturnsValue = true;
+
+ if (currentFunctionType->getBasicType() == EbtVoid) {
+ error(loc, "void function cannot return a value", "return", "");
+ return intermediate.addBranch(EOpReturn, loc);
+ } else if (*currentFunctionType != value->getType()) {
+ value = intermediate.addConversion(EOpReturn, *currentFunctionType, value);
+ if (value && *currentFunctionType != value->getType())
+ value = intermediate.addUniShapeConversion(EOpReturn, *currentFunctionType, value);
+ if (value == nullptr || *currentFunctionType != value->getType()) {
+ error(loc, "type does not match, or is not convertible to, the function's return type", "return", "");
+ return value;
+ }
+ }
+
+ return intermediate.addBranch(EOpReturn, value, loc);
+}
+
+void HlslParseContext::handleFunctionArgument(TFunction* function,
+ TIntermTyped*& arguments, TIntermTyped* newArg)
+{
+ TParameter param = { 0, new TType, nullptr };
+ param.type->shallowCopy(newArg->getType());
+
+ function->addParameter(param);
+ if (arguments)
+ arguments = intermediate.growAggregate(arguments, newArg);
+ else
+ arguments = newArg;
+}
+
+// Position may require special handling: we can optionally invert Y.
+// See: https://github.com/KhronosGroup/glslang/issues/1173
+// https://github.com/KhronosGroup/glslang/issues/494
+TIntermTyped* HlslParseContext::assignPosition(const TSourceLoc& loc, TOperator op,
+ TIntermTyped* left, TIntermTyped* right)
+{
+ // If we are not asked for Y inversion, use a plain old assign.
+ if (!intermediate.getInvertY())
+ return intermediate.addAssign(op, left, right, loc);
+
+ // If we get here, we should invert Y.
+ TIntermAggregate* assignList = nullptr;
+
+ // If this is a complex rvalue, we don't want to dereference it many times. Create a temporary.
+ TVariable* rhsTempVar = nullptr;
+ rhsTempVar = makeInternalVariable("@position", right->getType());
+ rhsTempVar->getWritableType().getQualifier().makeTemporary();
+
+ {
+ TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc);
+ assignList = intermediate.growAggregate(assignList,
+ intermediate.addAssign(EOpAssign, rhsTempSym, right, loc), loc);
+ }
+
+ // pos.y = -pos.y
+ {
+ const int Y = 1;
+
+ TIntermTyped* tempSymL = intermediate.addSymbol(*rhsTempVar, loc);
+ TIntermTyped* tempSymR = intermediate.addSymbol(*rhsTempVar, loc);
+ TIntermTyped* index = intermediate.addConstantUnion(Y, loc);
+
+ TIntermTyped* lhsElement = intermediate.addIndex(EOpIndexDirect, tempSymL, index, loc);
+ TIntermTyped* rhsElement = intermediate.addIndex(EOpIndexDirect, tempSymR, index, loc);
+
+ const TType derefType(right->getType(), 0);
+
+ lhsElement->setType(derefType);
+ rhsElement->setType(derefType);
+
+ TIntermTyped* yNeg = intermediate.addUnaryMath(EOpNegative, rhsElement, loc);
+
+ assignList = intermediate.growAggregate(assignList, intermediate.addAssign(EOpAssign, lhsElement, yNeg, loc));
+ }
+
+ // Assign the rhs temp (now with Y inversion) to the final output
+ {
+ TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc);
+ assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, rhsTempSym, loc));
+ }
+
+ assert(assignList != nullptr);
+ assignList->setOperator(EOpSequence);
+
+ return assignList;
+}
+
+// Clip and cull distance require special handling due to a semantic mismatch. In HLSL,
+// these can be float scalar, float vector, or arrays of float scalar or float vector.
+// In SPIR-V, they are arrays of scalar floats in all cases. We must copy individual components
+// (e.g, both x and y components of a float2) out into the destination float array.
+//
+// The values are assigned to sequential members of the output array. The inner dimension
+// is vector components. The outer dimension is array elements.
+TIntermAggregate* HlslParseContext::assignClipCullDistance(const TSourceLoc& loc, TOperator op, int semanticId,
+ TIntermTyped* left, TIntermTyped* right)
+{
+ switch (language) {
+ case EShLangFragment:
+ case EShLangVertex:
+ case EShLangGeometry:
+ break;
+ default:
+ error(loc, "unimplemented: clip/cull not currently implemented for this stage", "", "");
+ return nullptr;
+ }
+
+ TVariable** clipCullVar = nullptr;
+
+ // Figure out if we are assigning to, or from, clip or cull distance.
+ const bool isOutput = isClipOrCullDistance(left->getType());
+
+ // This is the rvalue or lvalue holding the clip or cull distance.
+ TIntermTyped* clipCullNode = isOutput ? left : right;
+ // This is the value going into or out of the clip or cull distance.
+ TIntermTyped* internalNode = isOutput ? right : left;
+
+ const TBuiltInVariable builtInType = clipCullNode->getQualifier().builtIn;
+
+ decltype(clipSemanticNSizeIn)* semanticNSize = nullptr;
+
+ // Refer to either the clip or the cull distance, depending on semantic.
+ switch (builtInType) {
+ case EbvClipDistance:
+ clipCullVar = isOutput ? &clipDistanceOutput : &clipDistanceInput;
+ semanticNSize = isOutput ? &clipSemanticNSizeOut : &clipSemanticNSizeIn;
+ break;
+ case EbvCullDistance:
+ clipCullVar = isOutput ? &cullDistanceOutput : &cullDistanceInput;
+ semanticNSize = isOutput ? &cullSemanticNSizeOut : &cullSemanticNSizeIn;
+ break;
+
+ // called invalidly: we expected a clip or a cull distance.
+ // static compile time problem: should not happen.
+ default: assert(0); return nullptr;
+ }
+
+ // This is the offset in the destination array of a given semantic's data
+ std::array<int, maxClipCullRegs> semanticOffset;
+
+ // Calculate offset of variable of semantic N in destination array
+ int arrayLoc = 0;
+ int vecItems = 0;
+
+ for (int x = 0; x < maxClipCullRegs; ++x) {
+ // See if we overflowed the vec4 packing
+ if ((vecItems + (*semanticNSize)[x]) > 4) {
+ arrayLoc = (arrayLoc + 3) & (~0x3); // round up to next multiple of 4
+ vecItems = 0;
+ }
+
+ semanticOffset[x] = arrayLoc;
+ vecItems += (*semanticNSize)[x];
+ arrayLoc += (*semanticNSize)[x];
+ }
+
+
+ // It can have up to 2 array dimensions (in the case of geometry shader inputs)
+ const TArraySizes* const internalArraySizes = internalNode->getType().getArraySizes();
+ const int internalArrayDims = internalNode->getType().isArray() ? internalArraySizes->getNumDims() : 0;
+ // vector sizes:
+ const int internalVectorSize = internalNode->getType().getVectorSize();
+ // array sizes, or 1 if it's not an array:
+ const int internalInnerArraySize = (internalArrayDims > 0 ? internalArraySizes->getDimSize(internalArrayDims-1) : 1);
+ const int internalOuterArraySize = (internalArrayDims > 1 ? internalArraySizes->getDimSize(0) : 1);
+
+ // The created type may be an array of arrays, e.g, for geometry shader inputs.
+ const bool isImplicitlyArrayed = (language == EShLangGeometry && !isOutput);
+
+ // If we haven't created the output already, create it now.
+ if (*clipCullVar == nullptr) {
+ // ClipDistance and CullDistance are handled specially in the entry point input/output copy
+ // algorithm, because they may need to be unpacked from components of vectors (or a scalar)
+ // into a float array, or vice versa. Here, we make the array the right size and type,
+ // which depends on the incoming data, which has several potential dimensions:
+ // * Semantic ID
+ // * vector size
+ // * array size
+ // Of those, semantic ID and array size cannot appear simultaneously.
+ //
+ // Also to note: for implicitly arrayed forms (e.g, geometry shader inputs), we need to create two
+ // array dimensions. The shader's declaration may have one or two array dimensions. One is always
+ // the geometry's dimension.
+
+ const bool useInnerSize = internalArrayDims > 1 || !isImplicitlyArrayed;
+
+ const int requiredInnerArraySize = arrayLoc * (useInnerSize ? internalInnerArraySize : 1);
+ const int requiredOuterArraySize = (internalArrayDims > 0) ? internalArraySizes->getDimSize(0) : 1;
+
+ TType clipCullType(EbtFloat, clipCullNode->getType().getQualifier().storage, 1);
+ clipCullType.getQualifier() = clipCullNode->getType().getQualifier();
+
+ // Create required array dimension
+ TArraySizes* arraySizes = new TArraySizes;
+ if (isImplicitlyArrayed)
+ arraySizes->addInnerSize(requiredOuterArraySize);
+ arraySizes->addInnerSize(requiredInnerArraySize);
+ clipCullType.transferArraySizes(arraySizes);
+
+ // Obtain symbol name: we'll use that for the symbol we introduce.
+ TIntermSymbol* sym = clipCullNode->getAsSymbolNode();
+ assert(sym != nullptr);
+
+ // We are moving the semantic ID from the layout location, so it is no longer needed or
+ // desired there.
+ clipCullType.getQualifier().layoutLocation = TQualifier::layoutLocationEnd;
+
+ // Create variable and track its linkage
+ *clipCullVar = makeInternalVariable(sym->getName().c_str(), clipCullType);
+
+ trackLinkage(**clipCullVar);
+ }
+
+ // Create symbol for the clip or cull variable.
+ TIntermSymbol* clipCullSym = intermediate.addSymbol(**clipCullVar);
+
+ // vector sizes:
+ const int clipCullVectorSize = clipCullSym->getType().getVectorSize();
+
+ // array sizes, or 1 if it's not an array:
+ const TArraySizes* const clipCullArraySizes = clipCullSym->getType().getArraySizes();
+ const int clipCullOuterArraySize = isImplicitlyArrayed ? clipCullArraySizes->getDimSize(0) : 1;
+ const int clipCullInnerArraySize = clipCullArraySizes->getDimSize(isImplicitlyArrayed ? 1 : 0);
+
+ // clipCullSym has got to be an array of scalar floats, per SPIR-V semantics.
+ // fixBuiltInIoType() should have handled that upstream.
+ assert(clipCullSym->getType().isArray());
+ assert(clipCullSym->getType().getVectorSize() == 1);
+ assert(clipCullSym->getType().getBasicType() == EbtFloat);
+
+ // We may be creating multiple sub-assignments. This is an aggregate to hold them.
+ // TODO: it would be possible to be clever sometimes and avoid the sequence node if not needed.
+ TIntermAggregate* assignList = nullptr;
+
+ // Holds individual component assignments as we make them.
+ TIntermTyped* clipCullAssign = nullptr;
+
+ // If the types are homomorphic, use a simple assign. No need to mess about with
+ // individual components.
+ if (clipCullSym->getType().isArray() == internalNode->getType().isArray() &&
+ clipCullInnerArraySize == internalInnerArraySize &&
+ clipCullOuterArraySize == internalOuterArraySize &&
+ clipCullVectorSize == internalVectorSize) {
+
+ if (isOutput)
+ clipCullAssign = intermediate.addAssign(op, clipCullSym, internalNode, loc);
+ else
+ clipCullAssign = intermediate.addAssign(op, internalNode, clipCullSym, loc);
+
+ assignList = intermediate.growAggregate(assignList, clipCullAssign);
+ assignList->setOperator(EOpSequence);
+
+ return assignList;
+ }
+
+ // We are going to copy each component of the internal (per array element if indicated) to sequential
+ // array elements of the clipCullSym. This tracks the lhs element we're writing to as we go along.
+ // We may be starting in the middle - e.g, for a non-zero semantic ID calculated above.
+ int clipCullInnerArrayPos = semanticOffset[semanticId];
+ int clipCullOuterArrayPos = 0;
+
+ // Lambda to add an index to a node, set the type of the result, and return the new node.
+ const auto addIndex = [this, &loc](TIntermTyped* node, int pos) -> TIntermTyped* {
+ const TType derefType(node->getType(), 0);
+ node = intermediate.addIndex(EOpIndexDirect, node, intermediate.addConstantUnion(pos, loc), loc);
+ node->setType(derefType);
+ return node;
+ };
+
+ // Loop through every component of every element of the internal, and copy to or from the matching external.
+ for (int internalOuterArrayPos = 0; internalOuterArrayPos < internalOuterArraySize; ++internalOuterArrayPos) {
+ for (int internalInnerArrayPos = 0; internalInnerArrayPos < internalInnerArraySize; ++internalInnerArrayPos) {
+ for (int internalComponent = 0; internalComponent < internalVectorSize; ++internalComponent) {
+ // clip/cull array member to read from / write to:
+ TIntermTyped* clipCullMember = clipCullSym;
+
+ // If implicitly arrayed, there is an outer array dimension involved
+ if (isImplicitlyArrayed)
+ clipCullMember = addIndex(clipCullMember, clipCullOuterArrayPos);
+
+ // Index into proper array position for clip cull member
+ clipCullMember = addIndex(clipCullMember, clipCullInnerArrayPos++);
+
+ // if needed, start over with next outer array slice.
+ if (isImplicitlyArrayed && clipCullInnerArrayPos >= clipCullInnerArraySize) {
+ clipCullInnerArrayPos = semanticOffset[semanticId];
+ ++clipCullOuterArrayPos;
+ }
+
+ // internal member to read from / write to:
+ TIntermTyped* internalMember = internalNode;
+
+ // If internal node has outer array dimension, index appropriately.
+ if (internalArrayDims > 1)
+ internalMember = addIndex(internalMember, internalOuterArrayPos);
+
+ // If internal node has inner array dimension, index appropriately.
+ if (internalArrayDims > 0)
+ internalMember = addIndex(internalMember, internalInnerArrayPos);
+
+ // If internal node is a vector, extract the component of interest.
+ if (internalNode->getType().isVector())
+ internalMember = addIndex(internalMember, internalComponent);
+
+ // Create an assignment: output from internal to clip cull, or input from clip cull to internal.
+ if (isOutput)
+ clipCullAssign = intermediate.addAssign(op, clipCullMember, internalMember, loc);
+ else
+ clipCullAssign = intermediate.addAssign(op, internalMember, clipCullMember, loc);
+
+ // Track assignment in the sequence.
+ assignList = intermediate.growAggregate(assignList, clipCullAssign);
+ }
+ }
+ }
+
+ assert(assignList != nullptr);
+ assignList->setOperator(EOpSequence);
+
+ return assignList;
+}
+
+// Some simple source assignments need to be flattened to a sequence
+// of AST assignments. Catch these and flatten, otherwise, pass through
+// to intermediate.addAssign().
+//
+// Also, assignment to matrix swizzles requires multiple component assignments,
+// intercept those as well.
+TIntermTyped* HlslParseContext::handleAssign(const TSourceLoc& loc, TOperator op, TIntermTyped* left,
+ TIntermTyped* right)
+{
+ if (left == nullptr || right == nullptr)
+ return nullptr;
+
+ // writing to opaques will require fixing transforms
+ if (left->getType().containsOpaque())
+ intermediate.setNeedsLegalization();
+
+ if (left->getAsOperator() && left->getAsOperator()->getOp() == EOpMatrixSwizzle)
+ return handleAssignToMatrixSwizzle(loc, op, left, right);
+
+ // Return true if the given node is an index operation into a split variable.
+ const auto indexesSplit = [this](const TIntermTyped* node) -> bool {
+ const TIntermBinary* binaryNode = node->getAsBinaryNode();
+
+ if (binaryNode == nullptr)
+ return false;
+
+ return (binaryNode->getOp() == EOpIndexDirect || binaryNode->getOp() == EOpIndexIndirect) &&
+ wasSplit(binaryNode->getLeft());
+ };
+
+ // Return true if this stage assigns clip position with potentially inverted Y
+ const auto assignsClipPos = [this](const TIntermTyped* node) -> bool {
+ return node->getType().getQualifier().builtIn == EbvPosition &&
+ (language == EShLangVertex || language == EShLangGeometry || language == EShLangTessEvaluation);
+ };
+
+ const bool isSplitLeft = wasSplit(left) || indexesSplit(left);
+ const bool isSplitRight = wasSplit(right) || indexesSplit(right);
+
+ const bool isFlattenLeft = wasFlattened(left);
+ const bool isFlattenRight = wasFlattened(right);
+
+ // OK to do a single assign if neither side is split or flattened. Otherwise,
+ // fall through to a member-wise copy.
+ if (!isFlattenLeft && !isFlattenRight && !isSplitLeft && !isSplitRight) {
+ // Clip and cull distance requires more processing. See comment above assignClipCullDistance.
+ if (isClipOrCullDistance(left->getType()) || isClipOrCullDistance(right->getType())) {
+ const bool isOutput = isClipOrCullDistance(left->getType());
+
+ const int semanticId = (isOutput ? left : right)->getType().getQualifier().layoutLocation;
+ return assignClipCullDistance(loc, op, semanticId, left, right);
+ } else if (assignsClipPos(left)) {
+ // Position can require special handling: see comment above assignPosition
+ return assignPosition(loc, op, left, right);
+ } else if (left->getQualifier().builtIn == EbvSampleMask) {
+ // Certain builtins are required to be arrayed outputs in SPIR-V, but may internally be scalars
+ // in the shader. Copy the scalar RHS into the LHS array element zero, if that happens.
+ if (left->isArray() && !right->isArray()) {
+ const TType derefType(left->getType(), 0);
+ left = intermediate.addIndex(EOpIndexDirect, left, intermediate.addConstantUnion(0, loc), loc);
+ left->setType(derefType);
+ // Fall through to add assign.
+ }
+ }
+
+ return intermediate.addAssign(op, left, right, loc);
+ }
+
+ TIntermAggregate* assignList = nullptr;
+ const TVector<TVariable*>* leftVariables = nullptr;
+ const TVector<TVariable*>* rightVariables = nullptr;
+
+ // A temporary to store the right node's value, so we don't keep indirecting into it
+ // if it's not a simple symbol.
+ TVariable* rhsTempVar = nullptr;
+
+ // If the RHS is a simple symbol node, we'll copy it for each member.
+ TIntermSymbol* cloneSymNode = nullptr;
+
+ int memberCount = 0;
+
+ // Track how many items there are to copy.
+ if (left->getType().isStruct())
+ memberCount = (int)left->getType().getStruct()->size();
+ if (left->getType().isArray())
+ memberCount = left->getType().getCumulativeArraySize();
+
+ if (isFlattenLeft)
+ leftVariables = &flattenMap.find(left->getAsSymbolNode()->getId())->second.members;
+
+ if (isFlattenRight) {
+ rightVariables = &flattenMap.find(right->getAsSymbolNode()->getId())->second.members;
+ } else {
+ // The RHS is not flattened. There are several cases:
+ // 1. 1 item to copy: Use the RHS directly.
+ // 2. >1 item, simple symbol RHS: we'll create a new TIntermSymbol node for each, but no assign to temp.
+ // 3. >1 item, complex RHS: assign it to a new temp variable, and create a TIntermSymbol for each member.
+
+ if (memberCount <= 1) {
+ // case 1: we'll use the symbol directly below. Nothing to do.
+ } else {
+ if (right->getAsSymbolNode() != nullptr) {
+ // case 2: we'll copy the symbol per iteration below.
+ cloneSymNode = right->getAsSymbolNode();
+ } else {
+ // case 3: assign to a temp, and indirect into that.
+ rhsTempVar = makeInternalVariable("flattenTemp", right->getType());
+ rhsTempVar->getWritableType().getQualifier().makeTemporary();
+ TIntermTyped* noFlattenRHS = intermediate.addSymbol(*rhsTempVar, loc);
+
+ // Add this to the aggregate being built.
+ assignList = intermediate.growAggregate(assignList,
+ intermediate.addAssign(op, noFlattenRHS, right, loc), loc);
+ }
+ }
+ }
+
+ // When dealing with split arrayed structures of built-ins, the arrayness is moved to the extracted built-in
+ // variables, which is awkward when copying between split and unsplit structures. This variable tracks
+ // array indirections so they can be percolated from outer structs to inner variables.
+ std::vector <int> arrayElement;
+
+ TStorageQualifier leftStorage = left->getType().getQualifier().storage;
+ TStorageQualifier rightStorage = right->getType().getQualifier().storage;
+
+ int leftOffset = findSubtreeOffset(*left);
+ int rightOffset = findSubtreeOffset(*right);
+
+ const auto getMember = [&](bool isLeft, const TType& type, int member, TIntermTyped* splitNode, int splitMember,
+ bool flattened)
+ -> TIntermTyped * {
+ const bool split = isLeft ? isSplitLeft : isSplitRight;
+
+ TIntermTyped* subTree;
+ const TType derefType(type, member);
+ const TVariable* builtInVar = nullptr;
+ if ((flattened || split) && derefType.isBuiltIn()) {
+ auto splitPair = splitBuiltIns.find(HlslParseContext::tInterstageIoData(
+ derefType.getQualifier().builtIn,
+ isLeft ? leftStorage : rightStorage));
+ if (splitPair != splitBuiltIns.end())
+ builtInVar = splitPair->second;
+ }
+ if (builtInVar != nullptr) {
+ // copy from interstage IO built-in if needed
+ subTree = intermediate.addSymbol(*builtInVar);
+
+ if (subTree->getType().isArray()) {
+ // Arrayness of builtIn symbols isn't handled by the normal recursion:
+ // it's been extracted and moved to the built-in.
+ if (!arrayElement.empty()) {
+ const TType splitDerefType(subTree->getType(), arrayElement.back());
+ subTree = intermediate.addIndex(EOpIndexDirect, subTree,
+ intermediate.addConstantUnion(arrayElement.back(), loc), loc);
+ subTree->setType(splitDerefType);
+ } else if (splitNode->getAsOperator() != nullptr && (splitNode->getAsOperator()->getOp() == EOpIndexIndirect)) {
+ // This might also be a stage with arrayed outputs, in which case there's an index
+ // operation we should transfer to the output builtin.
+
+ const TType splitDerefType(subTree->getType(), 0);
+ subTree = intermediate.addIndex(splitNode->getAsOperator()->getOp(), subTree,
+ splitNode->getAsBinaryNode()->getRight(), loc);
+ subTree->setType(splitDerefType);
+ }
+ }
+ } else if (flattened && !shouldFlatten(derefType, isLeft ? leftStorage : rightStorage, false)) {
+ if (isLeft)
+ subTree = intermediate.addSymbol(*(*leftVariables)[leftOffset++]);
+ else
+ subTree = intermediate.addSymbol(*(*rightVariables)[rightOffset++]);
+ } else {
+ // Index operator if it's an aggregate, else EOpNull
+ const TOperator accessOp = type.isArray() ? EOpIndexDirect
+ : type.isStruct() ? EOpIndexDirectStruct
+ : EOpNull;
+ if (accessOp == EOpNull) {
+ subTree = splitNode;
+ } else {
+ subTree = intermediate.addIndex(accessOp, splitNode, intermediate.addConstantUnion(splitMember, loc),
+ loc);
+ const TType splitDerefType(splitNode->getType(), splitMember);
+ subTree->setType(splitDerefType);
+ }
+ }
+
+ return subTree;
+ };
+
+ // Use the proper RHS node: a new symbol from a TVariable, copy
+ // of an TIntermSymbol node, or sometimes the right node directly.
+ right = rhsTempVar != nullptr ? intermediate.addSymbol(*rhsTempVar, loc) :
+ cloneSymNode != nullptr ? intermediate.addSymbol(*cloneSymNode) :
+ right;
+
+ // Cannot use auto here, because this is recursive, and auto can't work out the type without seeing the
+ // whole thing. So, we'll resort to an explicit type via std::function.
+ const std::function<void(TIntermTyped* left, TIntermTyped* right, TIntermTyped* splitLeft, TIntermTyped* splitRight,
+ bool topLevel)>
+ traverse = [&](TIntermTyped* left, TIntermTyped* right, TIntermTyped* splitLeft, TIntermTyped* splitRight,
+ bool topLevel) -> void {
+ // If we get here, we are assigning to or from a whole array or struct that must be
+ // flattened, so have to do member-by-member assignment:
+
+ bool shouldFlattenSubsetLeft = isFlattenLeft && shouldFlatten(left->getType(), leftStorage, topLevel);
+ bool shouldFlattenSubsetRight = isFlattenRight && shouldFlatten(right->getType(), rightStorage, topLevel);
+
+ if ((left->getType().isArray() || right->getType().isArray()) &&
+ (shouldFlattenSubsetLeft || isSplitLeft ||
+ shouldFlattenSubsetRight || isSplitRight)) {
+ const int elementsL = left->getType().isArray() ? left->getType().getOuterArraySize() : 1;
+ const int elementsR = right->getType().isArray() ? right->getType().getOuterArraySize() : 1;
+
+ // The arrays might not be the same size,
+ // e.g., if the size has been forced for EbvTessLevelInner/Outer.
+ const int elementsToCopy = std::min(elementsL, elementsR);
+
+ // array case
+ for (int element = 0; element < elementsToCopy; ++element) {
+ arrayElement.push_back(element);
+
+ // Add a new AST symbol node if we have a temp variable holding a complex RHS.
+ TIntermTyped* subLeft = getMember(true, left->getType(), element, left, element,
+ shouldFlattenSubsetLeft);
+ TIntermTyped* subRight = getMember(false, right->getType(), element, right, element,
+ shouldFlattenSubsetRight);
+
+ TIntermTyped* subSplitLeft = isSplitLeft ? getMember(true, left->getType(), element, splitLeft,
+ element, shouldFlattenSubsetLeft)
+ : subLeft;
+ TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right->getType(), element, splitRight,
+ element, shouldFlattenSubsetRight)
+ : subRight;
+
+ traverse(subLeft, subRight, subSplitLeft, subSplitRight, false);
+
+ arrayElement.pop_back();
+ }
+ } else if (left->getType().isStruct() && (shouldFlattenSubsetLeft || isSplitLeft ||
+ shouldFlattenSubsetRight || isSplitRight)) {
+ // struct case
+ const auto& membersL = *left->getType().getStruct();
+ const auto& membersR = *right->getType().getStruct();
+
+ // These track the members in the split structures corresponding to the same in the unsplit structures,
+ // which we traverse in parallel.
+ int memberL = 0;
+ int memberR = 0;
+
+ // Handle empty structure assignment
+ if (int(membersL.size()) == 0 && int(membersR.size()) == 0)
+ assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, right, loc), loc);
+
+ for (int member = 0; member < int(membersL.size()); ++member) {
+ const TType& typeL = *membersL[member].type;
+ const TType& typeR = *membersR[member].type;
+
+ TIntermTyped* subLeft = getMember(true, left->getType(), member, left, member,
+ shouldFlattenSubsetLeft);
+ TIntermTyped* subRight = getMember(false, right->getType(), member, right, member,
+ shouldFlattenSubsetRight);
+
+ // If there is no splitting, use the same values to avoid inefficiency.
+ TIntermTyped* subSplitLeft = isSplitLeft ? getMember(true, left->getType(), member, splitLeft,
+ memberL, shouldFlattenSubsetLeft)
+ : subLeft;
+ TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right->getType(), member, splitRight,
+ memberR, shouldFlattenSubsetRight)
+ : subRight;
+
+ if (isClipOrCullDistance(subSplitLeft->getType()) || isClipOrCullDistance(subSplitRight->getType())) {
+ // Clip and cull distance built-in assignment is complex in its own right, and is handled in
+ // a separate function dedicated to that task. See comment above assignClipCullDistance;
+
+ const bool isOutput = isClipOrCullDistance(subSplitLeft->getType());
+
+ // Since all clip/cull semantics boil down to the same built-in type, we need to get the
+ // semantic ID from the dereferenced type's layout location, to avoid an N-1 mapping.
+ const TType derefType((isOutput ? left : right)->getType(), member);
+ const int semanticId = derefType.getQualifier().layoutLocation;
+
+ TIntermAggregate* clipCullAssign = assignClipCullDistance(loc, op, semanticId,
+ subSplitLeft, subSplitRight);
+
+ assignList = intermediate.growAggregate(assignList, clipCullAssign, loc);
+ } else if (assignsClipPos(subSplitLeft)) {
+ // Position can require special handling: see comment above assignPosition
+ TIntermTyped* positionAssign = assignPosition(loc, op, subSplitLeft, subSplitRight);
+ assignList = intermediate.growAggregate(assignList, positionAssign, loc);
+ } else if (!shouldFlattenSubsetLeft && !shouldFlattenSubsetRight &&
+ !typeL.containsBuiltIn() && !typeR.containsBuiltIn()) {
+ // If this is the final flattening (no nested types below to flatten)
+ // we'll copy the member, else recurse into the type hierarchy.
+ // However, if splitting the struct, that means we can copy a whole
+ // subtree here IFF it does not itself contain any interstage built-in
+ // IO variables, so we only have to recurse into it if there's something
+ // for splitting to do. That can save a lot of AST verbosity for
+ // a bunch of memberwise copies.
+
+ assignList = intermediate.growAggregate(assignList,
+ intermediate.addAssign(op, subSplitLeft, subSplitRight, loc),
+ loc);
+ } else {
+ traverse(subLeft, subRight, subSplitLeft, subSplitRight, false);
+ }
+
+ memberL += (typeL.isBuiltIn() ? 0 : 1);
+ memberR += (typeR.isBuiltIn() ? 0 : 1);
+ }
+ } else {
+ // Member copy
+ assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, right, loc), loc);
+ }
+
+ };
+
+ TIntermTyped* splitLeft = left;
+ TIntermTyped* splitRight = right;
+
+ // If either left or right was a split structure, we must read or write it, but still have to
+ // parallel-recurse through the unsplit structure to identify the built-in IO vars.
+ // The left can be either a symbol, or an index into a symbol (e.g, array reference)
+ if (isSplitLeft) {
+ if (indexesSplit(left)) {
+ // Index case: Refer to the indexed symbol, if the left is an index operator.
+ const TIntermSymbol* symNode = left->getAsBinaryNode()->getLeft()->getAsSymbolNode();
+
+ TIntermTyped* splitLeftNonIo = intermediate.addSymbol(*getSplitNonIoVar(symNode->getId()), loc);
+
+ splitLeft = intermediate.addIndex(left->getAsBinaryNode()->getOp(), splitLeftNonIo,
+ left->getAsBinaryNode()->getRight(), loc);
+
+ const TType derefType(splitLeftNonIo->getType(), 0);
+ splitLeft->setType(derefType);
+ } else {
+ // Symbol case: otherwise, if not indexed, we have the symbol directly.
+ const TIntermSymbol* symNode = left->getAsSymbolNode();
+ splitLeft = intermediate.addSymbol(*getSplitNonIoVar(symNode->getId()), loc);
+ }
+ }
+
+ if (isSplitRight)
+ splitRight = intermediate.addSymbol(*getSplitNonIoVar(right->getAsSymbolNode()->getId()), loc);
+
+ // This makes the whole assignment, recursing through subtypes as needed.
+ traverse(left, right, splitLeft, splitRight, true);
+
+ assert(assignList != nullptr);
+ assignList->setOperator(EOpSequence);
+
+ return assignList;
+}
+
+// An assignment to matrix swizzle must be decomposed into individual assignments.
+// These must be selected component-wise from the RHS and stored component-wise
+// into the LHS.
+TIntermTyped* HlslParseContext::handleAssignToMatrixSwizzle(const TSourceLoc& loc, TOperator op, TIntermTyped* left,
+ TIntermTyped* right)
+{
+ assert(left->getAsOperator() && left->getAsOperator()->getOp() == EOpMatrixSwizzle);
+
+ if (op != EOpAssign)
+ error(loc, "only simple assignment to non-simple matrix swizzle is supported", "assign", "");
+
+ // isolate the matrix and swizzle nodes
+ TIntermTyped* matrix = left->getAsBinaryNode()->getLeft()->getAsTyped();
+ const TIntermSequence& swizzle = left->getAsBinaryNode()->getRight()->getAsAggregate()->getSequence();
+
+ // if the RHS isn't already a simple vector, let's store into one
+ TIntermSymbol* vector = right->getAsSymbolNode();
+ TIntermTyped* vectorAssign = nullptr;
+ if (vector == nullptr) {
+ // create a new intermediate vector variable to assign to
+ TType vectorType(matrix->getBasicType(), EvqTemporary, matrix->getQualifier().precision, (int)swizzle.size()/2);
+ vector = intermediate.addSymbol(*makeInternalVariable("intermVec", vectorType), loc);
+
+ // assign the right to the new vector
+ vectorAssign = handleAssign(loc, op, vector, right);
+ }
+
+ // Assign the vector components to the matrix components.
+ // Store this as a sequence, so a single aggregate node represents this
+ // entire operation.
+ TIntermAggregate* result = intermediate.makeAggregate(vectorAssign);
+ TType columnType(matrix->getType(), 0);
+ TType componentType(columnType, 0);
+ TType indexType(EbtInt);
+ for (int i = 0; i < (int)swizzle.size(); i += 2) {
+ // the right component, single index into the RHS vector
+ TIntermTyped* rightComp = intermediate.addIndex(EOpIndexDirect, vector,
+ intermediate.addConstantUnion(i/2, loc), loc);
+
+ // the left component, double index into the LHS matrix
+ TIntermTyped* leftComp = intermediate.addIndex(EOpIndexDirect, matrix,
+ intermediate.addConstantUnion(swizzle[i]->getAsConstantUnion()->getConstArray(),
+ indexType, loc),
+ loc);
+ leftComp->setType(columnType);
+ leftComp = intermediate.addIndex(EOpIndexDirect, leftComp,
+ intermediate.addConstantUnion(swizzle[i+1]->getAsConstantUnion()->getConstArray(),
+ indexType, loc),
+ loc);
+ leftComp->setType(componentType);
+
+ // Add the assignment to the aggregate
+ result = intermediate.growAggregate(result, intermediate.addAssign(op, leftComp, rightComp, loc));
+ }
+
+ result->setOp(EOpSequence);
+
+ return result;
+}
+
+//
+// HLSL atomic operations have slightly different arguments than
+// GLSL/AST/SPIRV. The semantics are converted below in decomposeIntrinsic.
+// This provides the post-decomposition equivalent opcode.
+//
+TOperator HlslParseContext::mapAtomicOp(const TSourceLoc& loc, TOperator op, bool isImage)
+{
+ switch (op) {
+ case EOpInterlockedAdd: return isImage ? EOpImageAtomicAdd : EOpAtomicAdd;
+ case EOpInterlockedAnd: return isImage ? EOpImageAtomicAnd : EOpAtomicAnd;
+ case EOpInterlockedCompareExchange: return isImage ? EOpImageAtomicCompSwap : EOpAtomicCompSwap;
+ case EOpInterlockedMax: return isImage ? EOpImageAtomicMax : EOpAtomicMax;
+ case EOpInterlockedMin: return isImage ? EOpImageAtomicMin : EOpAtomicMin;
+ case EOpInterlockedOr: return isImage ? EOpImageAtomicOr : EOpAtomicOr;
+ case EOpInterlockedXor: return isImage ? EOpImageAtomicXor : EOpAtomicXor;
+ case EOpInterlockedExchange: return isImage ? EOpImageAtomicExchange : EOpAtomicExchange;
+ case EOpInterlockedCompareStore: // TODO: ...
+ default:
+ error(loc, "unknown atomic operation", "unknown op", "");
+ return EOpNull;
+ }
+}
+
+//
+// Create a combined sampler/texture from separate sampler and texture.
+//
+TIntermAggregate* HlslParseContext::handleSamplerTextureCombine(const TSourceLoc& loc, TIntermTyped* argTex,
+ TIntermTyped* argSampler)
+{
+ TIntermAggregate* txcombine = new TIntermAggregate(EOpConstructTextureSampler);
+
+ txcombine->getSequence().push_back(argTex);
+ txcombine->getSequence().push_back(argSampler);
+
+ TSampler samplerType = argTex->getType().getSampler();
+ samplerType.combined = true;
+
+ // TODO:
+ // This block exists until the spec no longer requires shadow modes on texture objects.
+ // It can be deleted after that, along with the shadowTextureVariant member.
+ {
+ const bool shadowMode = argSampler->getType().getSampler().shadow;
+
+ TIntermSymbol* texSymbol = argTex->getAsSymbolNode();
+
+ if (texSymbol == nullptr)
+ texSymbol = argTex->getAsBinaryNode()->getLeft()->getAsSymbolNode();
+
+ if (texSymbol == nullptr) {
+ error(loc, "unable to find texture symbol", "", "");
+ return nullptr;
+ }
+
+ // This forces the texture's shadow state to be the sampler's
+ // shadow state. This depends on downstream optimization to
+ // DCE one variant in [shadow, nonshadow] if both are present,
+ // or the SPIR-V module would be invalid.
+ int newId = texSymbol->getId();
+
+ // Check to see if this texture has been given a shadow mode already.
+ // If so, look up the one we already have.
+ const auto textureShadowEntry = textureShadowVariant.find(texSymbol->getId());
+
+ if (textureShadowEntry != textureShadowVariant.end())
+ newId = textureShadowEntry->second->get(shadowMode);
+ else
+ textureShadowVariant[texSymbol->getId()] = NewPoolObject(tShadowTextureSymbols(), 1);
+
+ // Sometimes we have to create another symbol (if this texture has been seen before,
+ // and we haven't created the form for this shadow mode).
+ if (newId == -1) {
+ TType texType;
+ texType.shallowCopy(argTex->getType());
+ texType.getSampler().shadow = shadowMode; // set appropriate shadow mode.
+ globalQualifierFix(loc, texType.getQualifier());
+
+ TVariable* newTexture = makeInternalVariable(texSymbol->getName(), texType);
+
+ trackLinkage(*newTexture);
+
+ newId = newTexture->getUniqueId();
+ }
+
+ assert(newId != -1);
+
+ if (textureShadowVariant.find(newId) == textureShadowVariant.end())
+ textureShadowVariant[newId] = textureShadowVariant[texSymbol->getId()];
+
+ textureShadowVariant[newId]->set(shadowMode, newId);
+
+ // Remember this shadow mode in the texture and the merged type.
+ argTex->getWritableType().getSampler().shadow = shadowMode;
+ samplerType.shadow = shadowMode;
+
+ texSymbol->switchId(newId);
+ }
+
+ txcombine->setType(TType(samplerType, EvqTemporary));
+ txcombine->setLoc(loc);
+
+ return txcombine;
+}
+
+// Return true if this a buffer type that has an associated counter buffer.
+bool HlslParseContext::hasStructBuffCounter(const TType& type) const
+{
+ switch (type.getQualifier().declaredBuiltIn) {
+ case EbvAppendConsume: // fall through...
+ case EbvRWStructuredBuffer: // ...
+ return true;
+ default:
+ return false; // the other structuredbuffer types do not have a counter.
+ }
+}
+
+void HlslParseContext::counterBufferType(const TSourceLoc& loc, TType& type)
+{
+ // Counter type
+ TType* counterType = new TType(EbtUint, EvqBuffer);
+ counterType->setFieldName(intermediate.implicitCounterName);
+
+ TTypeList* blockStruct = new TTypeList;
+ TTypeLoc member = { counterType, loc };
+ blockStruct->push_back(member);
+
+ TType blockType(blockStruct, "", counterType->getQualifier());
+ blockType.getQualifier().storage = EvqBuffer;
+
+ type.shallowCopy(blockType);
+ shareStructBufferType(type);
+}
+
+// declare counter for a structured buffer type
+void HlslParseContext::declareStructBufferCounter(const TSourceLoc& loc, const TType& bufferType, const TString& name)
+{
+ // Bail out if not a struct buffer
+ if (! isStructBufferType(bufferType))
+ return;
+
+ if (! hasStructBuffCounter(bufferType))
+ return;
+
+ TType blockType;
+ counterBufferType(loc, blockType);
+
+ TString* blockName = NewPoolTString(intermediate.addCounterBufferName(name).c_str());
+
+ // Counter buffer is not yet in use
+ structBufferCounter[*blockName] = false;
+
+ shareStructBufferType(blockType);
+ declareBlock(loc, blockType, blockName);
+}
+
+// return the counter that goes with a given structuredbuffer
+TIntermTyped* HlslParseContext::getStructBufferCounter(const TSourceLoc& loc, TIntermTyped* buffer)
+{
+ // Bail out if not a struct buffer
+ if (buffer == nullptr || ! isStructBufferType(buffer->getType()))
+ return nullptr;
+
+ const TString counterBlockName(intermediate.addCounterBufferName(buffer->getAsSymbolNode()->getName()));
+
+ // Mark the counter as being used
+ structBufferCounter[counterBlockName] = true;
+
+ TIntermTyped* counterVar = handleVariable(loc, &counterBlockName); // find the block structure
+ TIntermTyped* index = intermediate.addConstantUnion(0, loc); // index to counter inside block struct
+
+ TIntermTyped* counterMember = intermediate.addIndex(EOpIndexDirectStruct, counterVar, index, loc);
+ counterMember->setType(TType(EbtUint));
+ return counterMember;
+}
+
+//
+// Decompose structure buffer methods into AST
+//
+void HlslParseContext::decomposeStructBufferMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
+{
+ if (node == nullptr || node->getAsOperator() == nullptr || arguments == nullptr)
+ return;
+
+ const TOperator op = node->getAsOperator()->getOp();
+ TIntermAggregate* argAggregate = arguments->getAsAggregate();
+
+ // Buffer is the object upon which method is called, so always arg 0
+ TIntermTyped* bufferObj = nullptr;
+
+ // The parameters can be an aggregate, or just a the object as a symbol if there are no fn params.
+ if (argAggregate) {
+ if (argAggregate->getSequence().empty())
+ return;
+ if (argAggregate->getSequence()[0])
+ bufferObj = argAggregate->getSequence()[0]->getAsTyped();
+ } else {
+ bufferObj = arguments->getAsSymbolNode();
+ }
+
+ if (bufferObj == nullptr || bufferObj->getAsSymbolNode() == nullptr)
+ return;
+
+ // Some methods require a hidden internal counter, obtained via getStructBufferCounter().
+ // This lambda adds something to it and returns the old value.
+ const auto incDecCounter = [&](int incval) -> TIntermTyped* {
+ TIntermTyped* incrementValue = intermediate.addConstantUnion(static_cast<unsigned int>(incval), loc, true);
+ TIntermTyped* counter = getStructBufferCounter(loc, bufferObj); // obtain the counter member
+
+ if (counter == nullptr)
+ return nullptr;
+
+ TIntermAggregate* counterIncrement = new TIntermAggregate(EOpAtomicAdd);
+ counterIncrement->setType(TType(EbtUint, EvqTemporary));
+ counterIncrement->setLoc(loc);
+ counterIncrement->getSequence().push_back(counter);
+ counterIncrement->getSequence().push_back(incrementValue);
+
+ return counterIncrement;
+ };
+
+ // Index to obtain the runtime sized array out of the buffer.
+ TIntermTyped* argArray = indexStructBufferContent(loc, bufferObj);
+ if (argArray == nullptr)
+ return; // It might not be a struct buffer method.
+
+ switch (op) {
+ case EOpMethodLoad:
+ {
+ TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped()); // index
+
+ const TType& bufferType = bufferObj->getType();
+
+ const TBuiltInVariable builtInType = bufferType.getQualifier().declaredBuiltIn;
+
+ // Byte address buffers index in bytes (only multiples of 4 permitted... not so much a byte address
+ // buffer then, but that's what it calls itself.
+ const bool isByteAddressBuffer = (builtInType == EbvByteAddressBuffer ||
+ builtInType == EbvRWByteAddressBuffer);
+
+
+ if (isByteAddressBuffer)
+ argIndex = intermediate.addBinaryNode(EOpRightShift, argIndex,
+ intermediate.addConstantUnion(2, loc, true),
+ loc, TType(EbtInt));
+
+ // Index into the array to find the item being loaded.
+ const TOperator idxOp = (argIndex->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
+
+ node = intermediate.addIndex(idxOp, argArray, argIndex, loc);
+
+ const TType derefType(argArray->getType(), 0);
+ node->setType(derefType);
+ }
+
+ break;
+
+ case EOpMethodLoad2:
+ case EOpMethodLoad3:
+ case EOpMethodLoad4:
+ {
+ TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped()); // index
+
+ TOperator constructOp = EOpNull;
+ int size = 0;
+
+ switch (op) {
+ case EOpMethodLoad2: size = 2; constructOp = EOpConstructVec2; break;
+ case EOpMethodLoad3: size = 3; constructOp = EOpConstructVec3; break;
+ case EOpMethodLoad4: size = 4; constructOp = EOpConstructVec4; break;
+ default: assert(0);
+ }
+
+ TIntermTyped* body = nullptr;
+
+ // First, we'll store the address in a variable to avoid multiple shifts
+ // (we must convert the byte address to an item address)
+ TIntermTyped* byteAddrIdx = intermediate.addBinaryNode(EOpRightShift, argIndex,
+ intermediate.addConstantUnion(2, loc, true),
+ loc, TType(EbtInt));
+
+ TVariable* byteAddrSym = makeInternalVariable("byteAddrTemp", TType(EbtInt, EvqTemporary));
+ TIntermTyped* byteAddrIdxVar = intermediate.addSymbol(*byteAddrSym, loc);
+
+ body = intermediate.growAggregate(body, intermediate.addAssign(EOpAssign, byteAddrIdxVar, byteAddrIdx, loc));
+
+ TIntermTyped* vec = nullptr;
+
+ // These are only valid on (rw)byteaddressbuffers, so we can always perform the >>2
+ // address conversion.
+ for (int idx=0; idx<size; ++idx) {
+ TIntermTyped* offsetIdx = byteAddrIdxVar;
+
+ // add index offset
+ if (idx != 0)
+ offsetIdx = intermediate.addBinaryNode(EOpAdd, offsetIdx,
+ intermediate.addConstantUnion(idx, loc, true),
+ loc, TType(EbtInt));
+
+ const TOperator idxOp = (offsetIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect
+ : EOpIndexIndirect;
+
+ TIntermTyped* indexVal = intermediate.addIndex(idxOp, argArray, offsetIdx, loc);
+
+ TType derefType(argArray->getType(), 0);
+ derefType.getQualifier().makeTemporary();
+ indexVal->setType(derefType);
+
+ vec = intermediate.growAggregate(vec, indexVal);
+ }
+
+ vec->setType(TType(argArray->getBasicType(), EvqTemporary, size));
+ vec->getAsAggregate()->setOperator(constructOp);
+
+ body = intermediate.growAggregate(body, vec);
+ body->setType(vec->getType());
+ body->getAsAggregate()->setOperator(EOpSequence);
+
+ node = body;
+ }
+
+ break;
+
+ case EOpMethodStore:
+ case EOpMethodStore2:
+ case EOpMethodStore3:
+ case EOpMethodStore4:
+ {
+ TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped()); // index
+ TIntermTyped* argValue = argAggregate->getSequence()[2]->getAsTyped(); // value
+
+ // Index into the array to find the item being loaded.
+ // Byte address buffers index in bytes (only multiples of 4 permitted... not so much a byte address
+ // buffer then, but that's what it calls itself).
+
+ int size = 0;
+
+ switch (op) {
+ case EOpMethodStore: size = 1; break;
+ case EOpMethodStore2: size = 2; break;
+ case EOpMethodStore3: size = 3; break;
+ case EOpMethodStore4: size = 4; break;
+ default: assert(0);
+ }
+
+ TIntermAggregate* body = nullptr;
+
+ // First, we'll store the address in a variable to avoid multiple shifts
+ // (we must convert the byte address to an item address)
+ TIntermTyped* byteAddrIdx = intermediate.addBinaryNode(EOpRightShift, argIndex,
+ intermediate.addConstantUnion(2, loc, true), loc, TType(EbtInt));
+
+ TVariable* byteAddrSym = makeInternalVariable("byteAddrTemp", TType(EbtInt, EvqTemporary));
+ TIntermTyped* byteAddrIdxVar = intermediate.addSymbol(*byteAddrSym, loc);
+
+ body = intermediate.growAggregate(body, intermediate.addAssign(EOpAssign, byteAddrIdxVar, byteAddrIdx, loc));
+
+ for (int idx=0; idx<size; ++idx) {
+ TIntermTyped* offsetIdx = byteAddrIdxVar;
+ TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true);
+
+ // add index offset
+ if (idx != 0)
+ offsetIdx = intermediate.addBinaryNode(EOpAdd, offsetIdx, idxConst, loc, TType(EbtInt));
+
+ const TOperator idxOp = (offsetIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect
+ : EOpIndexIndirect;
+
+ TIntermTyped* lValue = intermediate.addIndex(idxOp, argArray, offsetIdx, loc);
+ const TType derefType(argArray->getType(), 0);
+ lValue->setType(derefType);
+
+ TIntermTyped* rValue;
+ if (size == 1) {
+ rValue = argValue;
+ } else {
+ rValue = intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc);
+ const TType indexType(argValue->getType(), 0);
+ rValue->setType(indexType);
+ }
+
+ TIntermTyped* assign = intermediate.addAssign(EOpAssign, lValue, rValue, loc);
+
+ body = intermediate.growAggregate(body, assign);
+ }
+
+ body->setOperator(EOpSequence);
+ node = body;
+ }
+
+ break;
+
+ case EOpMethodGetDimensions:
+ {
+ const int numArgs = (int)argAggregate->getSequence().size();
+ TIntermTyped* argNumItems = argAggregate->getSequence()[1]->getAsTyped(); // out num items
+ TIntermTyped* argStride = numArgs > 2 ? argAggregate->getSequence()[2]->getAsTyped() : nullptr; // out stride
+
+ TIntermAggregate* body = nullptr;
+
+ // Length output:
+ if (argArray->getType().isSizedArray()) {
+ const int length = argArray->getType().getOuterArraySize();
+ TIntermTyped* assign = intermediate.addAssign(EOpAssign, argNumItems,
+ intermediate.addConstantUnion(length, loc, true), loc);
+ body = intermediate.growAggregate(body, assign, loc);
+ } else {
+ TIntermTyped* lengthCall = intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, argArray,
+ argNumItems->getType());
+ TIntermTyped* assign = intermediate.addAssign(EOpAssign, argNumItems, lengthCall, loc);
+ body = intermediate.growAggregate(body, assign, loc);
+ }
+
+ // Stride output:
+ if (argStride != nullptr) {
+ int size;
+ int stride;
+ intermediate.getMemberAlignment(argArray->getType(), size, stride, argArray->getType().getQualifier().layoutPacking,
+ argArray->getType().getQualifier().layoutMatrix == ElmRowMajor);
+
+ TIntermTyped* assign = intermediate.addAssign(EOpAssign, argStride,
+ intermediate.addConstantUnion(stride, loc, true), loc);
+
+ body = intermediate.growAggregate(body, assign);
+ }
+
+ body->setOperator(EOpSequence);
+ node = body;
+ }
+
+ break;
+
+ case EOpInterlockedAdd:
+ case EOpInterlockedAnd:
+ case EOpInterlockedExchange:
+ case EOpInterlockedMax:
+ case EOpInterlockedMin:
+ case EOpInterlockedOr:
+ case EOpInterlockedXor:
+ case EOpInterlockedCompareExchange:
+ case EOpInterlockedCompareStore:
+ {
+ // We'll replace the first argument with the block dereference, and let
+ // downstream decomposition handle the rest.
+
+ TIntermSequence& sequence = argAggregate->getSequence();
+
+ TIntermTyped* argIndex = makeIntegerIndex(sequence[1]->getAsTyped()); // index
+ argIndex = intermediate.addBinaryNode(EOpRightShift, argIndex, intermediate.addConstantUnion(2, loc, true),
+ loc, TType(EbtInt));
+
+ const TOperator idxOp = (argIndex->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
+ TIntermTyped* element = intermediate.addIndex(idxOp, argArray, argIndex, loc);
+
+ const TType derefType(argArray->getType(), 0);
+ element->setType(derefType);
+
+ // Replace the numeric byte offset parameter with array reference.
+ sequence[1] = element;
+ sequence.erase(sequence.begin(), sequence.begin()+1);
+ }
+ break;
+
+ case EOpMethodIncrementCounter:
+ {
+ node = incDecCounter(1);
+ break;
+ }
+
+ case EOpMethodDecrementCounter:
+ {
+ TIntermTyped* preIncValue = incDecCounter(-1); // result is original value
+ node = intermediate.addBinaryNode(EOpAdd, preIncValue, intermediate.addConstantUnion(-1, loc, true), loc,
+ preIncValue->getType());
+ break;
+ }
+
+ case EOpMethodAppend:
+ {
+ TIntermTyped* oldCounter = incDecCounter(1);
+
+ TIntermTyped* lValue = intermediate.addIndex(EOpIndexIndirect, argArray, oldCounter, loc);
+ TIntermTyped* rValue = argAggregate->getSequence()[1]->getAsTyped();
+
+ const TType derefType(argArray->getType(), 0);
+ lValue->setType(derefType);
+
+ node = intermediate.addAssign(EOpAssign, lValue, rValue, loc);
+
+ break;
+ }
+
+ case EOpMethodConsume:
+ {
+ TIntermTyped* oldCounter = incDecCounter(-1);
+
+ TIntermTyped* newCounter = intermediate.addBinaryNode(EOpAdd, oldCounter,
+ intermediate.addConstantUnion(-1, loc, true), loc,
+ oldCounter->getType());
+
+ node = intermediate.addIndex(EOpIndexIndirect, argArray, newCounter, loc);
+
+ const TType derefType(argArray->getType(), 0);
+ node->setType(derefType);
+
+ break;
+ }
+
+ default:
+ break; // most pass through unchanged
+ }
+}
+
+// Create array of standard sample positions for given sample count.
+// TODO: remove when a real method to query sample pos exists in SPIR-V.
+TIntermConstantUnion* HlslParseContext::getSamplePosArray(int count)
+{
+ struct tSamplePos { float x, y; };
+
+ static const tSamplePos pos1[] = {
+ { 0.0/16.0, 0.0/16.0 },
+ };
+
+ // standard sample positions for 2, 4, 8, and 16 samples.
+ static const tSamplePos pos2[] = {
+ { 4.0/16.0, 4.0/16.0 }, {-4.0/16.0, -4.0/16.0 },
+ };
+
+ static const tSamplePos pos4[] = {
+ {-2.0/16.0, -6.0/16.0 }, { 6.0/16.0, -2.0/16.0 }, {-6.0/16.0, 2.0/16.0 }, { 2.0/16.0, 6.0/16.0 },
+ };
+
+ static const tSamplePos pos8[] = {
+ { 1.0/16.0, -3.0/16.0 }, {-1.0/16.0, 3.0/16.0 }, { 5.0/16.0, 1.0/16.0 }, {-3.0/16.0, -5.0/16.0 },
+ {-5.0/16.0, 5.0/16.0 }, {-7.0/16.0, -1.0/16.0 }, { 3.0/16.0, 7.0/16.0 }, { 7.0/16.0, -7.0/16.0 },
+ };
+
+ static const tSamplePos pos16[] = {
+ { 1.0/16.0, 1.0/16.0 }, {-1.0/16.0, -3.0/16.0 }, {-3.0/16.0, 2.0/16.0 }, { 4.0/16.0, -1.0/16.0 },
+ {-5.0/16.0, -2.0/16.0 }, { 2.0/16.0, 5.0/16.0 }, { 5.0/16.0, 3.0/16.0 }, { 3.0/16.0, -5.0/16.0 },
+ {-2.0/16.0, 6.0/16.0 }, { 0.0/16.0, -7.0/16.0 }, {-4.0/16.0, -6.0/16.0 }, {-6.0/16.0, 4.0/16.0 },
+ {-8.0/16.0, 0.0/16.0 }, { 7.0/16.0, -4.0/16.0 }, { 6.0/16.0, 7.0/16.0 }, {-7.0/16.0, -8.0/16.0 },
+ };
+
+ const tSamplePos* sampleLoc = nullptr;
+ int numSamples = count;
+
+ switch (count) {
+ case 2: sampleLoc = pos2; break;
+ case 4: sampleLoc = pos4; break;
+ case 8: sampleLoc = pos8; break;
+ case 16: sampleLoc = pos16; break;
+ default:
+ sampleLoc = pos1;
+ numSamples = 1;
+ }
+
+ TConstUnionArray* values = new TConstUnionArray(numSamples*2);
+
+ for (int pos=0; pos<count; ++pos) {
+ TConstUnion x, y;
+ x.setDConst(sampleLoc[pos].x);
+ y.setDConst(sampleLoc[pos].y);
+
+ (*values)[pos*2+0] = x;
+ (*values)[pos*2+1] = y;
+ }
+
+ TType retType(EbtFloat, EvqConst, 2);
+
+ if (numSamples != 1) {
+ TArraySizes* arraySizes = new TArraySizes;
+ arraySizes->addInnerSize(numSamples);
+ retType.transferArraySizes(arraySizes);
+ }
+
+ return new TIntermConstantUnion(*values, retType);
+}
+
+//
+// Decompose DX9 and DX10 sample intrinsics & object methods into AST
+//
+void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
+{
+ if (node == nullptr || !node->getAsOperator())
+ return;
+
+ // Sampler return must always be a vec4, but we can construct a shorter vector or a structure from it.
+ const auto convertReturn = [&loc, &node, this](TIntermTyped* result, const TSampler& sampler) -> TIntermTyped* {
+ result->setType(TType(node->getType().getBasicType(), EvqTemporary, node->getVectorSize()));
+
+ TIntermTyped* convertedResult = nullptr;
+
+ TType retType;
+ getTextureReturnType(sampler, retType);
+
+ if (retType.isStruct()) {
+ // For type convenience, conversionAggregate points to the convertedResult (we know it's an aggregate here)
+ TIntermAggregate* conversionAggregate = new TIntermAggregate;
+ convertedResult = conversionAggregate;
+
+ // Convert vector output to return structure. We will need a temp symbol to copy the results to.
+ TVariable* structVar = makeInternalVariable("@sampleStructTemp", retType);
+
+ // We also need a temp symbol to hold the result of the texture. We don't want to re-fetch the
+ // sample each time we'll index into the result, so we'll copy to this, and index into the copy.
+ TVariable* sampleShadow = makeInternalVariable("@sampleResultShadow", result->getType());
+
+ // Initial copy from texture to our sample result shadow.
+ TIntermTyped* shadowCopy = intermediate.addAssign(EOpAssign, intermediate.addSymbol(*sampleShadow, loc),
+ result, loc);
+
+ conversionAggregate->getSequence().push_back(shadowCopy);
+
+ unsigned vec4Pos = 0;
+
+ for (unsigned m = 0; m < unsigned(retType.getStruct()->size()); ++m) {
+ const TType memberType(retType, m); // dereferenced type of the member we're about to assign.
+
+ // Check for bad struct members. This should have been caught upstream. Complain, because
+ // wwe don't know what to do with it. This algorithm could be generalized to handle
+ // other things, e.g, sub-structures, but HLSL doesn't allow them.
+ if (!memberType.isVector() && !memberType.isScalar()) {
+ error(loc, "expected: scalar or vector type in texture structure", "", "");
+ return nullptr;
+ }
+
+ // Index into the struct variable to find the member to assign.
+ TIntermTyped* structMember = intermediate.addIndex(EOpIndexDirectStruct,
+ intermediate.addSymbol(*structVar, loc),
+ intermediate.addConstantUnion(m, loc), loc);
+
+ structMember->setType(memberType);
+
+ // Assign each component of (possible) vector in struct member.
+ for (int component = 0; component < memberType.getVectorSize(); ++component) {
+ TIntermTyped* vec4Member = intermediate.addIndex(EOpIndexDirect,
+ intermediate.addSymbol(*sampleShadow, loc),
+ intermediate.addConstantUnion(vec4Pos++, loc), loc);
+ vec4Member->setType(TType(memberType.getBasicType(), EvqTemporary, 1));
+
+ TIntermTyped* memberAssign = nullptr;
+
+ if (memberType.isVector()) {
+ // Vector member: we need to create an access chain to the vector component.
+
+ TIntermTyped* structVecComponent = intermediate.addIndex(EOpIndexDirect, structMember,
+ intermediate.addConstantUnion(component, loc), loc);
+
+ memberAssign = intermediate.addAssign(EOpAssign, structVecComponent, vec4Member, loc);
+ } else {
+ // Scalar member: we can assign to it directly.
+ memberAssign = intermediate.addAssign(EOpAssign, structMember, vec4Member, loc);
+ }
+
+
+ conversionAggregate->getSequence().push_back(memberAssign);
+ }
+ }
+
+ // Add completed variable so the expression results in the whole struct value we just built.
+ conversionAggregate->getSequence().push_back(intermediate.addSymbol(*structVar, loc));
+
+ // Make it a sequence.
+ intermediate.setAggregateOperator(conversionAggregate, EOpSequence, retType, loc);
+ } else {
+ // vector clamp the output if template vector type is smaller than sample result.
+ if (retType.getVectorSize() < node->getVectorSize()) {
+ // Too many components. Construct shorter vector from it.
+ const TOperator op = intermediate.mapTypeToConstructorOp(retType);
+
+ convertedResult = constructBuiltIn(retType, op, result, loc, false);
+ } else {
+ // Enough components. Use directly.
+ convertedResult = result;
+ }
+ }
+
+ convertedResult->setLoc(loc);
+ return convertedResult;
+ };
+
+ const TOperator op = node->getAsOperator()->getOp();
+ const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
+
+ // Bail out if not a sampler method.
+ // Note though this is odd to do before checking the op, because the op
+ // could be something that takes the arguments, and the function in question
+ // takes the result of the op. So, this is not the final word.
+ if (arguments != nullptr) {
+ if (argAggregate == nullptr) {
+ if (arguments->getAsTyped()->getBasicType() != EbtSampler)
+ return;
+ } else {
+ if (argAggregate->getSequence().size() == 0 ||
+ argAggregate->getSequence()[0] == nullptr ||
+ argAggregate->getSequence()[0]->getAsTyped()->getBasicType() != EbtSampler)
+ return;
+ }
+ }
+
+ switch (op) {
+ // **** DX9 intrinsics: ****
+ case EOpTexture:
+ {
+ // Texture with ddx & ddy is really gradient form in HLSL
+ if (argAggregate->getSequence().size() == 4)
+ node->getAsAggregate()->setOperator(EOpTextureGrad);
+
+ break;
+ }
+ case EOpTextureLod: //is almost EOpTextureBias (only args & operations are different)
+ {
+ TIntermTyped *argSamp = argAggregate->getSequence()[0]->getAsTyped(); // sampler
+ TIntermTyped *argCoord = argAggregate->getSequence()[1]->getAsTyped(); // coord
+
+ assert(argCoord->getVectorSize() == 4);
+ TIntermTyped *w = intermediate.addConstantUnion(3, loc, true);
+ TIntermTyped *argLod = intermediate.addIndex(EOpIndexDirect, argCoord, w, loc);
+
+ TOperator constructOp = EOpNull;
+ const TSampler &sampler = argSamp->getType().getSampler();
+ int coordSize = 0;
+
+ switch (sampler.dim)
+ {
+ case Esd1D: constructOp = EOpConstructFloat; coordSize = 1; break; // 1D
+ case Esd2D: constructOp = EOpConstructVec2; coordSize = 2; break; // 2D
+ case Esd3D: constructOp = EOpConstructVec3; coordSize = 3; break; // 3D
+ case EsdCube: constructOp = EOpConstructVec3; coordSize = 3; break; // also 3D
+ default:
+ break;
+ }
+
+ TIntermAggregate *constructCoord = new TIntermAggregate(constructOp);
+ constructCoord->getSequence().push_back(argCoord);
+ constructCoord->setLoc(loc);
+ constructCoord->setType(TType(argCoord->getBasicType(), EvqTemporary, coordSize));
+
+ TIntermAggregate *tex = new TIntermAggregate(EOpTextureLod);
+ tex->getSequence().push_back(argSamp); // sampler
+ tex->getSequence().push_back(constructCoord); // coordinate
+ tex->getSequence().push_back(argLod); // lod
+
+ node = convertReturn(tex, sampler);
+
+ break;
+ }
+
+ case EOpTextureBias:
+ {
+ TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // sampler
+ TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // coord
+
+ // HLSL puts bias in W component of coordinate. We extract it and add it to
+ // the argument list, instead
+ TIntermTyped* w = intermediate.addConstantUnion(3, loc, true);
+ TIntermTyped* bias = intermediate.addIndex(EOpIndexDirect, arg1, w, loc);
+
+ TOperator constructOp = EOpNull;
+ const TSampler& sampler = arg0->getType().getSampler();
+
+ switch (sampler.dim) {
+ case Esd1D: constructOp = EOpConstructFloat; break; // 1D
+ case Esd2D: constructOp = EOpConstructVec2; break; // 2D
+ case Esd3D: constructOp = EOpConstructVec3; break; // 3D
+ case EsdCube: constructOp = EOpConstructVec3; break; // also 3D
+ default: break;
+ }
+
+ TIntermAggregate* constructCoord = new TIntermAggregate(constructOp);
+ constructCoord->getSequence().push_back(arg1);
+ constructCoord->setLoc(loc);
+
+ // The input vector should never be less than 2, since there's always a bias.
+ // The max is for safety, and should be a no-op.
+ constructCoord->setType(TType(arg1->getBasicType(), EvqTemporary, std::max(arg1->getVectorSize() - 1, 0)));
+
+ TIntermAggregate* tex = new TIntermAggregate(EOpTexture);
+ tex->getSequence().push_back(arg0); // sampler
+ tex->getSequence().push_back(constructCoord); // coordinate
+ tex->getSequence().push_back(bias); // bias
+
+ node = convertReturn(tex, sampler);
+
+ break;
+ }
+
+ // **** DX10 methods: ****
+ case EOpMethodSample: // fall through
+ case EOpMethodSampleBias: // ...
+ {
+ TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
+ TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
+ TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
+ TIntermTyped* argBias = nullptr;
+ TIntermTyped* argOffset = nullptr;
+ const TSampler& sampler = argTex->getType().getSampler();
+
+ int nextArg = 3;
+
+ if (op == EOpMethodSampleBias) // SampleBias has a bias arg
+ argBias = argAggregate->getSequence()[nextArg++]->getAsTyped();
+
+ TOperator textureOp = EOpTexture;
+
+ if ((int)argAggregate->getSequence().size() == (nextArg+1)) { // last parameter is offset form
+ textureOp = EOpTextureOffset;
+ argOffset = argAggregate->getSequence()[nextArg++]->getAsTyped();
+ }
+
+ TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
+
+ TIntermAggregate* txsample = new TIntermAggregate(textureOp);
+ txsample->getSequence().push_back(txcombine);
+ txsample->getSequence().push_back(argCoord);
+
+ if (argBias != nullptr)
+ txsample->getSequence().push_back(argBias);
+
+ if (argOffset != nullptr)
+ txsample->getSequence().push_back(argOffset);
+
+ node = convertReturn(txsample, sampler);
+
+ break;
+ }
+
+ case EOpMethodSampleGrad: // ...
+ {
+ TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
+ TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
+ TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
+ TIntermTyped* argDDX = argAggregate->getSequence()[3]->getAsTyped();
+ TIntermTyped* argDDY = argAggregate->getSequence()[4]->getAsTyped();
+ TIntermTyped* argOffset = nullptr;
+ const TSampler& sampler = argTex->getType().getSampler();
+
+ TOperator textureOp = EOpTextureGrad;
+
+ if (argAggregate->getSequence().size() == 6) { // last parameter is offset form
+ textureOp = EOpTextureGradOffset;
+ argOffset = argAggregate->getSequence()[5]->getAsTyped();
+ }
+
+ TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
+
+ TIntermAggregate* txsample = new TIntermAggregate(textureOp);
+ txsample->getSequence().push_back(txcombine);
+ txsample->getSequence().push_back(argCoord);
+ txsample->getSequence().push_back(argDDX);
+ txsample->getSequence().push_back(argDDY);
+
+ if (argOffset != nullptr)
+ txsample->getSequence().push_back(argOffset);
+
+ node = convertReturn(txsample, sampler);
+
+ break;
+ }
+
+ case EOpMethodGetDimensions:
+ {
+ // AST returns a vector of results, which we break apart component-wise into
+ // separate values to assign to the HLSL method's outputs, ala:
+ // tx . GetDimensions(width, height);
+ // float2 sizeQueryTemp = EOpTextureQuerySize
+ // width = sizeQueryTemp.X;
+ // height = sizeQueryTemp.Y;
+
+ TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
+ const TType& texType = argTex->getType();
+
+ assert(texType.getBasicType() == EbtSampler);
+
+ const TSampler& sampler = texType.getSampler();
+ const TSamplerDim dim = sampler.dim;
+ const bool isImage = sampler.isImage();
+ const bool isMs = sampler.isMultiSample();
+ const int numArgs = (int)argAggregate->getSequence().size();
+
+ int numDims = 0;
+
+ switch (dim) {
+ case Esd1D: numDims = 1; break; // W
+ case Esd2D: numDims = 2; break; // W, H
+ case Esd3D: numDims = 3; break; // W, H, D
+ case EsdCube: numDims = 2; break; // W, H (cube)
+ case EsdBuffer: numDims = 1; break; // W (buffers)
+ case EsdRect: numDims = 2; break; // W, H (rect)
+ default:
+ assert(0 && "unhandled texture dimension");
+ }
+
+ // Arrayed adds another dimension for the number of array elements
+ if (sampler.isArrayed())
+ ++numDims;
+
+ // Establish whether the method itself is querying mip levels. This can be false even
+ // if the underlying query requires a MIP level, due to the available HLSL method overloads.
+ const bool mipQuery = (numArgs > (numDims + 1 + (isMs ? 1 : 0)));
+
+ // Establish whether we must use the LOD form of query (even if the method did not supply a mip level to query).
+ // True if:
+ // 1. 1D/2D/3D/Cube AND multisample==0 AND NOT image (those can be sent to the non-LOD query)
+ // or,
+ // 2. There is a LOD (because the non-LOD query cannot be used in that case, per spec)
+ const bool mipRequired =
+ ((dim == Esd1D || dim == Esd2D || dim == Esd3D || dim == EsdCube) && !isMs && !isImage) || // 1...
+ mipQuery; // 2...
+
+ // AST assumes integer return. Will be converted to float if required.
+ TIntermAggregate* sizeQuery = new TIntermAggregate(isImage ? EOpImageQuerySize : EOpTextureQuerySize);
+ sizeQuery->getSequence().push_back(argTex);
+
+ // If we're building an LOD query, add the LOD.
+ if (mipRequired) {
+ // If the base HLSL query had no MIP level given, use level 0.
+ TIntermTyped* queryLod = mipQuery ? argAggregate->getSequence()[1]->getAsTyped() :
+ intermediate.addConstantUnion(0, loc, true);
+ sizeQuery->getSequence().push_back(queryLod);
+ }
+
+ sizeQuery->setType(TType(EbtUint, EvqTemporary, numDims));
+ sizeQuery->setLoc(loc);
+
+ // Return value from size query
+ TVariable* tempArg = makeInternalVariable("sizeQueryTemp", sizeQuery->getType());
+ tempArg->getWritableType().getQualifier().makeTemporary();
+ TIntermTyped* sizeQueryAssign = intermediate.addAssign(EOpAssign,
+ intermediate.addSymbol(*tempArg, loc),
+ sizeQuery, loc);
+
+ // Compound statement for assigning outputs
+ TIntermAggregate* compoundStatement = intermediate.makeAggregate(sizeQueryAssign, loc);
+ // Index of first output parameter
+ const int outParamBase = mipQuery ? 2 : 1;
+
+ for (int compNum = 0; compNum < numDims; ++compNum) {
+ TIntermTyped* indexedOut = nullptr;
+ TIntermSymbol* sizeQueryReturn = intermediate.addSymbol(*tempArg, loc);
+
+ if (numDims > 1) {
+ TIntermTyped* component = intermediate.addConstantUnion(compNum, loc, true);
+ indexedOut = intermediate.addIndex(EOpIndexDirect, sizeQueryReturn, component, loc);
+ indexedOut->setType(TType(EbtUint, EvqTemporary, 1));
+ indexedOut->setLoc(loc);
+ } else {
+ indexedOut = sizeQueryReturn;
+ }
+
+ TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + compNum]->getAsTyped();
+ TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, indexedOut, loc);
+
+ compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
+ }
+
+ // handle mip level parameter
+ if (mipQuery) {
+ TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + numDims]->getAsTyped();
+
+ TIntermAggregate* levelsQuery = new TIntermAggregate(EOpTextureQueryLevels);
+ levelsQuery->getSequence().push_back(argTex);
+ levelsQuery->setType(TType(EbtUint, EvqTemporary, 1));
+ levelsQuery->setLoc(loc);
+
+ TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, levelsQuery, loc);
+ compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
+ }
+
+ // 2DMS formats query # samples, which needs a different query op
+ if (sampler.isMultiSample()) {
+ TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + numDims]->getAsTyped();
+
+ TIntermAggregate* samplesQuery = new TIntermAggregate(EOpImageQuerySamples);
+ samplesQuery->getSequence().push_back(argTex);
+ samplesQuery->setType(TType(EbtUint, EvqTemporary, 1));
+ samplesQuery->setLoc(loc);
+
+ TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, samplesQuery, loc);
+ compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
+ }
+
+ compoundStatement->setOperator(EOpSequence);
+ compoundStatement->setLoc(loc);
+ compoundStatement->setType(TType(EbtVoid));
+
+ node = compoundStatement;
+
+ break;
+ }
+
+ case EOpMethodSampleCmp: // fall through...
+ case EOpMethodSampleCmpLevelZero:
+ {
+ TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
+ TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
+ TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
+ TIntermTyped* argCmpVal = argAggregate->getSequence()[3]->getAsTyped();
+ TIntermTyped* argOffset = nullptr;
+
+ // Sampler argument should be a sampler.
+ if (argSamp->getType().getBasicType() != EbtSampler) {
+ error(loc, "expected: sampler type", "", "");
+ return;
+ }
+
+ // Sampler should be a SamplerComparisonState
+ if (! argSamp->getType().getSampler().isShadow()) {
+ error(loc, "expected: SamplerComparisonState", "", "");
+ return;
+ }
+
+ // optional offset value
+ if (argAggregate->getSequence().size() > 4)
+ argOffset = argAggregate->getSequence()[4]->getAsTyped();
+
+ const int coordDimWithCmpVal = argCoord->getType().getVectorSize() + 1; // +1 for cmp
+
+ // AST wants comparison value as one of the texture coordinates
+ TOperator constructOp = EOpNull;
+ switch (coordDimWithCmpVal) {
+ // 1D can't happen: there's always at least 1 coordinate dimension + 1 cmp val
+ case 2: constructOp = EOpConstructVec2; break;
+ case 3: constructOp = EOpConstructVec3; break;
+ case 4: constructOp = EOpConstructVec4; break;
+ case 5: constructOp = EOpConstructVec4; break; // cubeArrayShadow, cmp value is separate arg.
+ default: assert(0); break;
+ }
+
+ TIntermAggregate* coordWithCmp = new TIntermAggregate(constructOp);
+ coordWithCmp->getSequence().push_back(argCoord);
+ if (coordDimWithCmpVal != 5) // cube array shadow is special.
+ coordWithCmp->getSequence().push_back(argCmpVal);
+ coordWithCmp->setLoc(loc);
+ coordWithCmp->setType(TType(argCoord->getBasicType(), EvqTemporary, std::min(coordDimWithCmpVal, 4)));
+
+ TOperator textureOp = (op == EOpMethodSampleCmpLevelZero ? EOpTextureLod : EOpTexture);
+ if (argOffset != nullptr)
+ textureOp = (op == EOpMethodSampleCmpLevelZero ? EOpTextureLodOffset : EOpTextureOffset);
+
+ // Create combined sampler & texture op
+ TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
+ TIntermAggregate* txsample = new TIntermAggregate(textureOp);
+ txsample->getSequence().push_back(txcombine);
+ txsample->getSequence().push_back(coordWithCmp);
+
+ if (coordDimWithCmpVal == 5) // cube array shadow is special: cmp val follows coord.
+ txsample->getSequence().push_back(argCmpVal);
+
+ // the LevelZero form uses 0 as an explicit LOD
+ if (op == EOpMethodSampleCmpLevelZero)
+ txsample->getSequence().push_back(intermediate.addConstantUnion(0.0, EbtFloat, loc, true));
+
+ // Add offset if present
+ if (argOffset != nullptr)
+ txsample->getSequence().push_back(argOffset);
+
+ txsample->setType(node->getType());
+ txsample->setLoc(loc);
+ node = txsample;
+
+ break;
+ }
+
+ case EOpMethodLoad:
+ {
+ TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
+ TIntermTyped* argCoord = argAggregate->getSequence()[1]->getAsTyped();
+ TIntermTyped* argOffset = nullptr;
+ TIntermTyped* lodComponent = nullptr;
+ TIntermTyped* coordSwizzle = nullptr;
+
+ const TSampler& sampler = argTex->getType().getSampler();
+ const bool isMS = sampler.isMultiSample();
+ const bool isBuffer = sampler.dim == EsdBuffer;
+ const bool isImage = sampler.isImage();
+ const TBasicType coordBaseType = argCoord->getType().getBasicType();
+
+ // Last component of coordinate is the mip level, for non-MS. we separate them here:
+ if (isMS || isBuffer || isImage) {
+ // MS, Buffer, and Image have no LOD
+ coordSwizzle = argCoord;
+ } else {
+ // Extract coordinate
+ int swizzleSize = argCoord->getType().getVectorSize() - (isMS ? 0 : 1);
+ TSwizzleSelectors<TVectorSelector> coordFields;
+ for (int i = 0; i < swizzleSize; ++i)
+ coordFields.push_back(i);
+ TIntermTyped* coordIdx = intermediate.addSwizzle(coordFields, loc);
+ coordSwizzle = intermediate.addIndex(EOpVectorSwizzle, argCoord, coordIdx, loc);
+ coordSwizzle->setType(TType(coordBaseType, EvqTemporary, coordFields.size()));
+
+ // Extract LOD
+ TIntermTyped* lodIdx = intermediate.addConstantUnion(coordFields.size(), loc, true);
+ lodComponent = intermediate.addIndex(EOpIndexDirect, argCoord, lodIdx, loc);
+ lodComponent->setType(TType(coordBaseType, EvqTemporary, 1));
+ }
+
+ const int numArgs = (int)argAggregate->getSequence().size();
+ const bool hasOffset = ((!isMS && numArgs == 3) || (isMS && numArgs == 4));
+
+ // Create texel fetch
+ const TOperator fetchOp = (isImage ? EOpImageLoad :
+ hasOffset ? EOpTextureFetchOffset :
+ EOpTextureFetch);
+ TIntermAggregate* txfetch = new TIntermAggregate(fetchOp);
+
+ // Build up the fetch
+ txfetch->getSequence().push_back(argTex);
+ txfetch->getSequence().push_back(coordSwizzle);
+
+ if (isMS) {
+ // add 2DMS sample index
+ TIntermTyped* argSampleIdx = argAggregate->getSequence()[2]->getAsTyped();
+ txfetch->getSequence().push_back(argSampleIdx);
+ } else if (isBuffer) {
+ // Nothing else to do for buffers.
+ } else if (isImage) {
+ // Nothing else to do for images.
+ } else {
+ // 2DMS and buffer have no LOD, but everything else does.
+ txfetch->getSequence().push_back(lodComponent);
+ }
+
+ // Obtain offset arg, if there is one.
+ if (hasOffset) {
+ const int offsetPos = (isMS ? 3 : 2);
+ argOffset = argAggregate->getSequence()[offsetPos]->getAsTyped();
+ txfetch->getSequence().push_back(argOffset);
+ }
+
+ node = convertReturn(txfetch, sampler);
+
+ break;
+ }
+
+ case EOpMethodSampleLevel:
+ {
+ TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
+ TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
+ TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
+ TIntermTyped* argLod = argAggregate->getSequence()[3]->getAsTyped();
+ TIntermTyped* argOffset = nullptr;
+ const TSampler& sampler = argTex->getType().getSampler();
+
+ const int numArgs = (int)argAggregate->getSequence().size();
+
+ if (numArgs == 5) // offset, if present
+ argOffset = argAggregate->getSequence()[4]->getAsTyped();
+
+ const TOperator textureOp = (argOffset == nullptr ? EOpTextureLod : EOpTextureLodOffset);
+ TIntermAggregate* txsample = new TIntermAggregate(textureOp);
+
+ TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
+
+ txsample->getSequence().push_back(txcombine);
+ txsample->getSequence().push_back(argCoord);
+ txsample->getSequence().push_back(argLod);
+
+ if (argOffset != nullptr)
+ txsample->getSequence().push_back(argOffset);
+
+ node = convertReturn(txsample, sampler);
+
+ break;
+ }
+
+ case EOpMethodGather:
+ {
+ TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
+ TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
+ TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
+ TIntermTyped* argOffset = nullptr;
+
+ // Offset is optional
+ if (argAggregate->getSequence().size() > 3)
+ argOffset = argAggregate->getSequence()[3]->getAsTyped();
+
+ const TOperator textureOp = (argOffset == nullptr ? EOpTextureGather : EOpTextureGatherOffset);
+ TIntermAggregate* txgather = new TIntermAggregate(textureOp);
+
+ TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
+
+ txgather->getSequence().push_back(txcombine);
+ txgather->getSequence().push_back(argCoord);
+ // Offset if not given is implicitly channel 0 (red)
+
+ if (argOffset != nullptr)
+ txgather->getSequence().push_back(argOffset);
+
+ txgather->setType(node->getType());
+ txgather->setLoc(loc);
+ node = txgather;
+
+ break;
+ }
+
+ case EOpMethodGatherRed: // fall through...
+ case EOpMethodGatherGreen: // ...
+ case EOpMethodGatherBlue: // ...
+ case EOpMethodGatherAlpha: // ...
+ case EOpMethodGatherCmpRed: // ...
+ case EOpMethodGatherCmpGreen: // ...
+ case EOpMethodGatherCmpBlue: // ...
+ case EOpMethodGatherCmpAlpha: // ...
+ {
+ int channel = 0; // the channel we are gathering
+ int cmpValues = 0; // 1 if there is a compare value (handier than a bool below)
+
+ switch (op) {
+ case EOpMethodGatherCmpRed: cmpValues = 1; // fall through
+ case EOpMethodGatherRed: channel = 0; break;
+ case EOpMethodGatherCmpGreen: cmpValues = 1; // fall through
+ case EOpMethodGatherGreen: channel = 1; break;
+ case EOpMethodGatherCmpBlue: cmpValues = 1; // fall through
+ case EOpMethodGatherBlue: channel = 2; break;
+ case EOpMethodGatherCmpAlpha: cmpValues = 1; // fall through
+ case EOpMethodGatherAlpha: channel = 3; break;
+ default: assert(0); break;
+ }
+
+ // For now, we have nothing to map the component-wise comparison forms
+ // to, because neither GLSL nor SPIR-V has such an opcode. Issue an
+ // unimplemented error instead. Most of the machinery is here if that
+ // should ever become available. However, red can be passed through
+ // to OpImageDrefGather. G/B/A cannot, because that opcode does not
+ // accept a component.
+ if (cmpValues != 0 && op != EOpMethodGatherCmpRed) {
+ error(loc, "unimplemented: component-level gather compare", "", "");
+ return;
+ }
+
+ int arg = 0;
+
+ TIntermTyped* argTex = argAggregate->getSequence()[arg++]->getAsTyped();
+ TIntermTyped* argSamp = argAggregate->getSequence()[arg++]->getAsTyped();
+ TIntermTyped* argCoord = argAggregate->getSequence()[arg++]->getAsTyped();
+ TIntermTyped* argOffset = nullptr;
+ TIntermTyped* argOffsets[4] = { nullptr, nullptr, nullptr, nullptr };
+ // TIntermTyped* argStatus = nullptr; // TODO: residency
+ TIntermTyped* argCmp = nullptr;
+
+ const TSamplerDim dim = argTex->getType().getSampler().dim;
+
+ const int argSize = (int)argAggregate->getSequence().size();
+ bool hasStatus = (argSize == (5+cmpValues) || argSize == (8+cmpValues));
+ bool hasOffset1 = false;
+ bool hasOffset4 = false;
+
+ // Sampler argument should be a sampler.
+ if (argSamp->getType().getBasicType() != EbtSampler) {
+ error(loc, "expected: sampler type", "", "");
+ return;
+ }
+
+ // Cmp forms require SamplerComparisonState
+ if (cmpValues > 0 && ! argSamp->getType().getSampler().isShadow()) {
+ error(loc, "expected: SamplerComparisonState", "", "");
+ return;
+ }
+
+ // Only 2D forms can have offsets. Discover if we have 0, 1 or 4 offsets.
+ if (dim == Esd2D) {
+ hasOffset1 = (argSize == (4+cmpValues) || argSize == (5+cmpValues));
+ hasOffset4 = (argSize == (7+cmpValues) || argSize == (8+cmpValues));
+ }
+
+ assert(!(hasOffset1 && hasOffset4));
+
+ TOperator textureOp = EOpTextureGather;
+
+ // Compare forms have compare value
+ if (cmpValues != 0)
+ argCmp = argOffset = argAggregate->getSequence()[arg++]->getAsTyped();
+
+ // Some forms have single offset
+ if (hasOffset1) {
+ textureOp = EOpTextureGatherOffset; // single offset form
+ argOffset = argAggregate->getSequence()[arg++]->getAsTyped();
+ }
+
+ // Some forms have 4 gather offsets
+ if (hasOffset4) {
+ textureOp = EOpTextureGatherOffsets; // note plural, for 4 offset form
+ for (int offsetNum = 0; offsetNum < 4; ++offsetNum)
+ argOffsets[offsetNum] = argAggregate->getSequence()[arg++]->getAsTyped();
+ }
+
+ // Residency status
+ if (hasStatus) {
+ // argStatus = argAggregate->getSequence()[arg++]->getAsTyped();
+ error(loc, "unimplemented: residency status", "", "");
+ return;
+ }
+
+ TIntermAggregate* txgather = new TIntermAggregate(textureOp);
+ TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
+
+ TIntermTyped* argChannel = intermediate.addConstantUnion(channel, loc, true);
+
+ txgather->getSequence().push_back(txcombine);
+ txgather->getSequence().push_back(argCoord);
+
+ // AST wants an array of 4 offsets, where HLSL has separate args. Here
+ // we construct an array from the separate args.
+ if (hasOffset4) {
+ TType arrayType(EbtInt, EvqTemporary, 2);
+ TArraySizes* arraySizes = new TArraySizes;
+ arraySizes->addInnerSize(4);
+ arrayType.transferArraySizes(arraySizes);
+
+ TIntermAggregate* initList = new TIntermAggregate(EOpNull);
+
+ for (int offsetNum = 0; offsetNum < 4; ++offsetNum)
+ initList->getSequence().push_back(argOffsets[offsetNum]);
+
+ argOffset = addConstructor(loc, initList, arrayType);
+ }
+
+ // Add comparison value if we have one
+ if (argCmp != nullptr)
+ txgather->getSequence().push_back(argCmp);
+
+ // Add offset (either 1, or an array of 4) if we have one
+ if (argOffset != nullptr)
+ txgather->getSequence().push_back(argOffset);
+
+ // Add channel value if the sampler is not shadow
+ if (! argSamp->getType().getSampler().isShadow())
+ txgather->getSequence().push_back(argChannel);
+
+ txgather->setType(node->getType());
+ txgather->setLoc(loc);
+ node = txgather;
+
+ break;
+ }
+
+ case EOpMethodCalculateLevelOfDetail:
+ case EOpMethodCalculateLevelOfDetailUnclamped:
+ {
+ TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
+ TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped();
+ TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped();
+
+ TIntermAggregate* txquerylod = new TIntermAggregate(EOpTextureQueryLod);
+
+ TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp);
+ txquerylod->getSequence().push_back(txcombine);
+ txquerylod->getSequence().push_back(argCoord);
+
+ TIntermTyped* lodComponent = intermediate.addConstantUnion(
+ op == EOpMethodCalculateLevelOfDetail ? 0 : 1,
+ loc, true);
+ TIntermTyped* lodComponentIdx = intermediate.addIndex(EOpIndexDirect, txquerylod, lodComponent, loc);
+ lodComponentIdx->setType(TType(EbtFloat, EvqTemporary, 1));
+ node = lodComponentIdx;
+
+ break;
+ }
+
+ case EOpMethodGetSamplePosition:
+ {
+ // TODO: this entire decomposition exists because there is not yet a way to query
+ // the sample position directly through SPIR-V. Instead, we return fixed sample
+ // positions for common cases. *** If the sample positions are set differently,
+ // this will be wrong. ***
+
+ TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped();
+ TIntermTyped* argSampIdx = argAggregate->getSequence()[1]->getAsTyped();
+
+ TIntermAggregate* samplesQuery = new TIntermAggregate(EOpImageQuerySamples);
+ samplesQuery->getSequence().push_back(argTex);
+ samplesQuery->setType(TType(EbtUint, EvqTemporary, 1));
+ samplesQuery->setLoc(loc);
+
+ TIntermAggregate* compoundStatement = nullptr;
+
+ TVariable* outSampleCount = makeInternalVariable("@sampleCount", TType(EbtUint));
+ outSampleCount->getWritableType().getQualifier().makeTemporary();
+ TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, intermediate.addSymbol(*outSampleCount, loc),
+ samplesQuery, loc);
+ compoundStatement = intermediate.growAggregate(compoundStatement, compAssign);
+
+ TIntermTyped* idxtest[4];
+
+ // Create tests against 2, 4, 8, and 16 sample values
+ int count = 0;
+ for (int val = 2; val <= 16; val *= 2)
+ idxtest[count++] =
+ intermediate.addBinaryNode(EOpEqual,
+ intermediate.addSymbol(*outSampleCount, loc),
+ intermediate.addConstantUnion(val, loc),
+ loc, TType(EbtBool));
+
+ const TOperator idxOp = (argSampIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
+
+ // Create index ops into position arrays given sample index.
+ // TODO: should it be clamped?
+ TIntermTyped* index[4];
+ count = 0;
+ for (int val = 2; val <= 16; val *= 2) {
+ index[count] = intermediate.addIndex(idxOp, getSamplePosArray(val), argSampIdx, loc);
+ index[count++]->setType(TType(EbtFloat, EvqTemporary, 2));
+ }
+
+ // Create expression as:
+ // (sampleCount == 2) ? pos2[idx] :
+ // (sampleCount == 4) ? pos4[idx] :
+ // (sampleCount == 8) ? pos8[idx] :
+ // (sampleCount == 16) ? pos16[idx] : float2(0,0);
+ TIntermTyped* test =
+ intermediate.addSelection(idxtest[0], index[0],
+ intermediate.addSelection(idxtest[1], index[1],
+ intermediate.addSelection(idxtest[2], index[2],
+ intermediate.addSelection(idxtest[3], index[3],
+ getSamplePosArray(1), loc), loc), loc), loc);
+
+ compoundStatement = intermediate.growAggregate(compoundStatement, test);
+ compoundStatement->setOperator(EOpSequence);
+ compoundStatement->setLoc(loc);
+ compoundStatement->setType(TType(EbtFloat, EvqTemporary, 2));
+
+ node = compoundStatement;
+
+ break;
+ }
+
+ case EOpSubpassLoad:
+ {
+ const TIntermTyped* argSubpass =
+ argAggregate ? argAggregate->getSequence()[0]->getAsTyped() :
+ arguments->getAsTyped();
+
+ const TSampler& sampler = argSubpass->getType().getSampler();
+
+ // subpass load: the multisample form is overloaded. Here, we convert that to
+ // the EOpSubpassLoadMS opcode.
+ if (argAggregate != nullptr && argAggregate->getSequence().size() > 1)
+ node->getAsOperator()->setOp(EOpSubpassLoadMS);
+
+ node = convertReturn(node, sampler);
+
+ break;
+ }
+
+
+ default:
+ break; // most pass through unchanged
+ }
+}
+
+//
+// Decompose geometry shader methods
+//
+void HlslParseContext::decomposeGeometryMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
+{
+ if (node == nullptr || !node->getAsOperator())
+ return;
+
+ const TOperator op = node->getAsOperator()->getOp();
+ const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
+
+ switch (op) {
+ case EOpMethodAppend:
+ if (argAggregate) {
+ // Don't emit these for non-GS stage, since we won't have the gsStreamOutput symbol.
+ if (language != EShLangGeometry) {
+ node = nullptr;
+ return;
+ }
+
+ TIntermAggregate* sequence = nullptr;
+ TIntermAggregate* emit = new TIntermAggregate(EOpEmitVertex);
+
+ emit->setLoc(loc);
+ emit->setType(TType(EbtVoid));
+
+ TIntermTyped* data = argAggregate->getSequence()[1]->getAsTyped();
+
+ // This will be patched in finalization during finalizeAppendMethods()
+ sequence = intermediate.growAggregate(sequence, data, loc);
+ sequence = intermediate.growAggregate(sequence, emit);
+
+ sequence->setOperator(EOpSequence);
+ sequence->setLoc(loc);
+ sequence->setType(TType(EbtVoid));
+
+ gsAppends.push_back({sequence, loc});
+
+ node = sequence;
+ }
+ break;
+
+ case EOpMethodRestartStrip:
+ {
+ // Don't emit these for non-GS stage, since we won't have the gsStreamOutput symbol.
+ if (language != EShLangGeometry) {
+ node = nullptr;
+ return;
+ }
+
+ TIntermAggregate* cut = new TIntermAggregate(EOpEndPrimitive);
+ cut->setLoc(loc);
+ cut->setType(TType(EbtVoid));
+ node = cut;
+ }
+ break;
+
+ default:
+ break; // most pass through unchanged
+ }
+}
+
+//
+// Optionally decompose intrinsics to AST opcodes.
+//
+void HlslParseContext::decomposeIntrinsic(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
+{
+ // Helper to find image data for image atomics:
+ // OpImageLoad(image[idx])
+ // We take the image load apart and add its params to the atomic op aggregate node
+ const auto imageAtomicParams = [this, &loc, &node](TIntermAggregate* atomic, TIntermTyped* load) {
+ TIntermAggregate* loadOp = load->getAsAggregate();
+ if (loadOp == nullptr) {
+ error(loc, "unknown image type in atomic operation", "", "");
+ node = nullptr;
+ return;
+ }
+
+ atomic->getSequence().push_back(loadOp->getSequence()[0]);
+ atomic->getSequence().push_back(loadOp->getSequence()[1]);
+ };
+
+ // Return true if this is an imageLoad, which we will change to an image atomic.
+ const auto isImageParam = [](TIntermTyped* image) -> bool {
+ TIntermAggregate* imageAggregate = image->getAsAggregate();
+ return imageAggregate != nullptr && imageAggregate->getOp() == EOpImageLoad;
+ };
+
+ const auto lookupBuiltinVariable = [&](const char* name, TBuiltInVariable builtin, TType& type) -> TIntermTyped* {
+ TSymbol* symbol = symbolTable.find(name);
+ if (nullptr == symbol) {
+ type.getQualifier().builtIn = builtin;
+
+ TVariable* variable = new TVariable(NewPoolTString(name), type);
+
+ symbolTable.insert(*variable);
+
+ symbol = symbolTable.find(name);
+ assert(symbol && "Inserted symbol could not be found!");
+ }
+
+ return intermediate.addSymbol(*(symbol->getAsVariable()), loc);
+ };
+
+ // HLSL intrinsics can be pass through to native AST opcodes, or decomposed here to existing AST
+ // opcodes for compatibility with existing software stacks.
+ static const bool decomposeHlslIntrinsics = true;
+
+ if (!decomposeHlslIntrinsics || !node || !node->getAsOperator())
+ return;
+
+ const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
+ TIntermUnary* fnUnary = node->getAsUnaryNode();
+ const TOperator op = node->getAsOperator()->getOp();
+
+ switch (op) {
+ case EOpGenMul:
+ {
+ // mul(a,b) -> MatrixTimesMatrix, MatrixTimesVector, MatrixTimesScalar, VectorTimesScalar, Dot, Mul
+ // Since we are treating HLSL rows like GLSL columns (the first matrix indirection),
+ // we must reverse the operand order here. Hence, arg0 gets sequence[1], etc.
+ TIntermTyped* arg0 = argAggregate->getSequence()[1]->getAsTyped();
+ TIntermTyped* arg1 = argAggregate->getSequence()[0]->getAsTyped();
+
+ if (arg0->isVector() && arg1->isVector()) { // vec * vec
+ node->getAsAggregate()->setOperator(EOpDot);
+ } else {
+ node = handleBinaryMath(loc, "mul", EOpMul, arg0, arg1);
+ }
+
+ break;
+ }
+
+ case EOpRcp:
+ {
+ // rcp(a) -> 1 / a
+ TIntermTyped* arg0 = fnUnary->getOperand();
+ TBasicType type0 = arg0->getBasicType();
+ TIntermTyped* one = intermediate.addConstantUnion(1, type0, loc, true);
+ node = handleBinaryMath(loc, "rcp", EOpDiv, one, arg0);
+
+ break;
+ }
+
+ case EOpAny: // fall through
+ case EOpAll:
+ {
+ TIntermTyped* typedArg = arguments->getAsTyped();
+
+ // HLSL allows float/etc types here, and the SPIR-V opcode requires a bool.
+ // We'll convert here. Note that for efficiency, we could add a smarter
+ // decomposition for some type cases, e.g, maybe by decomposing a dot product.
+ if (typedArg->getType().getBasicType() != EbtBool) {
+ const TType boolType(EbtBool, EvqTemporary,
+ typedArg->getVectorSize(),
+ typedArg->getMatrixCols(),
+ typedArg->getMatrixRows(),
+ typedArg->isVector());
+
+ typedArg = intermediate.addConversion(EOpConstructBool, boolType, typedArg);
+ node->getAsUnaryNode()->setOperand(typedArg);
+ }
+
+ break;
+ }
+
+ case EOpSaturate:
+ {
+ // saturate(a) -> clamp(a,0,1)
+ TIntermTyped* arg0 = fnUnary->getOperand();
+ TBasicType type0 = arg0->getBasicType();
+ TIntermAggregate* clamp = new TIntermAggregate(EOpClamp);
+
+ clamp->getSequence().push_back(arg0);
+ clamp->getSequence().push_back(intermediate.addConstantUnion(0, type0, loc, true));
+ clamp->getSequence().push_back(intermediate.addConstantUnion(1, type0, loc, true));
+ clamp->setLoc(loc);
+ clamp->setType(node->getType());
+ clamp->getWritableType().getQualifier().makeTemporary();
+ node = clamp;
+
+ break;
+ }
+
+ case EOpSinCos:
+ {
+ // sincos(a,b,c) -> b = sin(a), c = cos(a)
+ TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
+ TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
+ TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped();
+
+ TIntermTyped* sinStatement = handleUnaryMath(loc, "sin", EOpSin, arg0);
+ TIntermTyped* cosStatement = handleUnaryMath(loc, "cos", EOpCos, arg0);
+ TIntermTyped* sinAssign = intermediate.addAssign(EOpAssign, arg1, sinStatement, loc);
+ TIntermTyped* cosAssign = intermediate.addAssign(EOpAssign, arg2, cosStatement, loc);
+
+ TIntermAggregate* compoundStatement = intermediate.makeAggregate(sinAssign, loc);
+ compoundStatement = intermediate.growAggregate(compoundStatement, cosAssign);
+ compoundStatement->setOperator(EOpSequence);
+ compoundStatement->setLoc(loc);
+ compoundStatement->setType(TType(EbtVoid));
+
+ node = compoundStatement;
+
+ break;
+ }
+
+ case EOpClip:
+ {
+ // clip(a) -> if (any(a<0)) discard;
+ TIntermTyped* arg0 = fnUnary->getOperand();
+ TBasicType type0 = arg0->getBasicType();
+ TIntermTyped* compareNode = nullptr;
+
+ // For non-scalars: per experiment with FXC compiler, discard if any component < 0.
+ if (!arg0->isScalar()) {
+ // component-wise compare: a < 0
+ TIntermAggregate* less = new TIntermAggregate(EOpLessThan);
+ less->getSequence().push_back(arg0);
+ less->setLoc(loc);
+
+ // make vec or mat of bool matching dimensions of input
+ less->setType(TType(EbtBool, EvqTemporary,
+ arg0->getType().getVectorSize(),
+ arg0->getType().getMatrixCols(),
+ arg0->getType().getMatrixRows(),
+ arg0->getType().isVector()));
+
+ // calculate # of components for comparison const
+ const int constComponentCount =
+ std::max(arg0->getType().getVectorSize(), 1) *
+ std::max(arg0->getType().getMatrixCols(), 1) *
+ std::max(arg0->getType().getMatrixRows(), 1);
+
+ TConstUnion zero;
+ if (arg0->getType().isIntegerDomain())
+ zero.setDConst(0);
+ else
+ zero.setDConst(0.0);
+ TConstUnionArray zeros(constComponentCount, zero);
+
+ less->getSequence().push_back(intermediate.addConstantUnion(zeros, arg0->getType(), loc, true));
+
+ compareNode = intermediate.addBuiltInFunctionCall(loc, EOpAny, true, less, TType(EbtBool));
+ } else {
+ TIntermTyped* zero;
+ if (arg0->getType().isIntegerDomain())
+ zero = intermediate.addConstantUnion(0, loc, true);
+ else
+ zero = intermediate.addConstantUnion(0.0, type0, loc, true);
+ compareNode = handleBinaryMath(loc, "clip", EOpLessThan, arg0, zero);
+ }
+
+ TIntermBranch* killNode = intermediate.addBranch(EOpKill, loc);
+
+ node = new TIntermSelection(compareNode, killNode, nullptr);
+ node->setLoc(loc);
+
+ break;
+ }
+
+ case EOpLog10:
+ {
+ // log10(a) -> log2(a) * 0.301029995663981 (== 1/log2(10))
+ TIntermTyped* arg0 = fnUnary->getOperand();
+ TIntermTyped* log2 = handleUnaryMath(loc, "log2", EOpLog2, arg0);
+ TIntermTyped* base = intermediate.addConstantUnion(0.301029995663981f, EbtFloat, loc, true);
+
+ node = handleBinaryMath(loc, "mul", EOpMul, log2, base);
+
+ break;
+ }
+
+ case EOpDst:
+ {
+ // dest.x = 1;
+ // dest.y = src0.y * src1.y;
+ // dest.z = src0.z;
+ // dest.w = src1.w;
+
+ TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
+ TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
+
+ TIntermTyped* y = intermediate.addConstantUnion(1, loc, true);
+ TIntermTyped* z = intermediate.addConstantUnion(2, loc, true);
+ TIntermTyped* w = intermediate.addConstantUnion(3, loc, true);
+
+ TIntermTyped* src0y = intermediate.addIndex(EOpIndexDirect, arg0, y, loc);
+ TIntermTyped* src1y = intermediate.addIndex(EOpIndexDirect, arg1, y, loc);
+ TIntermTyped* src0z = intermediate.addIndex(EOpIndexDirect, arg0, z, loc);
+ TIntermTyped* src1w = intermediate.addIndex(EOpIndexDirect, arg1, w, loc);
+
+ TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4);
+
+ dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true));
+ dst->getSequence().push_back(handleBinaryMath(loc, "mul", EOpMul, src0y, src1y));
+ dst->getSequence().push_back(src0z);
+ dst->getSequence().push_back(src1w);
+ dst->setType(TType(EbtFloat, EvqTemporary, 4));
+ dst->setLoc(loc);
+ node = dst;
+
+ break;
+ }
+
+ case EOpInterlockedAdd: // optional last argument (if present) is assigned from return value
+ case EOpInterlockedMin: // ...
+ case EOpInterlockedMax: // ...
+ case EOpInterlockedAnd: // ...
+ case EOpInterlockedOr: // ...
+ case EOpInterlockedXor: // ...
+ case EOpInterlockedExchange: // always has output arg
+ {
+ TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // dest
+ TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // value
+ TIntermTyped* arg2 = nullptr;
+
+ if (argAggregate->getSequence().size() > 2)
+ arg2 = argAggregate->getSequence()[2]->getAsTyped();
+
+ const bool isImage = isImageParam(arg0);
+ const TOperator atomicOp = mapAtomicOp(loc, op, isImage);
+ TIntermAggregate* atomic = new TIntermAggregate(atomicOp);
+ atomic->setType(arg0->getType());
+ atomic->getWritableType().getQualifier().makeTemporary();
+ atomic->setLoc(loc);
+
+ if (isImage) {
+ // orig_value = imageAtomicOp(image, loc, data)
+ imageAtomicParams(atomic, arg0);
+ atomic->getSequence().push_back(arg1);
+
+ if (argAggregate->getSequence().size() > 2) {
+ node = intermediate.addAssign(EOpAssign, arg2, atomic, loc);
+ } else {
+ node = atomic; // no assignment needed, as there was no out var.
+ }
+ } else {
+ // Normal memory variable:
+ // arg0 = mem, arg1 = data, arg2(optional,out) = orig_value
+ if (argAggregate->getSequence().size() > 2) {
+ // optional output param is present. return value goes to arg2.
+ atomic->getSequence().push_back(arg0);
+ atomic->getSequence().push_back(arg1);
+
+ node = intermediate.addAssign(EOpAssign, arg2, atomic, loc);
+ } else {
+ // Set the matching operator. Since output is absent, this is all we need to do.
+ node->getAsAggregate()->setOperator(atomicOp);
+ node->setType(atomic->getType());
+ }
+ }
+
+ break;
+ }
+
+ case EOpInterlockedCompareExchange:
+ {
+ TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // dest
+ TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // cmp
+ TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped(); // value
+ TIntermTyped* arg3 = argAggregate->getSequence()[3]->getAsTyped(); // orig
+
+ const bool isImage = isImageParam(arg0);
+ TIntermAggregate* atomic = new TIntermAggregate(mapAtomicOp(loc, op, isImage));
+ atomic->setLoc(loc);
+ atomic->setType(arg2->getType());
+ atomic->getWritableType().getQualifier().makeTemporary();
+
+ if (isImage) {
+ imageAtomicParams(atomic, arg0);
+ } else {
+ atomic->getSequence().push_back(arg0);
+ }
+
+ atomic->getSequence().push_back(arg1);
+ atomic->getSequence().push_back(arg2);
+ node = intermediate.addAssign(EOpAssign, arg3, atomic, loc);
+
+ break;
+ }
+
+ case EOpEvaluateAttributeSnapped:
+ {
+ // SPIR-V InterpolateAtOffset uses float vec2 offset in pixels
+ // HLSL uses int2 offset on a 16x16 grid in [-8..7] on x & y:
+ // iU = (iU<<28)>>28
+ // fU = ((float)iU)/16
+ // Targets might handle this natively, in which case they can disable
+ // decompositions.
+
+ TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // value
+ TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // offset
+
+ TIntermTyped* i28 = intermediate.addConstantUnion(28, loc, true);
+ TIntermTyped* iU = handleBinaryMath(loc, ">>", EOpRightShift,
+ handleBinaryMath(loc, "<<", EOpLeftShift, arg1, i28),
+ i28);
+
+ TIntermTyped* recip16 = intermediate.addConstantUnion((1.0/16.0), EbtFloat, loc, true);
+ TIntermTyped* floatOffset = handleBinaryMath(loc, "mul", EOpMul,
+ intermediate.addConversion(EOpConstructFloat,
+ TType(EbtFloat, EvqTemporary, 2), iU),
+ recip16);
+
+ TIntermAggregate* interp = new TIntermAggregate(EOpInterpolateAtOffset);
+ interp->getSequence().push_back(arg0);
+ interp->getSequence().push_back(floatOffset);
+ interp->setLoc(loc);
+ interp->setType(arg0->getType());
+ interp->getWritableType().getQualifier().makeTemporary();
+
+ node = interp;
+
+ break;
+ }
+
+ case EOpLit:
+ {
+ TIntermTyped* n_dot_l = argAggregate->getSequence()[0]->getAsTyped();
+ TIntermTyped* n_dot_h = argAggregate->getSequence()[1]->getAsTyped();
+ TIntermTyped* m = argAggregate->getSequence()[2]->getAsTyped();
+
+ TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4);
+
+ // Ambient
+ dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true));
+
+ // Diffuse:
+ TIntermTyped* zero = intermediate.addConstantUnion(0.0, EbtFloat, loc, true);
+ TIntermAggregate* diffuse = new TIntermAggregate(EOpMax);
+ diffuse->getSequence().push_back(n_dot_l);
+ diffuse->getSequence().push_back(zero);
+ diffuse->setLoc(loc);
+ diffuse->setType(TType(EbtFloat));
+ dst->getSequence().push_back(diffuse);
+
+ // Specular:
+ TIntermAggregate* min_ndot = new TIntermAggregate(EOpMin);
+ min_ndot->getSequence().push_back(n_dot_l);
+ min_ndot->getSequence().push_back(n_dot_h);
+ min_ndot->setLoc(loc);
+ min_ndot->setType(TType(EbtFloat));
+
+ TIntermTyped* compare = handleBinaryMath(loc, "<", EOpLessThan, min_ndot, zero);
+ TIntermTyped* n_dot_h_m = handleBinaryMath(loc, "mul", EOpMul, n_dot_h, m); // n_dot_h * m
+
+ dst->getSequence().push_back(intermediate.addSelection(compare, zero, n_dot_h_m, loc));
+
+ // One:
+ dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true));
+
+ dst->setLoc(loc);
+ dst->setType(TType(EbtFloat, EvqTemporary, 4));
+ node = dst;
+ break;
+ }
+
+ case EOpAsDouble:
+ {
+ // asdouble accepts two 32 bit ints. we can use EOpUint64BitsToDouble, but must
+ // first construct a uint64.
+ TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
+ TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
+
+ if (arg0->getType().isVector()) { // TODO: ...
+ error(loc, "double2 conversion not implemented", "asdouble", "");
+ break;
+ }
+
+ TIntermAggregate* uint64 = new TIntermAggregate(EOpConstructUVec2);
+
+ uint64->getSequence().push_back(arg0);
+ uint64->getSequence().push_back(arg1);
+ uint64->setType(TType(EbtUint, EvqTemporary, 2)); // convert 2 uints to a uint2
+ uint64->setLoc(loc);
+
+ // bitcast uint2 to a double
+ TIntermTyped* convert = new TIntermUnary(EOpUint64BitsToDouble);
+ convert->getAsUnaryNode()->setOperand(uint64);
+ convert->setLoc(loc);
+ convert->setType(TType(EbtDouble, EvqTemporary));
+ node = convert;
+
+ break;
+ }
+
+ case EOpF16tof32:
+ {
+ // input uvecN with low 16 bits of each component holding a float16. convert to float32.
+ TIntermTyped* argValue = node->getAsUnaryNode()->getOperand();
+ TIntermTyped* zero = intermediate.addConstantUnion(0, loc, true);
+ const int vecSize = argValue->getType().getVectorSize();
+
+ TOperator constructOp = EOpNull;
+ switch (vecSize) {
+ case 1: constructOp = EOpNull; break; // direct use, no construct needed
+ case 2: constructOp = EOpConstructVec2; break;
+ case 3: constructOp = EOpConstructVec3; break;
+ case 4: constructOp = EOpConstructVec4; break;
+ default: assert(0); break;
+ }
+
+ // For scalar case, we don't need to construct another type.
+ TIntermAggregate* result = (vecSize > 1) ? new TIntermAggregate(constructOp) : nullptr;
+
+ if (result) {
+ result->setType(TType(EbtFloat, EvqTemporary, vecSize));
+ result->setLoc(loc);
+ }
+
+ for (int idx = 0; idx < vecSize; ++idx) {
+ TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true);
+ TIntermTyped* component = argValue->getType().isVector() ?
+ intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc) : argValue;
+
+ if (component != argValue)
+ component->setType(TType(argValue->getBasicType(), EvqTemporary));
+
+ TIntermTyped* unpackOp = new TIntermUnary(EOpUnpackHalf2x16);
+ unpackOp->setType(TType(EbtFloat, EvqTemporary, 2));
+ unpackOp->getAsUnaryNode()->setOperand(component);
+ unpackOp->setLoc(loc);
+
+ TIntermTyped* lowOrder = intermediate.addIndex(EOpIndexDirect, unpackOp, zero, loc);
+
+ if (result != nullptr) {
+ result->getSequence().push_back(lowOrder);
+ node = result;
+ } else {
+ node = lowOrder;
+ }
+ }
+
+ break;
+ }
+
+ case EOpF32tof16:
+ {
+ // input floatN converted to 16 bit float in low order bits of each component of uintN
+ TIntermTyped* argValue = node->getAsUnaryNode()->getOperand();
+
+ TIntermTyped* zero = intermediate.addConstantUnion(0.0, EbtFloat, loc, true);
+ const int vecSize = argValue->getType().getVectorSize();
+
+ TOperator constructOp = EOpNull;
+ switch (vecSize) {
+ case 1: constructOp = EOpNull; break; // direct use, no construct needed
+ case 2: constructOp = EOpConstructUVec2; break;
+ case 3: constructOp = EOpConstructUVec3; break;
+ case 4: constructOp = EOpConstructUVec4; break;
+ default: assert(0); break;
+ }
+
+ // For scalar case, we don't need to construct another type.
+ TIntermAggregate* result = (vecSize > 1) ? new TIntermAggregate(constructOp) : nullptr;
+
+ if (result) {
+ result->setType(TType(EbtUint, EvqTemporary, vecSize));
+ result->setLoc(loc);
+ }
+
+ for (int idx = 0; idx < vecSize; ++idx) {
+ TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true);
+ TIntermTyped* component = argValue->getType().isVector() ?
+ intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc) : argValue;
+
+ if (component != argValue)
+ component->setType(TType(argValue->getBasicType(), EvqTemporary));
+
+ TIntermAggregate* vec2ComponentAndZero = new TIntermAggregate(EOpConstructVec2);
+ vec2ComponentAndZero->getSequence().push_back(component);
+ vec2ComponentAndZero->getSequence().push_back(zero);
+ vec2ComponentAndZero->setType(TType(EbtFloat, EvqTemporary, 2));
+ vec2ComponentAndZero->setLoc(loc);
+
+ TIntermTyped* packOp = new TIntermUnary(EOpPackHalf2x16);
+ packOp->getAsUnaryNode()->setOperand(vec2ComponentAndZero);
+ packOp->setLoc(loc);
+ packOp->setType(TType(EbtUint, EvqTemporary));
+
+ if (result != nullptr) {
+ result->getSequence().push_back(packOp);
+ node = result;
+ } else {
+ node = packOp;
+ }
+ }
+
+ break;
+ }
+
+ case EOpD3DCOLORtoUBYTE4:
+ {
+ // ivec4 ( x.zyxw * 255.001953 );
+ TIntermTyped* arg0 = node->getAsUnaryNode()->getOperand();
+ TSwizzleSelectors<TVectorSelector> selectors;
+ selectors.push_back(2);
+ selectors.push_back(1);
+ selectors.push_back(0);
+ selectors.push_back(3);
+ TIntermTyped* swizzleIdx = intermediate.addSwizzle(selectors, loc);
+ TIntermTyped* swizzled = intermediate.addIndex(EOpVectorSwizzle, arg0, swizzleIdx, loc);
+ swizzled->setType(arg0->getType());
+ swizzled->getWritableType().getQualifier().makeTemporary();
+
+ TIntermTyped* conversion = intermediate.addConstantUnion(255.001953f, EbtFloat, loc, true);
+ TIntermTyped* rangeConverted = handleBinaryMath(loc, "mul", EOpMul, conversion, swizzled);
+ rangeConverted->setType(arg0->getType());
+ rangeConverted->getWritableType().getQualifier().makeTemporary();
+
+ node = intermediate.addConversion(EOpConstructInt, TType(EbtInt, EvqTemporary, 4), rangeConverted);
+ node->setLoc(loc);
+ node->setType(TType(EbtInt, EvqTemporary, 4));
+ break;
+ }
+
+ case EOpIsFinite:
+ {
+ // Since OPIsFinite in SPIR-V is only supported with the Kernel capability, we translate
+ // it to !isnan && !isinf
+
+ TIntermTyped* arg0 = node->getAsUnaryNode()->getOperand();
+
+ // We'll make a temporary in case the RHS is cmoplex
+ TVariable* tempArg = makeInternalVariable("@finitetmp", arg0->getType());
+ tempArg->getWritableType().getQualifier().makeTemporary();
+
+ TIntermTyped* tmpArgAssign = intermediate.addAssign(EOpAssign,
+ intermediate.addSymbol(*tempArg, loc),
+ arg0, loc);
+
+ TIntermAggregate* compoundStatement = intermediate.makeAggregate(tmpArgAssign, loc);
+
+ const TType boolType(EbtBool, EvqTemporary, arg0->getVectorSize(), arg0->getMatrixCols(),
+ arg0->getMatrixRows());
+
+ TIntermTyped* isnan = handleUnaryMath(loc, "isnan", EOpIsNan, intermediate.addSymbol(*tempArg, loc));
+ isnan->setType(boolType);
+
+ TIntermTyped* notnan = handleUnaryMath(loc, "!", EOpLogicalNot, isnan);
+ notnan->setType(boolType);
+
+ TIntermTyped* isinf = handleUnaryMath(loc, "isinf", EOpIsInf, intermediate.addSymbol(*tempArg, loc));
+ isinf->setType(boolType);
+
+ TIntermTyped* notinf = handleUnaryMath(loc, "!", EOpLogicalNot, isinf);
+ notinf->setType(boolType);
+
+ TIntermTyped* andNode = handleBinaryMath(loc, "and", EOpLogicalAnd, notnan, notinf);
+ andNode->setType(boolType);
+
+ compoundStatement = intermediate.growAggregate(compoundStatement, andNode);
+ compoundStatement->setOperator(EOpSequence);
+ compoundStatement->setLoc(loc);
+ compoundStatement->setType(boolType);
+
+ node = compoundStatement;
+
+ break;
+ }
+ case EOpWaveGetLaneCount:
+ {
+ // Mapped to gl_SubgroupSize builtin (We preprend @ to the symbol
+ // so that it inhabits the symbol table, but has a user-invalid name
+ // in-case some source HLSL defined the symbol also).
+ TType type(EbtUint, EvqVaryingIn);
+ node = lookupBuiltinVariable("@gl_SubgroupSize", EbvSubgroupSize2, type);
+ break;
+ }
+ case EOpWaveGetLaneIndex:
+ {
+ // Mapped to gl_SubgroupInvocationID builtin (We preprend @ to the
+ // symbol so that it inhabits the symbol table, but has a
+ // user-invalid name in-case some source HLSL defined the symbol
+ // also).
+ TType type(EbtUint, EvqVaryingIn);
+ node = lookupBuiltinVariable("@gl_SubgroupInvocationID", EbvSubgroupInvocation2, type);
+ break;
+ }
+ case EOpWaveActiveCountBits:
+ {
+ // Mapped to subgroupBallotBitCount(subgroupBallot()) builtin
+
+ // uvec4 type.
+ TType uvec4Type(EbtUint, EvqTemporary, 4);
+
+ // Get the uvec4 return from subgroupBallot().
+ TIntermTyped* res = intermediate.addBuiltInFunctionCall(loc,
+ EOpSubgroupBallot, true, arguments, uvec4Type);
+
+ // uint type.
+ TType uintType(EbtUint, EvqTemporary);
+
+ node = intermediate.addBuiltInFunctionCall(loc,
+ EOpSubgroupBallotBitCount, true, res, uintType);
+
+ break;
+ }
+ case EOpWavePrefixCountBits:
+ {
+ // Mapped to subgroupBallotInclusiveBitCount(subgroupBallot())
+ // builtin
+
+ // uvec4 type.
+ TType uvec4Type(EbtUint, EvqTemporary, 4);
+
+ // Get the uvec4 return from subgroupBallot().
+ TIntermTyped* res = intermediate.addBuiltInFunctionCall(loc,
+ EOpSubgroupBallot, true, arguments, uvec4Type);
+
+ // uint type.
+ TType uintType(EbtUint, EvqTemporary);
+
+ node = intermediate.addBuiltInFunctionCall(loc,
+ EOpSubgroupBallotInclusiveBitCount, true, res, uintType);
+
+ break;
+ }
+
+ default:
+ break; // most pass through unchanged
+ }
+}
+
+//
+// Handle seeing function call syntax in the grammar, which could be any of
+// - .length() method
+// - constructor
+// - a call to a built-in function mapped to an operator
+// - a call to a built-in function that will remain a function call (e.g., texturing)
+// - user function
+// - subroutine call (not implemented yet)
+//
+TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction* function, TIntermTyped* arguments)
+{
+ TIntermTyped* result = nullptr;
+
+ TOperator op = function->getBuiltInOp();
+ if (op != EOpNull) {
+ //
+ // Then this should be a constructor.
+ // Don't go through the symbol table for constructors.
+ // Their parameters will be verified algorithmically.
+ //
+ TType type(EbtVoid); // use this to get the type back
+ if (! constructorError(loc, arguments, *function, op, type)) {
+ //
+ // It's a constructor, of type 'type'.
+ //
+ result = handleConstructor(loc, arguments, type);
+ if (result == nullptr) {
+ error(loc, "cannot construct with these arguments", type.getCompleteString().c_str(), "");
+ return nullptr;
+ }
+ }
+ } else {
+ //
+ // Find it in the symbol table.
+ //
+ const TFunction* fnCandidate = nullptr;
+ bool builtIn = false;
+ int thisDepth = 0;
+
+ // For mat mul, the situation is unusual: we have to compare vector sizes to mat row or col sizes,
+ // and clamp the opposite arg. Since that's complex, we farm it off to a separate method.
+ // It doesn't naturally fall out of processing an argument at a time in isolation.
+ if (function->getName() == "mul")
+ addGenMulArgumentConversion(loc, *function, arguments);
+
+ TIntermAggregate* aggregate = arguments ? arguments->getAsAggregate() : nullptr;
+
+ // TODO: this needs improvement: there's no way at present to look up a signature in
+ // the symbol table for an arbitrary type. This is a temporary hack until that ability exists.
+ // It will have false positives, since it doesn't check arg counts or types.
+ if (arguments) {
+ // Check if first argument is struct buffer type. It may be an aggregate or a symbol, so we
+ // look for either case.
+
+ TIntermTyped* arg0 = nullptr;
+
+ if (aggregate && aggregate->getSequence().size() > 0 && aggregate->getSequence()[0])
+ arg0 = aggregate->getSequence()[0]->getAsTyped();
+ else if (arguments->getAsSymbolNode())
+ arg0 = arguments->getAsSymbolNode();
+
+ if (arg0 != nullptr && isStructBufferType(arg0->getType())) {
+ static const int methodPrefixSize = sizeof(BUILTIN_PREFIX)-1;
+
+ if (function->getName().length() > methodPrefixSize &&
+ isStructBufferMethod(function->getName().substr(methodPrefixSize))) {
+ const TString mangle = function->getName() + "(";
+ TSymbol* symbol = symbolTable.find(mangle, &builtIn);
+
+ if (symbol)
+ fnCandidate = symbol->getAsFunction();
+ }
+ }
+ }
+
+ if (fnCandidate == nullptr)
+ fnCandidate = findFunction(loc, *function, builtIn, thisDepth, arguments);
+
+ if (fnCandidate) {
+ // This is a declared function that might map to
+ // - a built-in operator,
+ // - a built-in function not mapped to an operator, or
+ // - a user function.
+
+ // turn an implicit member-function resolution into an explicit call
+ TString callerName;
+ if (thisDepth == 0)
+ callerName = fnCandidate->getMangledName();
+ else {
+ // get the explicit (full) name of the function
+ callerName = currentTypePrefix[currentTypePrefix.size() - thisDepth];
+ callerName += fnCandidate->getMangledName();
+ // insert the implicit calling argument
+ pushFrontArguments(intermediate.addSymbol(*getImplicitThis(thisDepth)), arguments);
+ }
+
+ // Convert 'in' arguments, so that types match.
+ // However, skip those that need expansion, that is covered next.
+ if (arguments)
+ addInputArgumentConversions(*fnCandidate, arguments);
+
+ // Expand arguments. Some arguments must physically expand to a different set
+ // than what the shader declared and passes.
+ if (arguments && !builtIn)
+ expandArguments(loc, *fnCandidate, arguments);
+
+ // Expansion may have changed the form of arguments
+ aggregate = arguments ? arguments->getAsAggregate() : nullptr;
+
+ op = fnCandidate->getBuiltInOp();
+ if (builtIn && op != EOpNull) {
+ // A function call mapped to a built-in operation.
+ result = intermediate.addBuiltInFunctionCall(loc, op, fnCandidate->getParamCount() == 1, arguments,
+ fnCandidate->getType());
+ if (result == nullptr) {
+ error(arguments->getLoc(), " wrong operand type", "Internal Error",
+ "built in unary operator function. Type: %s",
+ static_cast<TIntermTyped*>(arguments)->getCompleteString().c_str());
+ } else if (result->getAsOperator()) {
+ builtInOpCheck(loc, *fnCandidate, *result->getAsOperator());
+ }
+ } else {
+ // This is a function call not mapped to built-in operator.
+ // It could still be a built-in function, but only if PureOperatorBuiltins == false.
+ result = intermediate.setAggregateOperator(arguments, EOpFunctionCall, fnCandidate->getType(), loc);
+ TIntermAggregate* call = result->getAsAggregate();
+ call->setName(callerName);
+
+ // this is how we know whether the given function is a built-in function or a user-defined function
+ // if builtIn == false, it's a userDefined -> could be an overloaded built-in function also
+ // if builtIn == true, it's definitely a built-in function with EOpNull
+ if (! builtIn) {
+ call->setUserDefined();
+ intermediate.addToCallGraph(infoSink, currentCaller, callerName);
+ }
+ }
+
+ // for decompositions, since we want to operate on the function node, not the aggregate holding
+ // output conversions.
+ const TIntermTyped* fnNode = result;
+
+ decomposeStructBufferMethods(loc, result, arguments); // HLSL->AST struct buffer method decompositions
+ decomposeIntrinsic(loc, result, arguments); // HLSL->AST intrinsic decompositions
+ decomposeSampleMethods(loc, result, arguments); // HLSL->AST sample method decompositions
+ decomposeGeometryMethods(loc, result, arguments); // HLSL->AST geometry method decompositions
+
+ // Create the qualifier list, carried in the AST for the call.
+ // Because some arguments expand to multiple arguments, the qualifier list will
+ // be longer than the formal parameter list.
+ if (result == fnNode && result->getAsAggregate()) {
+ TQualifierList& qualifierList = result->getAsAggregate()->getQualifierList();
+ for (int i = 0; i < fnCandidate->getParamCount(); ++i) {
+ TStorageQualifier qual = (*fnCandidate)[i].type->getQualifier().storage;
+ if (hasStructBuffCounter(*(*fnCandidate)[i].type)) {
+ // add buffer and counter buffer argument qualifier
+ qualifierList.push_back(qual);
+ qualifierList.push_back(qual);
+ } else if (shouldFlatten(*(*fnCandidate)[i].type, (*fnCandidate)[i].type->getQualifier().storage,
+ true)) {
+ // add structure member expansion
+ for (int memb = 0; memb < (int)(*fnCandidate)[i].type->getStruct()->size(); ++memb)
+ qualifierList.push_back(qual);
+ } else {
+ // Normal 1:1 case
+ qualifierList.push_back(qual);
+ }
+ }
+ }
+
+ // Convert 'out' arguments. If it was a constant folded built-in, it won't be an aggregate anymore.
+ // Built-ins with a single argument aren't called with an aggregate, but they also don't have an output.
+ // Also, build the qualifier list for user function calls, which are always called with an aggregate.
+ // We don't do this is if there has been a decomposition, which will have added its own conversions
+ // for output parameters.
+ if (result == fnNode && result->getAsAggregate())
+ result = addOutputArgumentConversions(*fnCandidate, *result->getAsOperator());
+ }
+ }
+
+ // generic error recovery
+ // TODO: simplification: localize all the error recoveries that look like this, and taking type into account to
+ // reduce cascades
+ if (result == nullptr)
+ result = intermediate.addConstantUnion(0.0, EbtFloat, loc);
+
+ return result;
+}
+
+// An initial argument list is difficult: it can be null, or a single node,
+// or an aggregate if more than one argument. Add one to the front, maintaining
+// this lack of uniformity.
+void HlslParseContext::pushFrontArguments(TIntermTyped* front, TIntermTyped*& arguments)
+{
+ if (arguments == nullptr)
+ arguments = front;
+ else if (arguments->getAsAggregate() != nullptr)
+ arguments->getAsAggregate()->getSequence().insert(arguments->getAsAggregate()->getSequence().begin(), front);
+ else
+ arguments = intermediate.growAggregate(front, arguments);
+}
+
+//
+// HLSL allows mismatched dimensions on vec*mat, mat*vec, vec*vec, and mat*mat. This is a
+// situation not well suited to resolution in intrinsic selection, but we can do so here, since we
+// can look at both arguments insert explicit shape changes if required.
+//
+void HlslParseContext::addGenMulArgumentConversion(const TSourceLoc& loc, TFunction& call, TIntermTyped*& args)
+{
+ TIntermAggregate* argAggregate = args ? args->getAsAggregate() : nullptr;
+
+ if (argAggregate == nullptr || argAggregate->getSequence().size() != 2) {
+ // It really ought to have two arguments.
+ error(loc, "expected: mul arguments", "", "");
+ return;
+ }
+
+ TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped();
+ TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped();
+
+ if (arg0->isVector() && arg1->isVector()) {
+ // For:
+ // vec * vec: it's handled during intrinsic selection, so while we could do it here,
+ // we can also ignore it, which is easier.
+ } else if (arg0->isVector() && arg1->isMatrix()) {
+ // vec * mat: we clamp the vec if the mat col is smaller, else clamp the mat col.
+ if (arg0->getVectorSize() < arg1->getMatrixCols()) {
+ // vec is smaller, so truncate larger mat dimension
+ const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision,
+ 0, arg0->getVectorSize(), arg1->getMatrixRows());
+ arg1 = addConstructor(loc, arg1, truncType);
+ } else if (arg0->getVectorSize() > arg1->getMatrixCols()) {
+ // vec is larger, so truncate vec to mat size
+ const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision,
+ arg1->getMatrixCols());
+ arg0 = addConstructor(loc, arg0, truncType);
+ }
+ } else if (arg0->isMatrix() && arg1->isVector()) {
+ // mat * vec: we clamp the vec if the mat col is smaller, else clamp the mat col.
+ if (arg1->getVectorSize() < arg0->getMatrixRows()) {
+ // vec is smaller, so truncate larger mat dimension
+ const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision,
+ 0, arg0->getMatrixCols(), arg1->getVectorSize());
+ arg0 = addConstructor(loc, arg0, truncType);
+ } else if (arg1->getVectorSize() > arg0->getMatrixRows()) {
+ // vec is larger, so truncate vec to mat size
+ const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision,
+ arg0->getMatrixRows());
+ arg1 = addConstructor(loc, arg1, truncType);
+ }
+ } else if (arg0->isMatrix() && arg1->isMatrix()) {
+ // mat * mat: we clamp the smaller inner dimension to match the other matrix size.
+ // Remember, HLSL Mrc = GLSL/SPIRV Mcr.
+ if (arg0->getMatrixRows() > arg1->getMatrixCols()) {
+ const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision,
+ 0, arg0->getMatrixCols(), arg1->getMatrixCols());
+ arg0 = addConstructor(loc, arg0, truncType);
+ } else if (arg0->getMatrixRows() < arg1->getMatrixCols()) {
+ const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision,
+ 0, arg0->getMatrixRows(), arg1->getMatrixRows());
+ arg1 = addConstructor(loc, arg1, truncType);
+ }
+ } else {
+ // It's something with scalars: we'll just leave it alone. Function selection will handle it
+ // downstream.
+ }
+
+ // Warn if we altered one of the arguments
+ if (arg0 != argAggregate->getSequence()[0] || arg1 != argAggregate->getSequence()[1])
+ warn(loc, "mul() matrix size mismatch", "", "");
+
+ // Put arguments back. (They might be unchanged, in which case this is harmless).
+ argAggregate->getSequence()[0] = arg0;
+ argAggregate->getSequence()[1] = arg1;
+
+ call[0].type = &arg0->getWritableType();
+ call[1].type = &arg1->getWritableType();
+}
+
+//
+// Add any needed implicit conversions for function-call arguments to input parameters.
+//
+void HlslParseContext::addInputArgumentConversions(const TFunction& function, TIntermTyped*& arguments)
+{
+ TIntermAggregate* aggregate = arguments->getAsAggregate();
+
+ // Replace a single argument with a single argument.
+ const auto setArg = [&](int paramNum, TIntermTyped* arg) {
+ if (function.getParamCount() == 1)
+ arguments = arg;
+ else {
+ if (aggregate == nullptr)
+ arguments = arg;
+ else
+ aggregate->getSequence()[paramNum] = arg;
+ }
+ };
+
+ // Process each argument's conversion
+ for (int param = 0; param < function.getParamCount(); ++param) {
+ if (! function[param].type->getQualifier().isParamInput())
+ continue;
+
+ // At this early point there is a slight ambiguity between whether an aggregate 'arguments'
+ // is the single argument itself or its children are the arguments. Only one argument
+ // means take 'arguments' itself as the one argument.
+ TIntermTyped* arg = function.getParamCount() == 1
+ ? arguments->getAsTyped()
+ : (aggregate ?
+ aggregate->getSequence()[param]->getAsTyped() :
+ arguments->getAsTyped());
+ if (*function[param].type != arg->getType()) {
+ // In-qualified arguments just need an extra node added above the argument to
+ // convert to the correct type.
+ TIntermTyped* convArg = intermediate.addConversion(EOpFunctionCall, *function[param].type, arg);
+ if (convArg != nullptr)
+ convArg = intermediate.addUniShapeConversion(EOpFunctionCall, *function[param].type, convArg);
+ if (convArg != nullptr)
+ setArg(param, convArg);
+ else
+ error(arg->getLoc(), "cannot convert input argument, argument", "", "%d", param);
+ } else {
+ if (wasFlattened(arg)) {
+ // If both formal and calling arg are to be flattened, leave that to argument
+ // expansion, not conversion.
+ if (!shouldFlatten(*function[param].type, function[param].type->getQualifier().storage, true)) {
+ // Will make a two-level subtree.
+ // The deepest will copy member-by-member to build the structure to pass.
+ // The level above that will be a two-operand EOpComma sequence that follows the copy by the
+ // object itself.
+ TVariable* internalAggregate = makeInternalVariable("aggShadow", *function[param].type);
+ internalAggregate->getWritableType().getQualifier().makeTemporary();
+ TIntermSymbol* internalSymbolNode = new TIntermSymbol(internalAggregate->getUniqueId(),
+ internalAggregate->getName(),
+ internalAggregate->getType());
+ internalSymbolNode->setLoc(arg->getLoc());
+ // This makes the deepest level, the member-wise copy
+ TIntermAggregate* assignAgg = handleAssign(arg->getLoc(), EOpAssign,
+ internalSymbolNode, arg)->getAsAggregate();
+
+ // Now, pair that with the resulting aggregate.
+ assignAgg = intermediate.growAggregate(assignAgg, internalSymbolNode, arg->getLoc());
+ assignAgg->setOperator(EOpComma);
+ assignAgg->setType(internalAggregate->getType());
+ setArg(param, assignAgg);
+ }
+ }
+ }
+ }
+}
+
+//
+// Add any needed implicit expansion of calling arguments from what the shader listed to what's
+// internally needed for the AST (given the constraints downstream).
+//
+void HlslParseContext::expandArguments(const TSourceLoc& loc, const TFunction& function, TIntermTyped*& arguments)
+{
+ TIntermAggregate* aggregate = arguments->getAsAggregate();
+ int functionParamNumberOffset = 0;
+
+ // Replace a single argument with a single argument.
+ const auto setArg = [&](int paramNum, TIntermTyped* arg) {
+ if (function.getParamCount() + functionParamNumberOffset == 1)
+ arguments = arg;
+ else {
+ if (aggregate == nullptr)
+ arguments = arg;
+ else
+ aggregate->getSequence()[paramNum] = arg;
+ }
+ };
+
+ // Replace a single argument with a list of arguments
+ const auto setArgList = [&](int paramNum, const TVector<TIntermTyped*>& args) {
+ if (args.size() == 1)
+ setArg(paramNum, args.front());
+ else if (args.size() > 1) {
+ if (function.getParamCount() + functionParamNumberOffset == 1) {
+ arguments = intermediate.makeAggregate(args.front());
+ std::for_each(args.begin() + 1, args.end(),
+ [&](TIntermTyped* arg) {
+ arguments = intermediate.growAggregate(arguments, arg);
+ });
+ } else {
+ auto it = aggregate->getSequence().erase(aggregate->getSequence().begin() + paramNum);
+ aggregate->getSequence().insert(it, args.begin(), args.end());
+ }
+ functionParamNumberOffset += (int)(args.size() - 1);
+ }
+ };
+
+ // Process each argument's conversion
+ for (int param = 0; param < function.getParamCount(); ++param) {
+ // At this early point there is a slight ambiguity between whether an aggregate 'arguments'
+ // is the single argument itself or its children are the arguments. Only one argument
+ // means take 'arguments' itself as the one argument.
+ TIntermTyped* arg = function.getParamCount() == 1
+ ? arguments->getAsTyped()
+ : (aggregate ?
+ aggregate->getSequence()[param + functionParamNumberOffset]->getAsTyped() :
+ arguments->getAsTyped());
+
+ if (wasFlattened(arg) && shouldFlatten(*function[param].type, function[param].type->getQualifier().storage, true)) {
+ // Need to pass the structure members instead of the structure.
+ TVector<TIntermTyped*> memberArgs;
+ for (int memb = 0; memb < (int)arg->getType().getStruct()->size(); ++memb)
+ memberArgs.push_back(flattenAccess(arg, memb));
+ setArgList(param + functionParamNumberOffset, memberArgs);
+ }
+ }
+
+ // TODO: if we need both hidden counter args (below) and struct expansion (above)
+ // the two algorithms need to be merged: Each assumes the list starts out 1:1 between
+ // parameters and arguments.
+
+ // If any argument is a pass-by-reference struct buffer with an associated counter
+ // buffer, we have to add another hidden parameter for that counter.
+ if (aggregate)
+ addStructBuffArguments(loc, aggregate);
+}
+
+//
+// Add any needed implicit output conversions for function-call arguments. This
+// can require a new tree topology, complicated further by whether the function
+// has a return value.
+//
+// Returns a node of a subtree that evaluates to the return value of the function.
+//
+TIntermTyped* HlslParseContext::addOutputArgumentConversions(const TFunction& function, TIntermOperator& intermNode)
+{
+ assert (intermNode.getAsAggregate() != nullptr || intermNode.getAsUnaryNode() != nullptr);
+
+ const TSourceLoc& loc = intermNode.getLoc();
+
+ TIntermSequence argSequence; // temp sequence for unary node args
+
+ if (intermNode.getAsUnaryNode())
+ argSequence.push_back(intermNode.getAsUnaryNode()->getOperand());
+
+ TIntermSequence& arguments = argSequence.empty() ? intermNode.getAsAggregate()->getSequence() : argSequence;
+
+ const auto needsConversion = [&](int argNum) {
+ return function[argNum].type->getQualifier().isParamOutput() &&
+ (*function[argNum].type != arguments[argNum]->getAsTyped()->getType() ||
+ shouldConvertLValue(arguments[argNum]) ||
+ wasFlattened(arguments[argNum]->getAsTyped()));
+ };
+
+ // Will there be any output conversions?
+ bool outputConversions = false;
+ for (int i = 0; i < function.getParamCount(); ++i) {
+ if (needsConversion(i)) {
+ outputConversions = true;
+ break;
+ }
+ }
+
+ if (! outputConversions)
+ return &intermNode;
+
+ // Setup for the new tree, if needed:
+ //
+ // Output conversions need a different tree topology.
+ // Out-qualified arguments need a temporary of the correct type, with the call
+ // followed by an assignment of the temporary to the original argument:
+ // void: function(arg, ...) -> ( function(tempArg, ...), arg = tempArg, ...)
+ // ret = function(arg, ...) -> ret = (tempRet = function(tempArg, ...), arg = tempArg, ..., tempRet)
+ // Where the "tempArg" type needs no conversion as an argument, but will convert on assignment.
+ TIntermTyped* conversionTree = nullptr;
+ TVariable* tempRet = nullptr;
+ if (intermNode.getBasicType() != EbtVoid) {
+ // do the "tempRet = function(...), " bit from above
+ tempRet = makeInternalVariable("tempReturn", intermNode.getType());
+ TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, loc);
+ conversionTree = intermediate.addAssign(EOpAssign, tempRetNode, &intermNode, loc);
+ } else
+ conversionTree = &intermNode;
+
+ conversionTree = intermediate.makeAggregate(conversionTree);
+
+ // Process each argument's conversion
+ for (int i = 0; i < function.getParamCount(); ++i) {
+ if (needsConversion(i)) {
+ // Out-qualified arguments needing conversion need to use the topology setup above.
+ // Do the " ...(tempArg, ...), arg = tempArg" bit from above.
+
+ // Make a temporary for what the function expects the argument to look like.
+ TVariable* tempArg = makeInternalVariable("tempArg", *function[i].type);
+ tempArg->getWritableType().getQualifier().makeTemporary();
+ TIntermSymbol* tempArgNode = intermediate.addSymbol(*tempArg, loc);
+
+ // This makes the deepest level, the member-wise copy
+ TIntermTyped* tempAssign = handleAssign(arguments[i]->getLoc(), EOpAssign, arguments[i]->getAsTyped(),
+ tempArgNode);
+ tempAssign = handleLvalue(arguments[i]->getLoc(), "assign", tempAssign);
+ conversionTree = intermediate.growAggregate(conversionTree, tempAssign, arguments[i]->getLoc());
+
+ // replace the argument with another node for the same tempArg variable
+ arguments[i] = intermediate.addSymbol(*tempArg, loc);
+ }
+ }
+
+ // Finalize the tree topology (see bigger comment above).
+ if (tempRet) {
+ // do the "..., tempRet" bit from above
+ TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, loc);
+ conversionTree = intermediate.growAggregate(conversionTree, tempRetNode, loc);
+ }
+
+ conversionTree = intermediate.setAggregateOperator(conversionTree, EOpComma, intermNode.getType(), loc);
+
+ return conversionTree;
+}
+
+//
+// Add any needed "hidden" counter buffer arguments for function calls.
+//
+// Modifies the 'aggregate' argument if needed. Otherwise, is no-op.
+//
+void HlslParseContext::addStructBuffArguments(const TSourceLoc& loc, TIntermAggregate*& aggregate)
+{
+ // See if there are any SB types with counters.
+ const bool hasStructBuffArg =
+ std::any_of(aggregate->getSequence().begin(),
+ aggregate->getSequence().end(),
+ [this](const TIntermNode* node) {
+ return (node && node->getAsTyped() != nullptr) && hasStructBuffCounter(node->getAsTyped()->getType());
+ });
+
+ // Nothing to do, if we didn't find one.
+ if (! hasStructBuffArg)
+ return;
+
+ TIntermSequence argsWithCounterBuffers;
+
+ for (int param = 0; param < int(aggregate->getSequence().size()); ++param) {
+ argsWithCounterBuffers.push_back(aggregate->getSequence()[param]);
+
+ if (hasStructBuffCounter(aggregate->getSequence()[param]->getAsTyped()->getType())) {
+ const TIntermSymbol* blockSym = aggregate->getSequence()[param]->getAsSymbolNode();
+ if (blockSym != nullptr) {
+ TType counterType;
+ counterBufferType(loc, counterType);
+
+ const TString counterBlockName(intermediate.addCounterBufferName(blockSym->getName()));
+
+ TVariable* variable = makeInternalVariable(counterBlockName, counterType);
+
+ // Mark this buffer's counter block as being in use
+ structBufferCounter[counterBlockName] = true;
+
+ TIntermSymbol* sym = intermediate.addSymbol(*variable, loc);
+ argsWithCounterBuffers.push_back(sym);
+ }
+ }
+ }
+
+ // Swap with the temp list we've built up.
+ aggregate->getSequence().swap(argsWithCounterBuffers);
+}
+
+
+//
+// Do additional checking of built-in function calls that is not caught
+// by normal semantic checks on argument type, extension tagging, etc.
+//
+// Assumes there has been a semantically correct match to a built-in function prototype.
+//
+void HlslParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermOperator& callNode)
+{
+ // Set up convenience accessors to the argument(s). There is almost always
+ // multiple arguments for the cases below, but when there might be one,
+ // check the unaryArg first.
+ const TIntermSequence* argp = nullptr; // confusing to use [] syntax on a pointer, so this is to help get a reference
+ const TIntermTyped* unaryArg = nullptr;
+ const TIntermTyped* arg0 = nullptr;
+ if (callNode.getAsAggregate()) {
+ argp = &callNode.getAsAggregate()->getSequence();
+ if (argp->size() > 0)
+ arg0 = (*argp)[0]->getAsTyped();
+ } else {
+ assert(callNode.getAsUnaryNode());
+ unaryArg = callNode.getAsUnaryNode()->getOperand();
+ arg0 = unaryArg;
+ }
+ const TIntermSequence& aggArgs = *argp; // only valid when unaryArg is nullptr
+
+ switch (callNode.getOp()) {
+ case EOpTextureGather:
+ case EOpTextureGatherOffset:
+ case EOpTextureGatherOffsets:
+ {
+ // Figure out which variants are allowed by what extensions,
+ // and what arguments must be constant for which situations.
+
+ TString featureString = fnCandidate.getName() + "(...)";
+ const char* feature = featureString.c_str();
+ int compArg = -1; // track which argument, if any, is the constant component argument
+ switch (callNode.getOp()) {
+ case EOpTextureGather:
+ // More than two arguments needs gpu_shader5, and rectangular or shadow needs gpu_shader5,
+ // otherwise, need GL_ARB_texture_gather.
+ if (fnCandidate.getParamCount() > 2 || fnCandidate[0].type->getSampler().dim == EsdRect ||
+ fnCandidate[0].type->getSampler().shadow) {
+ if (! fnCandidate[0].type->getSampler().shadow)
+ compArg = 2;
+ }
+ break;
+ case EOpTextureGatherOffset:
+ // GL_ARB_texture_gather is good enough for 2D non-shadow textures with no component argument
+ if (! fnCandidate[0].type->getSampler().shadow)
+ compArg = 3;
+ break;
+ case EOpTextureGatherOffsets:
+ if (! fnCandidate[0].type->getSampler().shadow)
+ compArg = 3;
+ break;
+ default:
+ break;
+ }
+
+ if (compArg > 0 && compArg < fnCandidate.getParamCount()) {
+ if (aggArgs[compArg]->getAsConstantUnion()) {
+ int value = aggArgs[compArg]->getAsConstantUnion()->getConstArray()[0].getIConst();
+ if (value < 0 || value > 3)
+ error(loc, "must be 0, 1, 2, or 3:", feature, "component argument");
+ } else
+ error(loc, "must be a compile-time constant:", feature, "component argument");
+ }
+
+ break;
+ }
+
+ case EOpTextureOffset:
+ case EOpTextureFetchOffset:
+ case EOpTextureProjOffset:
+ case EOpTextureLodOffset:
+ case EOpTextureProjLodOffset:
+ case EOpTextureGradOffset:
+ case EOpTextureProjGradOffset:
+ {
+ // Handle texture-offset limits checking
+ // Pick which argument has to hold constant offsets
+ int arg = -1;
+ switch (callNode.getOp()) {
+ case EOpTextureOffset: arg = 2; break;
+ case EOpTextureFetchOffset: arg = (arg0->getType().getSampler().dim != EsdRect) ? 3 : 2; break;
+ case EOpTextureProjOffset: arg = 2; break;
+ case EOpTextureLodOffset: arg = 3; break;
+ case EOpTextureProjLodOffset: arg = 3; break;
+ case EOpTextureGradOffset: arg = 4; break;
+ case EOpTextureProjGradOffset: arg = 4; break;
+ default:
+ assert(0);
+ break;
+ }
+
+ if (arg > 0) {
+ if (aggArgs[arg]->getAsConstantUnion() == nullptr)
+ error(loc, "argument must be compile-time constant", "texel offset", "");
+ else {
+ const TType& type = aggArgs[arg]->getAsTyped()->getType();
+ for (int c = 0; c < type.getVectorSize(); ++c) {
+ int offset = aggArgs[arg]->getAsConstantUnion()->getConstArray()[c].getIConst();
+ if (offset > resources.maxProgramTexelOffset || offset < resources.minProgramTexelOffset)
+ error(loc, "value is out of range:", "texel offset",
+ "[gl_MinProgramTexelOffset, gl_MaxProgramTexelOffset]");
+ }
+ }
+ }
+
+ break;
+ }
+
+ case EOpTextureQuerySamples:
+ case EOpImageQuerySamples:
+ break;
+
+ case EOpImageAtomicAdd:
+ case EOpImageAtomicMin:
+ case EOpImageAtomicMax:
+ case EOpImageAtomicAnd:
+ case EOpImageAtomicOr:
+ case EOpImageAtomicXor:
+ case EOpImageAtomicExchange:
+ case EOpImageAtomicCompSwap:
+ break;
+
+ case EOpInterpolateAtCentroid:
+ case EOpInterpolateAtSample:
+ case EOpInterpolateAtOffset:
+ // Make sure the first argument is an interpolant, or an array element of an interpolant
+ if (arg0->getType().getQualifier().storage != EvqVaryingIn) {
+ // It might still be an array element.
+ //
+ // We could check more, but the semantics of the first argument are already met; the
+ // only way to turn an array into a float/vec* is array dereference and swizzle.
+ //
+ // ES and desktop 4.3 and earlier: swizzles may not be used
+ // desktop 4.4 and later: swizzles may be used
+ const TIntermTyped* base = TIntermediate::findLValueBase(arg0, true);
+ if (base == nullptr || base->getType().getQualifier().storage != EvqVaryingIn)
+ error(loc, "first argument must be an interpolant, or interpolant-array element",
+ fnCandidate.getName().c_str(), "");
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+//
+// Handle seeing something in a grammar production that can be done by calling
+// a constructor.
+//
+// The constructor still must be "handled" by handleFunctionCall(), which will
+// then call handleConstructor().
+//
+TFunction* HlslParseContext::makeConstructorCall(const TSourceLoc& loc, const TType& type)
+{
+ TOperator op = intermediate.mapTypeToConstructorOp(type);
+
+ if (op == EOpNull) {
+ error(loc, "cannot construct this type", type.getBasicString(), "");
+ return nullptr;
+ }
+
+ TString empty("");
+
+ return new TFunction(&empty, type, op);
+}
+
+//
+// Handle seeing a "COLON semantic" at the end of a type declaration,
+// by updating the type according to the semantic.
+//
+void HlslParseContext::handleSemantic(TSourceLoc loc, TQualifier& qualifier, TBuiltInVariable builtIn,
+ const TString& upperCase)
+{
+ // Parse and return semantic number. If limit is 0, it will be ignored. Otherwise, if the parsed
+ // semantic number is >= limit, errorMsg is issued and 0 is returned.
+ // TODO: it would be nicer if limit and errorMsg had default parameters, but some compilers don't yet
+ // accept those in lambda functions.
+ const auto getSemanticNumber = [this, loc](const TString& semantic, unsigned int limit, const char* errorMsg) -> unsigned int {
+ size_t pos = semantic.find_last_not_of("0123456789");
+ if (pos == std::string::npos)
+ return 0u;
+
+ unsigned int semanticNum = (unsigned int)atoi(semantic.c_str() + pos + 1);
+
+ if (limit != 0 && semanticNum >= limit) {
+ error(loc, errorMsg, semantic.c_str(), "");
+ return 0u;
+ }
+
+ return semanticNum;
+ };
+
+ switch(builtIn) {
+ case EbvNone:
+ // Get location numbers from fragment outputs, instead of
+ // auto-assigning them.
+ if (language == EShLangFragment && upperCase.compare(0, 9, "SV_TARGET") == 0) {
+ qualifier.layoutLocation = getSemanticNumber(upperCase, 0, nullptr);
+ nextOutLocation = std::max(nextOutLocation, qualifier.layoutLocation + 1u);
+ } else if (upperCase.compare(0, 15, "SV_CLIPDISTANCE") == 0) {
+ builtIn = EbvClipDistance;
+ qualifier.layoutLocation = getSemanticNumber(upperCase, maxClipCullRegs, "invalid clip semantic");
+ } else if (upperCase.compare(0, 15, "SV_CULLDISTANCE") == 0) {
+ builtIn = EbvCullDistance;
+ qualifier.layoutLocation = getSemanticNumber(upperCase, maxClipCullRegs, "invalid cull semantic");
+ }
+ break;
+ case EbvPosition:
+ // adjust for stage in/out
+ if (language == EShLangFragment)
+ builtIn = EbvFragCoord;
+ break;
+ case EbvFragStencilRef:
+ error(loc, "unimplemented; need ARB_shader_stencil_export", "SV_STENCILREF", "");
+ break;
+ case EbvTessLevelInner:
+ case EbvTessLevelOuter:
+ qualifier.patch = true;
+ break;
+ default:
+ break;
+ }
+
+ if (qualifier.builtIn == EbvNone)
+ qualifier.builtIn = builtIn;
+ qualifier.semanticName = intermediate.addSemanticName(upperCase);
+}
+
+//
+// Handle seeing something like "PACKOFFSET LEFT_PAREN c[Subcomponent][.component] RIGHT_PAREN"
+//
+// 'location' has the "c[Subcomponent]" part.
+// 'component' points to the "component" part, or nullptr if not present.
+//
+void HlslParseContext::handlePackOffset(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString& location,
+ const glslang::TString* component)
+{
+ if (location.size() == 0 || location[0] != 'c') {
+ error(loc, "expected 'c'", "packoffset", "");
+ return;
+ }
+ if (location.size() == 1)
+ return;
+ if (! isdigit(location[1])) {
+ error(loc, "expected number after 'c'", "packoffset", "");
+ return;
+ }
+
+ qualifier.layoutOffset = 16 * atoi(location.substr(1, location.size()).c_str());
+ if (component != nullptr) {
+ int componentOffset = 0;
+ switch ((*component)[0]) {
+ case 'x': componentOffset = 0; break;
+ case 'y': componentOffset = 4; break;
+ case 'z': componentOffset = 8; break;
+ case 'w': componentOffset = 12; break;
+ default:
+ componentOffset = -1;
+ break;
+ }
+ if (componentOffset < 0 || component->size() > 1) {
+ error(loc, "expected {x, y, z, w} for component", "packoffset", "");
+ return;
+ }
+ qualifier.layoutOffset += componentOffset;
+ }
+}
+
+//
+// Handle seeing something like "REGISTER LEFT_PAREN [shader_profile,] Type# RIGHT_PAREN"
+//
+// 'profile' points to the shader_profile part, or nullptr if not present.
+// 'desc' is the type# part.
+//
+void HlslParseContext::handleRegister(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString* profile,
+ const glslang::TString& desc, int subComponent, const glslang::TString* spaceDesc)
+{
+ if (profile != nullptr)
+ warn(loc, "ignoring shader_profile", "register", "");
+
+ if (desc.size() < 1) {
+ error(loc, "expected register type", "register", "");
+ return;
+ }
+
+ int regNumber = 0;
+ if (desc.size() > 1) {
+ if (isdigit(desc[1]))
+ regNumber = atoi(desc.substr(1, desc.size()).c_str());
+ else {
+ error(loc, "expected register number after register type", "register", "");
+ return;
+ }
+ }
+
+ // more information about register types see
+ // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl-variable-register
+ const std::vector<std::string>& resourceInfo = intermediate.getResourceSetBinding();
+ switch (std::tolower(desc[0])) {
+ case 'c':
+ // c register is the register slot in the global const buffer
+ // each slot is a vector of 4 32 bit components
+ qualifier.layoutOffset = regNumber * 4 * 4;
+ break;
+ // const buffer register slot
+ case 'b':
+ // textrues and structured buffers
+ case 't':
+ // samplers
+ case 's':
+ // uav resources
+ case 'u':
+ // if nothing else has set the binding, do so now
+ // (other mechanisms override this one)
+ if (!qualifier.hasBinding())
+ qualifier.layoutBinding = regNumber + subComponent;
+
+ // This handles per-register layout sets numbers. For the global mode which sets
+ // every symbol to the same value, see setLinkageLayoutSets().
+ if ((resourceInfo.size() % 3) == 0) {
+ // Apply per-symbol resource set and binding.
+ for (auto it = resourceInfo.cbegin(); it != resourceInfo.cend(); it = it + 3) {
+ if (strcmp(desc.c_str(), it[0].c_str()) == 0) {
+ qualifier.layoutSet = atoi(it[1].c_str());
+ qualifier.layoutBinding = atoi(it[2].c_str()) + subComponent;
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ warn(loc, "ignoring unrecognized register type", "register", "%c", desc[0]);
+ break;
+ }
+
+ // space
+ unsigned int setNumber;
+ const auto crackSpace = [&]() -> bool {
+ const int spaceLen = 5;
+ if (spaceDesc->size() < spaceLen + 1)
+ return false;
+ if (spaceDesc->compare(0, spaceLen, "space") != 0)
+ return false;
+ if (! isdigit((*spaceDesc)[spaceLen]))
+ return false;
+ setNumber = atoi(spaceDesc->substr(spaceLen, spaceDesc->size()).c_str());
+ return true;
+ };
+
+ // if nothing else has set the set, do so now
+ // (other mechanisms override this one)
+ if (spaceDesc && !qualifier.hasSet()) {
+ if (! crackSpace()) {
+ error(loc, "expected spaceN", "register", "");
+ return;
+ }
+ qualifier.layoutSet = setNumber;
+ }
+}
+
+// Convert to a scalar boolean, or if not allowed by HLSL semantics,
+// report an error and return nullptr.
+TIntermTyped* HlslParseContext::convertConditionalExpression(const TSourceLoc& loc, TIntermTyped* condition,
+ bool mustBeScalar)
+{
+ if (mustBeScalar && !condition->getType().isScalarOrVec1()) {
+ error(loc, "requires a scalar", "conditional expression", "");
+ return nullptr;
+ }
+
+ return intermediate.addConversion(EOpConstructBool, TType(EbtBool, EvqTemporary, condition->getVectorSize()),
+ condition);
+}
+
+//
+// Same error message for all places assignments don't work.
+//
+void HlslParseContext::assignError(const TSourceLoc& loc, const char* op, TString left, TString right)
+{
+ error(loc, "", op, "cannot convert from '%s' to '%s'",
+ right.c_str(), left.c_str());
+}
+
+//
+// Same error message for all places unary operations don't work.
+//
+void HlslParseContext::unaryOpError(const TSourceLoc& loc, const char* op, TString operand)
+{
+ error(loc, " wrong operand type", op,
+ "no operation '%s' exists that takes an operand of type %s (or there is no acceptable conversion)",
+ op, operand.c_str());
+}
+
+//
+// Same error message for all binary operations don't work.
+//
+void HlslParseContext::binaryOpError(const TSourceLoc& loc, const char* op, TString left, TString right)
+{
+ error(loc, " wrong operand types:", op,
+ "no operation '%s' exists that takes a left-hand operand of type '%s' and "
+ "a right operand of type '%s' (or there is no acceptable conversion)",
+ op, left.c_str(), right.c_str());
+}
+
+//
+// A basic type of EbtVoid is a key that the name string was seen in the source, but
+// it was not found as a variable in the symbol table. If so, give the error
+// message and insert a dummy variable in the symbol table to prevent future errors.
+//
+void HlslParseContext::variableCheck(TIntermTyped*& nodePtr)
+{
+ TIntermSymbol* symbol = nodePtr->getAsSymbolNode();
+ if (! symbol)
+ return;
+
+ if (symbol->getType().getBasicType() == EbtVoid) {
+ error(symbol->getLoc(), "undeclared identifier", symbol->getName().c_str(), "");
+
+ // Add to symbol table to prevent future error messages on the same name
+ if (symbol->getName().size() > 0) {
+ TVariable* fakeVariable = new TVariable(&symbol->getName(), TType(EbtFloat));
+ symbolTable.insert(*fakeVariable);
+
+ // substitute a symbol node for this new variable
+ nodePtr = intermediate.addSymbol(*fakeVariable, symbol->getLoc());
+ }
+ }
+}
+
+//
+// Both test, and if necessary spit out an error, to see if the node is really
+// a constant.
+//
+void HlslParseContext::constantValueCheck(TIntermTyped* node, const char* token)
+{
+ if (node->getQualifier().storage != EvqConst)
+ error(node->getLoc(), "constant expression required", token, "");
+}
+
+//
+// Both test, and if necessary spit out an error, to see if the node is really
+// an integer.
+//
+void HlslParseContext::integerCheck(const TIntermTyped* node, const char* token)
+{
+ if ((node->getBasicType() == EbtInt || node->getBasicType() == EbtUint) && node->isScalar())
+ return;
+
+ error(node->getLoc(), "scalar integer expression required", token, "");
+}
+
+//
+// Both test, and if necessary spit out an error, to see if we are currently
+// globally scoped.
+//
+void HlslParseContext::globalCheck(const TSourceLoc& loc, const char* token)
+{
+ if (! symbolTable.atGlobalLevel())
+ error(loc, "not allowed in nested scope", token, "");
+}
+
+bool HlslParseContext::builtInName(const TString& /*identifier*/)
+{
+ return false;
+}
+
+//
+// Make sure there is enough data and not too many arguments provided to the
+// constructor to build something of the type of the constructor. Also returns
+// the type of the constructor.
+//
+// Returns true if there was an error in construction.
+//
+bool HlslParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, TFunction& function,
+ TOperator op, TType& type)
+{
+ type.shallowCopy(function.getType());
+
+ bool constructingMatrix = false;
+ switch (op) {
+ case EOpConstructTextureSampler:
+ error(loc, "unhandled texture constructor", "constructor", "");
+ return true;
+ case EOpConstructMat2x2:
+ case EOpConstructMat2x3:
+ case EOpConstructMat2x4:
+ case EOpConstructMat3x2:
+ case EOpConstructMat3x3:
+ case EOpConstructMat3x4:
+ case EOpConstructMat4x2:
+ case EOpConstructMat4x3:
+ case EOpConstructMat4x4:
+ case EOpConstructDMat2x2:
+ case EOpConstructDMat2x3:
+ case EOpConstructDMat2x4:
+ case EOpConstructDMat3x2:
+ case EOpConstructDMat3x3:
+ case EOpConstructDMat3x4:
+ case EOpConstructDMat4x2:
+ case EOpConstructDMat4x3:
+ case EOpConstructDMat4x4:
+ case EOpConstructIMat2x2:
+ case EOpConstructIMat2x3:
+ case EOpConstructIMat2x4:
+ case EOpConstructIMat3x2:
+ case EOpConstructIMat3x3:
+ case EOpConstructIMat3x4:
+ case EOpConstructIMat4x2:
+ case EOpConstructIMat4x3:
+ case EOpConstructIMat4x4:
+ case EOpConstructUMat2x2:
+ case EOpConstructUMat2x3:
+ case EOpConstructUMat2x4:
+ case EOpConstructUMat3x2:
+ case EOpConstructUMat3x3:
+ case EOpConstructUMat3x4:
+ case EOpConstructUMat4x2:
+ case EOpConstructUMat4x3:
+ case EOpConstructUMat4x4:
+ case EOpConstructBMat2x2:
+ case EOpConstructBMat2x3:
+ case EOpConstructBMat2x4:
+ case EOpConstructBMat3x2:
+ case EOpConstructBMat3x3:
+ case EOpConstructBMat3x4:
+ case EOpConstructBMat4x2:
+ case EOpConstructBMat4x3:
+ case EOpConstructBMat4x4:
+ constructingMatrix = true;
+ break;
+ default:
+ break;
+ }
+
+ //
+ // Walk the arguments for first-pass checks and collection of information.
+ //
+
+ int size = 0;
+ bool constType = true;
+ bool full = false;
+ bool overFull = false;
+ bool matrixInMatrix = false;
+ bool arrayArg = false;
+ for (int arg = 0; arg < function.getParamCount(); ++arg) {
+ if (function[arg].type->isArray()) {
+ if (function[arg].type->isUnsizedArray()) {
+ // Can't construct from an unsized array.
+ error(loc, "array argument must be sized", "constructor", "");
+ return true;
+ }
+ arrayArg = true;
+ }
+ if (constructingMatrix && function[arg].type->isMatrix())
+ matrixInMatrix = true;
+
+ // 'full' will go to true when enough args have been seen. If we loop
+ // again, there is an extra argument.
+ if (full) {
+ // For vectors and matrices, it's okay to have too many components
+ // available, but not okay to have unused arguments.
+ overFull = true;
+ }
+
+ size += function[arg].type->computeNumComponents();
+ if (op != EOpConstructStruct && ! type.isArray() && size >= type.computeNumComponents())
+ full = true;
+
+ if (function[arg].type->getQualifier().storage != EvqConst)
+ constType = false;
+ }
+
+ if (constType)
+ type.getQualifier().storage = EvqConst;
+
+ if (type.isArray()) {
+ if (function.getParamCount() == 0) {
+ error(loc, "array constructor must have at least one argument", "constructor", "");
+ return true;
+ }
+
+ if (type.isUnsizedArray()) {
+ // auto adapt the constructor type to the number of arguments
+ type.changeOuterArraySize(function.getParamCount());
+ } else if (type.getOuterArraySize() != function.getParamCount() && type.computeNumComponents() > size) {
+ error(loc, "array constructor needs one argument per array element", "constructor", "");
+ return true;
+ }
+
+ if (type.isArrayOfArrays()) {
+ // Types have to match, but we're still making the type.
+ // Finish making the type, and the comparison is done later
+ // when checking for conversion.
+ TArraySizes& arraySizes = *type.getArraySizes();
+
+ // At least the dimensionalities have to match.
+ if (! function[0].type->isArray() ||
+ arraySizes.getNumDims() != function[0].type->getArraySizes()->getNumDims() + 1) {
+ error(loc, "array constructor argument not correct type to construct array element", "constructor", "");
+ return true;
+ }
+
+ if (arraySizes.isInnerUnsized()) {
+ // "Arrays of arrays ..., and the size for any dimension is optional"
+ // That means we need to adopt (from the first argument) the other array sizes into the type.
+ for (int d = 1; d < arraySizes.getNumDims(); ++d) {
+ if (arraySizes.getDimSize(d) == UnsizedArraySize) {
+ arraySizes.setDimSize(d, function[0].type->getArraySizes()->getDimSize(d - 1));
+ }
+ }
+ }
+ }
+ }
+
+ // Some array -> array type casts are okay
+ if (arrayArg && function.getParamCount() == 1 && op != EOpConstructStruct && type.isArray() &&
+ !type.isArrayOfArrays() && !function[0].type->isArrayOfArrays() &&
+ type.getVectorSize() >= 1 && function[0].type->getVectorSize() >= 1)
+ return false;
+
+ if (arrayArg && op != EOpConstructStruct && ! type.isArrayOfArrays()) {
+ error(loc, "constructing non-array constituent from array argument", "constructor", "");
+ return true;
+ }
+
+ if (matrixInMatrix && ! type.isArray()) {
+ return false;
+ }
+
+ if (overFull) {
+ error(loc, "too many arguments", "constructor", "");
+ return true;
+ }
+
+ if (op == EOpConstructStruct && ! type.isArray()) {
+ if (isScalarConstructor(node))
+ return false;
+
+ // Self-type construction: e.g, we can construct a struct from a single identically typed object.
+ if (function.getParamCount() == 1 && type == *function[0].type)
+ return false;
+
+ if ((int)type.getStruct()->size() != function.getParamCount()) {
+ error(loc, "Number of constructor parameters does not match the number of structure fields", "constructor", "");
+ return true;
+ }
+ }
+
+ if ((op != EOpConstructStruct && size != 1 && size < type.computeNumComponents()) ||
+ (op == EOpConstructStruct && size < type.computeNumComponents())) {
+ error(loc, "not enough data provided for construction", "constructor", "");
+ return true;
+ }
+
+ return false;
+}
+
+// See if 'node', in the context of constructing aggregates, is a scalar argument
+// to a constructor.
+//
+bool HlslParseContext::isScalarConstructor(const TIntermNode* node)
+{
+ // Obviously, it must be a scalar, but an aggregate node might not be fully
+ // completed yet: holding a sequence of initializers under an aggregate
+ // would not yet be typed, so don't check it's type. This corresponds to
+ // the aggregate operator also not being set yet. (An aggregate operation
+ // that legitimately yields a scalar will have a getOp() of that operator,
+ // not EOpNull.)
+
+ return node->getAsTyped() != nullptr &&
+ node->getAsTyped()->isScalar() &&
+ (node->getAsAggregate() == nullptr || node->getAsAggregate()->getOp() != EOpNull);
+}
+
+// Checks to see if a void variable has been declared and raise an error message for such a case
+//
+// returns true in case of an error
+//
+bool HlslParseContext::voidErrorCheck(const TSourceLoc& loc, const TString& identifier, const TBasicType basicType)
+{
+ if (basicType == EbtVoid) {
+ error(loc, "illegal use of type 'void'", identifier.c_str(), "");
+ return true;
+ }
+
+ return false;
+}
+
+//
+// Fix just a full qualifier (no variables or types yet, but qualifier is complete) at global level.
+//
+void HlslParseContext::globalQualifierFix(const TSourceLoc&, TQualifier& qualifier)
+{
+ // move from parameter/unknown qualifiers to pipeline in/out qualifiers
+ switch (qualifier.storage) {
+ case EvqIn:
+ qualifier.storage = EvqVaryingIn;
+ break;
+ case EvqOut:
+ qualifier.storage = EvqVaryingOut;
+ break;
+ default:
+ break;
+ }
+}
+
+//
+// Merge characteristics of the 'src' qualifier into the 'dst'.
+// If there is duplication, issue error messages, unless 'force'
+// is specified, which means to just override default settings.
+//
+// Also, when force is false, it will be assumed that 'src' follows
+// 'dst', for the purpose of error checking order for versions
+// that require specific orderings of qualifiers.
+//
+void HlslParseContext::mergeQualifiers(TQualifier& dst, const TQualifier& src)
+{
+ // Storage qualification
+ if (dst.storage == EvqTemporary || dst.storage == EvqGlobal)
+ dst.storage = src.storage;
+ else if ((dst.storage == EvqIn && src.storage == EvqOut) ||
+ (dst.storage == EvqOut && src.storage == EvqIn))
+ dst.storage = EvqInOut;
+ else if ((dst.storage == EvqIn && src.storage == EvqConst) ||
+ (dst.storage == EvqConst && src.storage == EvqIn))
+ dst.storage = EvqConstReadOnly;
+
+ // Layout qualifiers
+ mergeObjectLayoutQualifiers(dst, src, false);
+
+ // individual qualifiers
+ bool repeated = false;
+#define MERGE_SINGLETON(field) repeated |= dst.field && src.field; dst.field |= src.field;
+ MERGE_SINGLETON(invariant);
+ MERGE_SINGLETON(noContraction);
+ MERGE_SINGLETON(centroid);
+ MERGE_SINGLETON(smooth);
+ MERGE_SINGLETON(flat);
+ MERGE_SINGLETON(nopersp);
+ MERGE_SINGLETON(patch);
+ MERGE_SINGLETON(sample);
+ MERGE_SINGLETON(coherent);
+ MERGE_SINGLETON(volatil);
+ MERGE_SINGLETON(restrict);
+ MERGE_SINGLETON(readonly);
+ MERGE_SINGLETON(writeonly);
+ MERGE_SINGLETON(specConstant);
+ MERGE_SINGLETON(nonUniform);
+}
+
+// used to flatten the sampler type space into a single dimension
+// correlates with the declaration of defaultSamplerPrecision[]
+int HlslParseContext::computeSamplerTypeIndex(TSampler& sampler)
+{
+ int arrayIndex = sampler.arrayed ? 1 : 0;
+ int shadowIndex = sampler.shadow ? 1 : 0;
+ int externalIndex = sampler.external ? 1 : 0;
+
+ return EsdNumDims *
+ (EbtNumTypes * (2 * (2 * arrayIndex + shadowIndex) + externalIndex) + sampler.type) + sampler.dim;
+}
+
+//
+// Do size checking for an array type's size.
+//
+void HlslParseContext::arraySizeCheck(const TSourceLoc& loc, TIntermTyped* expr, TArraySize& sizePair)
+{
+ bool isConst = false;
+ sizePair.size = 1;
+ sizePair.node = nullptr;
+
+ TIntermConstantUnion* constant = expr->getAsConstantUnion();
+ if (constant) {
+ // handle true (non-specialization) constant
+ sizePair.size = constant->getConstArray()[0].getIConst();
+ isConst = true;
+ } else {
+ // see if it's a specialization constant instead
+ if (expr->getQualifier().isSpecConstant()) {
+ isConst = true;
+ sizePair.node = expr;
+ TIntermSymbol* symbol = expr->getAsSymbolNode();
+ if (symbol && symbol->getConstArray().size() > 0)
+ sizePair.size = symbol->getConstArray()[0].getIConst();
+ }
+ }
+
+ if (! isConst || (expr->getBasicType() != EbtInt && expr->getBasicType() != EbtUint)) {
+ error(loc, "array size must be a constant integer expression", "", "");
+ return;
+ }
+
+ if (sizePair.size <= 0) {
+ error(loc, "array size must be a positive integer", "", "");
+ return;
+ }
+}
+
+//
+// Require array to be completely sized
+//
+void HlslParseContext::arraySizeRequiredCheck(const TSourceLoc& loc, const TArraySizes& arraySizes)
+{
+ if (arraySizes.hasUnsized())
+ error(loc, "array size required", "", "");
+}
+
+void HlslParseContext::structArrayCheck(const TSourceLoc& /*loc*/, const TType& type)
+{
+ const TTypeList& structure = *type.getStruct();
+ for (int m = 0; m < (int)structure.size(); ++m) {
+ const TType& member = *structure[m].type;
+ if (member.isArray())
+ arraySizeRequiredCheck(structure[m].loc, *member.getArraySizes());
+ }
+}
+
+//
+// Do all the semantic checking for declaring or redeclaring an array, with and
+// without a size, and make the right changes to the symbol table.
+//
+void HlslParseContext::declareArray(const TSourceLoc& loc, const TString& identifier, const TType& type,
+ TSymbol*& symbol, bool track)
+{
+ if (symbol == nullptr) {
+ bool currentScope;
+ symbol = symbolTable.find(identifier, nullptr, &currentScope);
+
+ if (symbol && builtInName(identifier) && ! symbolTable.atBuiltInLevel()) {
+ // bad shader (errors already reported) trying to redeclare a built-in name as an array
+ return;
+ }
+ if (symbol == nullptr || ! currentScope) {
+ //
+ // Successfully process a new definition.
+ // (Redeclarations have to take place at the same scope; otherwise they are hiding declarations)
+ //
+ symbol = new TVariable(&identifier, type);
+ symbolTable.insert(*symbol);
+ if (track && symbolTable.atGlobalLevel())
+ trackLinkage(*symbol);
+
+ return;
+ }
+ if (symbol->getAsAnonMember()) {
+ error(loc, "cannot redeclare a user-block member array", identifier.c_str(), "");
+ symbol = nullptr;
+ return;
+ }
+ }
+
+ //
+ // Process a redeclaration.
+ //
+
+ if (symbol == nullptr) {
+ error(loc, "array variable name expected", identifier.c_str(), "");
+ return;
+ }
+
+ // redeclareBuiltinVariable() should have already done the copyUp()
+ TType& existingType = symbol->getWritableType();
+
+ if (existingType.isSizedArray()) {
+ // be more lenient for input arrays to geometry shaders and tessellation control outputs,
+ // where the redeclaration is the same size
+ return;
+ }
+
+ existingType.updateArraySizes(type);
+}
+
+//
+// Enforce non-initializer type/qualifier rules.
+//
+void HlslParseContext::fixConstInit(const TSourceLoc& loc, const TString& identifier, TType& type,
+ TIntermTyped*& initializer)
+{
+ //
+ // Make the qualifier make sense, given that there is an initializer.
+ //
+ if (initializer == nullptr) {
+ if (type.getQualifier().storage == EvqConst ||
+ type.getQualifier().storage == EvqConstReadOnly) {
+ initializer = intermediate.makeAggregate(loc);
+ warn(loc, "variable with qualifier 'const' not initialized; zero initializing", identifier.c_str(), "");
+ }
+ }
+}
+
+//
+// See if the identifier is a built-in symbol that can be redeclared, and if so,
+// copy the symbol table's read-only built-in variable to the current
+// global level, where it can be modified based on the passed in type.
+//
+// Returns nullptr if no redeclaration took place; meaning a normal declaration still
+// needs to occur for it, not necessarily an error.
+//
+// Returns a redeclared and type-modified variable if a redeclared occurred.
+//
+TSymbol* HlslParseContext::redeclareBuiltinVariable(const TSourceLoc& /*loc*/, const TString& identifier,
+ const TQualifier& /*qualifier*/,
+ const TShaderQualifiers& /*publicType*/)
+{
+ if (! builtInName(identifier) || symbolTable.atBuiltInLevel() || ! symbolTable.atGlobalLevel())
+ return nullptr;
+
+ return nullptr;
+}
+
+//
+// Generate index to the array element in a structure buffer (SSBO)
+//
+TIntermTyped* HlslParseContext::indexStructBufferContent(const TSourceLoc& loc, TIntermTyped* buffer) const
+{
+ // Bail out if not a struct buffer
+ if (buffer == nullptr || ! isStructBufferType(buffer->getType()))
+ return nullptr;
+
+ // Runtime sized array is always the last element.
+ const TTypeList* bufferStruct = buffer->getType().getStruct();
+ TIntermTyped* arrayPosition = intermediate.addConstantUnion(unsigned(bufferStruct->size()-1), loc);
+
+ TIntermTyped* argArray = intermediate.addIndex(EOpIndexDirectStruct, buffer, arrayPosition, loc);
+ argArray->setType(*(*bufferStruct)[bufferStruct->size()-1].type);
+
+ return argArray;
+}
+
+//
+// IFF type is a structuredbuffer/byteaddressbuffer type, return the content
+// (template) type. E.g, StructuredBuffer<MyType> -> MyType. Else return nullptr.
+//
+TType* HlslParseContext::getStructBufferContentType(const TType& type) const
+{
+ if (type.getBasicType() != EbtBlock || type.getQualifier().storage != EvqBuffer)
+ return nullptr;
+
+ const int memberCount = (int)type.getStruct()->size();
+ assert(memberCount > 0);
+
+ TType* contentType = (*type.getStruct())[memberCount-1].type;
+
+ return contentType->isUnsizedArray() ? contentType : nullptr;
+}
+
+//
+// If an existing struct buffer has a sharable type, then share it.
+//
+void HlslParseContext::shareStructBufferType(TType& type)
+{
+ // PackOffset must be equivalent to share types on a per-member basis.
+ // Note: cannot use auto type due to recursion. Thus, this is a std::function.
+ const std::function<bool(TType& lhs, TType& rhs)>
+ compareQualifiers = [&](TType& lhs, TType& rhs) -> bool {
+ if (lhs.getQualifier().layoutOffset != rhs.getQualifier().layoutOffset)
+ return false;
+
+ if (lhs.isStruct() != rhs.isStruct())
+ return false;
+
+ if (lhs.isStruct() && rhs.isStruct()) {
+ if (lhs.getStruct()->size() != rhs.getStruct()->size())
+ return false;
+
+ for (int i = 0; i < int(lhs.getStruct()->size()); ++i)
+ if (!compareQualifiers(*(*lhs.getStruct())[i].type, *(*rhs.getStruct())[i].type))
+ return false;
+ }
+
+ return true;
+ };
+
+ // We need to compare certain qualifiers in addition to the type.
+ const auto typeEqual = [compareQualifiers](TType& lhs, TType& rhs) -> bool {
+ if (lhs.getQualifier().readonly != rhs.getQualifier().readonly)
+ return false;
+
+ // If both are structures, recursively look for packOffset equality
+ // as well as type equality.
+ return compareQualifiers(lhs, rhs) && lhs == rhs;
+ };
+
+ // This is an exhaustive O(N) search, but real world shaders have
+ // only a small number of these.
+ for (int idx = 0; idx < int(structBufferTypes.size()); ++idx) {
+ // If the deep structure matches, modulo qualifiers, use it
+ if (typeEqual(*structBufferTypes[idx], type)) {
+ type.shallowCopy(*structBufferTypes[idx]);
+ return;
+ }
+ }
+
+ // Otherwise, remember it:
+ TType* typeCopy = new TType;
+ typeCopy->shallowCopy(type);
+ structBufferTypes.push_back(typeCopy);
+}
+
+void HlslParseContext::paramFix(TType& type)
+{
+ switch (type.getQualifier().storage) {
+ case EvqConst:
+ type.getQualifier().storage = EvqConstReadOnly;
+ break;
+ case EvqGlobal:
+ case EvqUniform:
+ case EvqTemporary:
+ type.getQualifier().storage = EvqIn;
+ break;
+ case EvqBuffer:
+ {
+ // SSBO parameter. These do not go through the declareBlock path since they are fn parameters.
+ correctUniform(type.getQualifier());
+ TQualifier bufferQualifier = globalBufferDefaults;
+ mergeObjectLayoutQualifiers(bufferQualifier, type.getQualifier(), true);
+ bufferQualifier.storage = type.getQualifier().storage;
+ bufferQualifier.readonly = type.getQualifier().readonly;
+ bufferQualifier.coherent = type.getQualifier().coherent;
+ bufferQualifier.declaredBuiltIn = type.getQualifier().declaredBuiltIn;
+ type.getQualifier() = bufferQualifier;
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void HlslParseContext::specializationCheck(const TSourceLoc& loc, const TType& type, const char* op)
+{
+ if (type.containsSpecializationSize())
+ error(loc, "can't use with types containing arrays sized with a specialization constant", op, "");
+}
+
+//
+// Layout qualifier stuff.
+//
+
+// Put the id's layout qualification into the public type, for qualifiers not having a number set.
+// This is before we know any type information for error checking.
+void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id)
+{
+ std::transform(id.begin(), id.end(), id.begin(), ::tolower);
+
+ if (id == TQualifier::getLayoutMatrixString(ElmColumnMajor)) {
+ qualifier.layoutMatrix = ElmRowMajor;
+ return;
+ }
+ if (id == TQualifier::getLayoutMatrixString(ElmRowMajor)) {
+ qualifier.layoutMatrix = ElmColumnMajor;
+ return;
+ }
+ if (id == "push_constant") {
+ requireVulkan(loc, "push_constant");
+ qualifier.layoutPushConstant = true;
+ return;
+ }
+ if (language == EShLangGeometry || language == EShLangTessEvaluation) {
+ if (id == TQualifier::getGeometryString(ElgTriangles)) {
+ // publicType.shaderQualifiers.geometry = ElgTriangles;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ if (language == EShLangGeometry) {
+ if (id == TQualifier::getGeometryString(ElgPoints)) {
+ // publicType.shaderQualifiers.geometry = ElgPoints;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ if (id == TQualifier::getGeometryString(ElgLineStrip)) {
+ // publicType.shaderQualifiers.geometry = ElgLineStrip;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ if (id == TQualifier::getGeometryString(ElgLines)) {
+ // publicType.shaderQualifiers.geometry = ElgLines;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ if (id == TQualifier::getGeometryString(ElgLinesAdjacency)) {
+ // publicType.shaderQualifiers.geometry = ElgLinesAdjacency;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ if (id == TQualifier::getGeometryString(ElgTrianglesAdjacency)) {
+ // publicType.shaderQualifiers.geometry = ElgTrianglesAdjacency;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ if (id == TQualifier::getGeometryString(ElgTriangleStrip)) {
+ // publicType.shaderQualifiers.geometry = ElgTriangleStrip;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ } else {
+ assert(language == EShLangTessEvaluation);
+
+ // input primitive
+ if (id == TQualifier::getGeometryString(ElgTriangles)) {
+ // publicType.shaderQualifiers.geometry = ElgTriangles;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ if (id == TQualifier::getGeometryString(ElgQuads)) {
+ // publicType.shaderQualifiers.geometry = ElgQuads;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ if (id == TQualifier::getGeometryString(ElgIsolines)) {
+ // publicType.shaderQualifiers.geometry = ElgIsolines;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+
+ // vertex spacing
+ if (id == TQualifier::getVertexSpacingString(EvsEqual)) {
+ // publicType.shaderQualifiers.spacing = EvsEqual;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ if (id == TQualifier::getVertexSpacingString(EvsFractionalEven)) {
+ // publicType.shaderQualifiers.spacing = EvsFractionalEven;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ if (id == TQualifier::getVertexSpacingString(EvsFractionalOdd)) {
+ // publicType.shaderQualifiers.spacing = EvsFractionalOdd;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+
+ // triangle order
+ if (id == TQualifier::getVertexOrderString(EvoCw)) {
+ // publicType.shaderQualifiers.order = EvoCw;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ if (id == TQualifier::getVertexOrderString(EvoCcw)) {
+ // publicType.shaderQualifiers.order = EvoCcw;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+
+ // point mode
+ if (id == "point_mode") {
+ // publicType.shaderQualifiers.pointMode = true;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ }
+ }
+ if (language == EShLangFragment) {
+ if (id == "origin_upper_left") {
+ // publicType.shaderQualifiers.originUpperLeft = true;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ if (id == "pixel_center_integer") {
+ // publicType.shaderQualifiers.pixelCenterInteger = true;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ if (id == "early_fragment_tests") {
+ // publicType.shaderQualifiers.earlyFragmentTests = true;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ for (TLayoutDepth depth = (TLayoutDepth)(EldNone + 1); depth < EldCount; depth = (TLayoutDepth)(depth + 1)) {
+ if (id == TQualifier::getLayoutDepthString(depth)) {
+ // publicType.shaderQualifiers.layoutDepth = depth;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ }
+ if (id.compare(0, 13, "blend_support") == 0) {
+ bool found = false;
+ for (TBlendEquationShift be = (TBlendEquationShift)0; be < EBlendCount; be = (TBlendEquationShift)(be + 1)) {
+ if (id == TQualifier::getBlendEquationString(be)) {
+ requireExtensions(loc, 1, &E_GL_KHR_blend_equation_advanced, "blend equation");
+ intermediate.addBlendEquation(be);
+ // publicType.shaderQualifiers.blendEquation = true;
+ warn(loc, "ignored", id.c_str(), "");
+ found = true;
+ break;
+ }
+ }
+ if (! found)
+ error(loc, "unknown blend equation", "blend_support", "");
+ return;
+ }
+ }
+ error(loc, "unrecognized layout identifier, or qualifier requires assignment (e.g., binding = 4)", id.c_str(), "");
+}
+
+// Put the id's layout qualifier value into the public type, for qualifiers having a number set.
+// This is before we know any type information for error checking.
+void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id,
+ const TIntermTyped* node)
+{
+ const char* feature = "layout-id value";
+ // const char* nonLiteralFeature = "non-literal layout-id value";
+
+ integerCheck(node, feature);
+ const TIntermConstantUnion* constUnion = node->getAsConstantUnion();
+ int value = 0;
+ if (constUnion) {
+ value = constUnion->getConstArray()[0].getIConst();
+ }
+
+ std::transform(id.begin(), id.end(), id.begin(), ::tolower);
+
+ if (id == "offset") {
+ qualifier.layoutOffset = value;
+ return;
+ } else if (id == "align") {
+ // "The specified alignment must be a power of 2, or a compile-time error results."
+ if (! IsPow2(value))
+ error(loc, "must be a power of 2", "align", "");
+ else
+ qualifier.layoutAlign = value;
+ return;
+ } else if (id == "location") {
+ if ((unsigned int)value >= TQualifier::layoutLocationEnd)
+ error(loc, "location is too large", id.c_str(), "");
+ else
+ qualifier.layoutLocation = value;
+ return;
+ } else if (id == "set") {
+ if ((unsigned int)value >= TQualifier::layoutSetEnd)
+ error(loc, "set is too large", id.c_str(), "");
+ else
+ qualifier.layoutSet = value;
+ return;
+ } else if (id == "binding") {
+ if ((unsigned int)value >= TQualifier::layoutBindingEnd)
+ error(loc, "binding is too large", id.c_str(), "");
+ else
+ qualifier.layoutBinding = value;
+ return;
+ } else if (id == "component") {
+ if ((unsigned)value >= TQualifier::layoutComponentEnd)
+ error(loc, "component is too large", id.c_str(), "");
+ else
+ qualifier.layoutComponent = value;
+ return;
+ } else if (id.compare(0, 4, "xfb_") == 0) {
+ // "Any shader making any static use (after preprocessing) of any of these
+ // *xfb_* qualifiers will cause the shader to be in a transform feedback
+ // capturing mode and hence responsible for describing the transform feedback
+ // setup."
+ intermediate.setXfbMode();
+ if (id == "xfb_buffer") {
+ // "It is a compile-time error to specify an *xfb_buffer* that is greater than
+ // the implementation-dependent constant gl_MaxTransformFeedbackBuffers."
+ if (value >= resources.maxTransformFeedbackBuffers)
+ error(loc, "buffer is too large:", id.c_str(), "gl_MaxTransformFeedbackBuffers is %d",
+ resources.maxTransformFeedbackBuffers);
+ if (value >= (int)TQualifier::layoutXfbBufferEnd)
+ error(loc, "buffer is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbBufferEnd - 1);
+ else
+ qualifier.layoutXfbBuffer = value;
+ return;
+ } else if (id == "xfb_offset") {
+ if (value >= (int)TQualifier::layoutXfbOffsetEnd)
+ error(loc, "offset is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbOffsetEnd - 1);
+ else
+ qualifier.layoutXfbOffset = value;
+ return;
+ } else if (id == "xfb_stride") {
+ // "The resulting stride (implicit or explicit), when divided by 4, must be less than or equal to the
+ // implementation-dependent constant gl_MaxTransformFeedbackInterleavedComponents."
+ if (value > 4 * resources.maxTransformFeedbackInterleavedComponents)
+ error(loc, "1/4 stride is too large:", id.c_str(), "gl_MaxTransformFeedbackInterleavedComponents is %d",
+ resources.maxTransformFeedbackInterleavedComponents);
+ else if (value >= (int)TQualifier::layoutXfbStrideEnd)
+ error(loc, "stride is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbStrideEnd - 1);
+ if (value < (int)TQualifier::layoutXfbStrideEnd)
+ qualifier.layoutXfbStride = value;
+ return;
+ }
+ }
+
+ if (id == "input_attachment_index") {
+ requireVulkan(loc, "input_attachment_index");
+ if (value >= (int)TQualifier::layoutAttachmentEnd)
+ error(loc, "attachment index is too large", id.c_str(), "");
+ else
+ qualifier.layoutAttachment = value;
+ return;
+ }
+ if (id == "constant_id") {
+ setSpecConstantId(loc, qualifier, value);
+ return;
+ }
+
+ switch (language) {
+ case EShLangVertex:
+ break;
+
+ case EShLangTessControl:
+ if (id == "vertices") {
+ if (value == 0)
+ error(loc, "must be greater than 0", "vertices", "");
+ else
+ // publicType.shaderQualifiers.vertices = value;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ break;
+
+ case EShLangTessEvaluation:
+ break;
+
+ case EShLangGeometry:
+ if (id == "invocations") {
+ if (value == 0)
+ error(loc, "must be at least 1", "invocations", "");
+ else
+ // publicType.shaderQualifiers.invocations = value;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ if (id == "max_vertices") {
+ // publicType.shaderQualifiers.vertices = value;
+ warn(loc, "ignored", id.c_str(), "");
+ if (value > resources.maxGeometryOutputVertices)
+ error(loc, "too large, must be less than gl_MaxGeometryOutputVertices", "max_vertices", "");
+ return;
+ }
+ if (id == "stream") {
+ qualifier.layoutStream = value;
+ return;
+ }
+ break;
+
+ case EShLangFragment:
+ if (id == "index") {
+ qualifier.layoutIndex = value;
+ return;
+ }
+ break;
+
+ case EShLangCompute:
+ if (id.compare(0, 11, "local_size_") == 0) {
+ if (id == "local_size_x") {
+ // publicType.shaderQualifiers.localSize[0] = value;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ if (id == "local_size_y") {
+ // publicType.shaderQualifiers.localSize[1] = value;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ if (id == "local_size_z") {
+ // publicType.shaderQualifiers.localSize[2] = value;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ if (spvVersion.spv != 0) {
+ if (id == "local_size_x_id") {
+ // publicType.shaderQualifiers.localSizeSpecId[0] = value;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ if (id == "local_size_y_id") {
+ // publicType.shaderQualifiers.localSizeSpecId[1] = value;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ if (id == "local_size_z_id") {
+ // publicType.shaderQualifiers.localSizeSpecId[2] = value;
+ warn(loc, "ignored", id.c_str(), "");
+ return;
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ error(loc, "there is no such layout identifier for this stage taking an assigned value", id.c_str(), "");
+}
+
+void HlslParseContext::setSpecConstantId(const TSourceLoc& loc, TQualifier& qualifier, int value)
+{
+ if (value >= (int)TQualifier::layoutSpecConstantIdEnd) {
+ error(loc, "specialization-constant id is too large", "constant_id", "");
+ } else {
+ qualifier.layoutSpecConstantId = value;
+ qualifier.specConstant = true;
+ if (! intermediate.addUsedConstantId(value))
+ error(loc, "specialization-constant id already used", "constant_id", "");
+ }
+ return;
+}
+
+// Merge any layout qualifier information from src into dst, leaving everything else in dst alone
+//
+// "More than one layout qualifier may appear in a single declaration.
+// Additionally, the same layout-qualifier-name can occur multiple times
+// within a layout qualifier or across multiple layout qualifiers in the
+// same declaration. When the same layout-qualifier-name occurs
+// multiple times, in a single declaration, the last occurrence overrides
+// the former occurrence(s). Further, if such a layout-qualifier-name
+// will effect subsequent declarations or other observable behavior, it
+// is only the last occurrence that will have any effect, behaving as if
+// the earlier occurrence(s) within the declaration are not present.
+// This is also true for overriding layout-qualifier-names, where one
+// overrides the other (e.g., row_major vs. column_major); only the last
+// occurrence has any effect."
+//
+void HlslParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifier& src, bool inheritOnly)
+{
+ if (src.hasMatrix())
+ dst.layoutMatrix = src.layoutMatrix;
+ if (src.hasPacking())
+ dst.layoutPacking = src.layoutPacking;
+
+ if (src.hasStream())
+ dst.layoutStream = src.layoutStream;
+
+ if (src.hasFormat())
+ dst.layoutFormat = src.layoutFormat;
+
+ if (src.hasXfbBuffer())
+ dst.layoutXfbBuffer = src.layoutXfbBuffer;
+
+ if (src.hasAlign())
+ dst.layoutAlign = src.layoutAlign;
+
+ if (! inheritOnly) {
+ if (src.hasLocation())
+ dst.layoutLocation = src.layoutLocation;
+ if (src.hasComponent())
+ dst.layoutComponent = src.layoutComponent;
+ if (src.hasIndex())
+ dst.layoutIndex = src.layoutIndex;
+
+ if (src.hasOffset())
+ dst.layoutOffset = src.layoutOffset;
+
+ if (src.hasSet())
+ dst.layoutSet = src.layoutSet;
+ if (src.layoutBinding != TQualifier::layoutBindingEnd)
+ dst.layoutBinding = src.layoutBinding;
+
+ if (src.hasXfbStride())
+ dst.layoutXfbStride = src.layoutXfbStride;
+ if (src.hasXfbOffset())
+ dst.layoutXfbOffset = src.layoutXfbOffset;
+ if (src.hasAttachment())
+ dst.layoutAttachment = src.layoutAttachment;
+ if (src.hasSpecConstantId())
+ dst.layoutSpecConstantId = src.layoutSpecConstantId;
+
+ if (src.layoutPushConstant)
+ dst.layoutPushConstant = true;
+ }
+}
+
+
+//
+// Look up a function name in the symbol table, and make sure it is a function.
+//
+// First, look for an exact match. If there is none, use the generic selector
+// TParseContextBase::selectFunction() to find one, parameterized by the
+// convertible() and better() predicates defined below.
+//
+// Return the function symbol if found, otherwise nullptr.
+//
+const TFunction* HlslParseContext::findFunction(const TSourceLoc& loc, TFunction& call, bool& builtIn, int& thisDepth,
+ TIntermTyped*& args)
+{
+ if (symbolTable.isFunctionNameVariable(call.getName())) {
+ error(loc, "can't use function syntax on variable", call.getName().c_str(), "");
+ return nullptr;
+ }
+
+ // first, look for an exact match
+ bool dummyScope;
+ TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn, &dummyScope, &thisDepth);
+ if (symbol)
+ return symbol->getAsFunction();
+
+ // no exact match, use the generic selector, parameterized by the GLSL rules
+
+ // create list of candidates to send
+ TVector<const TFunction*> candidateList;
+ symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn);
+
+ // These built-in ops can accept any type, so we bypass the argument selection
+ if (candidateList.size() == 1 && builtIn &&
+ (candidateList[0]->getBuiltInOp() == EOpMethodAppend ||
+ candidateList[0]->getBuiltInOp() == EOpMethodRestartStrip ||
+ candidateList[0]->getBuiltInOp() == EOpMethodIncrementCounter ||
+ candidateList[0]->getBuiltInOp() == EOpMethodDecrementCounter ||
+ candidateList[0]->getBuiltInOp() == EOpMethodAppend ||
+ candidateList[0]->getBuiltInOp() == EOpMethodConsume)) {
+ return candidateList[0];
+ }
+
+ bool allowOnlyUpConversions = true;
+
+ // can 'from' convert to 'to'?
+ const auto convertible = [&](const TType& from, const TType& to, TOperator op, int arg) -> bool {
+ if (from == to)
+ return true;
+
+ // no aggregate conversions
+ if (from.isArray() || to.isArray() ||
+ from.isStruct() || to.isStruct())
+ return false;
+
+ switch (op) {
+ case EOpInterlockedAdd:
+ case EOpInterlockedAnd:
+ case EOpInterlockedCompareExchange:
+ case EOpInterlockedCompareStore:
+ case EOpInterlockedExchange:
+ case EOpInterlockedMax:
+ case EOpInterlockedMin:
+ case EOpInterlockedOr:
+ case EOpInterlockedXor:
+ // We do not promote the texture or image type for these ocodes. Normally that would not
+ // be an issue because it's a buffer, but we haven't decomposed the opcode yet, and at this
+ // stage it's merely e.g, a basic integer type.
+ //
+ // Instead, we want to promote other arguments, but stay within the same family. In other
+ // words, InterlockedAdd(RWBuffer<int>, ...) will always use the int flavor, never the uint flavor,
+ // but it is allowed to promote its other arguments.
+ if (arg == 0)
+ return false;
+ break;
+ case EOpMethodSample:
+ case EOpMethodSampleBias:
+ case EOpMethodSampleCmp:
+ case EOpMethodSampleCmpLevelZero:
+ case EOpMethodSampleGrad:
+ case EOpMethodSampleLevel:
+ case EOpMethodLoad:
+ case EOpMethodGetDimensions:
+ case EOpMethodGetSamplePosition:
+ case EOpMethodGather:
+ case EOpMethodCalculateLevelOfDetail:
+ case EOpMethodCalculateLevelOfDetailUnclamped:
+ case EOpMethodGatherRed:
+ case EOpMethodGatherGreen:
+ case EOpMethodGatherBlue:
+ case EOpMethodGatherAlpha:
+ case EOpMethodGatherCmp:
+ case EOpMethodGatherCmpRed:
+ case EOpMethodGatherCmpGreen:
+ case EOpMethodGatherCmpBlue:
+ case EOpMethodGatherCmpAlpha:
+ case EOpMethodAppend:
+ case EOpMethodRestartStrip:
+ // those are method calls, the object type can not be changed
+ // they are equal if the dim and type match (is dim sufficient?)
+ if (arg == 0)
+ return from.getSampler().type == to.getSampler().type &&
+ from.getSampler().arrayed == to.getSampler().arrayed &&
+ from.getSampler().shadow == to.getSampler().shadow &&
+ from.getSampler().ms == to.getSampler().ms &&
+ from.getSampler().dim == to.getSampler().dim;
+ break;
+ default:
+ break;
+ }
+
+ // basic types have to be convertible
+ if (allowOnlyUpConversions)
+ if (! intermediate.canImplicitlyPromote(from.getBasicType(), to.getBasicType(), EOpFunctionCall))
+ return false;
+
+ // shapes have to be convertible
+ if ((from.isScalarOrVec1() && to.isScalarOrVec1()) ||
+ (from.isScalarOrVec1() && to.isVector()) ||
+ (from.isScalarOrVec1() && to.isMatrix()) ||
+ (from.isVector() && to.isVector() && from.getVectorSize() >= to.getVectorSize()))
+ return true;
+
+ // TODO: what are the matrix rules? they go here
+
+ return false;
+ };
+
+ // Is 'to2' a better conversion than 'to1'?
+ // Ties should not be considered as better.
+ // Assumes 'convertible' already said true.
+ const auto better = [](const TType& from, const TType& to1, const TType& to2) -> bool {
+ // exact match is always better than mismatch
+ if (from == to2)
+ return from != to1;
+ if (from == to1)
+ return false;
+
+ // shape changes are always worse
+ if (from.isScalar() || from.isVector()) {
+ if (from.getVectorSize() == to2.getVectorSize() &&
+ from.getVectorSize() != to1.getVectorSize())
+ return true;
+ if (from.getVectorSize() == to1.getVectorSize() &&
+ from.getVectorSize() != to2.getVectorSize())
+ return false;
+ }
+
+ // Handle sampler betterness: An exact sampler match beats a non-exact match.
+ // (If we just looked at basic type, all EbtSamplers would look the same).
+ // If any type is not a sampler, just use the linearize function below.
+ if (from.getBasicType() == EbtSampler && to1.getBasicType() == EbtSampler && to2.getBasicType() == EbtSampler) {
+ // We can ignore the vector size in the comparison.
+ TSampler to1Sampler = to1.getSampler();
+ TSampler to2Sampler = to2.getSampler();
+
+ to1Sampler.vectorSize = to2Sampler.vectorSize = from.getSampler().vectorSize;
+
+ if (from.getSampler() == to2Sampler)
+ return from.getSampler() != to1Sampler;
+ if (from.getSampler() == to1Sampler)
+ return false;
+ }
+
+ // Might or might not be changing shape, which means basic type might
+ // or might not match, so within that, the question is how big a
+ // basic-type conversion is being done.
+ //
+ // Use a hierarchy of domains, translated to order of magnitude
+ // in a linearized view:
+ // - floating-point vs. integer
+ // - 32 vs. 64 bit (or width in general)
+ // - bool vs. non bool
+ // - signed vs. not signed
+ const auto linearize = [](const TBasicType& basicType) -> int {
+ switch (basicType) {
+ case EbtBool: return 1;
+ case EbtInt: return 10;
+ case EbtUint: return 11;
+ case EbtInt64: return 20;
+ case EbtUint64: return 21;
+ case EbtFloat: return 100;
+ case EbtDouble: return 110;
+ default: return 0;
+ }
+ };
+
+ return abs(linearize(to2.getBasicType()) - linearize(from.getBasicType())) <
+ abs(linearize(to1.getBasicType()) - linearize(from.getBasicType()));
+ };
+
+ // for ambiguity reporting
+ bool tie = false;
+
+ // send to the generic selector
+ const TFunction* bestMatch = selectFunction(candidateList, call, convertible, better, tie);
+
+ if (bestMatch == nullptr) {
+ // If there is nothing selected by allowing only up-conversions (to a larger linearize() value),
+ // we instead try down-conversions, which are valid in HLSL, but not preferred if there are any
+ // upconversions possible.
+ allowOnlyUpConversions = false;
+ bestMatch = selectFunction(candidateList, call, convertible, better, tie);
+ }
+
+ if (bestMatch == nullptr) {
+ error(loc, "no matching overloaded function found", call.getName().c_str(), "");
+ return nullptr;
+ }
+
+ // For built-ins, we can convert across the arguments. This will happen in several steps:
+ // Step 1: If there's an exact match, use it.
+ // Step 2a: Otherwise, get the operator from the best match and promote arguments:
+ // Step 2b: reconstruct the TFunction based on the new arg types
+ // Step 3: Re-select after type promotion is applied, to find proper candidate.
+ if (builtIn) {
+ // Step 1: If there's an exact match, use it.
+ if (call.getMangledName() == bestMatch->getMangledName())
+ return bestMatch;
+
+ // Step 2a: Otherwise, get the operator from the best match and promote arguments as if we
+ // are that kind of operator.
+ if (args != nullptr) {
+ // The arg list can be a unary node, or an aggregate. We have to handle both.
+ // We will use the normal promote() facilities, which require an interm node.
+ TIntermOperator* promote = nullptr;
+
+ if (call.getParamCount() == 1) {
+ promote = new TIntermUnary(bestMatch->getBuiltInOp());
+ promote->getAsUnaryNode()->setOperand(args->getAsTyped());
+ } else {
+ promote = new TIntermAggregate(bestMatch->getBuiltInOp());
+ promote->getAsAggregate()->getSequence().swap(args->getAsAggregate()->getSequence());
+ }
+
+ if (! intermediate.promote(promote))
+ return nullptr;
+
+ // Obtain the promoted arg list.
+ if (call.getParamCount() == 1) {
+ args = promote->getAsUnaryNode()->getOperand();
+ } else {
+ promote->getAsAggregate()->getSequence().swap(args->getAsAggregate()->getSequence());
+ }
+ }
+
+ // Step 2b: reconstruct the TFunction based on the new arg types
+ TFunction convertedCall(&call.getName(), call.getType(), call.getBuiltInOp());
+
+ if (args->getAsAggregate()) {
+ // Handle aggregates: put all args into the new function call
+ for (int arg=0; arg<int(args->getAsAggregate()->getSequence().size()); ++arg) {
+ // TODO: But for constness, we could avoid the new & shallowCopy, and use the pointer directly.
+ TParameter param = { 0, new TType, nullptr };
+ param.type->shallowCopy(args->getAsAggregate()->getSequence()[arg]->getAsTyped()->getType());
+ convertedCall.addParameter(param);
+ }
+ } else if (args->getAsUnaryNode()) {
+ // Handle unaries: put all args into the new function call
+ TParameter param = { 0, new TType, nullptr };
+ param.type->shallowCopy(args->getAsUnaryNode()->getOperand()->getAsTyped()->getType());
+ convertedCall.addParameter(param);
+ } else if (args->getAsTyped()) {
+ // Handle bare e.g, floats, not in an aggregate.
+ TParameter param = { 0, new TType, nullptr };
+ param.type->shallowCopy(args->getAsTyped()->getType());
+ convertedCall.addParameter(param);
+ } else {
+ assert(0); // unknown argument list.
+ return nullptr;
+ }
+
+ // Step 3: Re-select after type promotion, to find proper candidate
+ // send to the generic selector
+ bestMatch = selectFunction(candidateList, convertedCall, convertible, better, tie);
+
+ // At this point, there should be no tie.
+ }
+
+ if (tie)
+ error(loc, "ambiguous best function under implicit type conversion", call.getName().c_str(), "");
+
+ // Append default parameter values if needed
+ if (!tie && bestMatch != nullptr) {
+ for (int defParam = call.getParamCount(); defParam < bestMatch->getParamCount(); ++defParam) {
+ handleFunctionArgument(&call, args, (*bestMatch)[defParam].defaultValue);
+ }
+ }
+
+ return bestMatch;
+}
+
+//
+// Do everything necessary to handle a typedef declaration, for a single symbol.
+//
+// 'parseType' is the type part of the declaration (to the left)
+// 'arraySizes' is the arrayness tagged on the identifier (to the right)
+//
+void HlslParseContext::declareTypedef(const TSourceLoc& loc, const TString& identifier, const TType& parseType)
+{
+ TVariable* typeSymbol = new TVariable(&identifier, parseType, true);
+ if (! symbolTable.insert(*typeSymbol))
+ error(loc, "name already defined", "typedef", identifier.c_str());
+}
+
+// Do everything necessary to handle a struct declaration, including
+// making IO aliases because HLSL allows mixed IO in a struct that specializes
+// based on the usage (input, output, uniform, none).
+void HlslParseContext::declareStruct(const TSourceLoc& loc, TString& structName, TType& type)
+{
+ // If it was named, which means the type can be reused later, add
+ // it to the symbol table. (Unless it's a block, in which
+ // case the name is not a type.)
+ if (type.getBasicType() == EbtBlock || structName.size() == 0)
+ return;
+
+ TVariable* userTypeDef = new TVariable(&structName, type, true);
+ if (! symbolTable.insert(*userTypeDef)) {
+ error(loc, "redefinition", structName.c_str(), "struct");
+ return;
+ }
+
+ // See if we need IO aliases for the structure typeList
+
+ const auto condAlloc = [](bool pred, TTypeList*& list) {
+ if (pred && list == nullptr)
+ list = new TTypeList;
+ };
+
+ tIoKinds newLists = { nullptr, nullptr, nullptr }; // allocate for each kind found
+ for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) {
+ condAlloc(hasUniform(member->type->getQualifier()), newLists.uniform);
+ condAlloc( hasInput(member->type->getQualifier()), newLists.input);
+ condAlloc( hasOutput(member->type->getQualifier()), newLists.output);
+
+ if (member->type->isStruct()) {
+ auto it = ioTypeMap.find(member->type->getStruct());
+ if (it != ioTypeMap.end()) {
+ condAlloc(it->second.uniform != nullptr, newLists.uniform);
+ condAlloc(it->second.input != nullptr, newLists.input);
+ condAlloc(it->second.output != nullptr, newLists.output);
+ }
+ }
+ }
+ if (newLists.uniform == nullptr &&
+ newLists.input == nullptr &&
+ newLists.output == nullptr) {
+ // Won't do any IO caching, clear up the type and get out now.
+ for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member)
+ clearUniformInputOutput(member->type->getQualifier());
+ return;
+ }
+
+ // We have IO involved.
+
+ // Make a pure typeList for the symbol table, and cache side copies of IO versions.
+ for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) {
+ const auto inheritStruct = [&](TTypeList* s, TTypeLoc& ioMember) {
+ if (s != nullptr) {
+ ioMember.type = new TType;
+ ioMember.type->shallowCopy(*member->type);
+ ioMember.type->setStruct(s);
+ }
+ };
+ const auto newMember = [&](TTypeLoc& m) {
+ if (m.type == nullptr) {
+ m.type = new TType;
+ m.type->shallowCopy(*member->type);
+ }
+ };
+
+ TTypeLoc newUniformMember = { nullptr, member->loc };
+ TTypeLoc newInputMember = { nullptr, member->loc };
+ TTypeLoc newOutputMember = { nullptr, member->loc };
+ if (member->type->isStruct()) {
+ // swap in an IO child if there is one
+ auto it = ioTypeMap.find(member->type->getStruct());
+ if (it != ioTypeMap.end()) {
+ inheritStruct(it->second.uniform, newUniformMember);
+ inheritStruct(it->second.input, newInputMember);
+ inheritStruct(it->second.output, newOutputMember);
+ }
+ }
+ if (newLists.uniform) {
+ newMember(newUniformMember);
+
+ // inherit default matrix layout (changeable via #pragma pack_matrix), if none given.
+ if (member->type->isMatrix() && member->type->getQualifier().layoutMatrix == ElmNone)
+ newUniformMember.type->getQualifier().layoutMatrix = globalUniformDefaults.layoutMatrix;
+
+ correctUniform(newUniformMember.type->getQualifier());
+ newLists.uniform->push_back(newUniformMember);
+ }
+ if (newLists.input) {
+ newMember(newInputMember);
+ correctInput(newInputMember.type->getQualifier());
+ newLists.input->push_back(newInputMember);
+ }
+ if (newLists.output) {
+ newMember(newOutputMember);
+ correctOutput(newOutputMember.type->getQualifier());
+ newLists.output->push_back(newOutputMember);
+ }
+
+ // make original pure
+ clearUniformInputOutput(member->type->getQualifier());
+ }
+ ioTypeMap[type.getStruct()] = newLists;
+}
+
+// Lookup a user-type by name.
+// If found, fill in the type and return the defining symbol.
+// If not found, return nullptr.
+TSymbol* HlslParseContext::lookupUserType(const TString& typeName, TType& type)
+{
+ TSymbol* symbol = symbolTable.find(typeName);
+ if (symbol && symbol->getAsVariable() && symbol->getAsVariable()->isUserType()) {
+ type.shallowCopy(symbol->getType());
+ return symbol;
+ } else
+ return nullptr;
+}
+
+//
+// Do everything necessary to handle a variable (non-block) declaration.
+// Either redeclaring a variable, or making a new one, updating the symbol
+// table, and all error checking.
+//
+// Returns a subtree node that computes an initializer, if needed.
+// Returns nullptr if there is no code to execute for initialization.
+//
+// 'parseType' is the type part of the declaration (to the left)
+// 'arraySizes' is the arrayness tagged on the identifier (to the right)
+//
+TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, const TString& identifier, TType& type,
+ TIntermTyped* initializer)
+{
+ if (voidErrorCheck(loc, identifier, type.getBasicType()))
+ return nullptr;
+
+ // Global consts with initializers that are non-const act like EvqGlobal in HLSL.
+ // This test is implicitly recursive, because initializers propagate constness
+ // up the aggregate node tree during creation. E.g, for:
+ // { { 1, 2 }, { 3, 4 } }
+ // the initializer list is marked EvqConst at the top node, and remains so here. However:
+ // { 1, { myvar, 2 }, 3 }
+ // is not a const intializer, and still becomes EvqGlobal here.
+
+ const bool nonConstInitializer = (initializer != nullptr && initializer->getQualifier().storage != EvqConst);
+
+ if (type.getQualifier().storage == EvqConst && symbolTable.atGlobalLevel() && nonConstInitializer) {
+ // Force to global
+ type.getQualifier().storage = EvqGlobal;
+ }
+
+ // make const and initialization consistent
+ fixConstInit(loc, identifier, type, initializer);
+
+ // Check for redeclaration of built-ins and/or attempting to declare a reserved name
+ TSymbol* symbol = nullptr;
+
+ inheritGlobalDefaults(type.getQualifier());
+
+ const bool flattenVar = shouldFlatten(type, type.getQualifier().storage, true);
+
+ // correct IO in the type
+ switch (type.getQualifier().storage) {
+ case EvqGlobal:
+ case EvqTemporary:
+ clearUniformInputOutput(type.getQualifier());
+ break;
+ case EvqUniform:
+ case EvqBuffer:
+ correctUniform(type.getQualifier());
+ if (type.isStruct()) {
+ auto it = ioTypeMap.find(type.getStruct());
+ if (it != ioTypeMap.end())
+ type.setStruct(it->second.uniform);
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ // Declare the variable
+ if (type.isArray()) {
+ // array case
+ declareArray(loc, identifier, type, symbol, !flattenVar);
+ } else {
+ // non-array case
+ if (symbol == nullptr)
+ symbol = declareNonArray(loc, identifier, type, !flattenVar);
+ else if (type != symbol->getType())
+ error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str());
+ }
+
+ if (symbol == nullptr)
+ return nullptr;
+
+ if (flattenVar)
+ flatten(*symbol->getAsVariable(), symbolTable.atGlobalLevel());
+
+ if (initializer == nullptr)
+ return nullptr;
+
+ // Deal with initializer
+ TVariable* variable = symbol->getAsVariable();
+ if (variable == nullptr) {
+ error(loc, "initializer requires a variable, not a member", identifier.c_str(), "");
+ return nullptr;
+ }
+ return executeInitializer(loc, initializer, variable);
+}
+
+// Pick up global defaults from the provide global defaults into dst.
+void HlslParseContext::inheritGlobalDefaults(TQualifier& dst) const
+{
+ if (dst.storage == EvqVaryingOut) {
+ if (! dst.hasStream() && language == EShLangGeometry)
+ dst.layoutStream = globalOutputDefaults.layoutStream;
+ if (! dst.hasXfbBuffer())
+ dst.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer;
+ }
+}
+
+//
+// Make an internal-only variable whose name is for debug purposes only
+// and won't be searched for. Callers will only use the return value to use
+// the variable, not the name to look it up. It is okay if the name
+// is the same as other names; there won't be any conflict.
+//
+TVariable* HlslParseContext::makeInternalVariable(const char* name, const TType& type) const
+{
+ TString* nameString = NewPoolTString(name);
+ TVariable* variable = new TVariable(nameString, type);
+ symbolTable.makeInternalVariable(*variable);
+
+ return variable;
+}
+
+// Make a symbol node holding a new internal temporary variable.
+TIntermSymbol* HlslParseContext::makeInternalVariableNode(const TSourceLoc& loc, const char* name,
+ const TType& type) const
+{
+ TVariable* tmpVar = makeInternalVariable(name, type);
+ tmpVar->getWritableType().getQualifier().makeTemporary();
+
+ return intermediate.addSymbol(*tmpVar, loc);
+}
+
+//
+// Declare a non-array variable, the main point being there is no redeclaration
+// for resizing allowed.
+//
+// Return the successfully declared variable.
+//
+TVariable* HlslParseContext::declareNonArray(const TSourceLoc& loc, const TString& identifier, const TType& type,
+ bool track)
+{
+ // make a new variable
+ TVariable* variable = new TVariable(&identifier, type);
+
+ // add variable to symbol table
+ if (symbolTable.insert(*variable)) {
+ if (track && symbolTable.atGlobalLevel())
+ trackLinkage(*variable);
+ return variable;
+ }
+
+ error(loc, "redefinition", variable->getName().c_str(), "");
+ return nullptr;
+}
+
+//
+// Handle all types of initializers from the grammar.
+//
+// Returning nullptr just means there is no code to execute to handle the
+// initializer, which will, for example, be the case for constant initializers.
+//
+// Returns a subtree that accomplished the initialization.
+//
+TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyped* initializer, TVariable* variable)
+{
+ //
+ // Identifier must be of type constant, a global, or a temporary, and
+ // starting at version 120, desktop allows uniforms to have initializers.
+ //
+ TStorageQualifier qualifier = variable->getType().getQualifier().storage;
+
+ //
+ // If the initializer was from braces { ... }, we convert the whole subtree to a
+ // constructor-style subtree, allowing the rest of the code to operate
+ // identically for both kinds of initializers.
+ //
+ //
+ // Type can't be deduced from the initializer list, so a skeletal type to
+ // follow has to be passed in. Constness and specialization-constness
+ // should be deduced bottom up, not dictated by the skeletal type.
+ //
+ TType skeletalType;
+ skeletalType.shallowCopy(variable->getType());
+ skeletalType.getQualifier().makeTemporary();
+ if (initializer->getAsAggregate() && initializer->getAsAggregate()->getOp() == EOpNull)
+ initializer = convertInitializerList(loc, skeletalType, initializer, nullptr);
+ if (initializer == nullptr) {
+ // error recovery; don't leave const without constant values
+ if (qualifier == EvqConst)
+ variable->getWritableType().getQualifier().storage = EvqTemporary;
+ return nullptr;
+ }
+
+ // Fix outer arrayness if variable is unsized, getting size from the initializer
+ if (initializer->getType().isSizedArray() && variable->getType().isUnsizedArray())
+ variable->getWritableType().changeOuterArraySize(initializer->getType().getOuterArraySize());
+
+ // Inner arrayness can also get set by an initializer
+ if (initializer->getType().isArrayOfArrays() && variable->getType().isArrayOfArrays() &&
+ initializer->getType().getArraySizes()->getNumDims() ==
+ variable->getType().getArraySizes()->getNumDims()) {
+ // adopt unsized sizes from the initializer's sizes
+ for (int d = 1; d < variable->getType().getArraySizes()->getNumDims(); ++d) {
+ if (variable->getType().getArraySizes()->getDimSize(d) == UnsizedArraySize) {
+ variable->getWritableType().getArraySizes()->setDimSize(d,
+ initializer->getType().getArraySizes()->getDimSize(d));
+ }
+ }
+ }
+
+ // Uniform and global consts require a constant initializer
+ if (qualifier == EvqUniform && initializer->getType().getQualifier().storage != EvqConst) {
+ error(loc, "uniform initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str());
+ variable->getWritableType().getQualifier().storage = EvqTemporary;
+ return nullptr;
+ }
+
+ // Const variables require a constant initializer
+ if (qualifier == EvqConst) {
+ if (initializer->getType().getQualifier().storage != EvqConst) {
+ variable->getWritableType().getQualifier().storage = EvqConstReadOnly;
+ qualifier = EvqConstReadOnly;
+ }
+ }
+
+ if (qualifier == EvqConst || qualifier == EvqUniform) {
+ // Compile-time tagging of the variable with its constant value...
+
+ initializer = intermediate.addConversion(EOpAssign, variable->getType(), initializer);
+ if (initializer != nullptr && variable->getType() != initializer->getType())
+ initializer = intermediate.addUniShapeConversion(EOpAssign, variable->getType(), initializer);
+ if (initializer == nullptr || !initializer->getAsConstantUnion() ||
+ variable->getType() != initializer->getType()) {
+ error(loc, "non-matching or non-convertible constant type for const initializer",
+ variable->getType().getStorageQualifierString(), "");
+ variable->getWritableType().getQualifier().storage = EvqTemporary;
+ return nullptr;
+ }
+
+ variable->setConstArray(initializer->getAsConstantUnion()->getConstArray());
+ } else {
+ // normal assigning of a value to a variable...
+ specializationCheck(loc, initializer->getType(), "initializer");
+ TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc);
+ TIntermNode* initNode = handleAssign(loc, EOpAssign, intermSymbol, initializer);
+ if (initNode == nullptr)
+ assignError(loc, "=", intermSymbol->getCompleteString(), initializer->getCompleteString());
+ return initNode;
+ }
+
+ return nullptr;
+}
+
+//
+// Reprocess any initializer-list { ... } parts of the initializer.
+// Need to hierarchically assign correct types and implicit
+// conversions. Will do this mimicking the same process used for
+// creating a constructor-style initializer, ensuring we get the
+// same form.
+//
+// Returns a node representing an expression for the initializer list expressed
+// as the correct type.
+//
+// Returns nullptr if there is an error.
+//
+TIntermTyped* HlslParseContext::convertInitializerList(const TSourceLoc& loc, const TType& type,
+ TIntermTyped* initializer, TIntermTyped* scalarInit)
+{
+ // Will operate recursively. Once a subtree is found that is constructor style,
+ // everything below it is already good: Only the "top part" of the initializer
+ // can be an initializer list, where "top part" can extend for several (or all) levels.
+
+ // see if we have bottomed out in the tree within the initializer-list part
+ TIntermAggregate* initList = initializer->getAsAggregate();
+ if (initList == nullptr || initList->getOp() != EOpNull) {
+ // We don't have a list, but if it's a scalar and the 'type' is a
+ // composite, we need to lengthen below to make it useful.
+ // Otherwise, this is an already formed object to initialize with.
+ if (type.isScalar() || !initializer->getType().isScalar())
+ return initializer;
+ else
+ initList = intermediate.makeAggregate(initializer);
+ }
+
+ // Of the initializer-list set of nodes, need to process bottom up,
+ // so recurse deep, then process on the way up.
+
+ // Go down the tree here...
+ if (type.isArray()) {
+ // The type's array might be unsized, which could be okay, so base sizes on the size of the aggregate.
+ // Later on, initializer execution code will deal with array size logic.
+ TType arrayType;
+ arrayType.shallowCopy(type); // sharing struct stuff is fine
+ arrayType.copyArraySizes(*type.getArraySizes()); // but get a fresh copy of the array information, to edit below
+
+ // edit array sizes to fill in unsized dimensions
+ if (type.isUnsizedArray())
+ arrayType.changeOuterArraySize((int)initList->getSequence().size());
+
+ // set unsized array dimensions that can be derived from the initializer's first element
+ if (arrayType.isArrayOfArrays() && initList->getSequence().size() > 0) {
+ TIntermTyped* firstInit = initList->getSequence()[0]->getAsTyped();
+ if (firstInit->getType().isArray() &&
+ arrayType.getArraySizes()->getNumDims() == firstInit->getType().getArraySizes()->getNumDims() + 1) {
+ for (int d = 1; d < arrayType.getArraySizes()->getNumDims(); ++d) {
+ if (arrayType.getArraySizes()->getDimSize(d) == UnsizedArraySize)
+ arrayType.getArraySizes()->setDimSize(d, firstInit->getType().getArraySizes()->getDimSize(d - 1));
+ }
+ }
+ }
+
+ // lengthen list to be long enough
+ lengthenList(loc, initList->getSequence(), arrayType.getOuterArraySize(), scalarInit);
+
+ // recursively process each element
+ TType elementType(arrayType, 0); // dereferenced type
+ for (int i = 0; i < arrayType.getOuterArraySize(); ++i) {
+ initList->getSequence()[i] = convertInitializerList(loc, elementType,
+ initList->getSequence()[i]->getAsTyped(), scalarInit);
+ if (initList->getSequence()[i] == nullptr)
+ return nullptr;
+ }
+
+ return addConstructor(loc, initList, arrayType);
+ } else if (type.isStruct()) {
+ // do we have implicit assignments to opaques?
+ for (size_t i = initList->getSequence().size(); i < type.getStruct()->size(); ++i) {
+ if ((*type.getStruct())[i].type->containsOpaque()) {
+ error(loc, "cannot implicitly initialize opaque members", "initializer list", "");
+ return nullptr;
+ }
+ }
+
+ // lengthen list to be long enough
+ lengthenList(loc, initList->getSequence(), static_cast<int>(type.getStruct()->size()), scalarInit);
+
+ if (type.getStruct()->size() != initList->getSequence().size()) {
+ error(loc, "wrong number of structure members", "initializer list", "");
+ return nullptr;
+ }
+ for (size_t i = 0; i < type.getStruct()->size(); ++i) {
+ initList->getSequence()[i] = convertInitializerList(loc, *(*type.getStruct())[i].type,
+ initList->getSequence()[i]->getAsTyped(), scalarInit);
+ if (initList->getSequence()[i] == nullptr)
+ return nullptr;
+ }
+ } else if (type.isMatrix()) {
+ if (type.computeNumComponents() == (int)initList->getSequence().size()) {
+ // This means the matrix is initialized component-wise, rather than as
+ // a series of rows and columns. We can just use the list directly as
+ // a constructor; no further processing needed.
+ } else {
+ // lengthen list to be long enough
+ lengthenList(loc, initList->getSequence(), type.getMatrixCols(), scalarInit);
+
+ if (type.getMatrixCols() != (int)initList->getSequence().size()) {
+ error(loc, "wrong number of matrix columns:", "initializer list", type.getCompleteString().c_str());
+ return nullptr;
+ }
+ TType vectorType(type, 0); // dereferenced type
+ for (int i = 0; i < type.getMatrixCols(); ++i) {
+ initList->getSequence()[i] = convertInitializerList(loc, vectorType,
+ initList->getSequence()[i]->getAsTyped(), scalarInit);
+ if (initList->getSequence()[i] == nullptr)
+ return nullptr;
+ }
+ }
+ } else if (type.isVector()) {
+ // lengthen list to be long enough
+ lengthenList(loc, initList->getSequence(), type.getVectorSize(), scalarInit);
+
+ // error check; we're at bottom, so work is finished below
+ if (type.getVectorSize() != (int)initList->getSequence().size()) {
+ error(loc, "wrong vector size (or rows in a matrix column):", "initializer list",
+ type.getCompleteString().c_str());
+ return nullptr;
+ }
+ } else if (type.isScalar()) {
+ // lengthen list to be long enough
+ lengthenList(loc, initList->getSequence(), 1, scalarInit);
+
+ if ((int)initList->getSequence().size() != 1) {
+ error(loc, "scalar expected one element:", "initializer list", type.getCompleteString().c_str());
+ return nullptr;
+ }
+ } else {
+ error(loc, "unexpected initializer-list type:", "initializer list", type.getCompleteString().c_str());
+ return nullptr;
+ }
+
+ // Now that the subtree is processed, process this node as if the
+ // initializer list is a set of arguments to a constructor.
+ TIntermTyped* emulatedConstructorArguments;
+ if (initList->getSequence().size() == 1)
+ emulatedConstructorArguments = initList->getSequence()[0]->getAsTyped();
+ else
+ emulatedConstructorArguments = initList;
+
+ return addConstructor(loc, emulatedConstructorArguments, type);
+}
+
+// Lengthen list to be long enough to cover any gap from the current list size
+// to 'size'. If the list is longer, do nothing.
+// The value to lengthen with is the default for short lists.
+//
+// By default, lists that are too short due to lack of initializers initialize to zero.
+// Alternatively, it could be a scalar initializer for a structure. Both cases are handled,
+// based on whether something is passed in as 'scalarInit'.
+//
+// 'scalarInit' must be safe to use each time this is called (no side effects replication).
+//
+void HlslParseContext::lengthenList(const TSourceLoc& loc, TIntermSequence& list, int size, TIntermTyped* scalarInit)
+{
+ for (int c = (int)list.size(); c < size; ++c) {
+ if (scalarInit == nullptr)
+ list.push_back(intermediate.addConstantUnion(0, loc));
+ else
+ list.push_back(scalarInit);
+ }
+}
+
+//
+// Test for the correctness of the parameters passed to various constructor functions
+// and also convert them to the right data type, if allowed and required.
+//
+// Returns nullptr for an error or the constructed node (aggregate or typed) for no error.
+//
+TIntermTyped* HlslParseContext::handleConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type)
+{
+ if (node == nullptr)
+ return nullptr;
+
+ // Construct identical type
+ if (type == node->getType())
+ return node;
+
+ // Handle the idiom "(struct type)<scalar value>"
+ if (type.isStruct() && isScalarConstructor(node)) {
+ // 'node' will almost always get used multiple times, so should not be used directly,
+ // it would create a DAG instead of a tree, which might be okay (would
+ // like to formalize that for constants and symbols), but if it has
+ // side effects, they would get executed multiple times, which is not okay.
+ if (node->getAsConstantUnion() == nullptr && node->getAsSymbolNode() == nullptr) {
+ TIntermAggregate* seq = intermediate.makeAggregate(loc);
+ TIntermSymbol* copy = makeInternalVariableNode(loc, "scalarCopy", node->getType());
+ seq = intermediate.growAggregate(seq, intermediate.addBinaryNode(EOpAssign, copy, node, loc));
+ seq = intermediate.growAggregate(seq, convertInitializerList(loc, type, intermediate.makeAggregate(loc), copy));
+ seq->setOp(EOpComma);
+ seq->setType(type);
+ return seq;
+ } else
+ return convertInitializerList(loc, type, intermediate.makeAggregate(loc), node);
+ }
+
+ return addConstructor(loc, node, type);
+}
+
+// Add a constructor, either from the grammar, or other programmatic reasons.
+//
+// 'node' is what to construct from.
+// 'type' is what type to construct.
+//
+// Returns the constructed object.
+// Return nullptr if it can't be done.
+//
+TIntermTyped* HlslParseContext::addConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type)
+{
+ TIntermAggregate* aggrNode = node->getAsAggregate();
+ TOperator op = intermediate.mapTypeToConstructorOp(type);
+
+ if (op == EOpConstructTextureSampler)
+ return intermediate.setAggregateOperator(aggrNode, op, type, loc);
+
+ TTypeList::const_iterator memberTypes;
+ if (op == EOpConstructStruct)
+ memberTypes = type.getStruct()->begin();
+
+ TType elementType;
+ if (type.isArray()) {
+ TType dereferenced(type, 0);
+ elementType.shallowCopy(dereferenced);
+ } else
+ elementType.shallowCopy(type);
+
+ bool singleArg;
+ if (aggrNode != nullptr) {
+ if (aggrNode->getOp() != EOpNull)
+ singleArg = true;
+ else
+ singleArg = false;
+ } else
+ singleArg = true;
+
+ TIntermTyped *newNode;
+ if (singleArg) {
+ // Handle array -> array conversion
+ // Constructing an array of one type from an array of another type is allowed,
+ // assuming there are enough components available (semantic-checked earlier).
+ if (type.isArray() && node->isArray())
+ newNode = convertArray(node, type);
+
+ // If structure constructor or array constructor is being called
+ // for only one parameter inside the aggregate, we need to call constructAggregate function once.
+ else if (type.isArray())
+ newNode = constructAggregate(node, elementType, 1, node->getLoc());
+ else if (op == EOpConstructStruct)
+ newNode = constructAggregate(node, *(*memberTypes).type, 1, node->getLoc());
+ else {
+ // shape conversion for matrix constructor from scalar. HLSL semantics are: scalar
+ // is replicated into every element of the matrix (not just the diagnonal), so
+ // that is handled specially here.
+ if (type.isMatrix() && node->getType().isScalarOrVec1())
+ node = intermediate.addShapeConversion(type, node);
+
+ newNode = constructBuiltIn(type, op, node, node->getLoc(), false);
+ }
+
+ if (newNode && (type.isArray() || op == EOpConstructStruct))
+ newNode = intermediate.setAggregateOperator(newNode, EOpConstructStruct, type, loc);
+
+ return newNode;
+ }
+
+ //
+ // Handle list of arguments.
+ //
+ TIntermSequence& sequenceVector = aggrNode->getSequence(); // Stores the information about the parameter to the constructor
+ // if the structure constructor contains more than one parameter, then construct
+ // each parameter
+
+ int paramCount = 0; // keeps a track of the constructor parameter number being checked
+
+ // for each parameter to the constructor call, check to see if the right type is passed or convert them
+ // to the right type if possible (and allowed).
+ // for structure constructors, just check if the right type is passed, no conversion is allowed.
+
+ for (TIntermSequence::iterator p = sequenceVector.begin();
+ p != sequenceVector.end(); p++, paramCount++) {
+ if (type.isArray())
+ newNode = constructAggregate(*p, elementType, paramCount + 1, node->getLoc());
+ else if (op == EOpConstructStruct)
+ newNode = constructAggregate(*p, *(memberTypes[paramCount]).type, paramCount + 1, node->getLoc());
+ else
+ newNode = constructBuiltIn(type, op, (*p)->getAsTyped(), node->getLoc(), true);
+
+ if (newNode)
+ *p = newNode;
+ else
+ return nullptr;
+ }
+
+ TIntermTyped* constructor = intermediate.setAggregateOperator(aggrNode, op, type, loc);
+
+ return constructor;
+}
+
+// Function for constructor implementation. Calls addUnaryMath with appropriate EOp value
+// for the parameter to the constructor (passed to this function). Essentially, it converts
+// the parameter types correctly. If a constructor expects an int (like ivec2) and is passed a
+// float, then float is converted to int.
+//
+// Returns nullptr for an error or the constructed node.
+//
+TIntermTyped* HlslParseContext::constructBuiltIn(const TType& type, TOperator op, TIntermTyped* node,
+ const TSourceLoc& loc, bool subset)
+{
+ TIntermTyped* newNode;
+ TOperator basicOp;
+
+ //
+ // First, convert types as needed.
+ //
+ switch (op) {
+ case EOpConstructF16Vec2:
+ case EOpConstructF16Vec3:
+ case EOpConstructF16Vec4:
+ case EOpConstructF16Mat2x2:
+ case EOpConstructF16Mat2x3:
+ case EOpConstructF16Mat2x4:
+ case EOpConstructF16Mat3x2:
+ case EOpConstructF16Mat3x3:
+ case EOpConstructF16Mat3x4:
+ case EOpConstructF16Mat4x2:
+ case EOpConstructF16Mat4x3:
+ case EOpConstructF16Mat4x4:
+ case EOpConstructFloat16:
+ basicOp = EOpConstructFloat16;
+ break;
+
+ case EOpConstructVec2:
+ case EOpConstructVec3:
+ case EOpConstructVec4:
+ case EOpConstructMat2x2:
+ case EOpConstructMat2x3:
+ case EOpConstructMat2x4:
+ case EOpConstructMat3x2:
+ case EOpConstructMat3x3:
+ case EOpConstructMat3x4:
+ case EOpConstructMat4x2:
+ case EOpConstructMat4x3:
+ case EOpConstructMat4x4:
+ case EOpConstructFloat:
+ basicOp = EOpConstructFloat;
+ break;
+
+ case EOpConstructDVec2:
+ case EOpConstructDVec3:
+ case EOpConstructDVec4:
+ case EOpConstructDMat2x2:
+ case EOpConstructDMat2x3:
+ case EOpConstructDMat2x4:
+ case EOpConstructDMat3x2:
+ case EOpConstructDMat3x3:
+ case EOpConstructDMat3x4:
+ case EOpConstructDMat4x2:
+ case EOpConstructDMat4x3:
+ case EOpConstructDMat4x4:
+ case EOpConstructDouble:
+ basicOp = EOpConstructDouble;
+ break;
+
+ case EOpConstructI16Vec2:
+ case EOpConstructI16Vec3:
+ case EOpConstructI16Vec4:
+ case EOpConstructInt16:
+ basicOp = EOpConstructInt16;
+ break;
+
+ case EOpConstructIVec2:
+ case EOpConstructIVec3:
+ case EOpConstructIVec4:
+ case EOpConstructIMat2x2:
+ case EOpConstructIMat2x3:
+ case EOpConstructIMat2x4:
+ case EOpConstructIMat3x2:
+ case EOpConstructIMat3x3:
+ case EOpConstructIMat3x4:
+ case EOpConstructIMat4x2:
+ case EOpConstructIMat4x3:
+ case EOpConstructIMat4x4:
+ case EOpConstructInt:
+ basicOp = EOpConstructInt;
+ break;
+
+ case EOpConstructU16Vec2:
+ case EOpConstructU16Vec3:
+ case EOpConstructU16Vec4:
+ case EOpConstructUint16:
+ basicOp = EOpConstructUint16;
+ break;
+
+ case EOpConstructUVec2:
+ case EOpConstructUVec3:
+ case EOpConstructUVec4:
+ case EOpConstructUMat2x2:
+ case EOpConstructUMat2x3:
+ case EOpConstructUMat2x4:
+ case EOpConstructUMat3x2:
+ case EOpConstructUMat3x3:
+ case EOpConstructUMat3x4:
+ case EOpConstructUMat4x2:
+ case EOpConstructUMat4x3:
+ case EOpConstructUMat4x4:
+ case EOpConstructUint:
+ basicOp = EOpConstructUint;
+ break;
+
+ case EOpConstructBVec2:
+ case EOpConstructBVec3:
+ case EOpConstructBVec4:
+ case EOpConstructBMat2x2:
+ case EOpConstructBMat2x3:
+ case EOpConstructBMat2x4:
+ case EOpConstructBMat3x2:
+ case EOpConstructBMat3x3:
+ case EOpConstructBMat3x4:
+ case EOpConstructBMat4x2:
+ case EOpConstructBMat4x3:
+ case EOpConstructBMat4x4:
+ case EOpConstructBool:
+ basicOp = EOpConstructBool;
+ break;
+
+ default:
+ error(loc, "unsupported construction", "", "");
+
+ return nullptr;
+ }
+ newNode = intermediate.addUnaryMath(basicOp, node, node->getLoc());
+ if (newNode == nullptr) {
+ error(loc, "can't convert", "constructor", "");
+ return nullptr;
+ }
+
+ //
+ // Now, if there still isn't an operation to do the construction, and we need one, add one.
+ //
+
+ // Otherwise, skip out early.
+ if (subset || (newNode != node && newNode->getType() == type))
+ return newNode;
+
+ // setAggregateOperator will insert a new node for the constructor, as needed.
+ return intermediate.setAggregateOperator(newNode, op, type, loc);
+}
+
+// Convert the array in node to the requested type, which is also an array.
+// Returns nullptr on failure, otherwise returns aggregate holding the list of
+// elements needed to construct the array.
+TIntermTyped* HlslParseContext::convertArray(TIntermTyped* node, const TType& type)
+{
+ assert(node->isArray() && type.isArray());
+ if (node->getType().computeNumComponents() < type.computeNumComponents())
+ return nullptr;
+
+ // TODO: write an argument replicator, for the case the argument should not be
+ // executed multiple times, yet multiple copies are needed.
+
+ TIntermTyped* constructee = node->getAsTyped();
+ // track where we are in consuming the argument
+ int constructeeElement = 0;
+ int constructeeComponent = 0;
+
+ // bump up to the next component to consume
+ const auto getNextComponent = [&]() {
+ TIntermTyped* component;
+ component = handleBracketDereference(node->getLoc(), constructee,
+ intermediate.addConstantUnion(constructeeElement, node->getLoc()));
+ if (component->isVector())
+ component = handleBracketDereference(node->getLoc(), component,
+ intermediate.addConstantUnion(constructeeComponent, node->getLoc()));
+ // bump component pointer up
+ ++constructeeComponent;
+ if (constructeeComponent == constructee->getVectorSize()) {
+ constructeeComponent = 0;
+ ++constructeeElement;
+ }
+ return component;
+ };
+
+ // make one subnode per constructed array element
+ TIntermAggregate* constructor = nullptr;
+ TType derefType(type, 0);
+ TType speculativeComponentType(derefType, 0);
+ TType* componentType = derefType.isVector() ? &speculativeComponentType : &derefType;
+ TOperator componentOp = intermediate.mapTypeToConstructorOp(*componentType);
+ TType crossType(node->getBasicType(), EvqTemporary, type.getVectorSize());
+ for (int e = 0; e < type.getOuterArraySize(); ++e) {
+ // construct an element
+ TIntermTyped* elementArg;
+ if (type.getVectorSize() == constructee->getVectorSize()) {
+ // same element shape
+ elementArg = handleBracketDereference(node->getLoc(), constructee,
+ intermediate.addConstantUnion(e, node->getLoc()));
+ } else {
+ // mismatched element shapes
+ if (type.getVectorSize() == 1)
+ elementArg = getNextComponent();
+ else {
+ // make a vector
+ TIntermAggregate* elementConstructee = nullptr;
+ for (int c = 0; c < type.getVectorSize(); ++c)
+ elementConstructee = intermediate.growAggregate(elementConstructee, getNextComponent());
+ elementArg = addConstructor(node->getLoc(), elementConstructee, crossType);
+ }
+ }
+ // convert basic types
+ elementArg = intermediate.addConversion(componentOp, derefType, elementArg);
+ if (elementArg == nullptr)
+ return nullptr;
+ // combine with top-level constructor
+ constructor = intermediate.growAggregate(constructor, elementArg);
+ }
+
+ return constructor;
+}
+
+// This function tests for the type of the parameters to the structure or array constructor. Raises
+// an error message if the expected type does not match the parameter passed to the constructor.
+//
+// Returns nullptr for an error or the input node itself if the expected and the given parameter types match.
+//
+TIntermTyped* HlslParseContext::constructAggregate(TIntermNode* node, const TType& type, int paramCount,
+ const TSourceLoc& loc)
+{
+ // Handle cases that map more 1:1 between constructor arguments and constructed.
+ TIntermTyped* converted = intermediate.addConversion(EOpConstructStruct, type, node->getAsTyped());
+ if (converted == nullptr || converted->getType() != type) {
+ error(loc, "", "constructor", "cannot convert parameter %d from '%s' to '%s'", paramCount,
+ node->getAsTyped()->getType().getCompleteString().c_str(), type.getCompleteString().c_str());
+
+ return nullptr;
+ }
+
+ return converted;
+}
+
+//
+// Do everything needed to add an interface block.
+//
+void HlslParseContext::declareBlock(const TSourceLoc& loc, TType& type, const TString* instanceName)
+{
+ assert(type.getWritableStruct() != nullptr);
+
+ // Clean up top-level decorations that don't belong.
+ switch (type.getQualifier().storage) {
+ case EvqUniform:
+ case EvqBuffer:
+ correctUniform(type.getQualifier());
+ break;
+ case EvqVaryingIn:
+ correctInput(type.getQualifier());
+ break;
+ case EvqVaryingOut:
+ correctOutput(type.getQualifier());
+ break;
+ default:
+ break;
+ }
+
+ TTypeList& typeList = *type.getWritableStruct();
+ // fix and check for member storage qualifiers and types that don't belong within a block
+ for (unsigned int member = 0; member < typeList.size(); ++member) {
+ TType& memberType = *typeList[member].type;
+ TQualifier& memberQualifier = memberType.getQualifier();
+ const TSourceLoc& memberLoc = typeList[member].loc;
+ globalQualifierFix(memberLoc, memberQualifier);
+ memberQualifier.storage = type.getQualifier().storage;
+
+ if (memberType.isStruct()) {
+ // clean up and pick up the right set of decorations
+ auto it = ioTypeMap.find(memberType.getStruct());
+ switch (type.getQualifier().storage) {
+ case EvqUniform:
+ case EvqBuffer:
+ correctUniform(type.getQualifier());
+ if (it != ioTypeMap.end() && it->second.uniform)
+ memberType.setStruct(it->second.uniform);
+ break;
+ case EvqVaryingIn:
+ correctInput(type.getQualifier());
+ if (it != ioTypeMap.end() && it->second.input)
+ memberType.setStruct(it->second.input);
+ break;
+ case EvqVaryingOut:
+ correctOutput(type.getQualifier());
+ if (it != ioTypeMap.end() && it->second.output)
+ memberType.setStruct(it->second.output);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // Make default block qualification, and adjust the member qualifications
+
+ TQualifier defaultQualification;
+ switch (type.getQualifier().storage) {
+ case EvqUniform: defaultQualification = globalUniformDefaults; break;
+ case EvqBuffer: defaultQualification = globalBufferDefaults; break;
+ case EvqVaryingIn: defaultQualification = globalInputDefaults; break;
+ case EvqVaryingOut: defaultQualification = globalOutputDefaults; break;
+ default: defaultQualification.clear(); break;
+ }
+
+ // Special case for "push_constant uniform", which has a default of std430,
+ // contrary to normal uniform defaults, and can't have a default tracked for it.
+ if (type.getQualifier().layoutPushConstant && ! type.getQualifier().hasPacking())
+ type.getQualifier().layoutPacking = ElpStd430;
+
+ // fix and check for member layout qualifiers
+
+ mergeObjectLayoutQualifiers(defaultQualification, type.getQualifier(), true);
+
+ bool memberWithLocation = false;
+ bool memberWithoutLocation = false;
+ for (unsigned int member = 0; member < typeList.size(); ++member) {
+ TQualifier& memberQualifier = typeList[member].type->getQualifier();
+ const TSourceLoc& memberLoc = typeList[member].loc;
+ if (memberQualifier.hasStream()) {
+ if (defaultQualification.layoutStream != memberQualifier.layoutStream)
+ error(memberLoc, "member cannot contradict block", "stream", "");
+ }
+
+ // "This includes a block's inheritance of the
+ // current global default buffer, a block member's inheritance of the block's
+ // buffer, and the requirement that any *xfb_buffer* declared on a block
+ // member must match the buffer inherited from the block."
+ if (memberQualifier.hasXfbBuffer()) {
+ if (defaultQualification.layoutXfbBuffer != memberQualifier.layoutXfbBuffer)
+ error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_buffer", "");
+ }
+
+ if (memberQualifier.hasLocation()) {
+ switch (type.getQualifier().storage) {
+ case EvqVaryingIn:
+ case EvqVaryingOut:
+ memberWithLocation = true;
+ break;
+ default:
+ break;
+ }
+ } else
+ memberWithoutLocation = true;
+
+ TQualifier newMemberQualification = defaultQualification;
+ mergeQualifiers(newMemberQualification, memberQualifier);
+ memberQualifier = newMemberQualification;
+ }
+
+ // Process the members
+ fixBlockLocations(loc, type.getQualifier(), typeList, memberWithLocation, memberWithoutLocation);
+ fixXfbOffsets(type.getQualifier(), typeList);
+ fixBlockUniformOffsets(type.getQualifier(), typeList);
+
+ // reverse merge, so that currentBlockQualifier now has all layout information
+ // (can't use defaultQualification directly, it's missing other non-layout-default-class qualifiers)
+ mergeObjectLayoutQualifiers(type.getQualifier(), defaultQualification, true);
+
+ //
+ // Build and add the interface block as a new type named 'blockName'
+ //
+
+ // Use the instance name as the interface name if one exists, else the block name.
+ const TString& interfaceName = (instanceName && !instanceName->empty()) ? *instanceName : type.getTypeName();
+
+ TType blockType(&typeList, interfaceName, type.getQualifier());
+ if (type.isArray())
+ blockType.transferArraySizes(type.getArraySizes());
+
+ // Add the variable, as anonymous or named instanceName.
+ // Make an anonymous variable if no name was provided.
+ if (instanceName == nullptr)
+ instanceName = NewPoolTString("");
+
+ TVariable& variable = *new TVariable(instanceName, blockType);
+ if (! symbolTable.insert(variable)) {
+ if (*instanceName == "")
+ error(loc, "nameless block contains a member that already has a name at global scope",
+ "" /* blockName->c_str() */, "");
+ else
+ error(loc, "block instance name redefinition", variable.getName().c_str(), "");
+
+ return;
+ }
+
+ // Save it in the AST for linker use.
+ if (symbolTable.atGlobalLevel())
+ trackLinkage(variable);
+}
+
+//
+// "For a block, this process applies to the entire block, or until the first member
+// is reached that has a location layout qualifier. When a block member is declared with a location
+// qualifier, its location comes from that qualifier: The member's location qualifier overrides the block-level
+// declaration. Subsequent members are again assigned consecutive locations, based on the newest location,
+// until the next member declared with a location qualifier. The values used for locations do not have to be
+// declared in increasing order."
+void HlslParseContext::fixBlockLocations(const TSourceLoc& loc, TQualifier& qualifier, TTypeList& typeList, bool memberWithLocation, bool memberWithoutLocation)
+{
+ // "If a block has no block-level location layout qualifier, it is required that either all or none of its members
+ // have a location layout qualifier, or a compile-time error results."
+ if (! qualifier.hasLocation() && memberWithLocation && memberWithoutLocation)
+ error(loc, "either the block needs a location, or all members need a location, or no members have a location", "location", "");
+ else {
+ if (memberWithLocation) {
+ // remove any block-level location and make it per *every* member
+ int nextLocation = 0; // by the rule above, initial value is not relevant
+ if (qualifier.hasAnyLocation()) {
+ nextLocation = qualifier.layoutLocation;
+ qualifier.layoutLocation = TQualifier::layoutLocationEnd;
+ if (qualifier.hasComponent()) {
+ // "It is a compile-time error to apply the *component* qualifier to a ... block"
+ error(loc, "cannot apply to a block", "component", "");
+ }
+ if (qualifier.hasIndex()) {
+ error(loc, "cannot apply to a block", "index", "");
+ }
+ }
+ for (unsigned int member = 0; member < typeList.size(); ++member) {
+ TQualifier& memberQualifier = typeList[member].type->getQualifier();
+ const TSourceLoc& memberLoc = typeList[member].loc;
+ if (! memberQualifier.hasLocation()) {
+ if (nextLocation >= (int)TQualifier::layoutLocationEnd)
+ error(memberLoc, "location is too large", "location", "");
+ memberQualifier.layoutLocation = nextLocation;
+ memberQualifier.layoutComponent = 0;
+ }
+ nextLocation = memberQualifier.layoutLocation +
+ intermediate.computeTypeLocationSize(*typeList[member].type, language);
+ }
+ }
+ }
+}
+
+void HlslParseContext::fixXfbOffsets(TQualifier& qualifier, TTypeList& typeList)
+{
+ // "If a block is qualified with xfb_offset, all its
+ // members are assigned transform feedback buffer offsets. If a block is not qualified with xfb_offset, any
+ // members of that block not qualified with an xfb_offset will not be assigned transform feedback buffer
+ // offsets."
+
+ if (! qualifier.hasXfbBuffer() || ! qualifier.hasXfbOffset())
+ return;
+
+ int nextOffset = qualifier.layoutXfbOffset;
+ for (unsigned int member = 0; member < typeList.size(); ++member) {
+ TQualifier& memberQualifier = typeList[member].type->getQualifier();
+ bool contains64BitType = false;
+#ifdef AMD_EXTENSIONS
+ bool contains32BitType = false;
+ bool contains16BitType = false;
+ int memberSize = intermediate.computeTypeXfbSize(*typeList[member].type, contains64BitType, contains32BitType, contains16BitType);
+#else
+ int memberSize = intermediate.computeTypeXfbSize(*typeList[member].type, contains64BitType);
+#endif
+ // see if we need to auto-assign an offset to this member
+ if (! memberQualifier.hasXfbOffset()) {
+ // "if applied to an aggregate containing a double or 64-bit integer, the offset must also be a multiple of 8"
+ if (contains64BitType)
+ RoundToPow2(nextOffset, 8);
+#ifdef AMD_EXTENSIONS
+ else if (contains32BitType)
+ RoundToPow2(nextOffset, 4);
+ // "if applied to an aggregate containing a half float or 16-bit integer, the offset must also be a multiple of 2"
+ else if (contains16BitType)
+ RoundToPow2(nextOffset, 2);
+#endif
+ memberQualifier.layoutXfbOffset = nextOffset;
+ } else
+ nextOffset = memberQualifier.layoutXfbOffset;
+ nextOffset += memberSize;
+ }
+
+ // The above gave all block members an offset, so we can take it off the block now,
+ // which will avoid double counting the offset usage.
+ qualifier.layoutXfbOffset = TQualifier::layoutXfbOffsetEnd;
+}
+
+// Calculate and save the offset of each block member, using the recursively
+// defined block offset rules and the user-provided offset and align.
+//
+// Also, compute and save the total size of the block. For the block's size, arrayness
+// is not taken into account, as each element is backed by a separate buffer.
+//
+void HlslParseContext::fixBlockUniformOffsets(const TQualifier& qualifier, TTypeList& typeList)
+{
+ if (! qualifier.isUniformOrBuffer())
+ return;
+ if (qualifier.layoutPacking != ElpStd140 && qualifier.layoutPacking != ElpStd430 && qualifier.layoutPacking != ElpScalar)
+ return;
+
+ int offset = 0;
+ int memberSize;
+ for (unsigned int member = 0; member < typeList.size(); ++member) {
+ TQualifier& memberQualifier = typeList[member].type->getQualifier();
+ const TSourceLoc& memberLoc = typeList[member].loc;
+
+ // "When align is applied to an array, it effects only the start of the array, not the array's internal stride."
+
+ // modify just the children's view of matrix layout, if there is one for this member
+ TLayoutMatrix subMatrixLayout = typeList[member].type->getQualifier().layoutMatrix;
+ int dummyStride;
+ int memberAlignment = intermediate.getMemberAlignment(*typeList[member].type, memberSize, dummyStride,
+ qualifier.layoutPacking,
+ subMatrixLayout != ElmNone
+ ? subMatrixLayout == ElmRowMajor
+ : qualifier.layoutMatrix == ElmRowMajor);
+ if (memberQualifier.hasOffset()) {
+ // "The specified offset must be a multiple
+ // of the base alignment of the type of the block member it qualifies, or a compile-time error results."
+ if (! IsMultipleOfPow2(memberQualifier.layoutOffset, memberAlignment))
+ error(memberLoc, "must be a multiple of the member's alignment", "offset", "");
+
+ // "The offset qualifier forces the qualified member to start at or after the specified
+ // integral-constant expression, which will be its byte offset from the beginning of the buffer.
+ // "The actual offset of a member is computed as
+ // follows: If offset was declared, start with that offset, otherwise start with the next available offset."
+ offset = std::max(offset, memberQualifier.layoutOffset);
+ }
+
+ // "The actual alignment of a member will be the greater of the specified align alignment and the standard
+ // (e.g., std140) base alignment for the member's type."
+ if (memberQualifier.hasAlign())
+ memberAlignment = std::max(memberAlignment, memberQualifier.layoutAlign);
+
+ // "If the resulting offset is not a multiple of the actual alignment,
+ // increase it to the first offset that is a multiple of
+ // the actual alignment."
+ RoundToPow2(offset, memberAlignment);
+ typeList[member].type->getQualifier().layoutOffset = offset;
+ offset += memberSize;
+ }
+}
+
+// For an identifier that is already declared, add more qualification to it.
+void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, const TString& identifier)
+{
+ TSymbol* symbol = symbolTable.find(identifier);
+ if (symbol == nullptr) {
+ error(loc, "identifier not previously declared", identifier.c_str(), "");
+ return;
+ }
+ if (symbol->getAsFunction()) {
+ error(loc, "cannot re-qualify a function name", identifier.c_str(), "");
+ return;
+ }
+
+ if (qualifier.isAuxiliary() ||
+ qualifier.isMemory() ||
+ qualifier.isInterpolation() ||
+ qualifier.hasLayout() ||
+ qualifier.storage != EvqTemporary ||
+ qualifier.precision != EpqNone) {
+ error(loc, "cannot add storage, auxiliary, memory, interpolation, layout, or precision qualifier to an existing variable", identifier.c_str(), "");
+ return;
+ }
+
+ // For read-only built-ins, add a new symbol for holding the modified qualifier.
+ // This will bring up an entire block, if a block type has to be modified (e.g., gl_Position inside a block)
+ if (symbol->isReadOnly())
+ symbol = symbolTable.copyUp(symbol);
+
+ if (qualifier.invariant) {
+ if (intermediate.inIoAccessed(identifier))
+ error(loc, "cannot change qualification after use", "invariant", "");
+ symbol->getWritableType().getQualifier().invariant = true;
+ } else if (qualifier.noContraction) {
+ if (intermediate.inIoAccessed(identifier))
+ error(loc, "cannot change qualification after use", "precise", "");
+ symbol->getWritableType().getQualifier().noContraction = true;
+ } else if (qualifier.specConstant) {
+ symbol->getWritableType().getQualifier().makeSpecConstant();
+ if (qualifier.hasSpecConstantId())
+ symbol->getWritableType().getQualifier().layoutSpecConstantId = qualifier.layoutSpecConstantId;
+ } else
+ warn(loc, "unknown requalification", "", "");
+}
+
+void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, TIdentifierList& identifiers)
+{
+ for (unsigned int i = 0; i < identifiers.size(); ++i)
+ addQualifierToExisting(loc, qualifier, *identifiers[i]);
+}
+
+//
+// Update the intermediate for the given input geometry
+//
+bool HlslParseContext::handleInputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry)
+{
+ switch (geometry) {
+ case ElgPoints: // fall through
+ case ElgLines: // ...
+ case ElgTriangles: // ...
+ case ElgLinesAdjacency: // ...
+ case ElgTrianglesAdjacency: // ...
+ if (! intermediate.setInputPrimitive(geometry)) {
+ error(loc, "input primitive geometry redefinition", TQualifier::getGeometryString(geometry), "");
+ return false;
+ }
+ break;
+
+ default:
+ error(loc, "cannot apply to 'in'", TQualifier::getGeometryString(geometry), "");
+ return false;
+ }
+
+ return true;
+}
+
+//
+// Update the intermediate for the given output geometry
+//
+bool HlslParseContext::handleOutputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry)
+{
+ // If this is not a geometry shader, ignore. It might be a mixed shader including several stages.
+ // Since that's an OK situation, return true for success.
+ if (language != EShLangGeometry)
+ return true;
+
+ switch (geometry) {
+ case ElgPoints:
+ case ElgLineStrip:
+ case ElgTriangleStrip:
+ if (! intermediate.setOutputPrimitive(geometry)) {
+ error(loc, "output primitive geometry redefinition", TQualifier::getGeometryString(geometry), "");
+ return false;
+ }
+ break;
+ default:
+ error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(geometry), "");
+ return false;
+ }
+
+ return true;
+}
+
+//
+// Selection attributes
+//
+void HlslParseContext::handleSelectionAttributes(const TSourceLoc& loc, TIntermSelection* selection,
+ const TAttributes& attributes)
+{
+ if (selection == nullptr)
+ return;
+
+ for (auto it = attributes.begin(); it != attributes.end(); ++it) {
+ switch (it->name) {
+ case EatFlatten:
+ selection->setFlatten();
+ break;
+ case EatBranch:
+ selection->setDontFlatten();
+ break;
+ default:
+ warn(loc, "attribute does not apply to a selection", "", "");
+ break;
+ }
+ }
+}
+
+//
+// Switch attributes
+//
+void HlslParseContext::handleSwitchAttributes(const TSourceLoc& loc, TIntermSwitch* selection,
+ const TAttributes& attributes)
+{
+ if (selection == nullptr)
+ return;
+
+ for (auto it = attributes.begin(); it != attributes.end(); ++it) {
+ switch (it->name) {
+ case EatFlatten:
+ selection->setFlatten();
+ break;
+ case EatBranch:
+ selection->setDontFlatten();
+ break;
+ default:
+ warn(loc, "attribute does not apply to a switch", "", "");
+ break;
+ }
+ }
+}
+
+//
+// Loop attributes
+//
+void HlslParseContext::handleLoopAttributes(const TSourceLoc& loc, TIntermLoop* loop,
+ const TAttributes& attributes)
+{
+ if (loop == nullptr)
+ return;
+
+ for (auto it = attributes.begin(); it != attributes.end(); ++it) {
+ switch (it->name) {
+ case EatUnroll:
+ loop->setUnroll();
+ break;
+ case EatLoop:
+ loop->setDontUnroll();
+ break;
+ default:
+ warn(loc, "attribute does not apply to a loop", "", "");
+ break;
+ }
+ }
+}
+
+//
+// Updating default qualifier for the case of a declaration with just a qualifier,
+// no type, block, or identifier.
+//
+void HlslParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, const TPublicType& publicType)
+{
+ if (publicType.shaderQualifiers.vertices != TQualifier::layoutNotSet) {
+ assert(language == EShLangTessControl || language == EShLangGeometry);
+ // const char* id = (language == EShLangTessControl) ? "vertices" : "max_vertices";
+ }
+ if (publicType.shaderQualifiers.invocations != TQualifier::layoutNotSet) {
+ if (! intermediate.setInvocations(publicType.shaderQualifiers.invocations))
+ error(loc, "cannot change previously set layout value", "invocations", "");
+ }
+ if (publicType.shaderQualifiers.geometry != ElgNone) {
+ if (publicType.qualifier.storage == EvqVaryingIn) {
+ switch (publicType.shaderQualifiers.geometry) {
+ case ElgPoints:
+ case ElgLines:
+ case ElgLinesAdjacency:
+ case ElgTriangles:
+ case ElgTrianglesAdjacency:
+ case ElgQuads:
+ case ElgIsolines:
+ break;
+ default:
+ error(loc, "cannot apply to input", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry),
+ "");
+ }
+ } else if (publicType.qualifier.storage == EvqVaryingOut) {
+ handleOutputGeometry(loc, publicType.shaderQualifiers.geometry);
+ } else
+ error(loc, "cannot apply to:", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry),
+ GetStorageQualifierString(publicType.qualifier.storage));
+ }
+ if (publicType.shaderQualifiers.spacing != EvsNone)
+ intermediate.setVertexSpacing(publicType.shaderQualifiers.spacing);
+ if (publicType.shaderQualifiers.order != EvoNone)
+ intermediate.setVertexOrder(publicType.shaderQualifiers.order);
+ if (publicType.shaderQualifiers.pointMode)
+ intermediate.setPointMode();
+ for (int i = 0; i < 3; ++i) {
+ if (publicType.shaderQualifiers.localSize[i] > 1) {
+ int max = 0;
+ switch (i) {
+ case 0: max = resources.maxComputeWorkGroupSizeX; break;
+ case 1: max = resources.maxComputeWorkGroupSizeY; break;
+ case 2: max = resources.maxComputeWorkGroupSizeZ; break;
+ default: break;
+ }
+ if (intermediate.getLocalSize(i) > (unsigned int)max)
+ error(loc, "too large; see gl_MaxComputeWorkGroupSize", "local_size", "");
+
+ // Fix the existing constant gl_WorkGroupSize with this new information.
+ TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize");
+ workGroupSize->getWritableConstArray()[i].setUConst(intermediate.getLocalSize(i));
+ }
+ if (publicType.shaderQualifiers.localSizeSpecId[i] != TQualifier::layoutNotSet) {
+ intermediate.setLocalSizeSpecId(i, publicType.shaderQualifiers.localSizeSpecId[i]);
+ // Set the workgroup built-in variable as a specialization constant
+ TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize");
+ workGroupSize->getWritableType().getQualifier().specConstant = true;
+ }
+ }
+ if (publicType.shaderQualifiers.earlyFragmentTests)
+ intermediate.setEarlyFragmentTests();
+
+ const TQualifier& qualifier = publicType.qualifier;
+
+ switch (qualifier.storage) {
+ case EvqUniform:
+ if (qualifier.hasMatrix())
+ globalUniformDefaults.layoutMatrix = qualifier.layoutMatrix;
+ if (qualifier.hasPacking())
+ globalUniformDefaults.layoutPacking = qualifier.layoutPacking;
+ break;
+ case EvqBuffer:
+ if (qualifier.hasMatrix())
+ globalBufferDefaults.layoutMatrix = qualifier.layoutMatrix;
+ if (qualifier.hasPacking())
+ globalBufferDefaults.layoutPacking = qualifier.layoutPacking;
+ break;
+ case EvqVaryingIn:
+ break;
+ case EvqVaryingOut:
+ if (qualifier.hasStream())
+ globalOutputDefaults.layoutStream = qualifier.layoutStream;
+ if (qualifier.hasXfbBuffer())
+ globalOutputDefaults.layoutXfbBuffer = qualifier.layoutXfbBuffer;
+ if (globalOutputDefaults.hasXfbBuffer() && qualifier.hasXfbStride()) {
+ if (! intermediate.setXfbBufferStride(globalOutputDefaults.layoutXfbBuffer, qualifier.layoutXfbStride))
+ error(loc, "all stride settings must match for xfb buffer", "xfb_stride", "%d",
+ qualifier.layoutXfbBuffer);
+ }
+ break;
+ default:
+ error(loc, "default qualifier requires 'uniform', 'buffer', 'in', or 'out' storage qualification", "", "");
+ return;
+ }
+}
+
+//
+// Take the sequence of statements that has been built up since the last case/default,
+// put it on the list of top-level nodes for the current (inner-most) switch statement,
+// and follow that by the case/default we are on now. (See switch topology comment on
+// TIntermSwitch.)
+//
+void HlslParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode)
+{
+ TIntermSequence* switchSequence = switchSequenceStack.back();
+
+ if (statements) {
+ statements->setOperator(EOpSequence);
+ switchSequence->push_back(statements);
+ }
+ if (branchNode) {
+ // check all previous cases for the same label (or both are 'default')
+ for (unsigned int s = 0; s < switchSequence->size(); ++s) {
+ TIntermBranch* prevBranch = (*switchSequence)[s]->getAsBranchNode();
+ if (prevBranch) {
+ TIntermTyped* prevExpression = prevBranch->getExpression();
+ TIntermTyped* newExpression = branchNode->getAsBranchNode()->getExpression();
+ if (prevExpression == nullptr && newExpression == nullptr)
+ error(branchNode->getLoc(), "duplicate label", "default", "");
+ else if (prevExpression != nullptr &&
+ newExpression != nullptr &&
+ prevExpression->getAsConstantUnion() &&
+ newExpression->getAsConstantUnion() &&
+ prevExpression->getAsConstantUnion()->getConstArray()[0].getIConst() ==
+ newExpression->getAsConstantUnion()->getConstArray()[0].getIConst())
+ error(branchNode->getLoc(), "duplicated value", "case", "");
+ }
+ }
+ switchSequence->push_back(branchNode);
+ }
+}
+
+//
+// Turn the top-level node sequence built up of wrapupSwitchSubsequence
+// into a switch node.
+//
+TIntermNode* HlslParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* expression,
+ TIntermAggregate* lastStatements, const TAttributes& attributes)
+{
+ wrapupSwitchSubsequence(lastStatements, nullptr);
+
+ if (expression == nullptr ||
+ (expression->getBasicType() != EbtInt && expression->getBasicType() != EbtUint) ||
+ expression->getType().isArray() || expression->getType().isMatrix() || expression->getType().isVector())
+ error(loc, "condition must be a scalar integer expression", "switch", "");
+
+ // If there is nothing to do, drop the switch but still execute the expression
+ TIntermSequence* switchSequence = switchSequenceStack.back();
+ if (switchSequence->size() == 0)
+ return expression;
+
+ if (lastStatements == nullptr) {
+ // emulate a break for error recovery
+ lastStatements = intermediate.makeAggregate(intermediate.addBranch(EOpBreak, loc));
+ lastStatements->setOperator(EOpSequence);
+ switchSequence->push_back(lastStatements);
+ }
+
+ TIntermAggregate* body = new TIntermAggregate(EOpSequence);
+ body->getSequence() = *switchSequenceStack.back();
+ body->setLoc(loc);
+
+ TIntermSwitch* switchNode = new TIntermSwitch(expression, body);
+ switchNode->setLoc(loc);
+ handleSwitchAttributes(loc, switchNode, attributes);
+
+ return switchNode;
+}
+
+// Make a new symbol-table level that is made out of the members of a structure.
+// This should be done as an anonymous struct (name is "") so that the symbol table
+// finds the members with no explicit reference to a 'this' variable.
+void HlslParseContext::pushThisScope(const TType& thisStruct, const TVector<TFunctionDeclarator>& functionDeclarators)
+{
+ // member variables
+ TVariable& thisVariable = *new TVariable(NewPoolTString(""), thisStruct);
+ symbolTable.pushThis(thisVariable);
+
+ // member functions
+ for (auto it = functionDeclarators.begin(); it != functionDeclarators.end(); ++it) {
+ // member should have a prefix matching currentTypePrefix.back()
+ // but, symbol lookup within the class scope will just use the
+ // unprefixed name. Hence, there are two: one fully prefixed and
+ // one with no prefix.
+ TFunction& member = *it->function->clone();
+ member.removePrefix(currentTypePrefix.back());
+ symbolTable.insert(member);
+ }
+}
+
+// Track levels of class/struct/namespace nesting with a prefix string using
+// the type names separated by the scoping operator. E.g., two levels
+// would look like:
+//
+// outer::inner
+//
+// The string is empty when at normal global level.
+//
+void HlslParseContext::pushNamespace(const TString& typeName)
+{
+ // make new type prefix
+ TString newPrefix;
+ if (currentTypePrefix.size() > 0)
+ newPrefix = currentTypePrefix.back();
+ newPrefix.append(typeName);
+ newPrefix.append(scopeMangler);
+ currentTypePrefix.push_back(newPrefix);
+}
+
+// Opposite of pushNamespace(), see above
+void HlslParseContext::popNamespace()
+{
+ currentTypePrefix.pop_back();
+}
+
+// Use the class/struct nesting string to create a global name for
+// a member of a class/struct.
+void HlslParseContext::getFullNamespaceName(TString*& name) const
+{
+ if (currentTypePrefix.size() == 0)
+ return;
+
+ TString* fullName = NewPoolTString(currentTypePrefix.back().c_str());
+ fullName->append(*name);
+ name = fullName;
+}
+
+// Helper function to add the namespace scope mangling syntax to a string.
+void HlslParseContext::addScopeMangler(TString& name)
+{
+ name.append(scopeMangler);
+}
+
+// Return true if this has uniform-interface like decorations.
+bool HlslParseContext::hasUniform(const TQualifier& qualifier) const
+{
+ return qualifier.hasUniformLayout() ||
+ qualifier.layoutPushConstant;
+}
+
+// Potentially not the opposite of hasUniform(), as if some characteristic is
+// ever used for more than one thing (e.g., uniform or input), hasUniform() should
+// say it exists, but clearUniform() should leave it in place.
+void HlslParseContext::clearUniform(TQualifier& qualifier)
+{
+ qualifier.clearUniformLayout();
+ qualifier.layoutPushConstant = false;
+}
+
+// Return false if builtIn by itself doesn't force this qualifier to be an input qualifier.
+bool HlslParseContext::isInputBuiltIn(const TQualifier& qualifier) const
+{
+ switch (qualifier.builtIn) {
+ case EbvPosition:
+ case EbvPointSize:
+ return language != EShLangVertex && language != EShLangCompute && language != EShLangFragment;
+ case EbvClipDistance:
+ case EbvCullDistance:
+ return language != EShLangVertex && language != EShLangCompute;
+ case EbvFragCoord:
+ case EbvFace:
+ case EbvHelperInvocation:
+ case EbvLayer:
+ case EbvPointCoord:
+ case EbvSampleId:
+ case EbvSampleMask:
+ case EbvSamplePosition:
+ case EbvViewportIndex:
+ return language == EShLangFragment;
+ case EbvGlobalInvocationId:
+ case EbvLocalInvocationIndex:
+ case EbvLocalInvocationId:
+ case EbvNumWorkGroups:
+ case EbvWorkGroupId:
+ case EbvWorkGroupSize:
+ return language == EShLangCompute;
+ case EbvInvocationId:
+ return language == EShLangTessControl || language == EShLangTessEvaluation || language == EShLangGeometry;
+ case EbvPatchVertices:
+ return language == EShLangTessControl || language == EShLangTessEvaluation;
+ case EbvInstanceId:
+ case EbvInstanceIndex:
+ case EbvVertexId:
+ case EbvVertexIndex:
+ return language == EShLangVertex;
+ case EbvPrimitiveId:
+ return language == EShLangGeometry || language == EShLangFragment || language == EShLangTessControl;
+ case EbvTessLevelInner:
+ case EbvTessLevelOuter:
+ return language == EShLangTessEvaluation;
+ case EbvTessCoord:
+ return language == EShLangTessEvaluation;
+ default:
+ return false;
+ }
+}
+
+// Return true if there are decorations to preserve for input-like storage.
+bool HlslParseContext::hasInput(const TQualifier& qualifier) const
+{
+ if (qualifier.hasAnyLocation())
+ return true;
+
+ if (language == EShLangFragment && (qualifier.isInterpolation() || qualifier.centroid || qualifier.sample))
+ return true;
+
+ if (language == EShLangTessEvaluation && qualifier.patch)
+ return true;
+
+ if (isInputBuiltIn(qualifier))
+ return true;
+
+ return false;
+}
+
+// Return false if builtIn by itself doesn't force this qualifier to be an output qualifier.
+bool HlslParseContext::isOutputBuiltIn(const TQualifier& qualifier) const
+{
+ switch (qualifier.builtIn) {
+ case EbvPosition:
+ case EbvPointSize:
+ case EbvClipVertex:
+ case EbvClipDistance:
+ case EbvCullDistance:
+ return language != EShLangFragment && language != EShLangCompute;
+ case EbvFragDepth:
+ case EbvFragDepthGreater:
+ case EbvFragDepthLesser:
+ case EbvSampleMask:
+ return language == EShLangFragment;
+ case EbvLayer:
+ case EbvViewportIndex:
+ return language == EShLangGeometry || language == EShLangVertex;
+ case EbvPrimitiveId:
+ return language == EShLangGeometry;
+ case EbvTessLevelInner:
+ case EbvTessLevelOuter:
+ return language == EShLangTessControl;
+ default:
+ return false;
+ }
+}
+
+// Return true if there are decorations to preserve for output-like storage.
+bool HlslParseContext::hasOutput(const TQualifier& qualifier) const
+{
+ if (qualifier.hasAnyLocation())
+ return true;
+
+ if (language != EShLangFragment && language != EShLangCompute && qualifier.hasXfb())
+ return true;
+
+ if (language == EShLangTessControl && qualifier.patch)
+ return true;
+
+ if (language == EShLangGeometry && qualifier.hasStream())
+ return true;
+
+ if (isOutputBuiltIn(qualifier))
+ return true;
+
+ return false;
+}
+
+// Make the IO decorations etc. be appropriate only for an input interface.
+void HlslParseContext::correctInput(TQualifier& qualifier)
+{
+ clearUniform(qualifier);
+ if (language == EShLangVertex)
+ qualifier.clearInterstage();
+ if (language != EShLangTessEvaluation)
+ qualifier.patch = false;
+ if (language != EShLangFragment) {
+ qualifier.clearInterpolation();
+ qualifier.sample = false;
+ }
+
+ qualifier.clearStreamLayout();
+ qualifier.clearXfbLayout();
+
+ if (! isInputBuiltIn(qualifier))
+ qualifier.builtIn = EbvNone;
+}
+
+// Make the IO decorations etc. be appropriate only for an output interface.
+void HlslParseContext::correctOutput(TQualifier& qualifier)
+{
+ clearUniform(qualifier);
+ if (language == EShLangFragment)
+ qualifier.clearInterstage();
+ if (language != EShLangGeometry)
+ qualifier.clearStreamLayout();
+ if (language == EShLangFragment)
+ qualifier.clearXfbLayout();
+ if (language != EShLangTessControl)
+ qualifier.patch = false;
+
+ switch (qualifier.builtIn) {
+ case EbvFragDepth:
+ intermediate.setDepthReplacing();
+ intermediate.setDepth(EldAny);
+ break;
+ case EbvFragDepthGreater:
+ intermediate.setDepthReplacing();
+ intermediate.setDepth(EldGreater);
+ qualifier.builtIn = EbvFragDepth;
+ break;
+ case EbvFragDepthLesser:
+ intermediate.setDepthReplacing();
+ intermediate.setDepth(EldLess);
+ qualifier.builtIn = EbvFragDepth;
+ break;
+ default:
+ break;
+ }
+
+ if (! isOutputBuiltIn(qualifier))
+ qualifier.builtIn = EbvNone;
+}
+
+// Make the IO decorations etc. be appropriate only for uniform type interfaces.
+void HlslParseContext::correctUniform(TQualifier& qualifier)
+{
+ if (qualifier.declaredBuiltIn == EbvNone)
+ qualifier.declaredBuiltIn = qualifier.builtIn;
+
+ qualifier.builtIn = EbvNone;
+ qualifier.clearInterstage();
+ qualifier.clearInterstageLayout();
+}
+
+// Clear out all IO/Uniform stuff, so this has nothing to do with being an IO interface.
+void HlslParseContext::clearUniformInputOutput(TQualifier& qualifier)
+{
+ clearUniform(qualifier);
+ correctUniform(qualifier);
+}
+
+
+// Set texture return type. Returns success (not all types are valid).
+bool HlslParseContext::setTextureReturnType(TSampler& sampler, const TType& retType, const TSourceLoc& loc)
+{
+ // Seed the output with an invalid index. We will set it to a valid one if we can.
+ sampler.structReturnIndex = TSampler::noReturnStruct;
+
+ // Arrays aren't supported.
+ if (retType.isArray()) {
+ error(loc, "Arrays not supported in texture template types", "", "");
+ return false;
+ }
+
+ // If return type is a vector, remember the vector size in the sampler, and return.
+ if (retType.isVector() || retType.isScalar()) {
+ sampler.vectorSize = retType.getVectorSize();
+ return true;
+ }
+
+ // If it wasn't a vector, it must be a struct meeting certain requirements. The requirements
+ // are checked below: just check for struct-ness here.
+ if (!retType.isStruct()) {
+ error(loc, "Invalid texture template type", "", "");
+ return false;
+ }
+
+ // TODO: Subpass doesn't handle struct returns, due to some oddities with fn overloading.
+ if (sampler.isSubpass()) {
+ error(loc, "Unimplemented: structure template type in subpass input", "", "");
+ return false;
+ }
+
+ TTypeList* members = retType.getWritableStruct();
+
+ // Check for too many or not enough structure members.
+ if (members->size() > 4 || members->size() == 0) {
+ error(loc, "Invalid member count in texture template structure", "", "");
+ return false;
+ }
+
+ // Error checking: We must have <= 4 total components, all of the same basic type.
+ unsigned totalComponents = 0;
+ for (unsigned m = 0; m < members->size(); ++m) {
+ // Check for bad member types
+ if (!(*members)[m].type->isScalar() && !(*members)[m].type->isVector()) {
+ error(loc, "Invalid texture template struct member type", "", "");
+ return false;
+ }
+
+ const unsigned memberVectorSize = (*members)[m].type->getVectorSize();
+ totalComponents += memberVectorSize;
+
+ // too many total member components
+ if (totalComponents > 4) {
+ error(loc, "Too many components in texture template structure type", "", "");
+ return false;
+ }
+
+ // All members must be of a common basic type
+ if ((*members)[m].type->getBasicType() != (*members)[0].type->getBasicType()) {
+ error(loc, "Texture template structure members must same basic type", "", "");
+ return false;
+ }
+ }
+
+ // If the structure in the return type already exists in the table, we'll use it. Otherwise, we'll make
+ // a new entry. This is a linear search, but it hardly ever happens, and the list cannot be very large.
+ for (unsigned int idx = 0; idx < textureReturnStruct.size(); ++idx) {
+ if (textureReturnStruct[idx] == members) {
+ sampler.structReturnIndex = idx;
+ return true;
+ }
+ }
+
+ // It wasn't found as an existing entry. See if we have room for a new one.
+ if (textureReturnStruct.size() >= TSampler::structReturnSlots) {
+ error(loc, "Texture template struct return slots exceeded", "", "");
+ return false;
+ }
+
+ // Insert it in the vector that tracks struct return types.
+ sampler.structReturnIndex = unsigned(textureReturnStruct.size());
+ textureReturnStruct.push_back(members);
+
+ // Success!
+ return true;
+}
+
+// Return the sampler return type in retType.
+void HlslParseContext::getTextureReturnType(const TSampler& sampler, TType& retType) const
+{
+ if (sampler.hasReturnStruct()) {
+ assert(textureReturnStruct.size() >= sampler.structReturnIndex);
+
+ // We land here if the texture return is a structure.
+ TTypeList* blockStruct = textureReturnStruct[sampler.structReturnIndex];
+
+ const TType resultType(blockStruct, "");
+ retType.shallowCopy(resultType);
+ } else {
+ // We land here if the texture return is a vector or scalar.
+ const TType resultType(sampler.type, EvqTemporary, sampler.getVectorSize());
+ retType.shallowCopy(resultType);
+ }
+}
+
+
+// Return a symbol for the tessellation linkage variable of the given TBuiltInVariable type
+TIntermSymbol* HlslParseContext::findTessLinkageSymbol(TBuiltInVariable biType) const
+{
+ const auto it = builtInTessLinkageSymbols.find(biType);
+ if (it == builtInTessLinkageSymbols.end()) // if it wasn't declared by the user, return nullptr
+ return nullptr;
+
+ return intermediate.addSymbol(*it->second->getAsVariable());
+}
+
+// Find the patch constant function (issues error, returns nullptr if not found)
+const TFunction* HlslParseContext::findPatchConstantFunction(const TSourceLoc& loc)
+{
+ if (symbolTable.isFunctionNameVariable(patchConstantFunctionName)) {
+ error(loc, "can't use variable in patch constant function", patchConstantFunctionName.c_str(), "");
+ return nullptr;
+ }
+
+ const TString mangledName = patchConstantFunctionName + "(";
+
+ // create list of PCF candidates
+ TVector<const TFunction*> candidateList;
+ bool builtIn;
+ symbolTable.findFunctionNameList(mangledName, candidateList, builtIn);
+
+ // We have to have one and only one, or we don't know which to pick: the patchconstantfunc does not
+ // allow any disambiguation of overloads.
+ if (candidateList.empty()) {
+ error(loc, "patch constant function not found", patchConstantFunctionName.c_str(), "");
+ return nullptr;
+ }
+
+ // Based on directed experiments, it appears that if there are overloaded patchconstantfunctions,
+ // HLSL picks the last one in shader source order. Since that isn't yet implemented here, error
+ // out if there is more than one candidate.
+ if (candidateList.size() > 1) {
+ error(loc, "ambiguous patch constant function", patchConstantFunctionName.c_str(), "");
+ return nullptr;
+ }
+
+ return candidateList[0];
+}
+
+// Finalization step: Add patch constant function invocation
+void HlslParseContext::addPatchConstantInvocation()
+{
+ TSourceLoc loc;
+ loc.init();
+
+ // If there's no patch constant function, or we're not a HS, do nothing.
+ if (patchConstantFunctionName.empty() || language != EShLangTessControl)
+ return;
+
+ // Look for built-in variables in a function's parameter list.
+ const auto findBuiltIns = [&](const TFunction& function, std::set<tInterstageIoData>& builtIns) {
+ for (int p=0; p<function.getParamCount(); ++p) {
+ TStorageQualifier storage = function[p].type->getQualifier().storage;
+
+ if (storage == EvqConstReadOnly) // treated identically to input
+ storage = EvqIn;
+
+ if (function[p].getDeclaredBuiltIn() != EbvNone)
+ builtIns.insert(HlslParseContext::tInterstageIoData(function[p].getDeclaredBuiltIn(), storage));
+ else
+ builtIns.insert(HlslParseContext::tInterstageIoData(function[p].type->getQualifier().builtIn, storage));
+ }
+ };
+
+ // If we synthesize a built-in interface variable, we must add it to the linkage.
+ const auto addToLinkage = [&](const TType& type, const TString* name, TIntermSymbol** symbolNode) {
+ if (name == nullptr) {
+ error(loc, "unable to locate patch function parameter name", "", "");
+ return;
+ } else {
+ TVariable& variable = *new TVariable(name, type);
+ if (! symbolTable.insert(variable)) {
+ error(loc, "unable to declare patch constant function interface variable", name->c_str(), "");
+ return;
+ }
+
+ globalQualifierFix(loc, variable.getWritableType().getQualifier());
+
+ if (symbolNode != nullptr)
+ *symbolNode = intermediate.addSymbol(variable);
+
+ trackLinkage(variable);
+ }
+ };
+
+ const auto isOutputPatch = [](TFunction& patchConstantFunction, int param) {
+ const TType& type = *patchConstantFunction[param].type;
+ const TBuiltInVariable biType = patchConstantFunction[param].getDeclaredBuiltIn();
+
+ return type.isSizedArray() && biType == EbvOutputPatch;
+ };
+
+ // We will perform these steps. Each is in a scoped block for separation: they could
+ // become separate functions to make addPatchConstantInvocation shorter.
+ //
+ // 1. Union the interfaces, and create built-ins for anything present in the PCF and
+ // declared as a built-in variable that isn't present in the entry point's signature.
+ //
+ // 2. Synthesizes a call to the patchconstfunction using built-in variables from either main,
+ // or the ones we created. Matching is based on built-in type. We may use synthesized
+ // variables from (1) above.
+ //
+ // 2B: Synthesize per control point invocations of wrapped entry point if the PCF requires them.
+ //
+ // 3. Create a return sequence: copy the return value (if any) from the PCF to a
+ // (non-sanitized) output variable. In case this may involve multiple copies, such as for
+ // an arrayed variable, a temporary copy of the PCF output is created to avoid multiple
+ // indirections into a complex R-value coming from the call to the PCF.
+ //
+ // 4. Create a barrier.
+ //
+ // 5/5B. Call the PCF inside an if test for (invocation id == 0).
+
+ TFunction* patchConstantFunctionPtr = const_cast<TFunction*>(findPatchConstantFunction(loc));
+
+ if (patchConstantFunctionPtr == nullptr)
+ return;
+
+ TFunction& patchConstantFunction = *patchConstantFunctionPtr;
+
+ const int pcfParamCount = patchConstantFunction.getParamCount();
+ TIntermSymbol* invocationIdSym = findTessLinkageSymbol(EbvInvocationId);
+ TIntermSequence& epBodySeq = entryPointFunctionBody->getAsAggregate()->getSequence();
+
+ int outPatchParam = -1; // -1 means there isn't one.
+
+ // ================ Step 1A: Union Interfaces ================
+ // Our patch constant function.
+ {
+ std::set<tInterstageIoData> pcfBuiltIns; // patch constant function built-ins
+ std::set<tInterstageIoData> epfBuiltIns; // entry point function built-ins
+
+ assert(entryPointFunction);
+ assert(entryPointFunctionBody);
+
+ findBuiltIns(patchConstantFunction, pcfBuiltIns);
+ findBuiltIns(*entryPointFunction, epfBuiltIns);
+
+ // Find the set of built-ins in the PCF that are not present in the entry point.
+ std::set<tInterstageIoData> notInEntryPoint;
+
+ notInEntryPoint = pcfBuiltIns;
+
+ // std::set_difference not usable on unordered containers
+ for (auto bi = epfBuiltIns.begin(); bi != epfBuiltIns.end(); ++bi)
+ notInEntryPoint.erase(*bi);
+
+ // Now we'll add those to the entry and to the linkage.
+ for (int p=0; p<pcfParamCount; ++p) {
+ const TBuiltInVariable biType = patchConstantFunction[p].getDeclaredBuiltIn();
+ TStorageQualifier storage = patchConstantFunction[p].type->getQualifier().storage;
+
+ // Track whether there is an output patch param
+ if (isOutputPatch(patchConstantFunction, p)) {
+ if (outPatchParam >= 0) {
+ // Presently we only support one per ctrl pt input.
+ error(loc, "unimplemented: multiple output patches in patch constant function", "", "");
+ return;
+ }
+ outPatchParam = p;
+ }
+
+ if (biType != EbvNone) {
+ TType* paramType = patchConstantFunction[p].type->clone();
+
+ if (storage == EvqConstReadOnly) // treated identically to input
+ storage = EvqIn;
+
+ // Presently, the only non-built-in we support is InputPatch, which is treated as
+ // a pseudo-built-in.
+ if (biType == EbvInputPatch) {
+ builtInTessLinkageSymbols[biType] = inputPatch;
+ } else if (biType == EbvOutputPatch) {
+ // Nothing...
+ } else {
+ // Use the original declaration type for the linkage
+ paramType->getQualifier().builtIn = biType;
+
+ if (notInEntryPoint.count(tInterstageIoData(biType, storage)) == 1)
+ addToLinkage(*paramType, patchConstantFunction[p].name, nullptr);
+ }
+ }
+ }
+
+ // If we didn't find it because the shader made one, add our own.
+ if (invocationIdSym == nullptr) {
+ TType invocationIdType(EbtUint, EvqIn, 1);
+ TString* invocationIdName = NewPoolTString("InvocationId");
+ invocationIdType.getQualifier().builtIn = EbvInvocationId;
+ addToLinkage(invocationIdType, invocationIdName, &invocationIdSym);
+ }
+
+ assert(invocationIdSym);
+ }
+
+ TIntermTyped* pcfArguments = nullptr;
+ TVariable* perCtrlPtVar = nullptr;
+
+ // ================ Step 1B: Argument synthesis ================
+ // Create pcfArguments for synthesis of patchconstantfunction invocation
+ {
+ for (int p=0; p<pcfParamCount; ++p) {
+ TIntermTyped* inputArg = nullptr;
+
+ if (p == outPatchParam) {
+ if (perCtrlPtVar == nullptr) {
+ perCtrlPtVar = makeInternalVariable(*patchConstantFunction[outPatchParam].name,
+ *patchConstantFunction[outPatchParam].type);
+
+ perCtrlPtVar->getWritableType().getQualifier().makeTemporary();
+ }
+ inputArg = intermediate.addSymbol(*perCtrlPtVar, loc);
+ } else {
+ // find which built-in it is
+ const TBuiltInVariable biType = patchConstantFunction[p].getDeclaredBuiltIn();
+
+ if (biType == EbvInputPatch && inputPatch == nullptr) {
+ error(loc, "unimplemented: PCF input patch without entry point input patch parameter", "", "");
+ return;
+ }
+
+ inputArg = findTessLinkageSymbol(biType);
+
+ if (inputArg == nullptr) {
+ error(loc, "unable to find patch constant function built-in variable", "", "");
+ return;
+ }
+ }
+
+ if (pcfParamCount == 1)
+ pcfArguments = inputArg;
+ else
+ pcfArguments = intermediate.growAggregate(pcfArguments, inputArg);
+ }
+ }
+
+ // ================ Step 2: Synthesize call to PCF ================
+ TIntermAggregate* pcfCallSequence = nullptr;
+ TIntermTyped* pcfCall = nullptr;
+
+ {
+ // Create a function call to the patchconstantfunction
+ if (pcfArguments)
+ addInputArgumentConversions(patchConstantFunction, pcfArguments);
+
+ // Synthetic call.
+ pcfCall = intermediate.setAggregateOperator(pcfArguments, EOpFunctionCall, patchConstantFunction.getType(), loc);
+ pcfCall->getAsAggregate()->setUserDefined();
+ pcfCall->getAsAggregate()->setName(patchConstantFunction.getMangledName());
+ intermediate.addToCallGraph(infoSink, intermediate.getEntryPointMangledName().c_str(),
+ patchConstantFunction.getMangledName());
+
+ if (pcfCall->getAsAggregate()) {
+ TQualifierList& qualifierList = pcfCall->getAsAggregate()->getQualifierList();
+ for (int i = 0; i < patchConstantFunction.getParamCount(); ++i) {
+ TStorageQualifier qual = patchConstantFunction[i].type->getQualifier().storage;
+ qualifierList.push_back(qual);
+ }
+ pcfCall = addOutputArgumentConversions(patchConstantFunction, *pcfCall->getAsOperator());
+ }
+ }
+
+ // ================ Step 2B: Per Control Point synthesis ================
+ // If there is per control point data, we must either emulate that with multiple
+ // invocations of the entry point to build up an array, or (TODO:) use a yet
+ // unavailable extension to look across the SIMD lanes. This is the former
+ // as a placeholder for the latter.
+ if (outPatchParam >= 0) {
+ // We must introduce a local temp variable of the type wanted by the PCF input.
+ const int arraySize = patchConstantFunction[outPatchParam].type->getOuterArraySize();
+
+ if (entryPointFunction->getType().getBasicType() == EbtVoid) {
+ error(loc, "entry point must return a value for use with patch constant function", "", "");
+ return;
+ }
+
+ // Create calls to wrapped main to fill in the array. We will substitute fixed values
+ // of invocation ID when calling the wrapped main.
+
+ // This is the type of the each member of the per ctrl point array.
+ const TType derefType(perCtrlPtVar->getType(), 0);
+
+ for (int cpt = 0; cpt < arraySize; ++cpt) {
+ // TODO: improve. substr(1) here is to avoid the '@' that was grafted on but isn't in the symtab
+ // for this function.
+ const TString origName = entryPointFunction->getName().substr(1);
+ TFunction callee(&origName, TType(EbtVoid));
+ TIntermTyped* callingArgs = nullptr;
+
+ for (int i = 0; i < entryPointFunction->getParamCount(); i++) {
+ TParameter& param = (*entryPointFunction)[i];
+ TType& paramType = *param.type;
+
+ if (paramType.getQualifier().isParamOutput()) {
+ error(loc, "unimplemented: entry point outputs in patch constant function invocation", "", "");
+ return;
+ }
+
+ if (paramType.getQualifier().isParamInput()) {
+ TIntermTyped* arg = nullptr;
+ if ((*entryPointFunction)[i].getDeclaredBuiltIn() == EbvInvocationId) {
+ // substitute invocation ID with the array element ID
+ arg = intermediate.addConstantUnion(cpt, loc);
+ } else {
+ TVariable* argVar = makeInternalVariable(*param.name, *param.type);
+ argVar->getWritableType().getQualifier().makeTemporary();
+ arg = intermediate.addSymbol(*argVar);
+ }
+
+ handleFunctionArgument(&callee, callingArgs, arg);
+ }
+ }
+
+ // Call and assign to per ctrl point variable
+ currentCaller = intermediate.getEntryPointMangledName().c_str();
+ TIntermTyped* callReturn = handleFunctionCall(loc, &callee, callingArgs);
+ TIntermTyped* index = intermediate.addConstantUnion(cpt, loc);
+ TIntermSymbol* perCtrlPtSym = intermediate.addSymbol(*perCtrlPtVar, loc);
+ TIntermTyped* element = intermediate.addIndex(EOpIndexDirect, perCtrlPtSym, index, loc);
+ element->setType(derefType);
+ element->setLoc(loc);
+
+ pcfCallSequence = intermediate.growAggregate(pcfCallSequence,
+ handleAssign(loc, EOpAssign, element, callReturn));
+ }
+ }
+
+ // ================ Step 3: Create return Sequence ================
+ // Return sequence: copy PCF result to a temporary, then to shader output variable.
+ if (pcfCall->getBasicType() != EbtVoid) {
+ const TType* retType = &patchConstantFunction.getType(); // return type from the PCF
+ TType outType; // output type that goes with the return type.
+ outType.shallowCopy(*retType);
+
+ // substitute the output type
+ const auto newLists = ioTypeMap.find(retType->getStruct());
+ if (newLists != ioTypeMap.end())
+ outType.setStruct(newLists->second.output);
+
+ // Substitute the top level type's built-in type
+ if (patchConstantFunction.getDeclaredBuiltInType() != EbvNone)
+ outType.getQualifier().builtIn = patchConstantFunction.getDeclaredBuiltInType();
+
+ outType.getQualifier().patch = true; // make it a per-patch variable
+
+ TVariable* pcfOutput = makeInternalVariable("@patchConstantOutput", outType);
+ pcfOutput->getWritableType().getQualifier().storage = EvqVaryingOut;
+
+ if (pcfOutput->getType().containsBuiltIn())
+ split(*pcfOutput);
+
+ assignToInterface(*pcfOutput);
+
+ TIntermSymbol* pcfOutputSym = intermediate.addSymbol(*pcfOutput, loc);
+
+ // The call to the PCF is a complex R-value: we want to store it in a temp to avoid
+ // repeated calls to the PCF:
+ TVariable* pcfCallResult = makeInternalVariable("@patchConstantResult", *retType);
+ pcfCallResult->getWritableType().getQualifier().makeTemporary();
+
+ TIntermSymbol* pcfResultVar = intermediate.addSymbol(*pcfCallResult, loc);
+ TIntermNode* pcfResultAssign = handleAssign(loc, EOpAssign, pcfResultVar, pcfCall);
+ TIntermNode* pcfResultToOut = handleAssign(loc, EOpAssign, pcfOutputSym,
+ intermediate.addSymbol(*pcfCallResult, loc));
+
+ pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfResultAssign);
+ pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfResultToOut);
+ } else {
+ pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfCall);
+ }
+
+ // ================ Step 4: Barrier ================
+ TIntermTyped* barrier = new TIntermAggregate(EOpBarrier);
+ barrier->setLoc(loc);
+ barrier->setType(TType(EbtVoid));
+ epBodySeq.insert(epBodySeq.end(), barrier);
+
+ // ================ Step 5: Test on invocation ID ================
+ TIntermTyped* zero = intermediate.addConstantUnion(0, loc, true);
+ TIntermTyped* cmp = intermediate.addBinaryNode(EOpEqual, invocationIdSym, zero, loc, TType(EbtBool));
+
+
+ // ================ Step 5B: Create if statement on Invocation ID == 0 ================
+ intermediate.setAggregateOperator(pcfCallSequence, EOpSequence, TType(EbtVoid), loc);
+ TIntermTyped* invocationIdTest = new TIntermSelection(cmp, pcfCallSequence, nullptr);
+ invocationIdTest->setLoc(loc);
+
+ // add our test sequence before the return.
+ epBodySeq.insert(epBodySeq.end(), invocationIdTest);
+}
+
+// Finalization step: remove unused buffer blocks from linkage (we don't know until the
+// shader is entirely compiled).
+// Preserve order of remaining symbols.
+void HlslParseContext::removeUnusedStructBufferCounters()
+{
+ const auto endIt = std::remove_if(linkageSymbols.begin(), linkageSymbols.end(),
+ [this](const TSymbol* sym) {
+ const auto sbcIt = structBufferCounter.find(sym->getName());
+ return sbcIt != structBufferCounter.end() && !sbcIt->second;
+ });
+
+ linkageSymbols.erase(endIt, linkageSymbols.end());
+}
+
+// Finalization step: patch texture shadow modes to match samplers they were combined with
+void HlslParseContext::fixTextureShadowModes()
+{
+ for (auto symbol = linkageSymbols.begin(); symbol != linkageSymbols.end(); ++symbol) {
+ TSampler& sampler = (*symbol)->getWritableType().getSampler();
+
+ if (sampler.isTexture()) {
+ const auto shadowMode = textureShadowVariant.find((*symbol)->getUniqueId());
+ if (shadowMode != textureShadowVariant.end()) {
+
+ if (shadowMode->second->overloaded())
+ // Texture needs legalization if it's been seen with both shadow and non-shadow modes.
+ intermediate.setNeedsLegalization();
+
+ sampler.shadow = shadowMode->second->isShadowId((*symbol)->getUniqueId());
+ }
+ }
+ }
+}
+
+// Finalization step: patch append methods to use proper stream output, which isn't known until
+// main is parsed, which could happen after the append method is parsed.
+void HlslParseContext::finalizeAppendMethods()
+{
+ TSourceLoc loc;
+ loc.init();
+
+ // Nothing to do: bypass test for valid stream output.
+ if (gsAppends.empty())
+ return;
+
+ if (gsStreamOutput == nullptr) {
+ error(loc, "unable to find output symbol for Append()", "", "");
+ return;
+ }
+
+ // Patch append sequences, now that we know the stream output symbol.
+ for (auto append = gsAppends.begin(); append != gsAppends.end(); ++append) {
+ append->node->getSequence()[0] =
+ handleAssign(append->loc, EOpAssign,
+ intermediate.addSymbol(*gsStreamOutput, append->loc),
+ append->node->getSequence()[0]->getAsTyped());
+ }
+}
+
+// post-processing
+void HlslParseContext::finish()
+{
+ // Error check: There was a dangling .mips operator. These are not nested constructs in the grammar, so
+ // cannot be detected there. This is not strictly needed in a non-validating parser; it's just helpful.
+ if (! mipsOperatorMipArg.empty()) {
+ error(mipsOperatorMipArg.back().loc, "unterminated mips operator:", "", "");
+ }
+
+ removeUnusedStructBufferCounters();
+ addPatchConstantInvocation();
+ fixTextureShadowModes();
+ finalizeAppendMethods();
+
+ // Communicate out (esp. for command line) that we formed AST that will make
+ // illegal AST SPIR-V and it needs transforms to legalize it.
+ if (intermediate.needsLegalization() && (messages & EShMsgHlslLegalization))
+ infoSink.info << "WARNING: AST will form illegal SPIR-V; need to transform to legalize";
+
+ TParseContextBase::finish();
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/hlsl/hlslParseHelper.h b/src/3rdparty/glslang/hlsl/hlslParseHelper.h
new file mode 100644
index 0000000..822de89
--- /dev/null
+++ b/src/3rdparty/glslang/hlsl/hlslParseHelper.h
@@ -0,0 +1,507 @@
+//
+// Copyright (C) 2016-2018 Google, Inc.
+// Copyright (C) 2016 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+#ifndef HLSL_PARSE_INCLUDED_
+#define HLSL_PARSE_INCLUDED_
+
+#include "../glslang/MachineIndependent/parseVersions.h"
+#include "../glslang/MachineIndependent/ParseHelper.h"
+#include "../glslang/MachineIndependent/attribute.h"
+
+#include <array>
+
+namespace glslang {
+
+class TFunctionDeclarator;
+
+class HlslParseContext : public TParseContextBase {
+public:
+ HlslParseContext(TSymbolTable&, TIntermediate&, bool parsingBuiltins,
+ int version, EProfile, const SpvVersion& spvVersion, EShLanguage, TInfoSink&,
+ const TString sourceEntryPointName,
+ bool forwardCompatible = false, EShMessages messages = EShMsgDefault);
+ virtual ~HlslParseContext();
+ void initializeExtensionBehavior() override;
+
+ void setLimits(const TBuiltInResource&) override;
+ bool parseShaderStrings(TPpContext&, TInputScanner& input, bool versionWillBeError = false) override;
+ virtual const char* getGlobalUniformBlockName() const override { return "$Global"; }
+ virtual void setUniformBlockDefaults(TType& block) const override
+ {
+ block.getQualifier().layoutPacking = ElpStd140;
+ block.getQualifier().layoutMatrix = ElmRowMajor;
+ }
+
+ void reservedPpErrorCheck(const TSourceLoc&, const char* /*name*/, const char* /*op*/) override { }
+ bool lineContinuationCheck(const TSourceLoc&, bool /*endOfComment*/) override { return true; }
+ bool lineDirectiveShouldSetNextLine() const override { return true; }
+ bool builtInName(const TString&);
+
+ void handlePragma(const TSourceLoc&, const TVector<TString>&) override;
+ TIntermTyped* handleVariable(const TSourceLoc&, const TString* string);
+ TIntermTyped* handleBracketDereference(const TSourceLoc&, TIntermTyped* base, TIntermTyped* index);
+ TIntermTyped* handleBracketOperator(const TSourceLoc&, TIntermTyped* base, TIntermTyped* index);
+
+ TIntermTyped* handleBinaryMath(const TSourceLoc&, const char* str, TOperator op, TIntermTyped* left, TIntermTyped* right);
+ TIntermTyped* handleUnaryMath(const TSourceLoc&, const char* str, TOperator op, TIntermTyped* childNode);
+ TIntermTyped* handleDotDereference(const TSourceLoc&, TIntermTyped* base, const TString& field);
+ bool isBuiltInMethod(const TSourceLoc&, TIntermTyped* base, const TString& field);
+ void assignToInterface(TVariable& variable);
+ void handleFunctionDeclarator(const TSourceLoc&, TFunction& function, bool prototype);
+ TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&, const TAttributes&, TIntermNode*& entryPointTree);
+ TIntermNode* transformEntryPoint(const TSourceLoc&, TFunction&, const TAttributes&);
+ void handleEntryPointAttributes(const TSourceLoc&, const TAttributes&);
+ void transferTypeAttributes(const TSourceLoc&, const TAttributes&, TType&, bool allowEntry = false);
+ void handleFunctionBody(const TSourceLoc&, TFunction&, TIntermNode* functionBody, TIntermNode*& node);
+ void remapEntryPointIO(TFunction& function, TVariable*& returnValue, TVector<TVariable*>& inputs, TVector<TVariable*>& outputs);
+ void remapNonEntryPointIO(TFunction& function);
+ TIntermNode* handleReturnValue(const TSourceLoc&, TIntermTyped*);
+ void handleFunctionArgument(TFunction*, TIntermTyped*& arguments, TIntermTyped* newArg);
+ TIntermTyped* handleAssign(const TSourceLoc&, TOperator, TIntermTyped* left, TIntermTyped* right);
+ TIntermTyped* handleAssignToMatrixSwizzle(const TSourceLoc&, TOperator, TIntermTyped* left, TIntermTyped* right);
+ TIntermTyped* handleFunctionCall(const TSourceLoc&, TFunction*, TIntermTyped*);
+ TIntermAggregate* assignClipCullDistance(const TSourceLoc&, TOperator, int semanticId, TIntermTyped* left, TIntermTyped* right);
+ TIntermTyped* assignPosition(const TSourceLoc&, TOperator, TIntermTyped* left, TIntermTyped* right);
+ void decomposeIntrinsic(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments);
+ void decomposeSampleMethods(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments);
+ void decomposeStructBufferMethods(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments);
+ void decomposeGeometryMethods(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments);
+ void pushFrontArguments(TIntermTyped* front, TIntermTyped*& arguments);
+ void addInputArgumentConversions(const TFunction&, TIntermTyped*&);
+ void expandArguments(const TSourceLoc&, const TFunction&, TIntermTyped*&);
+ TIntermTyped* addOutputArgumentConversions(const TFunction&, TIntermOperator&);
+ void builtInOpCheck(const TSourceLoc&, const TFunction&, TIntermOperator&);
+ TFunction* makeConstructorCall(const TSourceLoc&, const TType&);
+ void handleSemantic(TSourceLoc, TQualifier&, TBuiltInVariable, const TString& upperCase);
+ void handlePackOffset(const TSourceLoc&, TQualifier&, const glslang::TString& location,
+ const glslang::TString* component);
+ void handleRegister(const TSourceLoc&, TQualifier&, const glslang::TString* profile, const glslang::TString& desc,
+ int subComponent, const glslang::TString*);
+ TIntermTyped* convertConditionalExpression(const TSourceLoc&, TIntermTyped*, bool mustBeScalar = true);
+ TIntermAggregate* handleSamplerTextureCombine(const TSourceLoc& loc, TIntermTyped* argTex, TIntermTyped* argSampler);
+
+ bool parseMatrixSwizzleSelector(const TSourceLoc&, const TString&, int cols, int rows, TSwizzleSelectors<TMatrixSelector>&);
+ int getMatrixComponentsColumn(int rows, const TSwizzleSelectors<TMatrixSelector>&);
+ void assignError(const TSourceLoc&, const char* op, TString left, TString right);
+ void unaryOpError(const TSourceLoc&, const char* op, TString operand);
+ void binaryOpError(const TSourceLoc&, const char* op, TString left, TString right);
+ void variableCheck(TIntermTyped*& nodePtr);
+ void constantValueCheck(TIntermTyped* node, const char* token);
+ void integerCheck(const TIntermTyped* node, const char* token);
+ void globalCheck(const TSourceLoc&, const char* token);
+ bool constructorError(const TSourceLoc&, TIntermNode*, TFunction&, TOperator, TType&);
+ void arraySizeCheck(const TSourceLoc&, TIntermTyped* expr, TArraySize&);
+ void arraySizeRequiredCheck(const TSourceLoc&, const TArraySizes&);
+ void structArrayCheck(const TSourceLoc&, const TType& structure);
+ bool voidErrorCheck(const TSourceLoc&, const TString&, TBasicType);
+ void globalQualifierFix(const TSourceLoc&, TQualifier&);
+ bool structQualifierErrorCheck(const TSourceLoc&, const TPublicType& pType);
+ void mergeQualifiers(TQualifier& dst, const TQualifier& src);
+ int computeSamplerTypeIndex(TSampler&);
+ TSymbol* redeclareBuiltinVariable(const TSourceLoc&, const TString&, const TQualifier&, const TShaderQualifiers&);
+ void paramFix(TType& type);
+ void specializationCheck(const TSourceLoc&, const TType&, const char* op);
+
+ void setLayoutQualifier(const TSourceLoc&, TQualifier&, TString&);
+ void setLayoutQualifier(const TSourceLoc&, TQualifier&, TString&, const TIntermTyped*);
+ void setSpecConstantId(const TSourceLoc&, TQualifier&, int value);
+ void mergeObjectLayoutQualifiers(TQualifier& dest, const TQualifier& src, bool inheritOnly);
+ void checkNoShaderLayouts(const TSourceLoc&, const TShaderQualifiers&);
+
+ const TFunction* findFunction(const TSourceLoc& loc, TFunction& call, bool& builtIn, int& thisDepth, TIntermTyped*& args);
+ void addGenMulArgumentConversion(const TSourceLoc& loc, TFunction& call, TIntermTyped*& args);
+ void declareTypedef(const TSourceLoc&, const TString& identifier, const TType&);
+ void declareStruct(const TSourceLoc&, TString& structName, TType&);
+ TSymbol* lookupUserType(const TString&, TType&);
+ TIntermNode* declareVariable(const TSourceLoc&, const TString& identifier, TType&, TIntermTyped* initializer = 0);
+ void lengthenList(const TSourceLoc&, TIntermSequence& list, int size, TIntermTyped* scalarInit);
+ TIntermTyped* handleConstructor(const TSourceLoc&, TIntermTyped*, const TType&);
+ TIntermTyped* addConstructor(const TSourceLoc&, TIntermTyped*, const TType&);
+ TIntermTyped* convertArray(TIntermTyped*, const TType&);
+ TIntermTyped* constructAggregate(TIntermNode*, const TType&, int, const TSourceLoc&);
+ TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermTyped*, const TSourceLoc&, bool subset);
+ void declareBlock(const TSourceLoc&, TType&, const TString* instanceName = 0);
+ void declareStructBufferCounter(const TSourceLoc& loc, const TType& bufferType, const TString& name);
+ void fixBlockLocations(const TSourceLoc&, TQualifier&, TTypeList&, bool memberWithLocation, bool memberWithoutLocation);
+ void fixXfbOffsets(TQualifier&, TTypeList&);
+ void fixBlockUniformOffsets(const TQualifier&, TTypeList&);
+ void addQualifierToExisting(const TSourceLoc&, TQualifier, const TString& identifier);
+ void addQualifierToExisting(const TSourceLoc&, TQualifier, TIdentifierList&);
+ void updateStandaloneQualifierDefaults(const TSourceLoc&, const TPublicType&);
+ void wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode);
+ TIntermNode* addSwitch(const TSourceLoc&, TIntermTyped* expression, TIntermAggregate* body, const TAttributes&);
+
+ void nestLooping() { ++loopNestingLevel; }
+ void unnestLooping() { --loopNestingLevel; }
+ void nestAnnotations() { ++annotationNestingLevel; }
+ void unnestAnnotations() { --annotationNestingLevel; }
+ int getAnnotationNestingLevel() { return annotationNestingLevel; }
+ void pushScope() { symbolTable.push(); }
+ void popScope() { symbolTable.pop(0); }
+
+ void pushThisScope(const TType&, const TVector<TFunctionDeclarator>&);
+ void popThisScope() { symbolTable.pop(0); }
+
+ void pushImplicitThis(TVariable* thisParameter) { implicitThisStack.push_back(thisParameter); }
+ void popImplicitThis() { implicitThisStack.pop_back(); }
+ TVariable* getImplicitThis(int thisDepth) const { return implicitThisStack[implicitThisStack.size() - thisDepth]; }
+
+ void pushNamespace(const TString& name);
+ void popNamespace();
+ void getFullNamespaceName(TString*&) const;
+ void addScopeMangler(TString&);
+
+ void pushSwitchSequence(TIntermSequence* sequence) { switchSequenceStack.push_back(sequence); }
+ void popSwitchSequence() { switchSequenceStack.pop_back(); }
+
+ virtual void growGlobalUniformBlock(const TSourceLoc&, TType&, const TString& memberName,
+ TTypeList* typeList = nullptr) override;
+
+ // Apply L-value conversions. E.g, turning a write to a RWTexture into an ImageStore.
+ TIntermTyped* handleLvalue(const TSourceLoc&, const char* op, TIntermTyped*& node);
+ bool lValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*) override;
+
+ TLayoutFormat getLayoutFromTxType(const TSourceLoc&, const TType&);
+
+ bool handleOutputGeometry(const TSourceLoc&, const TLayoutGeometry& geometry);
+ bool handleInputGeometry(const TSourceLoc&, const TLayoutGeometry& geometry);
+
+ // Determine selection control from attributes
+ void handleSelectionAttributes(const TSourceLoc& loc, TIntermSelection*, const TAttributes& attributes);
+ void handleSwitchAttributes(const TSourceLoc& loc, TIntermSwitch*, const TAttributes& attributes);
+
+ // Determine loop control from attributes
+ void handleLoopAttributes(const TSourceLoc& loc, TIntermLoop*, const TAttributes& attributes);
+
+ // Share struct buffer deep types
+ void shareStructBufferType(TType&);
+
+ // Set texture return type of the given sampler. Returns success (not all types are valid).
+ bool setTextureReturnType(TSampler& sampler, const TType& retType, const TSourceLoc& loc);
+
+ // Obtain the sampler return type of the given sampler in retType.
+ void getTextureReturnType(const TSampler& sampler, TType& retType) const;
+
+ TAttributeType attributeFromName(const TString& nameSpace, const TString& name) const;
+
+protected:
+ struct TFlattenData {
+ TFlattenData() : nextBinding(TQualifier::layoutBindingEnd),
+ nextLocation(TQualifier::layoutLocationEnd) { }
+ TFlattenData(int nb, int nl) : nextBinding(nb), nextLocation(nl) { }
+
+ TVector<TVariable*> members; // individual flattened variables
+ TVector<int> offsets; // offset to next tree level
+ unsigned int nextBinding; // next binding to use.
+ unsigned int nextLocation; // next location to use
+ };
+
+ void fixConstInit(const TSourceLoc&, const TString& identifier, TType& type, TIntermTyped*& initializer);
+ void inheritGlobalDefaults(TQualifier& dst) const;
+ TVariable* makeInternalVariable(const char* name, const TType&) const;
+ TVariable* makeInternalVariable(const TString& name, const TType& type) const {
+ return makeInternalVariable(name.c_str(), type);
+ }
+ TIntermSymbol* makeInternalVariableNode(const TSourceLoc&, const char* name, const TType&) const;
+ TVariable* declareNonArray(const TSourceLoc&, const TString& identifier, const TType&, bool track);
+ void declareArray(const TSourceLoc&, const TString& identifier, const TType&, TSymbol*&, bool track);
+ TIntermNode* executeInitializer(const TSourceLoc&, TIntermTyped* initializer, TVariable* variable);
+ TIntermTyped* convertInitializerList(const TSourceLoc&, const TType&, TIntermTyped* initializer, TIntermTyped* scalarInit);
+ bool isScalarConstructor(const TIntermNode*);
+ TOperator mapAtomicOp(const TSourceLoc& loc, TOperator op, bool isImage);
+
+ // Return true if this node requires L-value conversion (e.g, to an imageStore).
+ bool shouldConvertLValue(const TIntermNode*) const;
+
+ // Array and struct flattening
+ TIntermTyped* flattenAccess(TIntermTyped* base, int member);
+ TIntermTyped* flattenAccess(int uniqueId, int member, TStorageQualifier outerStorage, const TType&, int subset = -1);
+ int findSubtreeOffset(const TIntermNode&) const;
+ int findSubtreeOffset(const TType&, int subset, const TVector<int>& offsets) const;
+ bool shouldFlatten(const TType&, TStorageQualifier, bool topLevel) const;
+ bool wasFlattened(const TIntermTyped* node) const;
+ bool wasFlattened(int id) const { return flattenMap.find(id) != flattenMap.end(); }
+ int addFlattenedMember(const TVariable&, const TType&, TFlattenData&, const TString& name, bool linkage,
+ const TQualifier& outerQualifier, const TArraySizes* builtInArraySizes);
+
+ // Structure splitting (splits interstage built-in types into its own struct)
+ void split(const TVariable&);
+ void splitBuiltIn(const TString& baseName, const TType& memberType, const TArraySizes*, const TQualifier&);
+ const TType& split(const TType& type, const TString& name, const TQualifier&);
+ bool wasSplit(const TIntermTyped* node) const;
+ bool wasSplit(int id) const { return splitNonIoVars.find(id) != splitNonIoVars.end(); }
+ TVariable* getSplitNonIoVar(int id) const;
+ void addPatchConstantInvocation();
+ void fixTextureShadowModes();
+ void finalizeAppendMethods();
+ TIntermTyped* makeIntegerIndex(TIntermTyped*);
+
+ void fixBuiltInIoType(TType&);
+
+ void flatten(const TVariable& variable, bool linkage);
+ int flatten(const TVariable& variable, const TType&, TFlattenData&, TString name, bool linkage,
+ const TQualifier& outerQualifier, const TArraySizes* builtInArraySizes);
+ int flattenStruct(const TVariable& variable, const TType&, TFlattenData&, TString name, bool linkage,
+ const TQualifier& outerQualifier, const TArraySizes* builtInArraySizes);
+ int flattenArray(const TVariable& variable, const TType&, TFlattenData&, TString name, bool linkage,
+ const TQualifier& outerQualifier);
+
+ bool hasUniform(const TQualifier& qualifier) const;
+ void clearUniform(TQualifier& qualifier);
+ bool isInputBuiltIn(const TQualifier& qualifier) const;
+ bool hasInput(const TQualifier& qualifier) const;
+ void correctOutput(TQualifier& qualifier);
+ bool isOutputBuiltIn(const TQualifier& qualifier) const;
+ bool hasOutput(const TQualifier& qualifier) const;
+ void correctInput(TQualifier& qualifier);
+ void correctUniform(TQualifier& qualifier);
+ void clearUniformInputOutput(TQualifier& qualifier);
+
+ // Test method names
+ bool isStructBufferMethod(const TString& name) const;
+ void counterBufferType(const TSourceLoc& loc, TType& type);
+
+ // Return standard sample position array
+ TIntermConstantUnion* getSamplePosArray(int count);
+
+ TType* getStructBufferContentType(const TType& type) const;
+ bool isStructBufferType(const TType& type) const { return getStructBufferContentType(type) != nullptr; }
+ TIntermTyped* indexStructBufferContent(const TSourceLoc& loc, TIntermTyped* buffer) const;
+ TIntermTyped* getStructBufferCounter(const TSourceLoc& loc, TIntermTyped* buffer);
+ TString getStructBuffCounterName(const TString&) const;
+ void addStructBuffArguments(const TSourceLoc& loc, TIntermAggregate*&);
+ void addStructBufferHiddenCounterParam(const TSourceLoc& loc, TParameter&, TIntermAggregate*&);
+
+ // Return true if this type is a reference. This is not currently a type method in case that's
+ // a language specific answer.
+ bool isReference(const TType& type) const { return isStructBufferType(type); }
+
+ // Return true if this a buffer type that has an associated counter buffer.
+ bool hasStructBuffCounter(const TType&) const;
+
+ // Finalization step: remove unused buffer blocks from linkage (we don't know until the
+ // shader is entirely compiled)
+ void removeUnusedStructBufferCounters();
+
+ static bool isClipOrCullDistance(TBuiltInVariable);
+ static bool isClipOrCullDistance(const TQualifier& qual) { return isClipOrCullDistance(qual.builtIn); }
+ static bool isClipOrCullDistance(const TType& type) { return isClipOrCullDistance(type.getQualifier()); }
+
+ // Find the patch constant function (issues error, returns nullptr if not found)
+ const TFunction* findPatchConstantFunction(const TSourceLoc& loc);
+
+ // Pass through to base class after remembering built-in mappings.
+ using TParseContextBase::trackLinkage;
+ void trackLinkage(TSymbol& variable) override;
+
+ void finish() override; // post-processing
+
+ // Linkage symbol helpers
+ TIntermSymbol* findTessLinkageSymbol(TBuiltInVariable biType) const;
+
+ // Current state of parsing
+ int annotationNestingLevel; // 0 if outside all annotations
+
+ HlslParseContext(HlslParseContext&);
+ HlslParseContext& operator=(HlslParseContext&);
+
+ static const int maxSamplerIndex = EsdNumDims * (EbtNumTypes * (2 * 2 * 2)); // see computeSamplerTypeIndex()
+ TQualifier globalBufferDefaults;
+ TQualifier globalUniformDefaults;
+ TQualifier globalInputDefaults;
+ TQualifier globalOutputDefaults;
+ TString currentCaller; // name of last function body entered (not valid when at global scope)
+ TIdSetType inductiveLoopIds;
+ TVector<TIntermTyped*> needsIndexLimitationChecking;
+
+ //
+ // Geometry shader input arrays:
+ // - array sizing is based on input primitive and/or explicit size
+ //
+ // Tessellation control output arrays:
+ // - array sizing is based on output layout(vertices=...) and/or explicit size
+ //
+ // Both:
+ // - array sizing is retroactive
+ // - built-in block redeclarations interact with this
+ //
+ // Design:
+ // - use a per-context "resize-list", a list of symbols whose array sizes
+ // can be fixed
+ //
+ // - the resize-list starts empty at beginning of user-shader compilation, it does
+ // not have built-ins in it
+ //
+ // - on built-in array use: copyUp() symbol and add it to the resize-list
+ //
+ // - on user array declaration: add it to the resize-list
+ //
+ // - on block redeclaration: copyUp() symbol and add it to the resize-list
+ // * note, that appropriately gives an error if redeclaring a block that
+ // was already used and hence already copied-up
+ //
+ // - on seeing a layout declaration that sizes the array, fix everything in the
+ // resize-list, giving errors for mismatch
+ //
+ // - on seeing an array size declaration, give errors on mismatch between it and previous
+ // array-sizing declarations
+ //
+ TVector<TSymbol*> ioArraySymbolResizeList;
+
+ TMap<int, TFlattenData> flattenMap;
+
+ // IO-type map. Maps a pure symbol-table form of a structure-member list into
+ // each of the (up to) three kinds of IO, as each as different allowed decorations,
+ // but HLSL allows mixing all in the same structure.
+ struct tIoKinds {
+ TTypeList* input;
+ TTypeList* output;
+ TTypeList* uniform;
+ };
+ TMap<const TTypeList*, tIoKinds> ioTypeMap;
+
+ // Structure splitting data:
+ TMap<int, TVariable*> splitNonIoVars; // variables with the built-in interstage IO removed, indexed by unique ID.
+
+ // Structuredbuffer shared types. Typically there are only a few.
+ TVector<TType*> structBufferTypes;
+
+ // This tracks texture sample user structure return types. Only a limited number are supported, as
+ // may fit in TSampler::structReturnIndex.
+ TVector<TTypeList*> textureReturnStruct;
+
+ TMap<TString, bool> structBufferCounter; // true if counter buffer is in use
+
+ // The built-in interstage IO map considers e.g, EvqPosition on input and output separately, so that we
+ // can build the linkage correctly if position appears on both sides. Otherwise, multiple positions
+ // are considered identical.
+ struct tInterstageIoData {
+ tInterstageIoData(TBuiltInVariable bi, TStorageQualifier q) :
+ builtIn(bi), storage(q) { }
+
+ TBuiltInVariable builtIn;
+ TStorageQualifier storage;
+
+ // ordering for maps
+ bool operator<(const tInterstageIoData d) const {
+ return (builtIn != d.builtIn) ? (builtIn < d.builtIn) : (storage < d.storage);
+ }
+ };
+
+ TMap<tInterstageIoData, TVariable*> splitBuiltIns; // split built-ins, indexed by built-in type.
+ TVariable* inputPatch; // input patch is special for PCF: it's the only non-builtin PCF input,
+ // and is handled as a pseudo-builtin.
+
+ unsigned int nextInLocation;
+ unsigned int nextOutLocation;
+
+ TFunction* entryPointFunction;
+ TIntermNode* entryPointFunctionBody;
+
+ TString patchConstantFunctionName; // hull shader patch constant function name, from function level attribute.
+ TMap<TBuiltInVariable, TSymbol*> builtInTessLinkageSymbols; // used for tessellation, finding declared built-ins
+
+ TVector<TString> currentTypePrefix; // current scoping prefix for nested structures
+ TVector<TVariable*> implicitThisStack; // currently active 'this' variables for nested structures
+
+ TVariable* gsStreamOutput; // geometry shader stream outputs, for emit (Append method)
+
+ TVariable* clipDistanceOutput; // synthesized clip distance out variable (shader might have >1)
+ TVariable* cullDistanceOutput; // synthesized cull distance out variable (shader might have >1)
+ TVariable* clipDistanceInput; // synthesized clip distance in variable (shader might have >1)
+ TVariable* cullDistanceInput; // synthesized cull distance in variable (shader might have >1)
+
+ static const int maxClipCullRegs = 2;
+ std::array<int, maxClipCullRegs> clipSemanticNSizeIn; // vector, indexed by clip semantic ID
+ std::array<int, maxClipCullRegs> cullSemanticNSizeIn; // vector, indexed by cull semantic ID
+ std::array<int, maxClipCullRegs> clipSemanticNSizeOut; // vector, indexed by clip semantic ID
+ std::array<int, maxClipCullRegs> cullSemanticNSizeOut; // vector, indexed by cull semantic ID
+
+ // This tracks the first (mip level) argument to the .mips[][] operator. Since this can be nested as
+ // in tx.mips[tx.mips[0][1].x][2], we need a stack. We also track the TSourceLoc for error reporting
+ // purposes.
+ struct tMipsOperatorData {
+ tMipsOperatorData(TSourceLoc l, TIntermTyped* m) : loc(l), mipLevel(m) { }
+ TSourceLoc loc;
+ TIntermTyped* mipLevel;
+ };
+
+ TVector<tMipsOperatorData> mipsOperatorMipArg;
+
+ // The geometry output stream is not copied out from the entry point as a typical output variable
+ // is. It's written via EmitVertex (hlsl=Append), which may happen in arbitrary control flow.
+ // For this we need the real output symbol. Since it may not be known at the time and Append()
+ // method is parsed, the sequence will be patched during finalization.
+ struct tGsAppendData {
+ TIntermAggregate* node;
+ TSourceLoc loc;
+ };
+
+ TVector<tGsAppendData> gsAppends;
+
+ // A texture object may be used with shadow and non-shadow samplers, but both may not be
+ // alive post-DCE in the same shader. We do not know at compilation time which are alive: that's
+ // only known post-DCE. If a texture is used both ways, we create two textures, and
+ // leave the elimiation of one to the optimizer. This maps the shader variant to
+ // the shadow variant.
+ //
+ // This can be removed if and when the texture shadow code in
+ // HlslParseContext::handleSamplerTextureCombine is removed.
+ struct tShadowTextureSymbols {
+ tShadowTextureSymbols() { symId.fill(-1); }
+
+ void set(bool shadow, int id) { symId[int(shadow)] = id; }
+ int get(bool shadow) const { return symId[int(shadow)]; }
+
+ // True if this texture has been seen with both shadow and non-shadow modes
+ bool overloaded() const { return symId[0] != -1 && symId[1] != -1; }
+ bool isShadowId(int id) const { return symId[1] == id; }
+
+ private:
+ std::array<int, 2> symId;
+ };
+
+ TMap<int, tShadowTextureSymbols*> textureShadowVariant;
+};
+
+// This is the prefix we use for built-in methods to avoid namespace collisions with
+// global scope user functions.
+// TODO: this would be better as a nonparseable character, but that would
+// require changing the scanner.
+#define BUILTIN_PREFIX "__BI_"
+
+} // end namespace glslang
+
+#endif // HLSL_PARSE_INCLUDED_
diff --git a/src/3rdparty/glslang/hlsl/hlslParseables.cpp b/src/3rdparty/glslang/hlsl/hlslParseables.cpp
new file mode 100644
index 0000000..a63ecb6
--- /dev/null
+++ b/src/3rdparty/glslang/hlsl/hlslParseables.cpp
@@ -0,0 +1,1324 @@
+//
+// Copyright (C) 2016 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+//
+// Create strings that declare built-in definitions, add built-ins programmatically
+// that cannot be expressed in the strings, and establish mappings between
+// built-in functions and operators.
+//
+// Where to put a built-in:
+// TBuiltInParseablesHlsl::initialize(version,profile) context-independent textual built-ins; add them to the right string
+// TBuiltInParseablesHlsl::initialize(resources,...) context-dependent textual built-ins; add them to the right string
+// TBuiltInParseablesHlsl::identifyBuiltIns(...,symbolTable) context-independent programmatic additions/mappings to the symbol table,
+// including identifying what extensions are needed if a version does not allow a symbol
+// TBuiltInParseablesHlsl::identifyBuiltIns(...,symbolTable, resources) context-dependent programmatic additions/mappings to the
+// symbol table, including identifying what extensions are needed if a version does
+// not allow a symbol
+//
+
+#include "hlslParseables.h"
+#include "hlslParseHelper.h"
+#include <cctype>
+#include <utility>
+#include <algorithm>
+
+namespace { // anonymous namespace functions
+
+const bool UseHlslTypes = true;
+
+const char* BaseTypeName(const char argOrder, const char* scalarName, const char* vecName, const char* matName)
+{
+ switch (argOrder) {
+ case 'S': return scalarName;
+ case 'V': return vecName;
+ case 'M': return matName;
+ default: return "UNKNOWN_TYPE";
+ }
+}
+
+// arg order queries
+bool IsSamplerType(const char argType) { return argType == 'S' || argType == 's'; }
+bool IsArrayed(const char argOrder) { return argOrder == '@' || argOrder == '&' || argOrder == '#'; }
+bool IsTextureNonMS(const char argOrder) { return argOrder == '%'; }
+bool IsSubpassInput(const char argOrder) { return argOrder == '[' || argOrder == ']'; }
+bool IsArrayedTexture(const char argOrder) { return argOrder == '@'; }
+bool IsTextureMS(const char argOrder) { return argOrder == '$' || argOrder == '&'; }
+bool IsMS(const char argOrder) { return IsTextureMS(argOrder) || argOrder == ']'; }
+bool IsBuffer(const char argOrder) { return argOrder == '*' || argOrder == '~'; }
+bool IsImage(const char argOrder) { return argOrder == '!' || argOrder == '#' || argOrder == '~'; }
+
+bool IsTextureType(const char argOrder)
+{
+ return IsTextureNonMS(argOrder) || IsArrayedTexture(argOrder) ||
+ IsTextureMS(argOrder) || IsBuffer(argOrder) || IsImage(argOrder);
+}
+
+// Reject certain combinations that are illegal sample methods. For example,
+// 3D arrays.
+bool IsIllegalSample(const glslang::TString& name, const char* argOrder, int dim0)
+{
+ const bool isArrayed = IsArrayed(*argOrder);
+ const bool isMS = IsTextureMS(*argOrder);
+ const bool isBuffer = IsBuffer(*argOrder);
+
+ // there are no 3D arrayed textures, or 3D SampleCmp(LevelZero)
+ if (dim0 == 3 && (isArrayed || name == "SampleCmp" || name == "SampleCmpLevelZero"))
+ return true;
+
+ const int numArgs = int(std::count(argOrder, argOrder + strlen(argOrder), ',')) + 1;
+
+ // Reject invalid offset forms with cubemaps
+ if (dim0 == 4) {
+ if ((name == "Sample" && numArgs >= 4) ||
+ (name == "SampleBias" && numArgs >= 5) ||
+ (name == "SampleCmp" && numArgs >= 5) ||
+ (name == "SampleCmpLevelZero" && numArgs >= 5) ||
+ (name == "SampleGrad" && numArgs >= 6) ||
+ (name == "SampleLevel" && numArgs >= 5))
+ return true;
+ }
+
+ const bool isGather =
+ (name == "Gather" ||
+ name == "GatherRed" ||
+ name == "GatherGreen" ||
+ name == "GatherBlue" ||
+ name == "GatherAlpha");
+
+ const bool isGatherCmp =
+ (name == "GatherCmp" ||
+ name == "GatherCmpRed" ||
+ name == "GatherCmpGreen" ||
+ name == "GatherCmpBlue" ||
+ name == "GatherCmpAlpha");
+
+ // Reject invalid Gathers
+ if (isGather || isGatherCmp) {
+ if (dim0 == 1 || dim0 == 3) // there are no 1D or 3D gathers
+ return true;
+
+ // no offset on cube or cube array gathers
+ if (dim0 == 4) {
+ if ((isGather && numArgs > 3) || (isGatherCmp && numArgs > 4))
+ return true;
+ }
+ }
+
+ // Reject invalid Loads
+ if (name == "Load" && dim0 == 4)
+ return true; // Load does not support any cubemaps, arrayed or not.
+
+ // Multisample formats are only 2D and 2Darray
+ if (isMS && dim0 != 2)
+ return true;
+
+ // Buffer are only 1D
+ if (isBuffer && dim0 != 1)
+ return true;
+
+ return false;
+}
+
+// Return the number of the coordinate arg, if any
+int CoordinateArgPos(const glslang::TString& name, bool isTexture)
+{
+ if (!isTexture || (name == "GetDimensions"))
+ return -1; // has none
+ else if (name == "Load")
+ return 1;
+ else
+ return 2; // other texture methods are 2
+}
+
+// Some texture methods use an addition coordinate dimension for the mip
+bool HasMipInCoord(const glslang::TString& name, bool isMS, bool isBuffer, bool isImage)
+{
+ return name == "Load" && !isMS && !isBuffer && !isImage;
+}
+
+// LOD calculations don't pass the array level in the coordinate.
+bool NoArrayCoord(const glslang::TString& name)
+{
+ return name == "CalculateLevelOfDetail" || name == "CalculateLevelOfDetailUnclamped";
+}
+
+// Handle IO params marked with > or <
+const char* IoParam(glslang::TString& s, const char* nthArgOrder)
+{
+ if (*nthArgOrder == '>') { // output params
+ ++nthArgOrder;
+ s.append("out ");
+ } else if (*nthArgOrder == '<') { // input params
+ ++nthArgOrder;
+ s.append("in ");
+ }
+
+ return nthArgOrder;
+}
+
+// Handle repeated args
+void HandleRepeatArg(const char*& arg, const char*& prev, const char* current)
+{
+ if (*arg == ',' || *arg == '\0')
+ arg = prev;
+ else
+ prev = current;
+}
+
+// Return true for the end of a single argument key, which can be the end of the string, or
+// the comma separator.
+inline bool IsEndOfArg(const char* arg)
+{
+ return arg == nullptr || *arg == '\0' || *arg == ',';
+}
+
+// If this is a fixed vector size, such as V3, return the size. Else return 0.
+int FixedVecSize(const char* arg)
+{
+ while (!IsEndOfArg(arg)) {
+ if (isdigit(*arg))
+ return *arg - '0';
+ ++arg;
+ }
+
+ return 0; // none found.
+}
+
+// Create and return a type name. This is done in GLSL, not HLSL conventions, until such
+// time as builtins are parsed using the HLSL parser.
+//
+// order: S = scalar, V = vector, M = matrix
+// argType: F = float, D = double, I = int, U = uint, B = bool, S = sampler
+// dim0 = vector dimension, or matrix 1st dimension
+// dim1 = matrix 2nd dimension
+glslang::TString& AppendTypeName(glslang::TString& s, const char* argOrder, const char* argType, int dim0, int dim1)
+{
+ const bool isTranspose = (argOrder[0] == '^');
+ const bool isTexture = IsTextureType(argOrder[0]);
+ const bool isArrayed = IsArrayed(argOrder[0]);
+ const bool isSampler = IsSamplerType(argType[0]);
+ const bool isMS = IsMS(argOrder[0]);
+ const bool isBuffer = IsBuffer(argOrder[0]);
+ const bool isImage = IsImage(argOrder[0]);
+ const bool isSubpass = IsSubpassInput(argOrder[0]);
+
+ char type = *argType;
+
+ if (isTranspose) { // Take transpose of matrix dimensions
+ std::swap(dim0, dim1);
+ } else if (isTexture || isSubpass) {
+ if (type == 'F') // map base type to texture of that type.
+ type = 'T'; // e.g, int -> itexture, uint -> utexture, etc.
+ else if (type == 'I')
+ type = 'i';
+ else if (type == 'U')
+ type = 'u';
+ }
+
+ if (isTranspose)
+ ++argOrder;
+
+ char order = *argOrder;
+
+ if (UseHlslTypes) {
+ switch (type) {
+ case '-': s += "void"; break;
+ case 'F': s += "float"; break;
+ case 'D': s += "double"; break;
+ case 'I': s += "int"; break;
+ case 'U': s += "uint"; break;
+ case 'L': s += "int64_t"; break;
+ case 'M': s += "uint64_t"; break;
+ case 'B': s += "bool"; break;
+ case 'S': s += "sampler"; break;
+ case 's': s += "SamplerComparisonState"; break;
+ case 'T': s += ((isBuffer && isImage) ? "RWBuffer" :
+ isSubpass ? "SubpassInput" :
+ isBuffer ? "Buffer" :
+ isImage ? "RWTexture" : "Texture"); break;
+ case 'i': s += ((isBuffer && isImage) ? "RWBuffer" :
+ isSubpass ? "SubpassInput" :
+ isBuffer ? "Buffer" :
+ isImage ? "RWTexture" : "Texture"); break;
+ case 'u': s += ((isBuffer && isImage) ? "RWBuffer" :
+ isSubpass ? "SubpassInput" :
+ isBuffer ? "Buffer" :
+ isImage ? "RWTexture" : "Texture"); break;
+ default: s += "UNKNOWN_TYPE"; break;
+ }
+
+ if (isSubpass && isMS)
+ s += "MS";
+
+ } else {
+ switch (type) {
+ case '-': s += "void"; break;
+ case 'F': s += BaseTypeName(order, "float", "vec", "mat"); break;
+ case 'D': s += BaseTypeName(order, "double", "dvec", "dmat"); break;
+ case 'I': s += BaseTypeName(order, "int", "ivec", "imat"); break;
+ case 'U': s += BaseTypeName(order, "uint", "uvec", "umat"); break;
+ case 'B': s += BaseTypeName(order, "bool", "bvec", "bmat"); break;
+ case 'S': s += "sampler"; break;
+ case 's': s += "samplerShadow"; break;
+ case 'T': // fall through
+ case 'i': // ...
+ case 'u': // ...
+ if (type != 'T') // create itexture, utexture, etc
+ s += type;
+
+ s += ((isImage && isBuffer) ? "imageBuffer" :
+ isSubpass ? "subpassInput" :
+ isImage ? "image" :
+ isBuffer ? "samplerBuffer" :
+ "texture");
+ break;
+
+ default: s += "UNKNOWN_TYPE"; break;
+ }
+ }
+
+ // handle fixed vector sizes, such as float3, and only ever 3.
+ const int fixedVecSize = FixedVecSize(argOrder);
+ if (fixedVecSize != 0)
+ dim0 = dim1 = fixedVecSize;
+
+ const char dim0Char = ('0' + char(dim0));
+ const char dim1Char = ('0' + char(dim1));
+
+ // Add sampler dimensions
+ if (isSampler || isTexture) {
+ if ((order == 'V' || isTexture) && !isBuffer) {
+ switch (dim0) {
+ case 1: s += "1D"; break;
+ case 2: s += (isMS ? "2DMS" : "2D"); break;
+ case 3: s += "3D"; break;
+ case 4: s += "Cube"; break;
+ default: s += "UNKNOWN_SAMPLER"; break;
+ }
+ }
+ } else {
+ // Non-sampler type:
+ // verify dimensions
+ if (((order == 'V' || order == 'M') && (dim0 < 1 || dim0 > 4)) ||
+ (order == 'M' && (dim1 < 1 || dim1 > 4))) {
+ s += "UNKNOWN_DIMENSION";
+ return s;
+ }
+
+ switch (order) {
+ case '-': break; // no dimensions for voids
+ case 'S': break; // no dimensions on scalars
+ case 'V':
+ s += dim0Char;
+ break;
+ case 'M':
+ s += dim0Char;
+ s += 'x';
+ s += dim1Char;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // handle arrayed textures
+ if (isArrayed)
+ s += "Array";
+
+ // For HLSL, append return type for texture types
+ if (UseHlslTypes) {
+ switch (type) {
+ case 'i': s += "<int"; s += dim0Char; s += ">"; break;
+ case 'u': s += "<uint"; s += dim0Char; s += ">"; break;
+ case 'T': s += "<float"; s += dim0Char; s += ">"; break;
+ default: break;
+ }
+ }
+
+ return s;
+}
+
+// The GLSL parser can be used to parse a subset of HLSL prototypes. However, many valid HLSL prototypes
+// are not valid GLSL prototypes. This rejects the invalid ones. Thus, there is a single switch below
+// to enable creation of the entire HLSL space.
+inline bool IsValid(const char* cname, char retOrder, char retType, char argOrder, char argType, int dim0, int dim1)
+{
+ const bool isVec = (argOrder == 'V');
+ const bool isMat = (argOrder == 'M');
+
+ const std::string name(cname);
+
+ // these do not have vec1 versions
+ if (dim0 == 1 && (name == "normalize" || name == "reflect" || name == "refract"))
+ return false;
+
+ if (!IsTextureType(argOrder) && (isVec && dim0 == 1)) // avoid vec1
+ return false;
+
+ if (UseHlslTypes) {
+ // NO further restrictions for HLSL
+ } else {
+ // GLSL parser restrictions
+ if ((isMat && (argType == 'I' || argType == 'U' || argType == 'B')) ||
+ (retOrder == 'M' && (retType == 'I' || retType == 'U' || retType == 'B')))
+ return false;
+
+ if (isMat && dim0 == 1 && dim1 == 1) // avoid mat1x1
+ return false;
+
+ if (isMat && dim1 == 1) // TODO: avoid mat Nx1 until we find the right GLSL profile
+ return false;
+
+ if (name == "GetRenderTargetSamplePosition" ||
+ name == "tex1D" ||
+ name == "tex1Dgrad")
+ return false;
+ }
+
+ return true;
+}
+
+// return position of end of argument specifier
+inline const char* FindEndOfArg(const char* arg)
+{
+ while (!IsEndOfArg(arg))
+ ++arg;
+
+ return *arg == '\0' ? nullptr : arg;
+}
+
+// Return pointer to beginning of Nth argument specifier in the string.
+inline const char* NthArg(const char* arg, int n)
+{
+ for (int x=0; x<n && arg; ++x)
+ if ((arg = FindEndOfArg(arg)) != nullptr)
+ ++arg; // skip arg separator
+
+ return arg;
+}
+
+inline void FindVectorMatrixBounds(const char* argOrder, int fixedVecSize, int& dim0Min, int& dim0Max, int& /*dim1Min*/, int& dim1Max)
+{
+ for (int arg = 0; ; ++arg) {
+ const char* nthArgOrder(NthArg(argOrder, arg));
+ if (nthArgOrder == nullptr)
+ break;
+ else if (*nthArgOrder == 'V' || IsSubpassInput(*nthArgOrder))
+ dim0Max = 4;
+ else if (*nthArgOrder == 'M')
+ dim0Max = dim1Max = 4;
+ }
+
+ if (fixedVecSize > 0) // handle fixed sized vectors
+ dim0Min = dim0Max = fixedVecSize;
+}
+
+} // end anonymous namespace
+
+namespace glslang {
+
+TBuiltInParseablesHlsl::TBuiltInParseablesHlsl()
+{
+}
+
+//
+// Handle creation of mat*mat specially, since it doesn't fall conveniently out of
+// the generic prototype creation code below.
+//
+void TBuiltInParseablesHlsl::createMatTimesMat()
+{
+ TString& s = commonBuiltins;
+
+ const int first = (UseHlslTypes ? 1 : 2);
+
+ for (int xRows = first; xRows <=4; xRows++) {
+ for (int xCols = first; xCols <=4; xCols++) {
+ const int yRows = xCols;
+ for (int yCols = first; yCols <=4; yCols++) {
+ const int retRows = xRows;
+ const int retCols = yCols;
+
+ // Create a mat * mat of the appropriate dimensions
+ AppendTypeName(s, "M", "F", retRows, retCols); // add return type
+ s.append(" "); // space between type and name
+ s.append("mul"); // intrinsic name
+ s.append("("); // open paren
+
+ AppendTypeName(s, "M", "F", xRows, xCols); // add X input
+ s.append(", ");
+ AppendTypeName(s, "M", "F", yRows, yCols); // add Y input
+
+ s.append(");\n"); // close paren
+ }
+
+ // Create M*V
+ AppendTypeName(s, "V", "F", xRows, 1); // add return type
+ s.append(" "); // space between type and name
+ s.append("mul"); // intrinsic name
+ s.append("("); // open paren
+
+ AppendTypeName(s, "M", "F", xRows, xCols); // add X input
+ s.append(", ");
+ AppendTypeName(s, "V", "F", xCols, 1); // add Y input
+
+ s.append(");\n"); // close paren
+
+ // Create V*M
+ AppendTypeName(s, "V", "F", xCols, 1); // add return type
+ s.append(" "); // space between type and name
+ s.append("mul"); // intrinsic name
+ s.append("("); // open paren
+
+ AppendTypeName(s, "V", "F", xRows, 1); // add Y input
+ s.append(", ");
+ AppendTypeName(s, "M", "F", xRows, xCols); // add X input
+
+ s.append(");\n"); // close paren
+ }
+ }
+}
+
+//
+// Add all context-independent built-in functions and variables that are present
+// for the given version and profile. Share common ones across stages, otherwise
+// make stage-specific entries.
+//
+// Most built-ins variables can be added as simple text strings. Some need to
+// be added programmatically, which is done later in IdentifyBuiltIns() below.
+//
+void TBuiltInParseablesHlsl::initialize(int /*version*/, EProfile /*profile*/, const SpvVersion& /*spvVersion*/)
+{
+ static const EShLanguageMask EShLangAll = EShLanguageMask(EShLangCount - 1);
+
+ // These are the actual stage masks defined in the documentation, in case they are
+ // needed for future validation. For now, they are commented out, and set below
+ // to EShLangAll, to allow any intrinsic to be used in any shader, which is legal
+ // if it is not called.
+ //
+ // static const EShLanguageMask EShLangPSCS = EShLanguageMask(EShLangFragmentMask | EShLangComputeMask);
+ // static const EShLanguageMask EShLangVSPSGS = EShLanguageMask(EShLangVertexMask | EShLangFragmentMask | EShLangGeometryMask);
+ // static const EShLanguageMask EShLangCS = EShLangComputeMask;
+ // static const EShLanguageMask EShLangPS = EShLangFragmentMask;
+ // static const EShLanguageMask EShLangHS = EShLangTessControlMask;
+
+ // This set uses EShLangAll for everything.
+ static const EShLanguageMask EShLangPSCS = EShLangAll;
+ static const EShLanguageMask EShLangVSPSGS = EShLangAll;
+ static const EShLanguageMask EShLangCS = EShLangAll;
+ static const EShLanguageMask EShLangPS = EShLangAll;
+ static const EShLanguageMask EShLangHS = EShLangAll;
+ static const EShLanguageMask EShLangGS = EShLangAll;
+
+ // This structure encodes the prototype information for each HLSL intrinsic.
+ // Because explicit enumeration would be cumbersome, it's procedurally generated.
+ // orderKey can be:
+ // S = scalar, V = vector, M = matrix, - = void
+ // typekey can be:
+ // D = double, F = float, U = uint, I = int, B = bool, S = sampler, s = shadowSampler, M = uint64_t, L = int64_t
+ // An empty order or type key repeats the first one. E.g: SVM,, means 3 args each of SVM.
+ // '>' as first letter of order creates an output parameter
+ // '<' as first letter of order creates an input parameter
+ // '^' as first letter of order takes transpose dimensions
+ // '%' as first letter of order creates texture of given F/I/U type (texture, itexture, etc)
+ // '@' as first letter of order creates arrayed texture of given type
+ // '$' / '&' as first letter of order creates 2DMS / 2DMSArray textures
+ // '*' as first letter of order creates buffer object
+ // '!' as first letter of order creates image object
+ // '#' as first letter of order creates arrayed image object
+ // '~' as first letter of order creates an image buffer object
+ // '[' / ']' as first letter of order creates a SubpassInput/SubpassInputMS object
+
+ static const struct {
+ const char* name; // intrinsic name
+ const char* retOrder; // return type key: empty matches order of 1st argument
+ const char* retType; // return type key: empty matches type of 1st argument
+ const char* argOrder; // argument order key
+ const char* argType; // argument type key
+ unsigned int stage; // stage mask
+ bool method; // true if it's a method.
+ } hlslIntrinsics[] = {
+ // name retOrd retType argOrder argType stage mask method
+ // ----------------------------------------------------------------------------------------------------------------
+ { "abort", nullptr, nullptr, "-", "-", EShLangAll, false },
+ { "abs", nullptr, nullptr, "SVM", "DFUI", EShLangAll, false },
+ { "acos", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "all", "S", "B", "SVM", "BFIU", EShLangAll, false },
+ { "AllMemoryBarrier", nullptr, nullptr, "-", "-", EShLangCS, false },
+ { "AllMemoryBarrierWithGroupSync", nullptr, nullptr, "-", "-", EShLangCS, false },
+ { "any", "S", "B", "SVM", "BFIU", EShLangAll, false },
+ { "asdouble", "S", "D", "S,", "UI,", EShLangAll, false },
+ { "asdouble", "V2", "D", "V2,", "UI,", EShLangAll, false },
+ { "asfloat", nullptr, "F", "SVM", "BFIU", EShLangAll, false },
+ { "asin", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "asint", nullptr, "I", "SVM", "FIU", EShLangAll, false },
+ { "asuint", nullptr, "U", "SVM", "FIU", EShLangAll, false },
+ { "atan", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "atan2", nullptr, nullptr, "SVM,", "F,", EShLangAll, false },
+ { "ceil", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "CheckAccessFullyMapped", "S", "B" , "S", "U", EShLangPSCS, false },
+ { "clamp", nullptr, nullptr, "SVM,,", "FUI,,", EShLangAll, false },
+ { "clip", "-", "-", "SVM", "FUI", EShLangPS, false },
+ { "cos", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "cosh", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "countbits", nullptr, nullptr, "SV", "UI", EShLangAll, false },
+ { "cross", nullptr, nullptr, "V3,", "F,", EShLangAll, false },
+ { "D3DCOLORtoUBYTE4", "V4", "I", "V4", "F", EShLangAll, false },
+ { "ddx", nullptr, nullptr, "SVM", "F", EShLangPS, false },
+ { "ddx_coarse", nullptr, nullptr, "SVM", "F", EShLangPS, false },
+ { "ddx_fine", nullptr, nullptr, "SVM", "F", EShLangPS, false },
+ { "ddy", nullptr, nullptr, "SVM", "F", EShLangPS, false },
+ { "ddy_coarse", nullptr, nullptr, "SVM", "F", EShLangPS, false },
+ { "ddy_fine", nullptr, nullptr, "SVM", "F", EShLangPS, false },
+ { "degrees", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "determinant", "S", "F", "M", "F", EShLangAll, false },
+ { "DeviceMemoryBarrier", nullptr, nullptr, "-", "-", EShLangPSCS, false },
+ { "DeviceMemoryBarrierWithGroupSync", nullptr, nullptr, "-", "-", EShLangCS, false },
+ { "distance", "S", "F", "SV,", "F,", EShLangAll, false },
+ { "dot", "S", nullptr, "SV,", "FI,", EShLangAll, false },
+ { "dst", nullptr, nullptr, "V4,", "F,", EShLangAll, false },
+ // { "errorf", "-", "-", "", "", EShLangAll, false }, TODO: varargs
+ { "EvaluateAttributeAtCentroid", nullptr, nullptr, "SVM", "F", EShLangPS, false },
+ { "EvaluateAttributeAtSample", nullptr, nullptr, "SVM,S", "F,U", EShLangPS, false },
+ { "EvaluateAttributeSnapped", nullptr, nullptr, "SVM,V2", "F,I", EShLangPS, false },
+ { "exp", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "exp2", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "f16tof32", nullptr, "F", "SV", "U", EShLangAll, false },
+ { "f32tof16", nullptr, "U", "SV", "F", EShLangAll, false },
+ { "faceforward", nullptr, nullptr, "V,,", "F,,", EShLangAll, false },
+ { "firstbithigh", nullptr, nullptr, "SV", "UI", EShLangAll, false },
+ { "firstbitlow", nullptr, nullptr, "SV", "UI", EShLangAll, false },
+ { "floor", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "fma", nullptr, nullptr, "SVM,,", "D,,", EShLangAll, false },
+ { "fmod", nullptr, nullptr, "SVM,", "F,", EShLangAll, false },
+ { "frac", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "frexp", nullptr, nullptr, "SVM,", "F,", EShLangAll, false },
+ { "fwidth", nullptr, nullptr, "SVM", "F", EShLangPS, false },
+ { "GetRenderTargetSampleCount", "S", "U", "-", "-", EShLangAll, false },
+ { "GetRenderTargetSamplePosition", "V2", "F", "V1", "I", EShLangAll, false },
+ { "GroupMemoryBarrier", nullptr, nullptr, "-", "-", EShLangCS, false },
+ { "GroupMemoryBarrierWithGroupSync", nullptr, nullptr, "-", "-", EShLangCS, false },
+ { "InterlockedAdd", "-", "-", "SVM,,>", "UI,,", EShLangPSCS, false },
+ { "InterlockedAdd", "-", "-", "SVM,", "UI,", EShLangPSCS, false },
+ { "InterlockedAnd", "-", "-", "SVM,,>", "UI,,", EShLangPSCS, false },
+ { "InterlockedAnd", "-", "-", "SVM,", "UI,", EShLangPSCS, false },
+ { "InterlockedCompareExchange", "-", "-", "SVM,,,>", "UI,,,", EShLangPSCS, false },
+ { "InterlockedCompareStore", "-", "-", "SVM,,", "UI,,", EShLangPSCS, false },
+ { "InterlockedExchange", "-", "-", "SVM,,>", "UI,,", EShLangPSCS, false },
+ { "InterlockedMax", "-", "-", "SVM,,>", "UI,,", EShLangPSCS, false },
+ { "InterlockedMax", "-", "-", "SVM,", "UI,", EShLangPSCS, false },
+ { "InterlockedMin", "-", "-", "SVM,,>", "UI,,", EShLangPSCS, false },
+ { "InterlockedMin", "-", "-", "SVM,", "UI,", EShLangPSCS, false },
+ { "InterlockedOr", "-", "-", "SVM,,>", "UI,,", EShLangPSCS, false },
+ { "InterlockedOr", "-", "-", "SVM,", "UI,", EShLangPSCS, false },
+ { "InterlockedXor", "-", "-", "SVM,,>", "UI,,", EShLangPSCS, false },
+ { "InterlockedXor", "-", "-", "SVM,", "UI,", EShLangPSCS, false },
+ { "isfinite", nullptr, "B" , "SVM", "F", EShLangAll, false },
+ { "isinf", nullptr, "B" , "SVM", "F", EShLangAll, false },
+ { "isnan", nullptr, "B" , "SVM", "F", EShLangAll, false },
+ { "ldexp", nullptr, nullptr, "SVM,", "F,", EShLangAll, false },
+ { "length", "S", "F", "SV", "F", EShLangAll, false },
+ { "lerp", nullptr, nullptr, "VM,,", "F,,", EShLangAll, false },
+ { "lerp", nullptr, nullptr, "SVM,,S", "F,,", EShLangAll, false },
+ { "lit", "V4", "F", "S,,", "F,,", EShLangAll, false },
+ { "log", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "log10", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "log2", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "mad", nullptr, nullptr, "SVM,,", "DFUI,,", EShLangAll, false },
+ { "max", nullptr, nullptr, "SVM,", "FIU,", EShLangAll, false },
+ { "min", nullptr, nullptr, "SVM,", "FIU,", EShLangAll, false },
+ { "modf", nullptr, nullptr, "SVM,>", "FIU,", EShLangAll, false },
+ { "msad4", "V4", "U", "S,V2,V4", "U,,", EShLangAll, false },
+ { "mul", "S", nullptr, "S,S", "FI,", EShLangAll, false },
+ { "mul", "V", nullptr, "S,V", "FI,", EShLangAll, false },
+ { "mul", "M", nullptr, "S,M", "FI,", EShLangAll, false },
+ { "mul", "V", nullptr, "V,S", "FI,", EShLangAll, false },
+ { "mul", "S", nullptr, "V,V", "FI,", EShLangAll, false },
+ { "mul", "M", nullptr, "M,S", "FI,", EShLangAll, false },
+ // mat*mat form of mul is handled in createMatTimesMat()
+ { "noise", "S", "F", "V", "F", EShLangPS, false },
+ { "normalize", nullptr, nullptr, "V", "F", EShLangAll, false },
+ { "pow", nullptr, nullptr, "SVM,", "F,", EShLangAll, false },
+ // { "printf", "-", "-", "", "", EShLangAll, false }, TODO: varargs
+ { "Process2DQuadTessFactorsAvg", "-", "-", "V4,V2,>V4,>V2,", "F,,,,", EShLangHS, false },
+ { "Process2DQuadTessFactorsMax", "-", "-", "V4,V2,>V4,>V2,", "F,,,,", EShLangHS, false },
+ { "Process2DQuadTessFactorsMin", "-", "-", "V4,V2,>V4,>V2,", "F,,,,", EShLangHS, false },
+ { "ProcessIsolineTessFactors", "-", "-", "S,,>,>", "F,,,", EShLangHS, false },
+ { "ProcessQuadTessFactorsAvg", "-", "-", "V4,S,>V4,>V2,", "F,,,,", EShLangHS, false },
+ { "ProcessQuadTessFactorsMax", "-", "-", "V4,S,>V4,>V2,", "F,,,,", EShLangHS, false },
+ { "ProcessQuadTessFactorsMin", "-", "-", "V4,S,>V4,>V2,", "F,,,,", EShLangHS, false },
+ { "ProcessTriTessFactorsAvg", "-", "-", "V3,S,>V3,>S,", "F,,,,", EShLangHS, false },
+ { "ProcessTriTessFactorsMax", "-", "-", "V3,S,>V3,>S,", "F,,,,", EShLangHS, false },
+ { "ProcessTriTessFactorsMin", "-", "-", "V3,S,>V3,>S,", "F,,,,", EShLangHS, false },
+ { "radians", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "rcp", nullptr, nullptr, "SVM", "FD", EShLangAll, false },
+ { "reflect", nullptr, nullptr, "V,", "F,", EShLangAll, false },
+ { "refract", nullptr, nullptr, "V,V,S", "F,,", EShLangAll, false },
+ { "reversebits", nullptr, nullptr, "SV", "UI", EShLangAll, false },
+ { "round", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "rsqrt", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "saturate", nullptr, nullptr , "SVM", "F", EShLangAll, false },
+ { "sign", nullptr, nullptr, "SVM", "FI", EShLangAll, false },
+ { "sin", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "sincos", "-", "-", "SVM,>,>", "F,,", EShLangAll, false },
+ { "sinh", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "smoothstep", nullptr, nullptr, "SVM,,", "F,,", EShLangAll, false },
+ { "sqrt", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "step", nullptr, nullptr, "SVM,", "F,", EShLangAll, false },
+ { "tan", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "tanh", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+ { "tex1D", "V4", "F", "S,S", "S,F", EShLangPS, false },
+ { "tex1D", "V4", "F", "S,S,V1,", "S,F,,", EShLangPS, false },
+ { "tex1Dbias", "V4", "F", "S,V4", "S,F", EShLangPS, false },
+ { "tex1Dgrad", "V4", "F", "S,,,", "S,F,,", EShLangPS, false },
+ { "tex1Dlod", "V4", "F", "S,V4", "S,F", EShLangPS, false },
+ { "tex1Dproj", "V4", "F", "S,V4", "S,F", EShLangPS, false },
+ { "tex2D", "V4", "F", "V2,", "S,F", EShLangPS, false },
+ { "tex2D", "V4", "F", "V2,,,", "S,F,,", EShLangPS, false },
+ { "tex2Dbias", "V4", "F", "V2,V4", "S,F", EShLangPS, false },
+ { "tex2Dgrad", "V4", "F", "V2,,,", "S,F,,", EShLangPS, false },
+ { "tex2Dlod", "V4", "F", "V2,V4", "S,F", EShLangAll, false },
+ { "tex2Dproj", "V4", "F", "V2,V4", "S,F", EShLangPS, false },
+ { "tex3D", "V4", "F", "V3,", "S,F", EShLangPS, false },
+ { "tex3D", "V4", "F", "V3,,,", "S,F,,", EShLangPS, false },
+ { "tex3Dbias", "V4", "F", "V3,V4", "S,F", EShLangPS, false },
+ { "tex3Dgrad", "V4", "F", "V3,,,", "S,F,,", EShLangPS, false },
+ { "tex3Dlod", "V4", "F", "V3,V4", "S,F", EShLangPS, false },
+ { "tex3Dproj", "V4", "F", "V3,V4", "S,F", EShLangPS, false },
+ { "texCUBE", "V4", "F", "V4,V3", "S,F", EShLangPS, false },
+ { "texCUBE", "V4", "F", "V4,V3,,", "S,F,,", EShLangPS, false },
+ { "texCUBEbias", "V4", "F", "V4,", "S,F", EShLangPS, false },
+ { "texCUBEgrad", "V4", "F", "V4,V3,,", "S,F,,", EShLangPS, false },
+ { "texCUBElod", "V4", "F", "V4,", "S,F", EShLangPS, false },
+ { "texCUBEproj", "V4", "F", "V4,", "S,F", EShLangPS, false },
+ { "transpose", "^M", nullptr, "M", "FUIB", EShLangAll, false },
+ { "trunc", nullptr, nullptr, "SVM", "F", EShLangAll, false },
+
+ // Texture object methods. Return type can be overridden by shader declaration.
+ // !O = no offset, O = offset
+ { "Sample", /*!O*/ "V4", nullptr, "%@,S,V", "FIU,S,F", EShLangPS, true },
+ { "Sample", /* O*/ "V4", nullptr, "%@,S,V,", "FIU,S,F,I", EShLangPS, true },
+
+ { "SampleBias", /*!O*/ "V4", nullptr, "%@,S,V,S", "FIU,S,F,", EShLangPS, true },
+ { "SampleBias", /* O*/ "V4", nullptr, "%@,S,V,S,V", "FIU,S,F,,I", EShLangPS, true },
+
+ // TODO: FXC accepts int/uint samplers here. unclear what that means.
+ { "SampleCmp", /*!O*/ "S", "F", "%@,S,V,S", "FIU,s,F,", EShLangPS, true },
+ { "SampleCmp", /* O*/ "S", "F", "%@,S,V,S,V", "FIU,s,F,,I", EShLangPS, true },
+
+ // TODO: FXC accepts int/uint samplers here. unclear what that means.
+ { "SampleCmpLevelZero", /*!O*/ "S", "F", "%@,S,V,S", "FIU,s,F,F", EShLangPS, true },
+ { "SampleCmpLevelZero", /* O*/ "S", "F", "%@,S,V,S,V", "FIU,s,F,F,I", EShLangPS, true },
+
+ { "SampleGrad", /*!O*/ "V4", nullptr, "%@,S,V,,", "FIU,S,F,,", EShLangAll, true },
+ { "SampleGrad", /* O*/ "V4", nullptr, "%@,S,V,,,", "FIU,S,F,,,I", EShLangAll, true },
+
+ { "SampleLevel", /*!O*/ "V4", nullptr, "%@,S,V,S", "FIU,S,F,", EShLangAll, true },
+ { "SampleLevel", /* O*/ "V4", nullptr, "%@,S,V,S,V", "FIU,S,F,,I", EShLangAll, true },
+
+ { "Load", /*!O*/ "V4", nullptr, "%@,V", "FIU,I", EShLangAll, true },
+ { "Load", /* O*/ "V4", nullptr, "%@,V,V", "FIU,I,I", EShLangAll, true },
+ { "Load", /* +sampleidex*/ "V4", nullptr, "$&,V,S", "FIU,I,I", EShLangAll, true },
+ { "Load", /* +samplindex, offset*/ "V4", nullptr, "$&,V,S,V", "FIU,I,I,I", EShLangAll, true },
+
+ // RWTexture loads
+ { "Load", "V4", nullptr, "!#,V", "FIU,I", EShLangAll, true },
+ // (RW)Buffer loads
+ { "Load", "V4", nullptr, "~*1,V", "FIU,I", EShLangAll, true },
+
+ { "Gather", /*!O*/ "V4", nullptr, "%@,S,V", "FIU,S,F", EShLangAll, true },
+ { "Gather", /* O*/ "V4", nullptr, "%@,S,V,V", "FIU,S,F,I", EShLangAll, true },
+
+ { "CalculateLevelOfDetail", "S", "F", "%@,S,V", "FUI,S,F", EShLangPS, true },
+ { "CalculateLevelOfDetailUnclamped", "S", "F", "%@,S,V", "FUI,S,F", EShLangPS, true },
+
+ { "GetSamplePosition", "V2", "F", "$&2,S", "FUI,I", EShLangVSPSGS,true },
+
+ //
+ // UINT Width
+ // UINT MipLevel, UINT Width, UINT NumberOfLevels
+ { "GetDimensions", /* 1D */ "-", "-", "%!~1,>S", "FUI,U", EShLangAll, true },
+ { "GetDimensions", /* 1D */ "-", "-", "%!~1,>S", "FUI,F", EShLangAll, true },
+ { "GetDimensions", /* 1D */ "-", "-", "%1,S,>S,", "FUI,U,,", EShLangAll, true },
+ { "GetDimensions", /* 1D */ "-", "-", "%1,S,>S,", "FUI,U,F,", EShLangAll, true },
+
+ // UINT Width, UINT Elements
+ // UINT MipLevel, UINT Width, UINT Elements, UINT NumberOfLevels
+ { "GetDimensions", /* 1DArray */ "-", "-", "@#1,>S,", "FUI,U,", EShLangAll, true },
+ { "GetDimensions", /* 1DArray */ "-", "-", "@#1,>S,", "FUI,F,", EShLangAll, true },
+ { "GetDimensions", /* 1DArray */ "-", "-", "@1,S,>S,,", "FUI,U,,,", EShLangAll, true },
+ { "GetDimensions", /* 1DArray */ "-", "-", "@1,S,>S,,", "FUI,U,F,,", EShLangAll, true },
+
+ // UINT Width, UINT Height
+ // UINT MipLevel, UINT Width, UINT Height, UINT NumberOfLevels
+ { "GetDimensions", /* 2D */ "-", "-", "%!2,>S,", "FUI,U,", EShLangAll, true },
+ { "GetDimensions", /* 2D */ "-", "-", "%!2,>S,", "FUI,F,", EShLangAll, true },
+ { "GetDimensions", /* 2D */ "-", "-", "%2,S,>S,,", "FUI,U,,,", EShLangAll, true },
+ { "GetDimensions", /* 2D */ "-", "-", "%2,S,>S,,", "FUI,U,F,,", EShLangAll, true },
+
+ // UINT Width, UINT Height, UINT Elements
+ // UINT MipLevel, UINT Width, UINT Height, UINT Elements, UINT NumberOfLevels
+ { "GetDimensions", /* 2DArray */ "-", "-", "@#2,>S,,", "FUI,U,,", EShLangAll, true },
+ { "GetDimensions", /* 2DArray */ "-", "-", "@#2,>S,,", "FUI,F,F,F", EShLangAll, true },
+ { "GetDimensions", /* 2DArray */ "-", "-", "@2,S,>S,,,", "FUI,U,,,,", EShLangAll, true },
+ { "GetDimensions", /* 2DArray */ "-", "-", "@2,S,>S,,,", "FUI,U,F,,,", EShLangAll, true },
+
+ // UINT Width, UINT Height, UINT Depth
+ // UINT MipLevel, UINT Width, UINT Height, UINT Depth, UINT NumberOfLevels
+ { "GetDimensions", /* 3D */ "-", "-", "%!3,>S,,", "FUI,U,,", EShLangAll, true },
+ { "GetDimensions", /* 3D */ "-", "-", "%!3,>S,,", "FUI,F,,", EShLangAll, true },
+ { "GetDimensions", /* 3D */ "-", "-", "%3,S,>S,,,", "FUI,U,,,,", EShLangAll, true },
+ { "GetDimensions", /* 3D */ "-", "-", "%3,S,>S,,,", "FUI,U,F,,,", EShLangAll, true },
+
+ // UINT Width, UINT Height
+ // UINT MipLevel, UINT Width, UINT Height, UINT NumberOfLevels
+ { "GetDimensions", /* Cube */ "-", "-", "%4,>S,", "FUI,U,", EShLangAll, true },
+ { "GetDimensions", /* Cube */ "-", "-", "%4,>S,", "FUI,F,", EShLangAll, true },
+ { "GetDimensions", /* Cube */ "-", "-", "%4,S,>S,,", "FUI,U,,,", EShLangAll, true },
+ { "GetDimensions", /* Cube */ "-", "-", "%4,S,>S,,", "FUI,U,F,,", EShLangAll, true },
+
+ // UINT Width, UINT Height, UINT Elements
+ // UINT MipLevel, UINT Width, UINT Height, UINT Elements, UINT NumberOfLevels
+ { "GetDimensions", /* CubeArray */ "-", "-", "@4,>S,,", "FUI,U,,", EShLangAll, true },
+ { "GetDimensions", /* CubeArray */ "-", "-", "@4,>S,,", "FUI,F,,", EShLangAll, true },
+ { "GetDimensions", /* CubeArray */ "-", "-", "@4,S,>S,,,", "FUI,U,,,,", EShLangAll, true },
+ { "GetDimensions", /* CubeArray */ "-", "-", "@4,S,>S,,,", "FUI,U,F,,,", EShLangAll, true },
+
+ // UINT Width, UINT Height, UINT Samples
+ // UINT Width, UINT Height, UINT Elements, UINT Samples
+ { "GetDimensions", /* 2DMS */ "-", "-", "$2,>S,,", "FUI,U,,", EShLangAll, true },
+ { "GetDimensions", /* 2DMS */ "-", "-", "$2,>S,,", "FUI,U,,", EShLangAll, true },
+ { "GetDimensions", /* 2DMSArray */ "-", "-", "&2,>S,,,", "FUI,U,,,", EShLangAll, true },
+ { "GetDimensions", /* 2DMSArray */ "-", "-", "&2,>S,,,", "FUI,U,,,", EShLangAll, true },
+
+ // SM5 texture methods
+ { "GatherRed", /*!O*/ "V4", nullptr, "%@,S,V", "FIU,S,F", EShLangAll, true },
+ { "GatherRed", /* O*/ "V4", nullptr, "%@,S,V,", "FIU,S,F,I", EShLangAll, true },
+ { "GatherRed", /* O, status*/ "V4", nullptr, "%@,S,V,,>S", "FIU,S,F,I,U", EShLangAll, true },
+ { "GatherRed", /* O-4 */ "V4", nullptr, "%@,S,V,,,,", "FIU,S,F,I,,,", EShLangAll, true },
+ { "GatherRed", /* O-4, status */"V4", nullptr, "%@,S,V,,,,,S", "FIU,S,F,I,,,,U", EShLangAll, true },
+
+ { "GatherGreen", /*!O*/ "V4", nullptr, "%@,S,V", "FIU,S,F", EShLangAll, true },
+ { "GatherGreen", /* O*/ "V4", nullptr, "%@,S,V,", "FIU,S,F,I", EShLangAll, true },
+ { "GatherGreen", /* O, status*/ "V4", nullptr, "%@,S,V,,>S", "FIU,S,F,I,U", EShLangAll, true },
+ { "GatherGreen", /* O-4 */ "V4", nullptr, "%@,S,V,,,,", "FIU,S,F,I,,,", EShLangAll, true },
+ { "GatherGreen", /* O-4, status */"V4", nullptr, "%@,S,V,,,,,S", "FIU,S,F,I,,,,U", EShLangAll, true },
+
+ { "GatherBlue", /*!O*/ "V4", nullptr, "%@,S,V", "FIU,S,F", EShLangAll, true },
+ { "GatherBlue", /* O*/ "V4", nullptr, "%@,S,V,", "FIU,S,F,I", EShLangAll, true },
+ { "GatherBlue", /* O, status*/ "V4", nullptr, "%@,S,V,,>S", "FIU,S,F,I,U", EShLangAll, true },
+ { "GatherBlue", /* O-4 */ "V4", nullptr, "%@,S,V,,,,", "FIU,S,F,I,,,", EShLangAll, true },
+ { "GatherBlue", /* O-4, status */"V4", nullptr, "%@,S,V,,,,,S", "FIU,S,F,I,,,,U", EShLangAll, true },
+
+ { "GatherAlpha", /*!O*/ "V4", nullptr, "%@,S,V", "FIU,S,F", EShLangAll, true },
+ { "GatherAlpha", /* O*/ "V4", nullptr, "%@,S,V,", "FIU,S,F,I", EShLangAll, true },
+ { "GatherAlpha", /* O, status*/ "V4", nullptr, "%@,S,V,,>S", "FIU,S,F,I,U", EShLangAll, true },
+ { "GatherAlpha", /* O-4 */ "V4", nullptr, "%@,S,V,,,,", "FIU,S,F,I,,,", EShLangAll, true },
+ { "GatherAlpha", /* O-4, status */"V4", nullptr, "%@,S,V,,,,,S", "FIU,S,F,I,,,,U", EShLangAll, true },
+
+ { "GatherCmp", /*!O*/ "V4", nullptr, "%@,S,V,S", "FIU,s,F,", EShLangAll, true },
+ { "GatherCmp", /* O*/ "V4", nullptr, "%@,S,V,S,V", "FIU,s,F,,I", EShLangAll, true },
+ { "GatherCmp", /* O, status*/ "V4", nullptr, "%@,S,V,S,V,>S", "FIU,s,F,,I,U", EShLangAll, true },
+ { "GatherCmp", /* O-4 */ "V4", nullptr, "%@,S,V,S,V,,,", "FIU,s,F,,I,,,", EShLangAll, true },
+ { "GatherCmp", /* O-4, status */"V4", nullptr, "%@,S,V,S,V,,V,S","FIU,s,F,,I,,,,U",EShLangAll, true },
+
+ { "GatherCmpRed", /*!O*/ "V4", nullptr, "%@,S,V,S", "FIU,s,F,", EShLangAll, true },
+ { "GatherCmpRed", /* O*/ "V4", nullptr, "%@,S,V,S,V", "FIU,s,F,,I", EShLangAll, true },
+ { "GatherCmpRed", /* O, status*/ "V4", nullptr, "%@,S,V,S,V,>S", "FIU,s,F,,I,U", EShLangAll, true },
+ { "GatherCmpRed", /* O-4 */ "V4", nullptr, "%@,S,V,S,V,,,", "FIU,s,F,,I,,,", EShLangAll, true },
+ { "GatherCmpRed", /* O-4, status */"V4", nullptr, "%@,S,V,S,V,,V,S","FIU,s,F,,I,,,,U",EShLangAll, true },
+
+ { "GatherCmpGreen", /*!O*/ "V4", nullptr, "%@,S,V,S", "FIU,s,F,", EShLangAll, true },
+ { "GatherCmpGreen", /* O*/ "V4", nullptr, "%@,S,V,S,V", "FIU,s,F,,I", EShLangAll, true },
+ { "GatherCmpGreen", /* O, status*/ "V4", nullptr, "%@,S,V,S,V,>S", "FIU,s,F,,I,U", EShLangAll, true },
+ { "GatherCmpGreen", /* O-4 */ "V4", nullptr, "%@,S,V,S,V,,,", "FIU,s,F,,I,,,", EShLangAll, true },
+ { "GatherCmpGreen", /* O-4, status */"V4", nullptr, "%@,S,V,S,V,,,,S","FIU,s,F,,I,,,,U",EShLangAll, true },
+
+ { "GatherCmpBlue", /*!O*/ "V4", nullptr, "%@,S,V,S", "FIU,s,F,", EShLangAll, true },
+ { "GatherCmpBlue", /* O*/ "V4", nullptr, "%@,S,V,S,V", "FIU,s,F,,I", EShLangAll, true },
+ { "GatherCmpBlue", /* O, status*/ "V4", nullptr, "%@,S,V,S,V,>S", "FIU,s,F,,I,U", EShLangAll, true },
+ { "GatherCmpBlue", /* O-4 */ "V4", nullptr, "%@,S,V,S,V,,,", "FIU,s,F,,I,,,", EShLangAll, true },
+ { "GatherCmpBlue", /* O-4, status */"V4", nullptr, "%@,S,V,S,V,,,,S","FIU,s,F,,I,,,,U",EShLangAll, true },
+
+ { "GatherCmpAlpha", /*!O*/ "V4", nullptr, "%@,S,V,S", "FIU,s,F,", EShLangAll, true },
+ { "GatherCmpAlpha", /* O*/ "V4", nullptr, "%@,S,V,S,V", "FIU,s,F,,I", EShLangAll, true },
+ { "GatherCmpAlpha", /* O, status*/ "V4", nullptr, "%@,S,V,S,V,>S", "FIU,s,F,,I,U", EShLangAll, true },
+ { "GatherCmpAlpha", /* O-4 */ "V4", nullptr, "%@,S,V,S,V,,,", "FIU,s,F,,I,,,", EShLangAll, true },
+ { "GatherCmpAlpha", /* O-4, status */"V4", nullptr, "%@,S,V,S,V,,,,S","FIU,s,F,,I,,,,U",EShLangAll, true },
+
+ // geometry methods
+ { "Append", "-", "-", "-", "-", EShLangGS , true },
+ { "RestartStrip", "-", "-", "-", "-", EShLangGS , true },
+
+ // Methods for structurebuffers. TODO: wildcard type matching.
+ { "Load", nullptr, nullptr, "-", "-", EShLangAll, true },
+ { "Load2", nullptr, nullptr, "-", "-", EShLangAll, true },
+ { "Load3", nullptr, nullptr, "-", "-", EShLangAll, true },
+ { "Load4", nullptr, nullptr, "-", "-", EShLangAll, true },
+ { "Store", nullptr, nullptr, "-", "-", EShLangAll, true },
+ { "Store2", nullptr, nullptr, "-", "-", EShLangAll, true },
+ { "Store3", nullptr, nullptr, "-", "-", EShLangAll, true },
+ { "Store4", nullptr, nullptr, "-", "-", EShLangAll, true },
+ { "GetDimensions", nullptr, nullptr, "-", "-", EShLangAll, true },
+ { "InterlockedAdd", nullptr, nullptr, "-", "-", EShLangAll, true },
+ { "InterlockedAnd", nullptr, nullptr, "-", "-", EShLangAll, true },
+ { "InterlockedCompareExchange", nullptr, nullptr, "-", "-", EShLangAll, true },
+ { "InterlockedCompareStore", nullptr, nullptr, "-", "-", EShLangAll, true },
+ { "InterlockedExchange", nullptr, nullptr, "-", "-", EShLangAll, true },
+ { "InterlockedMax", nullptr, nullptr, "-", "-", EShLangAll, true },
+ { "InterlockedMin", nullptr, nullptr, "-", "-", EShLangAll, true },
+ { "InterlockedOr", nullptr, nullptr, "-", "-", EShLangAll, true },
+ { "InterlockedXor", nullptr, nullptr, "-", "-", EShLangAll, true },
+ { "IncrementCounter", nullptr, nullptr, "-", "-", EShLangAll, true },
+ { "DecrementCounter", nullptr, nullptr, "-", "-", EShLangAll, true },
+ { "Consume", nullptr, nullptr, "-", "-", EShLangAll, true },
+
+ // SM 6.0
+
+ { "WaveIsFirstLane", "S", "B", "-", "-", EShLangPSCS, false},
+ { "WaveGetLaneCount", "S", "U", "-", "-", EShLangPSCS, false},
+ { "WaveGetLaneIndex", "S", "U", "-", "-", EShLangPSCS, false},
+ { "WaveActiveAnyTrue", "S", "B", "S", "B", EShLangPSCS, false},
+ { "WaveActiveAllTrue", "S", "B", "S", "B", EShLangPSCS, false},
+ { "WaveActiveBallot", "V4", "U", "S", "B", EShLangPSCS, false},
+ { "WaveReadLaneAt", nullptr, nullptr, "SV,S", "DFUI,U", EShLangPSCS, false},
+ { "WaveReadLaneFirst", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false},
+ { "WaveActiveAllEqual", "S", "B", "SV", "DFUI", EShLangPSCS, false},
+ { "WaveActiveAllEqualBool", "S", "B", "S", "B", EShLangPSCS, false},
+ { "WaveActiveCountBits", "S", "U", "S", "B", EShLangPSCS, false},
+
+ { "WaveActiveSum", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false},
+ { "WaveActiveProduct", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false},
+ { "WaveActiveBitAnd", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false},
+ { "WaveActiveBitOr", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false},
+ { "WaveActiveBitXor", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false},
+ { "WaveActiveMin", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false},
+ { "WaveActiveMax", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false},
+ { "WavePrefixSum", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false},
+ { "WavePrefixProduct", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false},
+ { "WavePrefixCountBits", "S", "U", "S", "B", EShLangPSCS, false},
+ { "QuadReadAcrossX", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false},
+ { "QuadReadAcrossY", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false},
+ { "QuadReadAcrossDiagonal", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false},
+ { "QuadReadLaneAt", nullptr, nullptr, "SV,S", "DFUI,U", EShLangPSCS, false},
+
+ // Methods for subpass input objects
+ { "SubpassLoad", "V4", nullptr, "[", "FIU", EShLangPS, true },
+ { "SubpassLoad", "V4", nullptr, "],S", "FIU,I", EShLangPS, true },
+
+ // Mark end of list, since we want to avoid a range-based for, as some compilers don't handle it yet.
+ { nullptr, nullptr, nullptr, nullptr, nullptr, 0, false },
+ };
+
+ // Create prototypes for the intrinsics. TODO: Avoid ranged based for until all compilers can handle it.
+ for (int icount = 0; hlslIntrinsics[icount].name; ++icount) {
+ const auto& intrinsic = hlslIntrinsics[icount];
+
+ for (int stage = 0; stage < EShLangCount; ++stage) { // for each stage...
+ if ((intrinsic.stage & (1<<stage)) == 0) // skip inapplicable stages
+ continue;
+
+ // reference to either the common builtins, or stage specific builtins.
+ TString& s = (intrinsic.stage == EShLangAll) ? commonBuiltins : stageBuiltins[stage];
+
+ for (const char* argOrder = intrinsic.argOrder; !IsEndOfArg(argOrder); ++argOrder) { // for each order...
+ const bool isTexture = IsTextureType(*argOrder);
+ const bool isArrayed = IsArrayed(*argOrder);
+ const bool isMS = IsTextureMS(*argOrder);
+ const bool isBuffer = IsBuffer(*argOrder);
+ const bool isImage = IsImage(*argOrder);
+ const bool mipInCoord = HasMipInCoord(intrinsic.name, isMS, isBuffer, isImage);
+ const int fixedVecSize = FixedVecSize(argOrder);
+ const int coordArg = CoordinateArgPos(intrinsic.name, isTexture);
+
+ // calculate min and max vector and matrix dimensions
+ int dim0Min = 1;
+ int dim0Max = 1;
+ int dim1Min = 1;
+ int dim1Max = 1;
+
+ FindVectorMatrixBounds(argOrder, fixedVecSize, dim0Min, dim0Max, dim1Min, dim1Max);
+
+ for (const char* argType = intrinsic.argType; !IsEndOfArg(argType); ++argType) { // for each type...
+ for (int dim0 = dim0Min; dim0 <= dim0Max; ++dim0) { // for each dim 0...
+ for (int dim1 = dim1Min; dim1 <= dim1Max; ++dim1) { // for each dim 1...
+ const char* retOrder = intrinsic.retOrder ? intrinsic.retOrder : argOrder;
+ const char* retType = intrinsic.retType ? intrinsic.retType : argType;
+
+ if (!IsValid(intrinsic.name, *retOrder, *retType, *argOrder, *argType, dim0, dim1))
+ continue;
+
+ // Reject some forms of sample methods that don't exist.
+ if (isTexture && IsIllegalSample(intrinsic.name, argOrder, dim0))
+ continue;
+
+ AppendTypeName(s, retOrder, retType, dim0, dim1); // add return type
+ s.append(" "); // space between type and name
+
+ // methods have a prefix. TODO: it would be better as an invalid identifier character,
+ // but that requires a scanner change.
+ if (intrinsic.method)
+ s.append(BUILTIN_PREFIX);
+
+ s.append(intrinsic.name); // intrinsic name
+ s.append("("); // open paren
+
+ const char* prevArgOrder = nullptr;
+ const char* prevArgType = nullptr;
+
+ // Append argument types, if any.
+ for (int arg = 0; ; ++arg) {
+ const char* nthArgOrder(NthArg(argOrder, arg));
+ const char* nthArgType(NthArg(argType, arg));
+
+ if (nthArgOrder == nullptr || nthArgType == nullptr)
+ break;
+
+ // cube textures use vec3 coordinates
+ int argDim0 = isTexture && arg > 0 ? std::min(dim0, 3) : dim0;
+
+ s.append(arg > 0 ? ", ": ""); // comma separator if needed
+
+ const char* orderBegin = nthArgOrder;
+ nthArgOrder = IoParam(s, nthArgOrder);
+
+ // Comma means use the previous argument order and type.
+ HandleRepeatArg(nthArgOrder, prevArgOrder, orderBegin);
+ HandleRepeatArg(nthArgType, prevArgType, nthArgType);
+
+ // In case the repeated arg has its own I/O marker
+ nthArgOrder = IoParam(s, nthArgOrder);
+
+ // arrayed textures have one extra coordinate dimension, except for
+ // the CalculateLevelOfDetail family.
+ if (isArrayed && arg == coordArg && !NoArrayCoord(intrinsic.name))
+ argDim0++;
+
+ // Some texture methods use an addition arg dimension to hold mip
+ if (arg == coordArg && mipInCoord)
+ argDim0++;
+
+ // For textures, the 1D case isn't a 1-vector, but a scalar.
+ if (isTexture && argDim0 == 1 && arg > 0 && *nthArgOrder == 'V')
+ nthArgOrder = "S";
+
+ AppendTypeName(s, nthArgOrder, nthArgType, argDim0, dim1); // Add arguments
+ }
+
+ s.append(");\n"); // close paren and trailing semicolon
+ } // dim 1 loop
+ } // dim 0 loop
+ } // arg type loop
+
+ // skip over special characters
+ if (isTexture && isalpha(argOrder[1]))
+ ++argOrder;
+ if (isdigit(argOrder[1]))
+ ++argOrder;
+ } // arg order loop
+
+ if (intrinsic.stage == EShLangAll) // common builtins are only added once.
+ break;
+ }
+ }
+
+ createMatTimesMat(); // handle this case separately, for convenience
+
+ // printf("Common:\n%s\n", getCommonString().c_str());
+ // printf("Frag:\n%s\n", getStageString(EShLangFragment).c_str());
+ // printf("Vertex:\n%s\n", getStageString(EShLangVertex).c_str());
+ // printf("Geo:\n%s\n", getStageString(EShLangGeometry).c_str());
+ // printf("TessCtrl:\n%s\n", getStageString(EShLangTessControl).c_str());
+ // printf("TessEval:\n%s\n", getStageString(EShLangTessEvaluation).c_str());
+ // printf("Compute:\n%s\n", getStageString(EShLangCompute).c_str());
+}
+
+//
+// Add context-dependent built-in functions and variables that are present
+// for the given version and profile. All the results are put into just the
+// commonBuiltins, because it is called for just a specific stage. So,
+// add stage-specific entries to the commonBuiltins, and only if that stage
+// was requested.
+//
+void TBuiltInParseablesHlsl::initialize(const TBuiltInResource& /*resources*/, int /*version*/, EProfile /*profile*/,
+ const SpvVersion& /*spvVersion*/, EShLanguage /*language*/)
+{
+}
+
+//
+// Finish adding/processing context-independent built-in symbols.
+// 1) Programmatically add symbols that could not be added by simple text strings above.
+// 2) Map built-in functions to operators, for those that will turn into an operation node
+// instead of remaining a function call.
+// 3) Tag extension-related symbols added to their base version with their extensions, so
+// that if an early version has the extension turned off, there is an error reported on use.
+//
+void TBuiltInParseablesHlsl::identifyBuiltIns(int /*version*/, EProfile /*profile*/, const SpvVersion& /*spvVersion*/, EShLanguage /*language*/,
+ TSymbolTable& symbolTable)
+{
+ // symbolTable.relateToOperator("abort", EOpAbort);
+ symbolTable.relateToOperator("abs", EOpAbs);
+ symbolTable.relateToOperator("acos", EOpAcos);
+ symbolTable.relateToOperator("all", EOpAll);
+ symbolTable.relateToOperator("AllMemoryBarrier", EOpMemoryBarrier);
+ symbolTable.relateToOperator("AllMemoryBarrierWithGroupSync", EOpAllMemoryBarrierWithGroupSync);
+ symbolTable.relateToOperator("any", EOpAny);
+ symbolTable.relateToOperator("asdouble", EOpAsDouble);
+ symbolTable.relateToOperator("asfloat", EOpIntBitsToFloat);
+ symbolTable.relateToOperator("asin", EOpAsin);
+ symbolTable.relateToOperator("asint", EOpFloatBitsToInt);
+ symbolTable.relateToOperator("asuint", EOpFloatBitsToUint);
+ symbolTable.relateToOperator("atan", EOpAtan);
+ symbolTable.relateToOperator("atan2", EOpAtan);
+ symbolTable.relateToOperator("ceil", EOpCeil);
+ // symbolTable.relateToOperator("CheckAccessFullyMapped");
+ symbolTable.relateToOperator("clamp", EOpClamp);
+ symbolTable.relateToOperator("clip", EOpClip);
+ symbolTable.relateToOperator("cos", EOpCos);
+ symbolTable.relateToOperator("cosh", EOpCosh);
+ symbolTable.relateToOperator("countbits", EOpBitCount);
+ symbolTable.relateToOperator("cross", EOpCross);
+ symbolTable.relateToOperator("D3DCOLORtoUBYTE4", EOpD3DCOLORtoUBYTE4);
+ symbolTable.relateToOperator("ddx", EOpDPdx);
+ symbolTable.relateToOperator("ddx_coarse", EOpDPdxCoarse);
+ symbolTable.relateToOperator("ddx_fine", EOpDPdxFine);
+ symbolTable.relateToOperator("ddy", EOpDPdy);
+ symbolTable.relateToOperator("ddy_coarse", EOpDPdyCoarse);
+ symbolTable.relateToOperator("ddy_fine", EOpDPdyFine);
+ symbolTable.relateToOperator("degrees", EOpDegrees);
+ symbolTable.relateToOperator("determinant", EOpDeterminant);
+ symbolTable.relateToOperator("DeviceMemoryBarrier", EOpDeviceMemoryBarrier);
+ symbolTable.relateToOperator("DeviceMemoryBarrierWithGroupSync", EOpDeviceMemoryBarrierWithGroupSync);
+ symbolTable.relateToOperator("distance", EOpDistance);
+ symbolTable.relateToOperator("dot", EOpDot);
+ symbolTable.relateToOperator("dst", EOpDst);
+ // symbolTable.relateToOperator("errorf", EOpErrorf);
+ symbolTable.relateToOperator("EvaluateAttributeAtCentroid", EOpInterpolateAtCentroid);
+ symbolTable.relateToOperator("EvaluateAttributeAtSample", EOpInterpolateAtSample);
+ symbolTable.relateToOperator("EvaluateAttributeSnapped", EOpEvaluateAttributeSnapped);
+ symbolTable.relateToOperator("exp", EOpExp);
+ symbolTable.relateToOperator("exp2", EOpExp2);
+ symbolTable.relateToOperator("f16tof32", EOpF16tof32);
+ symbolTable.relateToOperator("f32tof16", EOpF32tof16);
+ symbolTable.relateToOperator("faceforward", EOpFaceForward);
+ symbolTable.relateToOperator("firstbithigh", EOpFindMSB);
+ symbolTable.relateToOperator("firstbitlow", EOpFindLSB);
+ symbolTable.relateToOperator("floor", EOpFloor);
+ symbolTable.relateToOperator("fma", EOpFma);
+ symbolTable.relateToOperator("fmod", EOpMod);
+ symbolTable.relateToOperator("frac", EOpFract);
+ symbolTable.relateToOperator("frexp", EOpFrexp);
+ symbolTable.relateToOperator("fwidth", EOpFwidth);
+ // symbolTable.relateToOperator("GetRenderTargetSampleCount");
+ // symbolTable.relateToOperator("GetRenderTargetSamplePosition");
+ symbolTable.relateToOperator("GroupMemoryBarrier", EOpWorkgroupMemoryBarrier);
+ symbolTable.relateToOperator("GroupMemoryBarrierWithGroupSync", EOpWorkgroupMemoryBarrierWithGroupSync);
+ symbolTable.relateToOperator("InterlockedAdd", EOpInterlockedAdd);
+ symbolTable.relateToOperator("InterlockedAnd", EOpInterlockedAnd);
+ symbolTable.relateToOperator("InterlockedCompareExchange", EOpInterlockedCompareExchange);
+ symbolTable.relateToOperator("InterlockedCompareStore", EOpInterlockedCompareStore);
+ symbolTable.relateToOperator("InterlockedExchange", EOpInterlockedExchange);
+ symbolTable.relateToOperator("InterlockedMax", EOpInterlockedMax);
+ symbolTable.relateToOperator("InterlockedMin", EOpInterlockedMin);
+ symbolTable.relateToOperator("InterlockedOr", EOpInterlockedOr);
+ symbolTable.relateToOperator("InterlockedXor", EOpInterlockedXor);
+ symbolTable.relateToOperator("isfinite", EOpIsFinite);
+ symbolTable.relateToOperator("isinf", EOpIsInf);
+ symbolTable.relateToOperator("isnan", EOpIsNan);
+ symbolTable.relateToOperator("ldexp", EOpLdexp);
+ symbolTable.relateToOperator("length", EOpLength);
+ symbolTable.relateToOperator("lerp", EOpMix);
+ symbolTable.relateToOperator("lit", EOpLit);
+ symbolTable.relateToOperator("log", EOpLog);
+ symbolTable.relateToOperator("log10", EOpLog10);
+ symbolTable.relateToOperator("log2", EOpLog2);
+ symbolTable.relateToOperator("mad", EOpFma);
+ symbolTable.relateToOperator("max", EOpMax);
+ symbolTable.relateToOperator("min", EOpMin);
+ symbolTable.relateToOperator("modf", EOpModf);
+ // symbolTable.relateToOperator("msad4", EOpMsad4);
+ symbolTable.relateToOperator("mul", EOpGenMul);
+ // symbolTable.relateToOperator("noise", EOpNoise); // TODO: check return type
+ symbolTable.relateToOperator("normalize", EOpNormalize);
+ symbolTable.relateToOperator("pow", EOpPow);
+ // symbolTable.relateToOperator("printf", EOpPrintf);
+ // symbolTable.relateToOperator("Process2DQuadTessFactorsAvg");
+ // symbolTable.relateToOperator("Process2DQuadTessFactorsMax");
+ // symbolTable.relateToOperator("Process2DQuadTessFactorsMin");
+ // symbolTable.relateToOperator("ProcessIsolineTessFactors");
+ // symbolTable.relateToOperator("ProcessQuadTessFactorsAvg");
+ // symbolTable.relateToOperator("ProcessQuadTessFactorsMax");
+ // symbolTable.relateToOperator("ProcessQuadTessFactorsMin");
+ // symbolTable.relateToOperator("ProcessTriTessFactorsAvg");
+ // symbolTable.relateToOperator("ProcessTriTessFactorsMax");
+ // symbolTable.relateToOperator("ProcessTriTessFactorsMin");
+ symbolTable.relateToOperator("radians", EOpRadians);
+ symbolTable.relateToOperator("rcp", EOpRcp);
+ symbolTable.relateToOperator("reflect", EOpReflect);
+ symbolTable.relateToOperator("refract", EOpRefract);
+ symbolTable.relateToOperator("reversebits", EOpBitFieldReverse);
+ symbolTable.relateToOperator("round", EOpRoundEven);
+ symbolTable.relateToOperator("rsqrt", EOpInverseSqrt);
+ symbolTable.relateToOperator("saturate", EOpSaturate);
+ symbolTable.relateToOperator("sign", EOpSign);
+ symbolTable.relateToOperator("sin", EOpSin);
+ symbolTable.relateToOperator("sincos", EOpSinCos);
+ symbolTable.relateToOperator("sinh", EOpSinh);
+ symbolTable.relateToOperator("smoothstep", EOpSmoothStep);
+ symbolTable.relateToOperator("sqrt", EOpSqrt);
+ symbolTable.relateToOperator("step", EOpStep);
+ symbolTable.relateToOperator("tan", EOpTan);
+ symbolTable.relateToOperator("tanh", EOpTanh);
+ symbolTable.relateToOperator("tex1D", EOpTexture);
+ symbolTable.relateToOperator("tex1Dbias", EOpTextureBias);
+ symbolTable.relateToOperator("tex1Dgrad", EOpTextureGrad);
+ symbolTable.relateToOperator("tex1Dlod", EOpTextureLod);
+ symbolTable.relateToOperator("tex1Dproj", EOpTextureProj);
+ symbolTable.relateToOperator("tex2D", EOpTexture);
+ symbolTable.relateToOperator("tex2Dbias", EOpTextureBias);
+ symbolTable.relateToOperator("tex2Dgrad", EOpTextureGrad);
+ symbolTable.relateToOperator("tex2Dlod", EOpTextureLod);
+ symbolTable.relateToOperator("tex2Dproj", EOpTextureProj);
+ symbolTable.relateToOperator("tex3D", EOpTexture);
+ symbolTable.relateToOperator("tex3Dbias", EOpTextureBias);
+ symbolTable.relateToOperator("tex3Dgrad", EOpTextureGrad);
+ symbolTable.relateToOperator("tex3Dlod", EOpTextureLod);
+ symbolTable.relateToOperator("tex3Dproj", EOpTextureProj);
+ symbolTable.relateToOperator("texCUBE", EOpTexture);
+ symbolTable.relateToOperator("texCUBEbias", EOpTextureBias);
+ symbolTable.relateToOperator("texCUBEgrad", EOpTextureGrad);
+ symbolTable.relateToOperator("texCUBElod", EOpTextureLod);
+ symbolTable.relateToOperator("texCUBEproj", EOpTextureProj);
+ symbolTable.relateToOperator("transpose", EOpTranspose);
+ symbolTable.relateToOperator("trunc", EOpTrunc);
+
+ // Texture methods
+ symbolTable.relateToOperator(BUILTIN_PREFIX "Sample", EOpMethodSample);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "SampleBias", EOpMethodSampleBias);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "SampleCmp", EOpMethodSampleCmp);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "SampleCmpLevelZero", EOpMethodSampleCmpLevelZero);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "SampleGrad", EOpMethodSampleGrad);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "SampleLevel", EOpMethodSampleLevel);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "Load", EOpMethodLoad);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "GetDimensions", EOpMethodGetDimensions);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "GetSamplePosition", EOpMethodGetSamplePosition);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "Gather", EOpMethodGather);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "CalculateLevelOfDetail", EOpMethodCalculateLevelOfDetail);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "CalculateLevelOfDetailUnclamped", EOpMethodCalculateLevelOfDetailUnclamped);
+
+ // Structure buffer methods (excluding associations already made above for texture methods w/ same name)
+ symbolTable.relateToOperator(BUILTIN_PREFIX "Load2", EOpMethodLoad2);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "Load3", EOpMethodLoad3);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "Load4", EOpMethodLoad4);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "Store", EOpMethodStore);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "Store2", EOpMethodStore2);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "Store3", EOpMethodStore3);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "Store4", EOpMethodStore4);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "IncrementCounter", EOpMethodIncrementCounter);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "DecrementCounter", EOpMethodDecrementCounter);
+ // Append is also a GS method: we don't add it twice
+ symbolTable.relateToOperator(BUILTIN_PREFIX "Consume", EOpMethodConsume);
+
+ symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedAdd", EOpInterlockedAdd);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedAnd", EOpInterlockedAnd);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedCompareExchange", EOpInterlockedCompareExchange);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedCompareStore", EOpInterlockedCompareStore);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedExchange", EOpInterlockedExchange);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedMax", EOpInterlockedMax);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedMin", EOpInterlockedMin);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedOr", EOpInterlockedOr);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedXor", EOpInterlockedXor);
+
+ // SM5 Texture methods
+ symbolTable.relateToOperator(BUILTIN_PREFIX "GatherRed", EOpMethodGatherRed);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "GatherGreen", EOpMethodGatherGreen);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "GatherBlue", EOpMethodGatherBlue);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "GatherAlpha", EOpMethodGatherAlpha);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "GatherCmp", EOpMethodGatherCmpRed); // alias
+ symbolTable.relateToOperator(BUILTIN_PREFIX "GatherCmpRed", EOpMethodGatherCmpRed);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "GatherCmpGreen", EOpMethodGatherCmpGreen);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "GatherCmpBlue", EOpMethodGatherCmpBlue);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "GatherCmpAlpha", EOpMethodGatherCmpAlpha);
+
+ // GS methods
+ symbolTable.relateToOperator(BUILTIN_PREFIX "Append", EOpMethodAppend);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "RestartStrip", EOpMethodRestartStrip);
+
+ // Wave ops
+ symbolTable.relateToOperator("WaveIsFirstLane", EOpSubgroupElect);
+ symbolTable.relateToOperator("WaveGetLaneCount", EOpWaveGetLaneCount);
+ symbolTable.relateToOperator("WaveGetLaneIndex", EOpWaveGetLaneIndex);
+ symbolTable.relateToOperator("WaveActiveAnyTrue", EOpSubgroupAny);
+ symbolTable.relateToOperator("WaveActiveAllTrue", EOpSubgroupAll);
+ symbolTable.relateToOperator("WaveActiveBallot", EOpSubgroupBallot);
+ symbolTable.relateToOperator("WaveReadLaneFirst", EOpSubgroupBroadcastFirst);
+ symbolTable.relateToOperator("WaveReadLaneAt", EOpSubgroupShuffle);
+ symbolTable.relateToOperator("WaveActiveAllEqual", EOpSubgroupAllEqual);
+ symbolTable.relateToOperator("WaveActiveAllEqualBool", EOpSubgroupAllEqual);
+ symbolTable.relateToOperator("WaveActiveCountBits", EOpWaveActiveCountBits);
+ symbolTable.relateToOperator("WaveActiveSum", EOpSubgroupAdd);
+ symbolTable.relateToOperator("WaveActiveProduct", EOpSubgroupMul);
+ symbolTable.relateToOperator("WaveActiveBitAnd", EOpSubgroupAnd);
+ symbolTable.relateToOperator("WaveActiveBitOr", EOpSubgroupOr);
+ symbolTable.relateToOperator("WaveActiveBitXor", EOpSubgroupXor);
+ symbolTable.relateToOperator("WaveActiveMin", EOpSubgroupMin);
+ symbolTable.relateToOperator("WaveActiveMax", EOpSubgroupMax);
+ symbolTable.relateToOperator("WavePrefixSum", EOpSubgroupInclusiveAdd);
+ symbolTable.relateToOperator("WavePrefixProduct", EOpSubgroupInclusiveMul);
+ symbolTable.relateToOperator("WavePrefixCountBits", EOpWavePrefixCountBits);
+ symbolTable.relateToOperator("QuadReadAcrossX", EOpSubgroupQuadSwapHorizontal);
+ symbolTable.relateToOperator("QuadReadAcrossY", EOpSubgroupQuadSwapVertical);
+ symbolTable.relateToOperator("QuadReadAcrossDiagonal", EOpSubgroupQuadSwapDiagonal);
+ symbolTable.relateToOperator("QuadReadLaneAt", EOpSubgroupQuadBroadcast);
+
+ // Subpass input methods
+ symbolTable.relateToOperator(BUILTIN_PREFIX "SubpassLoad", EOpSubpassLoad);
+ symbolTable.relateToOperator(BUILTIN_PREFIX "SubpassLoadMS", EOpSubpassLoadMS);
+}
+
+//
+// Add context-dependent (resource-specific) built-ins not handled by the above. These
+// would be ones that need to be programmatically added because they cannot
+// be added by simple text strings. For these, also
+// 1) Map built-in functions to operators, for those that will turn into an operation node
+// instead of remaining a function call.
+// 2) Tag extension-related symbols added to their base version with their extensions, so
+// that if an early version has the extension turned off, there is an error reported on use.
+//
+void TBuiltInParseablesHlsl::identifyBuiltIns(int /*version*/, EProfile /*profile*/, const SpvVersion& /*spvVersion*/, EShLanguage /*language*/,
+ TSymbolTable& /*symbolTable*/, const TBuiltInResource& /*resources*/)
+{
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/hlsl/hlslParseables.h b/src/3rdparty/glslang/hlsl/hlslParseables.h
new file mode 100644
index 0000000..28f424b
--- /dev/null
+++ b/src/3rdparty/glslang/hlsl/hlslParseables.h
@@ -0,0 +1,64 @@
+//
+// Copyright (C) 2016 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef _HLSLPARSEABLES_INCLUDED_
+#define _HLSLPARSEABLES_INCLUDED_
+
+#include "../glslang/MachineIndependent/Initialize.h"
+
+namespace glslang {
+
+//
+// This is an HLSL specific derivation of TBuiltInParseables. See comment
+// above TBuiltInParseables for details.
+//
+class TBuiltInParseablesHlsl : public TBuiltInParseables {
+public:
+ POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator())
+ TBuiltInParseablesHlsl();
+ void initialize(int version, EProfile, const SpvVersion& spvVersion);
+ void initialize(const TBuiltInResource& resources, int version, EProfile, const SpvVersion& spvVersion, EShLanguage);
+
+ void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable);
+
+ void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable, const TBuiltInResource &resources);
+
+private:
+ void createMatTimesMat();
+};
+
+} // end namespace glslang
+
+#endif // _HLSLPARSEABLES_INCLUDED_
diff --git a/src/3rdparty/glslang/hlsl/hlslScanContext.cpp b/src/3rdparty/glslang/hlsl/hlslScanContext.cpp
new file mode 100644
index 0000000..28a66bb
--- /dev/null
+++ b/src/3rdparty/glslang/hlsl/hlslScanContext.cpp
@@ -0,0 +1,903 @@
+//
+// Copyright (C) 2016 Google, Inc.
+// Copyright (C) 2016 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of Google, Inc., nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+//
+// HLSL scanning, leveraging the scanning done by the preprocessor.
+//
+
+#include <cstring>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "../glslang/Include/Types.h"
+#include "../glslang/MachineIndependent/SymbolTable.h"
+#include "../glslang/MachineIndependent/ParseHelper.h"
+#include "hlslScanContext.h"
+#include "hlslTokens.h"
+
+// preprocessor includes
+#include "../glslang/MachineIndependent/preprocessor/PpContext.h"
+#include "../glslang/MachineIndependent/preprocessor/PpTokens.h"
+
+namespace {
+
+struct str_eq
+{
+ bool operator()(const char* lhs, const char* rhs) const
+ {
+ return strcmp(lhs, rhs) == 0;
+ }
+};
+
+struct str_hash
+{
+ size_t operator()(const char* str) const
+ {
+ // djb2
+ unsigned long hash = 5381;
+ int c;
+
+ while ((c = *str++) != 0)
+ hash = ((hash << 5) + hash) + c;
+
+ return hash;
+ }
+};
+
+// A single global usable by all threads, by all versions, by all languages.
+// After a single process-level initialization, this is read only and thread safe
+std::unordered_map<const char*, glslang::EHlslTokenClass, str_hash, str_eq>* KeywordMap = nullptr;
+std::unordered_set<const char*, str_hash, str_eq>* ReservedSet = nullptr;
+std::unordered_map<const char*, glslang::TBuiltInVariable, str_hash, str_eq>* SemanticMap = nullptr;
+
+};
+
+namespace glslang {
+
+void HlslScanContext::fillInKeywordMap()
+{
+ if (KeywordMap != nullptr) {
+ // this is really an error, as this should called only once per process
+ // but, the only risk is if two threads called simultaneously
+ return;
+ }
+ KeywordMap = new std::unordered_map<const char*, EHlslTokenClass, str_hash, str_eq>;
+
+ (*KeywordMap)["static"] = EHTokStatic;
+ (*KeywordMap)["const"] = EHTokConst;
+ (*KeywordMap)["unorm"] = EHTokUnorm;
+ (*KeywordMap)["snorm"] = EHTokSNorm;
+ (*KeywordMap)["extern"] = EHTokExtern;
+ (*KeywordMap)["uniform"] = EHTokUniform;
+ (*KeywordMap)["volatile"] = EHTokVolatile;
+ (*KeywordMap)["precise"] = EHTokPrecise;
+ (*KeywordMap)["shared"] = EHTokShared;
+ (*KeywordMap)["groupshared"] = EHTokGroupShared;
+ (*KeywordMap)["linear"] = EHTokLinear;
+ (*KeywordMap)["centroid"] = EHTokCentroid;
+ (*KeywordMap)["nointerpolation"] = EHTokNointerpolation;
+ (*KeywordMap)["noperspective"] = EHTokNoperspective;
+ (*KeywordMap)["sample"] = EHTokSample;
+ (*KeywordMap)["row_major"] = EHTokRowMajor;
+ (*KeywordMap)["column_major"] = EHTokColumnMajor;
+ (*KeywordMap)["packoffset"] = EHTokPackOffset;
+ (*KeywordMap)["in"] = EHTokIn;
+ (*KeywordMap)["out"] = EHTokOut;
+ (*KeywordMap)["inout"] = EHTokInOut;
+ (*KeywordMap)["layout"] = EHTokLayout;
+ (*KeywordMap)["globallycoherent"] = EHTokGloballyCoherent;
+ (*KeywordMap)["inline"] = EHTokInline;
+
+ (*KeywordMap)["point"] = EHTokPoint;
+ (*KeywordMap)["line"] = EHTokLine;
+ (*KeywordMap)["triangle"] = EHTokTriangle;
+ (*KeywordMap)["lineadj"] = EHTokLineAdj;
+ (*KeywordMap)["triangleadj"] = EHTokTriangleAdj;
+
+ (*KeywordMap)["PointStream"] = EHTokPointStream;
+ (*KeywordMap)["LineStream"] = EHTokLineStream;
+ (*KeywordMap)["TriangleStream"] = EHTokTriangleStream;
+
+ (*KeywordMap)["InputPatch"] = EHTokInputPatch;
+ (*KeywordMap)["OutputPatch"] = EHTokOutputPatch;
+
+ (*KeywordMap)["Buffer"] = EHTokBuffer;
+ (*KeywordMap)["vector"] = EHTokVector;
+ (*KeywordMap)["matrix"] = EHTokMatrix;
+
+ (*KeywordMap)["void"] = EHTokVoid;
+ (*KeywordMap)["string"] = EHTokString;
+ (*KeywordMap)["bool"] = EHTokBool;
+ (*KeywordMap)["int"] = EHTokInt;
+ (*KeywordMap)["uint"] = EHTokUint;
+ (*KeywordMap)["uint64_t"] = EHTokUint64;
+ (*KeywordMap)["dword"] = EHTokDword;
+ (*KeywordMap)["half"] = EHTokHalf;
+ (*KeywordMap)["float"] = EHTokFloat;
+ (*KeywordMap)["double"] = EHTokDouble;
+ (*KeywordMap)["min16float"] = EHTokMin16float;
+ (*KeywordMap)["min10float"] = EHTokMin10float;
+ (*KeywordMap)["min16int"] = EHTokMin16int;
+ (*KeywordMap)["min12int"] = EHTokMin12int;
+ (*KeywordMap)["min16uint"] = EHTokMin16uint;
+
+ (*KeywordMap)["bool1"] = EHTokBool1;
+ (*KeywordMap)["bool2"] = EHTokBool2;
+ (*KeywordMap)["bool3"] = EHTokBool3;
+ (*KeywordMap)["bool4"] = EHTokBool4;
+ (*KeywordMap)["float1"] = EHTokFloat1;
+ (*KeywordMap)["float2"] = EHTokFloat2;
+ (*KeywordMap)["float3"] = EHTokFloat3;
+ (*KeywordMap)["float4"] = EHTokFloat4;
+ (*KeywordMap)["int1"] = EHTokInt1;
+ (*KeywordMap)["int2"] = EHTokInt2;
+ (*KeywordMap)["int3"] = EHTokInt3;
+ (*KeywordMap)["int4"] = EHTokInt4;
+ (*KeywordMap)["double1"] = EHTokDouble1;
+ (*KeywordMap)["double2"] = EHTokDouble2;
+ (*KeywordMap)["double3"] = EHTokDouble3;
+ (*KeywordMap)["double4"] = EHTokDouble4;
+ (*KeywordMap)["uint1"] = EHTokUint1;
+ (*KeywordMap)["uint2"] = EHTokUint2;
+ (*KeywordMap)["uint3"] = EHTokUint3;
+ (*KeywordMap)["uint4"] = EHTokUint4;
+
+ (*KeywordMap)["half1"] = EHTokHalf1;
+ (*KeywordMap)["half2"] = EHTokHalf2;
+ (*KeywordMap)["half3"] = EHTokHalf3;
+ (*KeywordMap)["half4"] = EHTokHalf4;
+ (*KeywordMap)["min16float1"] = EHTokMin16float1;
+ (*KeywordMap)["min16float2"] = EHTokMin16float2;
+ (*KeywordMap)["min16float3"] = EHTokMin16float3;
+ (*KeywordMap)["min16float4"] = EHTokMin16float4;
+ (*KeywordMap)["min10float1"] = EHTokMin10float1;
+ (*KeywordMap)["min10float2"] = EHTokMin10float2;
+ (*KeywordMap)["min10float3"] = EHTokMin10float3;
+ (*KeywordMap)["min10float4"] = EHTokMin10float4;
+ (*KeywordMap)["min16int1"] = EHTokMin16int1;
+ (*KeywordMap)["min16int2"] = EHTokMin16int2;
+ (*KeywordMap)["min16int3"] = EHTokMin16int3;
+ (*KeywordMap)["min16int4"] = EHTokMin16int4;
+ (*KeywordMap)["min12int1"] = EHTokMin12int1;
+ (*KeywordMap)["min12int2"] = EHTokMin12int2;
+ (*KeywordMap)["min12int3"] = EHTokMin12int3;
+ (*KeywordMap)["min12int4"] = EHTokMin12int4;
+ (*KeywordMap)["min16uint1"] = EHTokMin16uint1;
+ (*KeywordMap)["min16uint2"] = EHTokMin16uint2;
+ (*KeywordMap)["min16uint3"] = EHTokMin16uint3;
+ (*KeywordMap)["min16uint4"] = EHTokMin16uint4;
+
+ (*KeywordMap)["bool1x1"] = EHTokBool1x1;
+ (*KeywordMap)["bool1x2"] = EHTokBool1x2;
+ (*KeywordMap)["bool1x3"] = EHTokBool1x3;
+ (*KeywordMap)["bool1x4"] = EHTokBool1x4;
+ (*KeywordMap)["bool2x1"] = EHTokBool2x1;
+ (*KeywordMap)["bool2x2"] = EHTokBool2x2;
+ (*KeywordMap)["bool2x3"] = EHTokBool2x3;
+ (*KeywordMap)["bool2x4"] = EHTokBool2x4;
+ (*KeywordMap)["bool3x1"] = EHTokBool3x1;
+ (*KeywordMap)["bool3x2"] = EHTokBool3x2;
+ (*KeywordMap)["bool3x3"] = EHTokBool3x3;
+ (*KeywordMap)["bool3x4"] = EHTokBool3x4;
+ (*KeywordMap)["bool4x1"] = EHTokBool4x1;
+ (*KeywordMap)["bool4x2"] = EHTokBool4x2;
+ (*KeywordMap)["bool4x3"] = EHTokBool4x3;
+ (*KeywordMap)["bool4x4"] = EHTokBool4x4;
+ (*KeywordMap)["int1x1"] = EHTokInt1x1;
+ (*KeywordMap)["int1x2"] = EHTokInt1x2;
+ (*KeywordMap)["int1x3"] = EHTokInt1x3;
+ (*KeywordMap)["int1x4"] = EHTokInt1x4;
+ (*KeywordMap)["int2x1"] = EHTokInt2x1;
+ (*KeywordMap)["int2x2"] = EHTokInt2x2;
+ (*KeywordMap)["int2x3"] = EHTokInt2x3;
+ (*KeywordMap)["int2x4"] = EHTokInt2x4;
+ (*KeywordMap)["int3x1"] = EHTokInt3x1;
+ (*KeywordMap)["int3x2"] = EHTokInt3x2;
+ (*KeywordMap)["int3x3"] = EHTokInt3x3;
+ (*KeywordMap)["int3x4"] = EHTokInt3x4;
+ (*KeywordMap)["int4x1"] = EHTokInt4x1;
+ (*KeywordMap)["int4x2"] = EHTokInt4x2;
+ (*KeywordMap)["int4x3"] = EHTokInt4x3;
+ (*KeywordMap)["int4x4"] = EHTokInt4x4;
+ (*KeywordMap)["uint1x1"] = EHTokUint1x1;
+ (*KeywordMap)["uint1x2"] = EHTokUint1x2;
+ (*KeywordMap)["uint1x3"] = EHTokUint1x3;
+ (*KeywordMap)["uint1x4"] = EHTokUint1x4;
+ (*KeywordMap)["uint2x1"] = EHTokUint2x1;
+ (*KeywordMap)["uint2x2"] = EHTokUint2x2;
+ (*KeywordMap)["uint2x3"] = EHTokUint2x3;
+ (*KeywordMap)["uint2x4"] = EHTokUint2x4;
+ (*KeywordMap)["uint3x1"] = EHTokUint3x1;
+ (*KeywordMap)["uint3x2"] = EHTokUint3x2;
+ (*KeywordMap)["uint3x3"] = EHTokUint3x3;
+ (*KeywordMap)["uint3x4"] = EHTokUint3x4;
+ (*KeywordMap)["uint4x1"] = EHTokUint4x1;
+ (*KeywordMap)["uint4x2"] = EHTokUint4x2;
+ (*KeywordMap)["uint4x3"] = EHTokUint4x3;
+ (*KeywordMap)["uint4x4"] = EHTokUint4x4;
+ (*KeywordMap)["bool1x1"] = EHTokBool1x1;
+ (*KeywordMap)["bool1x2"] = EHTokBool1x2;
+ (*KeywordMap)["bool1x3"] = EHTokBool1x3;
+ (*KeywordMap)["bool1x4"] = EHTokBool1x4;
+ (*KeywordMap)["bool2x1"] = EHTokBool2x1;
+ (*KeywordMap)["bool2x2"] = EHTokBool2x2;
+ (*KeywordMap)["bool2x3"] = EHTokBool2x3;
+ (*KeywordMap)["bool2x4"] = EHTokBool2x4;
+ (*KeywordMap)["bool3x1"] = EHTokBool3x1;
+ (*KeywordMap)["bool3x2"] = EHTokBool3x2;
+ (*KeywordMap)["bool3x3"] = EHTokBool3x3;
+ (*KeywordMap)["bool3x4"] = EHTokBool3x4;
+ (*KeywordMap)["bool4x1"] = EHTokBool4x1;
+ (*KeywordMap)["bool4x2"] = EHTokBool4x2;
+ (*KeywordMap)["bool4x3"] = EHTokBool4x3;
+ (*KeywordMap)["bool4x4"] = EHTokBool4x4;
+ (*KeywordMap)["float1x1"] = EHTokFloat1x1;
+ (*KeywordMap)["float1x2"] = EHTokFloat1x2;
+ (*KeywordMap)["float1x3"] = EHTokFloat1x3;
+ (*KeywordMap)["float1x4"] = EHTokFloat1x4;
+ (*KeywordMap)["float2x1"] = EHTokFloat2x1;
+ (*KeywordMap)["float2x2"] = EHTokFloat2x2;
+ (*KeywordMap)["float2x3"] = EHTokFloat2x3;
+ (*KeywordMap)["float2x4"] = EHTokFloat2x4;
+ (*KeywordMap)["float3x1"] = EHTokFloat3x1;
+ (*KeywordMap)["float3x2"] = EHTokFloat3x2;
+ (*KeywordMap)["float3x3"] = EHTokFloat3x3;
+ (*KeywordMap)["float3x4"] = EHTokFloat3x4;
+ (*KeywordMap)["float4x1"] = EHTokFloat4x1;
+ (*KeywordMap)["float4x2"] = EHTokFloat4x2;
+ (*KeywordMap)["float4x3"] = EHTokFloat4x3;
+ (*KeywordMap)["float4x4"] = EHTokFloat4x4;
+ (*KeywordMap)["half1x1"] = EHTokHalf1x1;
+ (*KeywordMap)["half1x2"] = EHTokHalf1x2;
+ (*KeywordMap)["half1x3"] = EHTokHalf1x3;
+ (*KeywordMap)["half1x4"] = EHTokHalf1x4;
+ (*KeywordMap)["half2x1"] = EHTokHalf2x1;
+ (*KeywordMap)["half2x2"] = EHTokHalf2x2;
+ (*KeywordMap)["half2x3"] = EHTokHalf2x3;
+ (*KeywordMap)["half2x4"] = EHTokHalf2x4;
+ (*KeywordMap)["half3x1"] = EHTokHalf3x1;
+ (*KeywordMap)["half3x2"] = EHTokHalf3x2;
+ (*KeywordMap)["half3x3"] = EHTokHalf3x3;
+ (*KeywordMap)["half3x4"] = EHTokHalf3x4;
+ (*KeywordMap)["half4x1"] = EHTokHalf4x1;
+ (*KeywordMap)["half4x2"] = EHTokHalf4x2;
+ (*KeywordMap)["half4x3"] = EHTokHalf4x3;
+ (*KeywordMap)["half4x4"] = EHTokHalf4x4;
+ (*KeywordMap)["double1x1"] = EHTokDouble1x1;
+ (*KeywordMap)["double1x2"] = EHTokDouble1x2;
+ (*KeywordMap)["double1x3"] = EHTokDouble1x3;
+ (*KeywordMap)["double1x4"] = EHTokDouble1x4;
+ (*KeywordMap)["double2x1"] = EHTokDouble2x1;
+ (*KeywordMap)["double2x2"] = EHTokDouble2x2;
+ (*KeywordMap)["double2x3"] = EHTokDouble2x3;
+ (*KeywordMap)["double2x4"] = EHTokDouble2x4;
+ (*KeywordMap)["double3x1"] = EHTokDouble3x1;
+ (*KeywordMap)["double3x2"] = EHTokDouble3x2;
+ (*KeywordMap)["double3x3"] = EHTokDouble3x3;
+ (*KeywordMap)["double3x4"] = EHTokDouble3x4;
+ (*KeywordMap)["double4x1"] = EHTokDouble4x1;
+ (*KeywordMap)["double4x2"] = EHTokDouble4x2;
+ (*KeywordMap)["double4x3"] = EHTokDouble4x3;
+ (*KeywordMap)["double4x4"] = EHTokDouble4x4;
+
+ (*KeywordMap)["sampler"] = EHTokSampler;
+ (*KeywordMap)["sampler1D"] = EHTokSampler1d;
+ (*KeywordMap)["sampler2D"] = EHTokSampler2d;
+ (*KeywordMap)["sampler3D"] = EHTokSampler3d;
+ (*KeywordMap)["samplerCube"] = EHTokSamplerCube;
+ (*KeywordMap)["sampler_state"] = EHTokSamplerState;
+ (*KeywordMap)["SamplerState"] = EHTokSamplerState;
+ (*KeywordMap)["SamplerComparisonState"] = EHTokSamplerComparisonState;
+ (*KeywordMap)["texture"] = EHTokTexture;
+ (*KeywordMap)["Texture1D"] = EHTokTexture1d;
+ (*KeywordMap)["Texture1DArray"] = EHTokTexture1darray;
+ (*KeywordMap)["Texture2D"] = EHTokTexture2d;
+ (*KeywordMap)["Texture2DArray"] = EHTokTexture2darray;
+ (*KeywordMap)["Texture3D"] = EHTokTexture3d;
+ (*KeywordMap)["TextureCube"] = EHTokTextureCube;
+ (*KeywordMap)["TextureCubeArray"] = EHTokTextureCubearray;
+ (*KeywordMap)["Texture2DMS"] = EHTokTexture2DMS;
+ (*KeywordMap)["Texture2DMSArray"] = EHTokTexture2DMSarray;
+ (*KeywordMap)["RWTexture1D"] = EHTokRWTexture1d;
+ (*KeywordMap)["RWTexture1DArray"] = EHTokRWTexture1darray;
+ (*KeywordMap)["RWTexture2D"] = EHTokRWTexture2d;
+ (*KeywordMap)["RWTexture2DArray"] = EHTokRWTexture2darray;
+ (*KeywordMap)["RWTexture3D"] = EHTokRWTexture3d;
+ (*KeywordMap)["RWBuffer"] = EHTokRWBuffer;
+ (*KeywordMap)["SubpassInput"] = EHTokSubpassInput;
+ (*KeywordMap)["SubpassInputMS"] = EHTokSubpassInputMS;
+
+ (*KeywordMap)["AppendStructuredBuffer"] = EHTokAppendStructuredBuffer;
+ (*KeywordMap)["ByteAddressBuffer"] = EHTokByteAddressBuffer;
+ (*KeywordMap)["ConsumeStructuredBuffer"] = EHTokConsumeStructuredBuffer;
+ (*KeywordMap)["RWByteAddressBuffer"] = EHTokRWByteAddressBuffer;
+ (*KeywordMap)["RWStructuredBuffer"] = EHTokRWStructuredBuffer;
+ (*KeywordMap)["StructuredBuffer"] = EHTokStructuredBuffer;
+ (*KeywordMap)["TextureBuffer"] = EHTokTextureBuffer;
+
+ (*KeywordMap)["class"] = EHTokClass;
+ (*KeywordMap)["struct"] = EHTokStruct;
+ (*KeywordMap)["cbuffer"] = EHTokCBuffer;
+ (*KeywordMap)["ConstantBuffer"] = EHTokConstantBuffer;
+ (*KeywordMap)["tbuffer"] = EHTokTBuffer;
+ (*KeywordMap)["typedef"] = EHTokTypedef;
+ (*KeywordMap)["this"] = EHTokThis;
+ (*KeywordMap)["namespace"] = EHTokNamespace;
+
+ (*KeywordMap)["true"] = EHTokBoolConstant;
+ (*KeywordMap)["false"] = EHTokBoolConstant;
+
+ (*KeywordMap)["for"] = EHTokFor;
+ (*KeywordMap)["do"] = EHTokDo;
+ (*KeywordMap)["while"] = EHTokWhile;
+ (*KeywordMap)["break"] = EHTokBreak;
+ (*KeywordMap)["continue"] = EHTokContinue;
+ (*KeywordMap)["if"] = EHTokIf;
+ (*KeywordMap)["else"] = EHTokElse;
+ (*KeywordMap)["discard"] = EHTokDiscard;
+ (*KeywordMap)["return"] = EHTokReturn;
+ (*KeywordMap)["switch"] = EHTokSwitch;
+ (*KeywordMap)["case"] = EHTokCase;
+ (*KeywordMap)["default"] = EHTokDefault;
+
+ // TODO: get correct set here
+ ReservedSet = new std::unordered_set<const char*, str_hash, str_eq>;
+
+ ReservedSet->insert("auto");
+ ReservedSet->insert("catch");
+ ReservedSet->insert("char");
+ ReservedSet->insert("const_cast");
+ ReservedSet->insert("enum");
+ ReservedSet->insert("explicit");
+ ReservedSet->insert("friend");
+ ReservedSet->insert("goto");
+ ReservedSet->insert("long");
+ ReservedSet->insert("mutable");
+ ReservedSet->insert("new");
+ ReservedSet->insert("operator");
+ ReservedSet->insert("private");
+ ReservedSet->insert("protected");
+ ReservedSet->insert("public");
+ ReservedSet->insert("reinterpret_cast");
+ ReservedSet->insert("short");
+ ReservedSet->insert("signed");
+ ReservedSet->insert("sizeof");
+ ReservedSet->insert("static_cast");
+ ReservedSet->insert("template");
+ ReservedSet->insert("throw");
+ ReservedSet->insert("try");
+ ReservedSet->insert("typename");
+ ReservedSet->insert("union");
+ ReservedSet->insert("unsigned");
+ ReservedSet->insert("using");
+ ReservedSet->insert("virtual");
+
+ SemanticMap = new std::unordered_map<const char*, glslang::TBuiltInVariable, str_hash, str_eq>;
+
+ // in DX9, all outputs had to have a semantic associated with them, that was either consumed
+ // by the system or was a specific register assignment
+ // in DX10+, only semantics with the SV_ prefix have any meaning beyond decoration
+ // Fxc will only accept DX9 style semantics in compat mode
+ // Also, in DX10 if a SV value is present as the input of a stage, but isn't appropriate for that
+ // stage, it would just be ignored as it is likely there as part of an output struct from one stage
+ // to the next
+ bool bParseDX9 = false;
+ if (bParseDX9) {
+ (*SemanticMap)["PSIZE"] = EbvPointSize;
+ (*SemanticMap)["FOG"] = EbvFogFragCoord;
+ (*SemanticMap)["DEPTH"] = EbvFragDepth;
+ (*SemanticMap)["VFACE"] = EbvFace;
+ (*SemanticMap)["VPOS"] = EbvFragCoord;
+ }
+
+ (*SemanticMap)["SV_POSITION"] = EbvPosition;
+ (*SemanticMap)["SV_VERTEXID"] = EbvVertexIndex;
+ (*SemanticMap)["SV_VIEWPORTARRAYINDEX"] = EbvViewportIndex;
+ (*SemanticMap)["SV_TESSFACTOR"] = EbvTessLevelOuter;
+ (*SemanticMap)["SV_SAMPLEINDEX"] = EbvSampleId;
+ (*SemanticMap)["SV_RENDERTARGETARRAYINDEX"] = EbvLayer;
+ (*SemanticMap)["SV_PRIMITIVEID"] = EbvPrimitiveId;
+ (*SemanticMap)["SV_OUTPUTCONTROLPOINTID"] = EbvInvocationId;
+ (*SemanticMap)["SV_ISFRONTFACE"] = EbvFace;
+ (*SemanticMap)["SV_INSTANCEID"] = EbvInstanceIndex;
+ (*SemanticMap)["SV_INSIDETESSFACTOR"] = EbvTessLevelInner;
+ (*SemanticMap)["SV_GSINSTANCEID"] = EbvInvocationId;
+ (*SemanticMap)["SV_DISPATCHTHREADID"] = EbvGlobalInvocationId;
+ (*SemanticMap)["SV_GROUPTHREADID"] = EbvLocalInvocationId;
+ (*SemanticMap)["SV_GROUPINDEX"] = EbvLocalInvocationIndex;
+ (*SemanticMap)["SV_GROUPID"] = EbvWorkGroupId;
+ (*SemanticMap)["SV_DOMAINLOCATION"] = EbvTessCoord;
+ (*SemanticMap)["SV_DEPTH"] = EbvFragDepth;
+ (*SemanticMap)["SV_COVERAGE"] = EbvSampleMask;
+ (*SemanticMap)["SV_DEPTHGREATEREQUAL"] = EbvFragDepthGreater;
+ (*SemanticMap)["SV_DEPTHLESSEQUAL"] = EbvFragDepthLesser;
+ (*SemanticMap)["SV_STENCILREF"] = EbvFragStencilRef;
+}
+
+void HlslScanContext::deleteKeywordMap()
+{
+ delete KeywordMap;
+ KeywordMap = nullptr;
+ delete ReservedSet;
+ ReservedSet = nullptr;
+ delete SemanticMap;
+ SemanticMap = nullptr;
+}
+
+// Wrapper for tokenizeClass() to get everything inside the token.
+void HlslScanContext::tokenize(HlslToken& token)
+{
+ EHlslTokenClass tokenClass = tokenizeClass(token);
+ token.tokenClass = tokenClass;
+}
+
+glslang::TBuiltInVariable HlslScanContext::mapSemantic(const char* upperCase)
+{
+ auto it = SemanticMap->find(upperCase);
+ if (it != SemanticMap->end())
+ return it->second;
+ else
+ return glslang::EbvNone;
+}
+
+//
+// Fill in token information for the next token, except for the token class.
+// Returns the enum value of the token class of the next token found.
+// Return 0 (EndOfTokens) on end of input.
+//
+EHlslTokenClass HlslScanContext::tokenizeClass(HlslToken& token)
+{
+ do {
+ parserToken = &token;
+ TPpToken ppToken;
+ int token = ppContext.tokenize(ppToken);
+ if (token == EndOfInput)
+ return EHTokNone;
+
+ tokenText = ppToken.name;
+ loc = ppToken.loc;
+ parserToken->loc = loc;
+ switch (token) {
+ case ';': return EHTokSemicolon;
+ case ',': return EHTokComma;
+ case ':': return EHTokColon;
+ case '=': return EHTokAssign;
+ case '(': return EHTokLeftParen;
+ case ')': return EHTokRightParen;
+ case '.': return EHTokDot;
+ case '!': return EHTokBang;
+ case '-': return EHTokDash;
+ case '~': return EHTokTilde;
+ case '+': return EHTokPlus;
+ case '*': return EHTokStar;
+ case '/': return EHTokSlash;
+ case '%': return EHTokPercent;
+ case '<': return EHTokLeftAngle;
+ case '>': return EHTokRightAngle;
+ case '|': return EHTokVerticalBar;
+ case '^': return EHTokCaret;
+ case '&': return EHTokAmpersand;
+ case '?': return EHTokQuestion;
+ case '[': return EHTokLeftBracket;
+ case ']': return EHTokRightBracket;
+ case '{': return EHTokLeftBrace;
+ case '}': return EHTokRightBrace;
+ case '\\':
+ parseContext.error(loc, "illegal use of escape character", "\\", "");
+ break;
+
+ case PPAtomAddAssign: return EHTokAddAssign;
+ case PPAtomSubAssign: return EHTokSubAssign;
+ case PPAtomMulAssign: return EHTokMulAssign;
+ case PPAtomDivAssign: return EHTokDivAssign;
+ case PPAtomModAssign: return EHTokModAssign;
+
+ case PpAtomRight: return EHTokRightOp;
+ case PpAtomLeft: return EHTokLeftOp;
+
+ case PpAtomRightAssign: return EHTokRightAssign;
+ case PpAtomLeftAssign: return EHTokLeftAssign;
+ case PpAtomAndAssign: return EHTokAndAssign;
+ case PpAtomOrAssign: return EHTokOrAssign;
+ case PpAtomXorAssign: return EHTokXorAssign;
+
+ case PpAtomAnd: return EHTokAndOp;
+ case PpAtomOr: return EHTokOrOp;
+ case PpAtomXor: return EHTokXorOp;
+
+ case PpAtomEQ: return EHTokEqOp;
+ case PpAtomGE: return EHTokGeOp;
+ case PpAtomNE: return EHTokNeOp;
+ case PpAtomLE: return EHTokLeOp;
+
+ case PpAtomDecrement: return EHTokDecOp;
+ case PpAtomIncrement: return EHTokIncOp;
+
+ case PpAtomColonColon: return EHTokColonColon;
+
+ case PpAtomConstInt: parserToken->i = ppToken.ival; return EHTokIntConstant;
+ case PpAtomConstUint: parserToken->i = ppToken.ival; return EHTokUintConstant;
+ case PpAtomConstFloat16: parserToken->d = ppToken.dval; return EHTokFloat16Constant;
+ case PpAtomConstFloat: parserToken->d = ppToken.dval; return EHTokFloatConstant;
+ case PpAtomConstDouble: parserToken->d = ppToken.dval; return EHTokDoubleConstant;
+ case PpAtomIdentifier:
+ {
+ EHlslTokenClass token = tokenizeIdentifier();
+ return token;
+ }
+
+ case PpAtomConstString: {
+ parserToken->string = NewPoolTString(tokenText);
+ return EHTokStringConstant;
+ }
+
+ case EndOfInput: return EHTokNone;
+
+ default:
+ if (token < PpAtomMaxSingle) {
+ char buf[2];
+ buf[0] = (char)token;
+ buf[1] = 0;
+ parseContext.error(loc, "unexpected token", buf, "");
+ } else if (tokenText[0] != 0)
+ parseContext.error(loc, "unexpected token", tokenText, "");
+ else
+ parseContext.error(loc, "unexpected token", "", "");
+ break;
+ }
+ } while (true);
+}
+
+EHlslTokenClass HlslScanContext::tokenizeIdentifier()
+{
+ if (ReservedSet->find(tokenText) != ReservedSet->end())
+ return reservedWord();
+
+ auto it = KeywordMap->find(tokenText);
+ if (it == KeywordMap->end()) {
+ // Should have an identifier of some sort
+ return identifierOrType();
+ }
+ keyword = it->second;
+
+ switch (keyword) {
+
+ // qualifiers
+ case EHTokStatic:
+ case EHTokConst:
+ case EHTokSNorm:
+ case EHTokUnorm:
+ case EHTokExtern:
+ case EHTokUniform:
+ case EHTokVolatile:
+ case EHTokShared:
+ case EHTokGroupShared:
+ case EHTokLinear:
+ case EHTokCentroid:
+ case EHTokNointerpolation:
+ case EHTokNoperspective:
+ case EHTokSample:
+ case EHTokRowMajor:
+ case EHTokColumnMajor:
+ case EHTokPackOffset:
+ case EHTokIn:
+ case EHTokOut:
+ case EHTokInOut:
+ case EHTokPrecise:
+ case EHTokLayout:
+ case EHTokGloballyCoherent:
+ case EHTokInline:
+ return keyword;
+
+ // primitive types
+ case EHTokPoint:
+ case EHTokLine:
+ case EHTokTriangle:
+ case EHTokLineAdj:
+ case EHTokTriangleAdj:
+ return keyword;
+
+ // stream out types
+ case EHTokPointStream:
+ case EHTokLineStream:
+ case EHTokTriangleStream:
+ return keyword;
+
+ // Tessellation patches
+ case EHTokInputPatch:
+ case EHTokOutputPatch:
+ return keyword;
+
+ case EHTokBuffer:
+ case EHTokVector:
+ case EHTokMatrix:
+ return keyword;
+
+ // scalar types
+ case EHTokVoid:
+ case EHTokString:
+ case EHTokBool:
+ case EHTokInt:
+ case EHTokUint:
+ case EHTokUint64:
+ case EHTokDword:
+ case EHTokHalf:
+ case EHTokFloat:
+ case EHTokDouble:
+ case EHTokMin16float:
+ case EHTokMin10float:
+ case EHTokMin16int:
+ case EHTokMin12int:
+ case EHTokMin16uint:
+
+ // vector types
+ case EHTokBool1:
+ case EHTokBool2:
+ case EHTokBool3:
+ case EHTokBool4:
+ case EHTokFloat1:
+ case EHTokFloat2:
+ case EHTokFloat3:
+ case EHTokFloat4:
+ case EHTokInt1:
+ case EHTokInt2:
+ case EHTokInt3:
+ case EHTokInt4:
+ case EHTokDouble1:
+ case EHTokDouble2:
+ case EHTokDouble3:
+ case EHTokDouble4:
+ case EHTokUint1:
+ case EHTokUint2:
+ case EHTokUint3:
+ case EHTokUint4:
+ case EHTokHalf1:
+ case EHTokHalf2:
+ case EHTokHalf3:
+ case EHTokHalf4:
+ case EHTokMin16float1:
+ case EHTokMin16float2:
+ case EHTokMin16float3:
+ case EHTokMin16float4:
+ case EHTokMin10float1:
+ case EHTokMin10float2:
+ case EHTokMin10float3:
+ case EHTokMin10float4:
+ case EHTokMin16int1:
+ case EHTokMin16int2:
+ case EHTokMin16int3:
+ case EHTokMin16int4:
+ case EHTokMin12int1:
+ case EHTokMin12int2:
+ case EHTokMin12int3:
+ case EHTokMin12int4:
+ case EHTokMin16uint1:
+ case EHTokMin16uint2:
+ case EHTokMin16uint3:
+ case EHTokMin16uint4:
+
+ // matrix types
+ case EHTokBool1x1:
+ case EHTokBool1x2:
+ case EHTokBool1x3:
+ case EHTokBool1x4:
+ case EHTokBool2x1:
+ case EHTokBool2x2:
+ case EHTokBool2x3:
+ case EHTokBool2x4:
+ case EHTokBool3x1:
+ case EHTokBool3x2:
+ case EHTokBool3x3:
+ case EHTokBool3x4:
+ case EHTokBool4x1:
+ case EHTokBool4x2:
+ case EHTokBool4x3:
+ case EHTokBool4x4:
+ case EHTokInt1x1:
+ case EHTokInt1x2:
+ case EHTokInt1x3:
+ case EHTokInt1x4:
+ case EHTokInt2x1:
+ case EHTokInt2x2:
+ case EHTokInt2x3:
+ case EHTokInt2x4:
+ case EHTokInt3x1:
+ case EHTokInt3x2:
+ case EHTokInt3x3:
+ case EHTokInt3x4:
+ case EHTokInt4x1:
+ case EHTokInt4x2:
+ case EHTokInt4x3:
+ case EHTokInt4x4:
+ case EHTokUint1x1:
+ case EHTokUint1x2:
+ case EHTokUint1x3:
+ case EHTokUint1x4:
+ case EHTokUint2x1:
+ case EHTokUint2x2:
+ case EHTokUint2x3:
+ case EHTokUint2x4:
+ case EHTokUint3x1:
+ case EHTokUint3x2:
+ case EHTokUint3x3:
+ case EHTokUint3x4:
+ case EHTokUint4x1:
+ case EHTokUint4x2:
+ case EHTokUint4x3:
+ case EHTokUint4x4:
+ case EHTokFloat1x1:
+ case EHTokFloat1x2:
+ case EHTokFloat1x3:
+ case EHTokFloat1x4:
+ case EHTokFloat2x1:
+ case EHTokFloat2x2:
+ case EHTokFloat2x3:
+ case EHTokFloat2x4:
+ case EHTokFloat3x1:
+ case EHTokFloat3x2:
+ case EHTokFloat3x3:
+ case EHTokFloat3x4:
+ case EHTokFloat4x1:
+ case EHTokFloat4x2:
+ case EHTokFloat4x3:
+ case EHTokFloat4x4:
+ case EHTokHalf1x1:
+ case EHTokHalf1x2:
+ case EHTokHalf1x3:
+ case EHTokHalf1x4:
+ case EHTokHalf2x1:
+ case EHTokHalf2x2:
+ case EHTokHalf2x3:
+ case EHTokHalf2x4:
+ case EHTokHalf3x1:
+ case EHTokHalf3x2:
+ case EHTokHalf3x3:
+ case EHTokHalf3x4:
+ case EHTokHalf4x1:
+ case EHTokHalf4x2:
+ case EHTokHalf4x3:
+ case EHTokHalf4x4:
+ case EHTokDouble1x1:
+ case EHTokDouble1x2:
+ case EHTokDouble1x3:
+ case EHTokDouble1x4:
+ case EHTokDouble2x1:
+ case EHTokDouble2x2:
+ case EHTokDouble2x3:
+ case EHTokDouble2x4:
+ case EHTokDouble3x1:
+ case EHTokDouble3x2:
+ case EHTokDouble3x3:
+ case EHTokDouble3x4:
+ case EHTokDouble4x1:
+ case EHTokDouble4x2:
+ case EHTokDouble4x3:
+ case EHTokDouble4x4:
+ return keyword;
+
+ // texturing types
+ case EHTokSampler:
+ case EHTokSampler1d:
+ case EHTokSampler2d:
+ case EHTokSampler3d:
+ case EHTokSamplerCube:
+ case EHTokSamplerState:
+ case EHTokSamplerComparisonState:
+ case EHTokTexture:
+ case EHTokTexture1d:
+ case EHTokTexture1darray:
+ case EHTokTexture2d:
+ case EHTokTexture2darray:
+ case EHTokTexture3d:
+ case EHTokTextureCube:
+ case EHTokTextureCubearray:
+ case EHTokTexture2DMS:
+ case EHTokTexture2DMSarray:
+ case EHTokRWTexture1d:
+ case EHTokRWTexture1darray:
+ case EHTokRWTexture2d:
+ case EHTokRWTexture2darray:
+ case EHTokRWTexture3d:
+ case EHTokRWBuffer:
+ case EHTokAppendStructuredBuffer:
+ case EHTokByteAddressBuffer:
+ case EHTokConsumeStructuredBuffer:
+ case EHTokRWByteAddressBuffer:
+ case EHTokRWStructuredBuffer:
+ case EHTokStructuredBuffer:
+ case EHTokTextureBuffer:
+ case EHTokSubpassInput:
+ case EHTokSubpassInputMS:
+ return keyword;
+
+ // variable, user type, ...
+ case EHTokClass:
+ case EHTokStruct:
+ case EHTokTypedef:
+ case EHTokCBuffer:
+ case EHTokConstantBuffer:
+ case EHTokTBuffer:
+ case EHTokThis:
+ case EHTokNamespace:
+ return keyword;
+
+ case EHTokBoolConstant:
+ if (strcmp("true", tokenText) == 0)
+ parserToken->b = true;
+ else
+ parserToken->b = false;
+ return keyword;
+
+ // control flow
+ case EHTokFor:
+ case EHTokDo:
+ case EHTokWhile:
+ case EHTokBreak:
+ case EHTokContinue:
+ case EHTokIf:
+ case EHTokElse:
+ case EHTokDiscard:
+ case EHTokReturn:
+ case EHTokCase:
+ case EHTokSwitch:
+ case EHTokDefault:
+ return keyword;
+
+ default:
+ parseContext.infoSink.info.message(EPrefixInternalError, "Unknown glslang keyword", loc);
+ return EHTokNone;
+ }
+}
+
+EHlslTokenClass HlslScanContext::identifierOrType()
+{
+ parserToken->string = NewPoolTString(tokenText);
+
+ return EHTokIdentifier;
+}
+
+// Give an error for use of a reserved symbol.
+// However, allow built-in declarations to use reserved words, to allow
+// extension support before the extension is enabled.
+EHlslTokenClass HlslScanContext::reservedWord()
+{
+ if (! parseContext.symbolTable.atBuiltInLevel())
+ parseContext.error(loc, "Reserved word.", tokenText, "", "");
+
+ return EHTokNone;
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/hlsl/hlslScanContext.h b/src/3rdparty/glslang/hlsl/hlslScanContext.h
new file mode 100644
index 0000000..9d30a12
--- /dev/null
+++ b/src/3rdparty/glslang/hlsl/hlslScanContext.h
@@ -0,0 +1,109 @@
+//
+// Copyright (C) 2016 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of Google, Inc., nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+//
+// This holds context specific to the HLSL scanner, which
+// sits between the preprocessor scanner and HLSL parser.
+//
+
+#ifndef HLSLSCANCONTEXT_H_
+#define HLSLSCANCONTEXT_H_
+
+#include "../glslang/MachineIndependent/ParseHelper.h"
+#include "hlslTokens.h"
+
+namespace glslang {
+
+class TPpContext;
+class TPpToken;
+
+
+//
+// Everything needed to fully describe a token.
+//
+struct HlslToken {
+ HlslToken() : string(nullptr) { loc.init(); }
+ TSourceLoc loc; // location of token in the source
+ EHlslTokenClass tokenClass; // what kind of token it is
+ union { // what data the token holds
+ glslang::TString *string; // for identifiers
+ int i; // for literals
+ unsigned int u;
+ bool b;
+ double d;
+ };
+};
+
+//
+// The state of scanning and translating raw tokens to slightly richer
+// semantics, like knowing if an identifier is an existing symbol, or
+// user-defined type.
+//
+class HlslScanContext {
+public:
+ HlslScanContext(TParseContextBase& parseContext, TPpContext& ppContext)
+ : parseContext(parseContext), ppContext(ppContext) { }
+ virtual ~HlslScanContext() { }
+
+ static void fillInKeywordMap();
+ static void deleteKeywordMap();
+
+ void tokenize(HlslToken&);
+ glslang::TBuiltInVariable mapSemantic(const char*);
+
+protected:
+ HlslScanContext(HlslScanContext&);
+ HlslScanContext& operator=(HlslScanContext&);
+
+ EHlslTokenClass tokenizeClass(HlslToken&);
+ EHlslTokenClass tokenizeIdentifier();
+ EHlslTokenClass identifierOrType();
+ EHlslTokenClass reservedWord();
+ EHlslTokenClass identifierOrReserved(bool reserved);
+ EHlslTokenClass nonreservedKeyword(int version);
+
+ TParseContextBase& parseContext;
+ TPpContext& ppContext;
+ TSourceLoc loc;
+ TPpToken* ppToken;
+ HlslToken* parserToken;
+
+ const char* tokenText;
+ EHlslTokenClass keyword;
+};
+
+} // end namespace glslang
+
+#endif // HLSLSCANCONTEXT_H_
diff --git a/src/3rdparty/glslang/hlsl/hlslTokenStream.cpp b/src/3rdparty/glslang/hlsl/hlslTokenStream.cpp
new file mode 100644
index 0000000..5d9311c
--- /dev/null
+++ b/src/3rdparty/glslang/hlsl/hlslTokenStream.cpp
@@ -0,0 +1,150 @@
+//
+// Copyright (C) 2016 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of Google, Inc., nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "hlslTokenStream.h"
+
+namespace glslang {
+
+void HlslTokenStream::pushPreToken(const HlslToken& tok)
+{
+ assert(preTokenStackSize < tokenBufferSize);
+ preTokenStack[preTokenStackSize++] = tok;
+}
+
+HlslToken HlslTokenStream::popPreToken()
+{
+ assert(preTokenStackSize > 0);
+
+ return preTokenStack[--preTokenStackSize];
+}
+
+void HlslTokenStream::pushTokenBuffer(const HlslToken& tok)
+{
+ tokenBuffer[tokenBufferPos] = tok;
+ tokenBufferPos = (tokenBufferPos+1) % tokenBufferSize;
+}
+
+HlslToken HlslTokenStream::popTokenBuffer()
+{
+ // Back up
+ tokenBufferPos = (tokenBufferPos+tokenBufferSize-1) % tokenBufferSize;
+
+ return tokenBuffer[tokenBufferPos];
+}
+
+//
+// Make a new source of tokens, not from the source, but from an
+// already pre-processed token stream.
+//
+// This interrupts current token processing which must be restored
+// later. Some simplifying assumptions are made (and asserted).
+//
+void HlslTokenStream::pushTokenStream(const TVector<HlslToken>* tokens)
+{
+ // not yet setup to interrupt a stream that has been receded
+ // and not yet reconsumed
+ assert(preTokenStackSize == 0);
+
+ // save current state
+ currentTokenStack.push_back(token);
+
+ // set up new token stream
+ tokenStreamStack.push_back(tokens);
+
+ // start position at first token:
+ token = (*tokens)[0];
+ tokenPosition.push_back(0);
+}
+
+// Undo pushTokenStream(), see above
+void HlslTokenStream::popTokenStream()
+{
+ tokenStreamStack.pop_back();
+ tokenPosition.pop_back();
+ token = currentTokenStack.back();
+ currentTokenStack.pop_back();
+}
+
+// Load 'token' with the next token in the stream of tokens.
+void HlslTokenStream::advanceToken()
+{
+ pushTokenBuffer(token);
+ if (preTokenStackSize > 0)
+ token = popPreToken();
+ else {
+ if (tokenStreamStack.size() == 0)
+ scanner.tokenize(token);
+ else {
+ ++tokenPosition.back();
+ if (tokenPosition.back() >= (int)tokenStreamStack.back()->size())
+ token.tokenClass = EHTokNone;
+ else
+ token = (*tokenStreamStack.back())[tokenPosition.back()];
+ }
+ }
+}
+
+void HlslTokenStream::recedeToken()
+{
+ pushPreToken(token);
+ token = popTokenBuffer();
+}
+
+// Return the current token class.
+EHlslTokenClass HlslTokenStream::peek() const
+{
+ return token.tokenClass;
+}
+
+// Return true, without advancing to the next token, if the current token is
+// the expected (passed in) token class.
+bool HlslTokenStream::peekTokenClass(EHlslTokenClass tokenClass) const
+{
+ return peek() == tokenClass;
+}
+
+// Return true and advance to the next token if the current token is the
+// expected (passed in) token class.
+bool HlslTokenStream::acceptTokenClass(EHlslTokenClass tokenClass)
+{
+ if (peekTokenClass(tokenClass)) {
+ advanceToken();
+ return true;
+ }
+
+ return false;
+}
+
+} // end namespace glslang
diff --git a/src/3rdparty/glslang/hlsl/hlslTokenStream.h b/src/3rdparty/glslang/hlsl/hlslTokenStream.h
new file mode 100644
index 0000000..cb6c9e7
--- /dev/null
+++ b/src/3rdparty/glslang/hlsl/hlslTokenStream.h
@@ -0,0 +1,96 @@
+//
+// Copyright (C) 2016 Google, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of Google, Inc., nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef HLSLTOKENSTREAM_H_
+#define HLSLTOKENSTREAM_H_
+
+#include "hlslScanContext.h"
+
+namespace glslang {
+
+ class HlslTokenStream {
+ public:
+ explicit HlslTokenStream(HlslScanContext& scanner)
+ : scanner(scanner), preTokenStackSize(0), tokenBufferPos(0) { }
+ virtual ~HlslTokenStream() { }
+
+ public:
+ void advanceToken();
+ void recedeToken();
+ bool acceptTokenClass(EHlslTokenClass);
+ EHlslTokenClass peek() const;
+ bool peekTokenClass(EHlslTokenClass) const;
+ glslang::TBuiltInVariable mapSemantic(const char* upperCase) { return scanner.mapSemantic(upperCase); }
+
+ void pushTokenStream(const TVector<HlslToken>* tokens);
+ void popTokenStream();
+
+ protected:
+ HlslToken token; // the token we are currently looking at, but have not yet accepted
+
+ private:
+ HlslTokenStream();
+ HlslTokenStream& operator=(const HlslTokenStream&);
+
+ HlslScanContext& scanner; // lexical scanner, to get next token from source file
+ TVector<const TVector<HlslToken>*> tokenStreamStack; // for getting the next token from an existing vector of tokens
+ TVector<int> tokenPosition;
+ TVector<HlslToken> currentTokenStack;
+
+ // This is the number of tokens we can recedeToken() over.
+ static const int tokenBufferSize = 2;
+
+ // Previously scanned tokens, returned for future advances,
+ // so logically in front of the token stream.
+ // Is logically a stack; needs last in last out semantics.
+ // Currently implemented as a stack of size 2.
+ HlslToken preTokenStack[tokenBufferSize];
+ int preTokenStackSize;
+ void pushPreToken(const HlslToken&);
+ HlslToken popPreToken();
+
+ // Previously scanned tokens, not yet returned for future advances,
+ // but available for that.
+ // Is logically a fifo for normal advances, and a stack for recession.
+ // Currently implemented with an intrinsic size of 2.
+ HlslToken tokenBuffer[tokenBufferSize];
+ int tokenBufferPos;
+ void pushTokenBuffer(const HlslToken&);
+ HlslToken popTokenBuffer();
+ };
+
+} // end namespace glslang
+
+#endif // HLSLTOKENSTREAM_H_
diff --git a/src/3rdparty/glslang/hlsl/hlslTokens.h b/src/3rdparty/glslang/hlsl/hlslTokens.h
new file mode 100644
index 0000000..4426bcc
--- /dev/null
+++ b/src/3rdparty/glslang/hlsl/hlslTokens.h
@@ -0,0 +1,374 @@
+//
+// Copyright (C) 2016 Google, Inc.
+// Copyright (C) 2016 LunarG, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of Google, Inc., nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef EHLSLTOKENS_H_
+#define EHLSLTOKENS_H_
+
+namespace glslang {
+
+enum EHlslTokenClass {
+ EHTokNone = 0,
+
+ // qualifiers
+ EHTokStatic,
+ EHTokConst,
+ EHTokSNorm,
+ EHTokUnorm,
+ EHTokExtern,
+ EHTokUniform,
+ EHTokVolatile,
+ EHTokPrecise,
+ EHTokShared,
+ EHTokGroupShared,
+ EHTokLinear,
+ EHTokCentroid,
+ EHTokNointerpolation,
+ EHTokNoperspective,
+ EHTokSample,
+ EHTokRowMajor,
+ EHTokColumnMajor,
+ EHTokPackOffset,
+ EHTokIn,
+ EHTokOut,
+ EHTokInOut,
+ EHTokLayout,
+ EHTokGloballyCoherent,
+ EHTokInline,
+
+ // primitive types
+ EHTokPoint,
+ EHTokLine,
+ EHTokTriangle,
+ EHTokLineAdj,
+ EHTokTriangleAdj,
+
+ // stream out types
+ EHTokPointStream,
+ EHTokLineStream,
+ EHTokTriangleStream,
+
+ // Tessellation patches
+ EHTokInputPatch,
+ EHTokOutputPatch,
+
+ // template types
+ EHTokBuffer,
+ EHTokVector,
+ EHTokMatrix,
+
+ // scalar types
+ EHTokVoid,
+ EHTokString,
+ EHTokBool,
+ EHTokInt,
+ EHTokUint,
+ EHTokUint64,
+ EHTokDword,
+ EHTokHalf,
+ EHTokFloat,
+ EHTokDouble,
+ EHTokMin16float,
+ EHTokMin10float,
+ EHTokMin16int,
+ EHTokMin12int,
+ EHTokMin16uint,
+
+ // vector types
+ EHTokBool1,
+ EHTokBool2,
+ EHTokBool3,
+ EHTokBool4,
+ EHTokFloat1,
+ EHTokFloat2,
+ EHTokFloat3,
+ EHTokFloat4,
+ EHTokInt1,
+ EHTokInt2,
+ EHTokInt3,
+ EHTokInt4,
+ EHTokDouble1,
+ EHTokDouble2,
+ EHTokDouble3,
+ EHTokDouble4,
+ EHTokUint1,
+ EHTokUint2,
+ EHTokUint3,
+ EHTokUint4,
+ EHTokHalf1,
+ EHTokHalf2,
+ EHTokHalf3,
+ EHTokHalf4,
+ EHTokMin16float1,
+ EHTokMin16float2,
+ EHTokMin16float3,
+ EHTokMin16float4,
+ EHTokMin10float1,
+ EHTokMin10float2,
+ EHTokMin10float3,
+ EHTokMin10float4,
+ EHTokMin16int1,
+ EHTokMin16int2,
+ EHTokMin16int3,
+ EHTokMin16int4,
+ EHTokMin12int1,
+ EHTokMin12int2,
+ EHTokMin12int3,
+ EHTokMin12int4,
+ EHTokMin16uint1,
+ EHTokMin16uint2,
+ EHTokMin16uint3,
+ EHTokMin16uint4,
+
+ // matrix types
+ EHTokInt1x1,
+ EHTokInt1x2,
+ EHTokInt1x3,
+ EHTokInt1x4,
+ EHTokInt2x1,
+ EHTokInt2x2,
+ EHTokInt2x3,
+ EHTokInt2x4,
+ EHTokInt3x1,
+ EHTokInt3x2,
+ EHTokInt3x3,
+ EHTokInt3x4,
+ EHTokInt4x1,
+ EHTokInt4x2,
+ EHTokInt4x3,
+ EHTokInt4x4,
+ EHTokUint1x1,
+ EHTokUint1x2,
+ EHTokUint1x3,
+ EHTokUint1x4,
+ EHTokUint2x1,
+ EHTokUint2x2,
+ EHTokUint2x3,
+ EHTokUint2x4,
+ EHTokUint3x1,
+ EHTokUint3x2,
+ EHTokUint3x3,
+ EHTokUint3x4,
+ EHTokUint4x1,
+ EHTokUint4x2,
+ EHTokUint4x3,
+ EHTokUint4x4,
+ EHTokBool1x1,
+ EHTokBool1x2,
+ EHTokBool1x3,
+ EHTokBool1x4,
+ EHTokBool2x1,
+ EHTokBool2x2,
+ EHTokBool2x3,
+ EHTokBool2x4,
+ EHTokBool3x1,
+ EHTokBool3x2,
+ EHTokBool3x3,
+ EHTokBool3x4,
+ EHTokBool4x1,
+ EHTokBool4x2,
+ EHTokBool4x3,
+ EHTokBool4x4,
+ EHTokFloat1x1,
+ EHTokFloat1x2,
+ EHTokFloat1x3,
+ EHTokFloat1x4,
+ EHTokFloat2x1,
+ EHTokFloat2x2,
+ EHTokFloat2x3,
+ EHTokFloat2x4,
+ EHTokFloat3x1,
+ EHTokFloat3x2,
+ EHTokFloat3x3,
+ EHTokFloat3x4,
+ EHTokFloat4x1,
+ EHTokFloat4x2,
+ EHTokFloat4x3,
+ EHTokFloat4x4,
+ EHTokHalf1x1,
+ EHTokHalf1x2,
+ EHTokHalf1x3,
+ EHTokHalf1x4,
+ EHTokHalf2x1,
+ EHTokHalf2x2,
+ EHTokHalf2x3,
+ EHTokHalf2x4,
+ EHTokHalf3x1,
+ EHTokHalf3x2,
+ EHTokHalf3x3,
+ EHTokHalf3x4,
+ EHTokHalf4x1,
+ EHTokHalf4x2,
+ EHTokHalf4x3,
+ EHTokHalf4x4,
+ EHTokDouble1x1,
+ EHTokDouble1x2,
+ EHTokDouble1x3,
+ EHTokDouble1x4,
+ EHTokDouble2x1,
+ EHTokDouble2x2,
+ EHTokDouble2x3,
+ EHTokDouble2x4,
+ EHTokDouble3x1,
+ EHTokDouble3x2,
+ EHTokDouble3x3,
+ EHTokDouble3x4,
+ EHTokDouble4x1,
+ EHTokDouble4x2,
+ EHTokDouble4x3,
+ EHTokDouble4x4,
+
+ // texturing types
+ EHTokSampler,
+ EHTokSampler1d,
+ EHTokSampler2d,
+ EHTokSampler3d,
+ EHTokSamplerCube,
+ EHTokSamplerState,
+ EHTokSamplerComparisonState,
+ EHTokTexture,
+ EHTokTexture1d,
+ EHTokTexture1darray,
+ EHTokTexture2d,
+ EHTokTexture2darray,
+ EHTokTexture3d,
+ EHTokTextureCube,
+ EHTokTextureCubearray,
+ EHTokTexture2DMS,
+ EHTokTexture2DMSarray,
+ EHTokRWTexture1d,
+ EHTokRWTexture1darray,
+ EHTokRWTexture2d,
+ EHTokRWTexture2darray,
+ EHTokRWTexture3d,
+ EHTokRWBuffer,
+ EHTokSubpassInput,
+ EHTokSubpassInputMS,
+
+ // Structure buffer variants
+ EHTokAppendStructuredBuffer,
+ EHTokByteAddressBuffer,
+ EHTokConsumeStructuredBuffer,
+ EHTokRWByteAddressBuffer,
+ EHTokRWStructuredBuffer,
+ EHTokStructuredBuffer,
+ EHTokTextureBuffer,
+
+ // variable, user type, ...
+ EHTokIdentifier,
+ EHTokClass,
+ EHTokStruct,
+ EHTokCBuffer,
+ EHTokTBuffer,
+ EHTokTypedef,
+ EHTokThis,
+ EHTokNamespace,
+ EHTokConstantBuffer,
+
+ // constant
+ EHTokFloat16Constant,
+ EHTokFloatConstant,
+ EHTokDoubleConstant,
+ EHTokIntConstant,
+ EHTokUintConstant,
+ EHTokBoolConstant,
+ EHTokStringConstant,
+
+ // control flow
+ EHTokFor,
+ EHTokDo,
+ EHTokWhile,
+ EHTokBreak,
+ EHTokContinue,
+ EHTokIf,
+ EHTokElse,
+ EHTokDiscard,
+ EHTokReturn,
+ EHTokSwitch,
+ EHTokCase,
+ EHTokDefault,
+
+ // expressions
+ EHTokLeftOp,
+ EHTokRightOp,
+ EHTokIncOp,
+ EHTokDecOp,
+ EHTokLeOp,
+ EHTokGeOp,
+ EHTokEqOp,
+ EHTokNeOp,
+ EHTokAndOp,
+ EHTokOrOp,
+ EHTokXorOp,
+ EHTokAssign,
+ EHTokMulAssign,
+ EHTokDivAssign,
+ EHTokAddAssign,
+ EHTokModAssign,
+ EHTokLeftAssign,
+ EHTokRightAssign,
+ EHTokAndAssign,
+ EHTokXorAssign,
+ EHTokOrAssign,
+ EHTokSubAssign,
+ EHTokLeftParen,
+ EHTokRightParen,
+ EHTokLeftBracket,
+ EHTokRightBracket,
+ EHTokLeftBrace,
+ EHTokRightBrace,
+ EHTokDot,
+ EHTokComma,
+ EHTokColon,
+ EHTokColonColon,
+ EHTokSemicolon,
+ EHTokBang,
+ EHTokDash,
+ EHTokTilde,
+ EHTokPlus,
+ EHTokStar,
+ EHTokSlash,
+ EHTokPercent,
+ EHTokLeftAngle,
+ EHTokRightAngle,
+ EHTokVerticalBar,
+ EHTokCaret,
+ EHTokAmpersand,
+ EHTokQuestion,
+};
+
+} // end namespace glslang
+
+#endif // EHLSLTOKENS_H_
diff --git a/src/3rdparty/glslang/qt_attribution.json b/src/3rdparty/glslang/qt_attribution.json
new file mode 100644
index 0000000..2fe30da
--- /dev/null
+++ b/src/3rdparty/glslang/qt_attribution.json
@@ -0,0 +1,16 @@
+[
+ {
+ "Id": "glslang",
+ "Name": "glslang",
+ "QDocModule": "qtshadertools",
+ "Description": "An OpenGL and OpenGL ES shader front end and validator.",
+ "QtUsage": "Compile GLSL to SPIR-V.",
+
+ "Homepage": "https://github.com/KhronosGroup/glslang",
+ "Version": "e0d59bbe1857e75134989eddb7437e9c068ec915",
+ "License": "BSD License",
+ "LicenseId": "BSD",
+ "LicenseFile": "LICENSE.glslang",
+ "Copyright": "Copyright (C) 2002-2005 3Dlabs Inc. Ltd. Copyright (C) 2012-2014 LunarG, Inc. Copyright (C) 2002-2010 The ANGLE Project Authors. Copyright (C) 2015-2016 Google, Inc."
+ }
+]
diff --git a/src/SPIRV-Cross/SPIRV-Cross.pro b/src/SPIRV-Cross/SPIRV-Cross.pro
new file mode 100644
index 0000000..f118662
--- /dev/null
+++ b/src/SPIRV-Cross/SPIRV-Cross.pro
@@ -0,0 +1,27 @@
+TARGET = qtspirv-cross
+
+# Exceptions must be enabled since that is the only sane way to get errors reported.
+# They will not propagate outside of the shadertools module though so should be safe enough.
+
+CONFIG += \
+ static \
+ hide_symbols \
+ warn_off \
+ exceptions
+
+load(qt_helper_lib)
+
+SPIRVCROSS_PATH=$$PWD/../3rdparty/SPIRV-Cross
+
+SOURCES += \
+ $$SPIRVCROSS_PATH/spirv_cfg.cpp \
+ $$SPIRVCROSS_PATH/spirv_cpp.cpp \
+ $$SPIRVCROSS_PATH/spirv_cross.cpp \
+ $$SPIRVCROSS_PATH/spirv_cross_c.cpp \
+ $$SPIRVCROSS_PATH/spirv_cross_parsed_ir.cpp \
+ $$SPIRVCROSS_PATH/spirv_cross_util.cpp \
+ $$SPIRVCROSS_PATH/spirv_glsl.cpp \
+ $$SPIRVCROSS_PATH/spirv_hlsl.cpp \
+ $$SPIRVCROSS_PATH/spirv_msl.cpp \
+ $$SPIRVCROSS_PATH/spirv_parser.cpp \
+ $$SPIRVCROSS_PATH/spirv_reflect.cpp
diff --git a/src/glslang/glslang-glslang.pro b/src/glslang/glslang-glslang.pro
new file mode 100644
index 0000000..e5645c4
--- /dev/null
+++ b/src/glslang/glslang-glslang.pro
@@ -0,0 +1,42 @@
+TARGET = qtglslang-glslang
+
+CONFIG += \
+ static \
+ hide_symbols \
+ exceptions_off rtti_off warn_off
+
+load(qt_helper_lib)
+
+include($$PWD/glslang_common.pri)
+
+SOURCES += \
+ $$GLSLANG_PATH/glslang/MachineIndependent/glslang_tab.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/attribute.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/Constant.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/iomapper.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/InfoSink.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/Initialize.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/IntermTraverse.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/Intermediate.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/ParseContextBase.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/ParseHelper.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/PoolAlloc.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/RemoveTree.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/Scan.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/ShaderLang.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/SymbolTable.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/Versions.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/intermOut.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/limits.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/linkValidate.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/parseConst.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/pch.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/reflection.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/preprocessor/Pp.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/preprocessor/PpAtom.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/preprocessor/PpContext.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/preprocessor/PpScanner.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/preprocessor/PpTokens.cpp \
+ $$GLSLANG_PATH/glslang/MachineIndependent/propagateNoContraction.cpp \
+ $$GLSLANG_PATH/glslang/GenericCodeGen/CodeGen.cpp \
+ $$GLSLANG_PATH/glslang/GenericCodeGen/Link.cpp
diff --git a/src/glslang/glslang-hlsl.pro b/src/glslang/glslang-hlsl.pro
new file mode 100644
index 0000000..b324c30
--- /dev/null
+++ b/src/glslang/glslang-hlsl.pro
@@ -0,0 +1,19 @@
+TARGET = qtglslang-hlsl
+
+CONFIG += \
+ static \
+ hide_symbols \
+ exceptions_off rtti_off warn_off
+
+load(qt_helper_lib)
+
+include($$PWD/glslang_common.pri)
+
+SOURCES += \
+ $$GLSLANG_PATH/hlsl/hlslAttributes.cpp \
+ $$GLSLANG_PATH/hlsl/hlslParseHelper.cpp \
+ $$GLSLANG_PATH/hlsl/hlslScanContext.cpp \
+ $$GLSLANG_PATH/hlsl/hlslOpMap.cpp \
+ $$GLSLANG_PATH/hlsl/hlslTokenStream.cpp \
+ $$GLSLANG_PATH/hlsl/hlslGrammar.cpp \
+ $$GLSLANG_PATH/hlsl/hlslParseables.cpp
diff --git a/src/glslang/glslang-oglcompiler.pro b/src/glslang/glslang-oglcompiler.pro
new file mode 100644
index 0000000..38e3231
--- /dev/null
+++ b/src/glslang/glslang-oglcompiler.pro
@@ -0,0 +1,13 @@
+TARGET = qtglslang-oglcompiler
+
+CONFIG += \
+ static \
+ hide_symbols \
+ exceptions_off rtti_off warn_off
+
+load(qt_helper_lib)
+
+include($$PWD/glslang_common.pri)
+
+SOURCES += \
+ $$GLSLANG_PATH/OGLCompilersDLL/InitializeDLL.cpp
diff --git a/src/glslang/glslang-osdependent.pro b/src/glslang/glslang-osdependent.pro
new file mode 100644
index 0000000..6a12fd4
--- /dev/null
+++ b/src/glslang/glslang-osdependent.pro
@@ -0,0 +1,17 @@
+TARGET = qtglslang-osdependent
+
+CONFIG += \
+ static \
+ hide_symbols \
+ exceptions_off rtti_off warn_off
+
+load(qt_helper_lib)
+
+include($$PWD/glslang_common.pri)
+
+win32: GLSLANG_OSDEP_PATH=$$GLSLANG_PATH/glslang/OSDependent/Windows
+unix: GLSLANG_OSDEP_PATH=$$GLSLANG_PATH/glslang/OSDependent/Unix
+linux: LIBS += -lpthread
+
+SOURCES += \
+ $$GLSLANG_OSDEP_PATH/ossource.cpp
diff --git a/src/glslang/glslang-spirv.pro b/src/glslang/glslang-spirv.pro
new file mode 100644
index 0000000..c3c7f0e
--- /dev/null
+++ b/src/glslang/glslang-spirv.pro
@@ -0,0 +1,21 @@
+TARGET = qtglslang-spirv
+
+CONFIG += \
+ static \
+ hide_symbols \
+ exceptions_off rtti_off warn_off
+
+load(qt_helper_lib)
+
+include($$PWD/glslang_common.pri)
+
+SOURCES += \
+ $$GLSLANG_PATH/SPIRV/GlslangToSpv.cpp \
+ $$GLSLANG_PATH/SPIRV/InReadableOrder.cpp \
+ $$GLSLANG_PATH/SPIRV/Logger.cpp \
+ $$GLSLANG_PATH/SPIRV/SpvBuilder.cpp \
+ $$GLSLANG_PATH/SPIRV/SpvPostProcess.cpp \
+ $$GLSLANG_PATH/SPIRV/doc.cpp \
+ $$GLSLANG_PATH/SPIRV/disassemble.cpp \
+ $$GLSLANG_PATH/SPIRV/SPVRemapper.cpp \
+ $$GLSLANG_PATH/SPIRV/SPVTools.cpp
diff --git a/src/glslang/glslang.pro b/src/glslang/glslang.pro
new file mode 100644
index 0000000..ee26163
--- /dev/null
+++ b/src/glslang/glslang.pro
@@ -0,0 +1,8 @@
+TEMPLATE = subdirs
+CONFIG += ordered
+
+SUBDIRS += glslang-spirv.pro
+SUBDIRS += glslang-osdependent.pro
+SUBDIRS += glslang-oglcompiler.pro
+# SUBDIRS += glslang-hlsl.pro
+SUBDIRS += glslang-glslang.pro
diff --git a/src/glslang/glslang_common.pri b/src/glslang/glslang_common.pri
new file mode 100644
index 0000000..f688149
--- /dev/null
+++ b/src/glslang/glslang_common.pri
@@ -0,0 +1,6 @@
+GLSLANG_PATH=$$PWD/../3rdparty/glslang
+
+win32: DEFINES += GLSLANG_OSINCLUDE_WIN32
+unix: DEFINES += GLSLANG_OSINCLUDE_UNIX
+
+# DEFINES += ENABLE_HLSL
diff --git a/src/shadertools/doc/doc.pri b/src/shadertools/doc/doc.pri
new file mode 100644
index 0000000..3fa7632
--- /dev/null
+++ b/src/shadertools/doc/doc.pri
@@ -0,0 +1,3 @@
+QMAKE_DOCS = $$PWD/qtshadertools.qdocconf
+
+OTHER_FILES += $$PWD/src/*.qdoc
diff --git a/src/shadertools/doc/qtshadertools.qdocconf b/src/shadertools/doc/qtshadertools.qdocconf
new file mode 100644
index 0000000..9afd3f7
--- /dev/null
+++ b/src/shadertools/doc/qtshadertools.qdocconf
@@ -0,0 +1,49 @@
+include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
+
+project = QtShaderTools
+description = Qt Shader Tools Reference Documentation
+version = $QT_VERSION
+
+examplesinstallpath = shadertools
+
+qhp.projects = QtShaderTools
+
+qhp.QtShaderTools.file = qtshadertools.qhp
+qhp.QtShaderTools.namespace = org.qt-project.qtshadertools.$QT_VERSION_TAG
+qhp.QtShaderTools.virtualFolder = qtshadertools
+qhp.QtShaderTools.indexTitle = Qt Shader Tools
+qhp.QtShaderTools.indexRoot =
+
+qhp.QtShaderTools.filterAttributes = qtshadertools $QT_VERSION qtrefdoc
+qhp.QtShaderTools.customFilters.Qt.name = QtShaderTools $QT_VERSION
+qhp.QtShaderTools.customFilters.Qt.filterAttributes = qtshadertools $QT_VERSION
+
+qhp.QtShaderTools.subprojects = classes examples
+
+qhp.QtShaderTools.subprojects.classes.title = C++ Classes
+qhp.QtShaderTools.subprojects.classes.indexTitle = Qt Shader Tools C++ Classes
+qhp.QtShaderTools.subprojects.classes.selectors = class fake:headerfile
+qhp.QtShaderTools.subprojects.classes.sortPages = true
+
+qhp.QtShaderTools.subprojects.examples.title = Examples
+qhp.QtShaderTools.subprojects.examples.indexTitle = Qt Shader Tools Examples and Tutorials
+qhp.QtShaderTools.subprojects.examples.selectors = fake:example
+
+tagfile = ../../../doc/qtshadertools/qtshadertools.tags
+
+depends += qtcore qtgui
+
+headerdirs += ..
+
+sourcedirs += ..
+
+exampledirs += ../../../examples/shadertools \
+ snippets
+
+imagedirs += images
+
+navigation.landingpage = "Qt Shader Tools"
+navigation.landingtitle = "Shader Tools"
+navigation.cppclassespage = "Qt Shader Tools C++ Classes"
+
+Cpp.ignoretokens += Q_SHADERTOOLS_EXPORT
diff --git a/src/shadertools/doc/snippets/color.frag b/src/shadertools/doc/snippets/color.frag
new file mode 100644
index 0000000..859946a
--- /dev/null
+++ b/src/shadertools/doc/snippets/color.frag
@@ -0,0 +1,16 @@
+//! [0]
+#version 440
+
+layout(location = 0) in vec3 v_color;
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ float opacity;
+} ubuf;
+
+void main()
+{
+ fragColor = vec4(v_color * ubuf.opacity, ubuf.opacity);
+}
+//! [0]
diff --git a/src/shadertools/doc/snippets/color.vert b/src/shadertools/doc/snippets/color.vert
new file mode 100644
index 0000000..9e21794
--- /dev/null
+++ b/src/shadertools/doc/snippets/color.vert
@@ -0,0 +1,20 @@
+//! [0]
+#version 440
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec3 color;
+layout(location = 0) out vec3 v_color;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ float opacity;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ v_color = color;
+ gl_Position = ubuf.mvp * position;
+}
+//! [0]
diff --git a/src/shadertools/doc/src/qtshadertools-copyright.qdoc b/src/shadertools/doc/src/qtshadertools-copyright.qdoc
new file mode 100644
index 0000000..f7a5e7c
--- /dev/null
+++ b/src/shadertools/doc/src/qtshadertools-copyright.qdoc
@@ -0,0 +1,37 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+\page copyright-notices.html
+\title Copyright Notices
+\section1 Third-party Licenses
+
+The following table lists parts (modules) of Qt Rendering Hardware Interface that
+incorporate code licensed under third-party open-source licenses:
+
+\annotatedlist attributions-qtshadertools
+*/
diff --git a/src/shadertools/doc/src/qtshadertools-cpp.qdoc b/src/shadertools/doc/src/qtshadertools-cpp.qdoc
new file mode 100644
index 0000000..2d1ea91
--- /dev/null
+++ b/src/shadertools/doc/src/qtshadertools-cpp.qdoc
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \module QtShaderTools
+ \title Qt Shader Tools C++ Classes
+ \ingroup modules
+ \qtvariable shadertools
+
+ To include the definitions of the module's classes, use the following directive:
+
+ \badcode
+ #include <QtShaderTools>
+ \endcode
+
+ To link against the module, add this line to your \l qmake \c .pro file:
+
+ \badcode
+ QT += shadertools
+ \endcode
+*/
diff --git a/src/shadertools/doc/src/qtshadertools-index.qdoc b/src/shadertools/doc/src/qtshadertools-index.qdoc
new file mode 100644
index 0000000..4e0d8aa
--- /dev/null
+++ b/src/shadertools/doc/src/qtshadertools-index.qdoc
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+\title Qt Shader Tools
+\page qtshadertools-index.html
+
+\section1 Introduction
+
+The Qt Shader Tools module builds on the SPIR-V Open Source Ecosystem as
+described at \l{https://www.khronos.org/spir/}{the Khronos SPIR-V web
+site}. For compiling into SPIR-V
+\l{https://github.com/KhronosGroup/glslang}{glslang} is used, while
+translating and reflecting is done via
+\l{https://github.com/KhronosGroup/SPIRV-Cross}{SPIRV-Cross}.
+
+In order to allow shader code to be written once in Qt applications and
+libraries, all shaders are expected to be written in a single language
+which is then compiled into SPIR-V. Versions for various shading language
+are then generated from that, together with reflection information (inputs,
+outputs, shader resources). This is then packed into easily and efficiently
+serializable QRhiShader instances. The Qt Rendering Hardware Interface
+consumes QRhiShader instances directly.
+
+The two main components are:
+
+\list
+\li QShaderBaker and the \c qsb command-line tool, and
+\li QRhiShader (part of the Qt Rhi module)
+\endlist
+
+\section1 Table of Contents
+
+\list
+ \li \l {Qt Shader Tools C++ Classes}
+ \li \l {Copyright Notices}
+\endlist
+
+*/
diff --git a/src/shadertools/qshaderbaker.cpp b/src/shadertools/qshaderbaker.cpp
new file mode 100644
index 0000000..fbb0a2b
--- /dev/null
+++ b/src/shadertools/qshaderbaker.cpp
@@ -0,0 +1,421 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qshaderbaker.h"
+#include "qspirvcompiler_p.h"
+#include "qspirvshader_p.h"
+#include <QFileInfo>
+#include <QFile>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QShaderBaker
+ \inmodule QtShaderTools
+
+ \brief Compiles a GLSL/Vulkan shader into SPIR-V, translates into other
+ shading languages, and gathers reflection metadata.
+
+ QShaderBaker takes a graphics (vertex, fragment, etc.) or compute shader,
+ and produces multiple - either source or bytecode - variants of it,
+ together with reflection information. The results are represented by a
+ QRhiShader instance, which also provides simple and fast serialization
+ and deserialization.
+
+ \note Applications and libraries are recommended to avoid using this class
+ directly. Rather, all Qt users are encouraged to rely on offline
+ compilation by invoking the \c qsb command-line tool at build time. This
+ tool uses QShaderBaker itself and writes the serialized version of the
+ generated QRhiShader into a file. The usage of this class should be
+ restricted to cases where run time compilation cannot be avoided, such as
+ when working with user-provided shader source strings.
+
+ The input format is always assumed to be Vulkan-flavored GLSL at the
+ moment. See the
+ \l{https://github.com/KhronosGroup/GLSL/blob/master/extensions/khr/GL_KHR_vulkan_glsl.txt}{GL_KHR_vulkan_glsl
+ specification} for an overview, keeping in mind that the Qt Shader Tools
+ module is meant to be used in combination with the QRhi classes from Qt
+ Rendering Hardware Interface module, and therefore a number of concepts and
+ constructs (push constants, storage buffers, subpasses, etc.) are not
+ applicable at the moment. Additional options may be introduced in the
+ future, for example, by enabling
+ \l{https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl}{HLSL}
+ as a source format, once HLSL to SPIR-V compilation is deemed suitable.
+
+ The reflection metadata is retrievable from the resulting QRhiShader by
+ calling QRhiShader::description(). This is essential when having to
+ discover what set of vertex inputs and shader resources a shader expects,
+ and what the layouts of those are, as many modern graphics APIs offer no
+ built-in shader reflection capabilities.
+
+ \section2 Typical Workflow
+
+ Let's assume an application has a vertex and fragment shader like the following:
+
+ Vertex shader:
+ \snippet color.vert 0
+
+ Fragment shader:
+ \snippet color.frag 0
+
+ To get QRhiShader instances that can be passed as-is to a
+ QRhiGraphicsPipeline, there are two options: doing the shader pack
+ generation off line, or at run time.
+
+ The former involves running the \c qsb tool:
+
+ \badcode
+ qsb --glsl "100 es,120" --hlsl 50 --msl 12 color.vert -o color.vert.qsb
+ qsb --glsl "100 es,120" --hlsl 50 --msl 12 color.frag -o color.frag.qsb
+ \endcode
+
+ The example uses the translation targets as appropriate for QRhi. This
+ means GLSL/ES 100, GLSL 120, HLSL Shader Model 5.0, and Metal Shading
+ Language 1.2.
+
+ Note how the command line options correspond to what can be specified via
+ setGeneratedShaders(). Once the resulting files are available, they can be
+ shipped with the application (typically embedded into the executable the
+ the Qt Resource System), and can be loaded and passed to
+ QRhiShader::fromSerialized() at run time.
+
+ While not shown here, \c qsb can do more: it is also able to invoke \c fxc
+ on Windows or the appropriate XCode tools on macOS to compile the generated
+ HLSL or Metal shader code into bytecode and include the compiled versions
+ in the QRhiShader. After a baked shader pack is written into a file, its
+ contents can be examined by running \c{qsb -d} on it. Run \c qsb with
+ \c{--help} for more information.
+
+ The alternative approach is to perform the same at run time. This involves
+ creating a QShaderBaker instance, calling setSourceFileName(), and then
+ setting up the translation targets via setGeneratedShaders():
+
+ \badcode
+ baker.setGeneratedShaderVariants({ QRhiShaderKey::StandardShader });
+ QVector<QShaderBaker::GeneratedShader> targets;
+ targets.append({ QRhiShaderKey::SpirvShader, QRhiShaderVersion(100) });
+ targets.append({ QRhiShaderKey::GlslShader, QRhiShaderVersion(100, QRhiShaderVersion::GlslEs) });
+ targets.append({ QRhiShaderKey::SpirvShader, QRhiShaderVersion(120) });
+ targets.append({ QRhiShaderKey::HlslShader, QRhiShaderVersion(50) });
+ targets.append({ QRhiShaderKey::MslShader, QRhiShaderVersion(12) });
+ baker.setGeneratedShaders(targets);
+ QRhiShader shaders = baker.bake();
+ if (!shaders.isValid())
+ qWarning() << baker.errorMessage();
+ \endcode
+
+ \sa QRhiShader
+ */
+
+struct QShaderBakerPrivate
+{
+ bool readFile(const QString &fn);
+
+ QString sourceFileName;
+ QByteArray source;
+ QRhiShader::ShaderStage stage;
+ QVector<QShaderBaker::GeneratedShader> reqVersions;
+ QVector<QRhiShaderKey::ShaderVariant> variants;
+ QSpirvCompiler compiler;
+ QString errorMessage;
+};
+
+bool QShaderBakerPrivate::readFile(const QString &fn)
+{
+ QFile f(fn);
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qWarning("QShaderBaker: Failed to open %s", qPrintable(fn));
+ return false;
+ }
+ source = f.readAll();
+ sourceFileName = fn;
+ return true;
+}
+
+/*!
+ Constructs a new QShaderBaker.
+ */
+QShaderBaker::QShaderBaker()
+ : d(new QShaderBakerPrivate)
+{
+}
+
+/*!
+ Destructor.
+ */
+QShaderBaker::~QShaderBaker()
+{
+ delete d;
+}
+
+/*!
+ Sets the name of the shader source file to \a fileName. This is the file
+ that will be read when calling bake(). The shader stage is deduced
+ automatically from the file extension. When this is not desired or not
+ possible, use the overload with the stage argument instead.
+
+ The supported file extensions are:
+ \list
+ \li \c{.vert} - vertex shader
+ \li \c{.frag} - fragment (pixel) shader
+ \li \c{.tesc} - tessellation control (hull) shader
+ \li \c{.tese} - tessellation evaluation (domain) shader
+ \li \c{.geom} - geometry shader
+ \li \c{.comp} - compute shader
+ \endlist
+ */
+void QShaderBaker::setSourceFileName(const QString &fileName)
+{
+ if (!d->readFile(fileName))
+ return;
+
+ const QString suffix = QFileInfo(fileName).suffix();
+ if (suffix == QStringLiteral("vert")) {
+ d->stage = QRhiShader::VertexStage;
+ } else if (suffix == QStringLiteral("frag")) {
+ d->stage = QRhiShader::FragmentStage;
+ } else if (suffix == QStringLiteral("tesc")) {
+ d->stage = QRhiShader::TessControlStage;
+ } else if (suffix == QStringLiteral("tese")) {
+ d->stage = QRhiShader::TessEvaluationStage;
+ } else if (suffix == QStringLiteral("geom")) {
+ d->stage = QRhiShader::GeometryStage;
+ } else if (suffix == QStringLiteral("comp")) {
+ d->stage = QRhiShader::ComputeStage;
+ } else {
+ qWarning("QShaderBaker: Unknown shader stage, defaulting to vertex");
+ d->stage = QRhiShader::VertexStage;
+ }
+}
+
+/*!
+ Sets the name of the shader source file to \a fileName. This is the file
+ that will be read when calling bake(). The shader stage is specified by \a
+ stage.
+ */
+void QShaderBaker::setSourceFileName(const QString &fileName, QRhiShader::ShaderStage stage)
+{
+ if (d->readFile(fileName))
+ d->stage = stage;
+}
+
+/*!
+ Sets the source \a device. This allows using any QIODevice instead of just
+ files. \a stage specifies the shader stage, while the optional \a fileName
+ contains a filename that is used in the error messages.
+ */
+void QShaderBaker::setSourceDevice(QIODevice *device, QRhiShader::ShaderStage stage, const QString &fileName)
+{
+ setSourceString(device->readAll(), stage, fileName);
+}
+
+/*!
+ Sets the input shader \a sourceString. \a stage specified the shader stage,
+ while the optional \a fileName contains a filename that is used in the
+ error messages.
+ */
+void QShaderBaker::setSourceString(const QByteArray &sourceString, QRhiShader::ShaderStage stage, const QString &fileName)
+{
+ d->sourceFileName = fileName; // for error messages, include handling, etc.
+ d->source = sourceString;
+ d->stage = stage;
+}
+
+/*!
+ \typedef QShaderBaker::GeneratedShader
+
+ Synonym for QPair<QRhiShaderKey::ShaderSource, QRhiShaderVersion>.
+*/
+
+/*!
+ Specifies what kind of shaders to compile or translate to. Nothing is
+ generated by default so calling this function before bake() is mandatory
+
+ \note when this function is not called or \a v is empty or contains only invalid
+ entries, the resulting QRhiShader will be empty and thus invalid.
+
+ For example, the minimal possible baking target is SPIR-V, without any
+ additional translations to other languages. To request this, do:
+
+ \badcode
+ baker.setGeneratedShaders({ QRhiShaderKey::SpirvShader, QRhiShaderVersion(100) });
+ \endcode
+ */
+void QShaderBaker::setGeneratedShaders(const QVector<GeneratedShader> &v)
+{
+ d->reqVersions = v;
+}
+
+/*!
+ Specifies which shader variants are genetated. Each shader version can have
+ multiple variants in the resulting QRhiShader.
+
+ In most cases \a v contains a single entry, QRhiShaderKey::StandardShader.
+
+ \note when no variants are set, the resulting QRhiShader will be empty and
+ thus invalid.
+ */
+void QShaderBaker::setGeneratedShaderVariants(const QVector<QRhiShaderKey::ShaderVariant> &v)
+{
+ d->variants = v;
+}
+
+/*!
+ Runs the compilation and translation process.
+
+ \return a QRhiShader instance. To check if the process was successful,
+ call QRhiShader::isValid(). When that indicates \c false, call
+ errorMessage() to retrieve the log.
+
+ This is an expensive operation. When calling this from applications, it can
+ be advisable to do it on a separate thread.
+
+ \note QShaderBaker instances are reusable: after calling bake(), the same
+ instance can be used with different inputs again. However, a QShaderBaker
+ instance should only be used on one single thread during its lifetime.
+ */
+QRhiShader QShaderBaker::bake()
+{
+ d->errorMessage.clear();
+
+ if (d->source.isEmpty()) {
+ d->errorMessage = QLatin1String("QShaderBaker: No source specified");
+ return QRhiShader();
+ }
+
+ d->compiler.setSourceString(d->source, d->stage, d->sourceFileName);
+ d->compiler.setFlags(0);
+ QByteArray spirv = d->compiler.compileToSpirv();
+ if (spirv.isEmpty()) {
+ d->errorMessage = d->compiler.errorMessage();
+ return QRhiShader();
+ }
+
+ QByteArray batchableSpirv;
+ if (d->stage == QRhiShader::VertexStage && d->variants.contains(QRhiShaderKey::BatchableVertexShader)) {
+ d->compiler.setFlags(QSpirvCompiler::RewriteToMakeBatchableForSG);
+ batchableSpirv = d->compiler.compileToSpirv();
+ if (batchableSpirv.isEmpty()) {
+ d->errorMessage = d->compiler.errorMessage();
+ return QRhiShader();
+ }
+ }
+
+ QRhiShader bs;
+ bs.setStage(d->stage);
+
+ QSpirvShader spirvShader;
+ spirvShader.setSpirvBinary(spirv);
+
+ QSpirvShader batchableSpirvShader;
+ if (!batchableSpirv.isEmpty()) {
+ batchableSpirvShader.setSpirvBinary(batchableSpirv);
+ bs.setDescription(batchableSpirvShader.shaderDescription());
+ } else {
+ bs.setDescription(spirvShader.shaderDescription());
+ }
+
+ for (const GeneratedShader &req: d->reqVersions) {
+ for (const QRhiShaderKey::ShaderVariant &v : d->variants) {
+ QByteArray *currentSpirv = &spirv;
+ QSpirvShader *currentSpirvShader = &spirvShader;
+ if (v == QRhiShaderKey::BatchableVertexShader) {
+ if (!batchableSpirv.isEmpty()) {
+ currentSpirv = &batchableSpirv;
+ currentSpirvShader = &batchableSpirvShader;
+ } else {
+ continue;
+ }
+ }
+ const QRhiShaderKey key(req.first, req.second, v);
+ QRhiShaderCode shader;
+ shader.setEntryPoint(QByteArrayLiteral("main"));
+ switch (req.first) {
+ case QRhiShaderKey::SpirvShader:
+ shader.setShader(*currentSpirv);
+ break;
+ case QRhiShaderKey::GlslShader:
+ {
+ QSpirvShader::GlslFlags flags = 0;
+ if (req.second.flags().testFlag(QRhiShaderVersion::GlslEs))
+ flags |= QSpirvShader::GlslEs;
+ shader.setShader(currentSpirvShader->translateToGLSL(req.second.version(), flags));
+ if (shader.shader().isEmpty()) {
+ d->errorMessage = currentSpirvShader->translationErrorMessage();
+ return QRhiShader();
+ }
+ }
+ break;
+ case QRhiShaderKey::HlslShader:
+ shader.setShader(currentSpirvShader->translateToHLSL(req.second.version()));
+ if (shader.shader().isEmpty()) {
+ d->errorMessage = currentSpirvShader->translationErrorMessage();
+ return QRhiShader();
+ }
+ break;
+ case QRhiShaderKey::MslShader:
+ shader.setShader(currentSpirvShader->translateToMSL(req.second.version()));
+ if (shader.shader().isEmpty()) {
+ d->errorMessage = currentSpirvShader->translationErrorMessage();
+ return QRhiShader();
+ }
+ shader.setEntryPoint(QByteArrayLiteral("main0"));
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ bs.setShader(key, shader);
+ }
+ }
+
+ return bs;
+}
+
+/*!
+ \return the error message from the last bake() run, or an empty string if
+ there was no error.
+
+ \note Errors include file read errors, compilation, and translation
+ failures. Not requesting any targets or variants does not count as an error
+ even though the resulting QRhiShader is invalid.
+ */
+QString QShaderBaker::errorMessage() const
+{
+ return d->errorMessage;
+}
+
+QT_END_NAMESPACE
diff --git a/src/shadertools/qshaderbaker.h b/src/shadertools/qshaderbaker.h
new file mode 100644
index 0000000..de6159b
--- /dev/null
+++ b/src/shadertools/qshaderbaker.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSHADERBAKER_H
+#define QSHADERBAKER_H
+
+#include <QtShaderTools/qtshadertoolsglobal.h>
+#include <QtGui/qrhishader.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QShaderBakerPrivate;
+class QIODevice;
+
+class Q_SHADERTOOLS_EXPORT QShaderBaker
+{
+public:
+ QShaderBaker();
+ ~QShaderBaker();
+
+ void setSourceFileName(const QString &fileName);
+ void setSourceFileName(const QString &fileName, QRhiShader::ShaderStage stage);
+
+ void setSourceDevice(QIODevice *device, QRhiShader::ShaderStage stage,
+ const QString &fileName = QString());
+
+ void setSourceString(const QByteArray &sourceString, QRhiShader::ShaderStage stage,
+ const QString &fileName = QString());
+
+ typedef QPair<QRhiShaderKey::ShaderSource, QRhiShaderVersion> GeneratedShader;
+ void setGeneratedShaders(const QVector<GeneratedShader> &v);
+ void setGeneratedShaderVariants(const QVector<QRhiShaderKey::ShaderVariant> &v);
+
+ QRhiShader bake();
+
+ QString errorMessage() const;
+
+private:
+ Q_DISABLE_COPY(QShaderBaker)
+ QShaderBakerPrivate *d = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/shadertools/qshaderbatchablerewriter.cpp b/src/shadertools/qshaderbatchablerewriter.cpp
new file mode 100644
index 0000000..72b369f
--- /dev/null
+++ b/src/shadertools/qshaderbatchablerewriter.cpp
@@ -0,0 +1,223 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qshaderbatchablerewriter_p.h"
+
+// This is a slightly modified version of qsgshaderrewriter.cpp from
+// qtdeclarative/src/quick/scenegraph/coreapi. Here we insert an extra vertex
+// attribute (_qt_order) at location 7.
+
+QT_BEGIN_NAMESPACE
+
+namespace QShaderBatchableRewriter {
+
+struct Tokenizer {
+
+ enum Token {
+ Token_Void,
+ Token_OpenBrace,
+ Token_CloseBrace,
+ Token_SemiColon,
+ Token_Identifier,
+ Token_Macro,
+ Token_Unspecified,
+
+ Token_EOF
+ };
+
+ static const char *NAMES[];
+
+ void initialize(const QByteArray &input);
+ Token next();
+
+ const char *stream;
+ const char *pos;
+ const char *identifier;
+};
+
+const char *Tokenizer::NAMES[] = {
+ "Void",
+ "OpenBrace",
+ "CloseBrace",
+ "SemiColon",
+ "Identifier",
+ "Macro",
+ "Unspecified",
+ "EOF"
+};
+
+void Tokenizer::initialize(const QByteArray &input)
+{
+ stream = input.constData();
+ pos = input;
+ identifier = input;
+}
+
+Tokenizer::Token Tokenizer::next()
+{
+ while (*pos != 0) {
+ char c = *pos++;
+ switch (c) {
+ case '/':
+
+ if (*pos == '/') {
+ // '//' comment
+ ++pos;
+ while (*pos != 0 && *pos != '\n') ++pos;
+ if (*pos != 0) ++pos; // skip the newline
+
+ } else if (*pos == '*') {
+ // /* */ comment
+ ++pos;
+ while (*pos != 0 && *pos != '*' && pos[1] != '/') ++pos;
+ if (*pos != 0) pos += 2;
+ }
+ break;
+
+ case '#': {
+ while (*pos != 0) {
+ if (*pos == '\n') {
+ ++pos;
+ break;
+ } else if (*pos == '\\') {
+ ++pos;
+ while (*pos != 0 && (*pos == ' ' || *pos == '\t'))
+ ++pos;
+ if (*pos != 0 && (*pos == '\n' || (*pos == '\r' && pos[1] == '\n')))
+ pos+=2;
+ } else {
+ ++pos;
+ }
+ }
+ break;
+ }
+
+ case 'v': {
+ if (*pos == 'o' && pos[1] == 'i' && pos[2] == 'd') {
+ pos += 3;
+ return Token_Void;
+ }
+ Q_FALLTHROUGH();
+ }
+
+ case ';': return Token_SemiColon;
+ case 0: return Token_EOF;
+ case '{': return Token_OpenBrace;
+ case '}': return Token_CloseBrace;
+
+ case ' ':
+ case '\n':
+ case '\r': break;
+ default:
+ // Identifier...
+ if ((c >= 'a' && c <= 'z' ) || (c >= 'A' && c <= 'Z' ) || c == '_') {
+ identifier = pos - 1;
+ while (*pos != 0 && ((*pos >= 'a' && *pos <= 'z')
+ || (*pos >= 'A' && *pos <= 'Z')
+ || *pos == '_'
+ || (*pos >= '0' && *pos <= '9'))) {
+ ++pos;
+ }
+ return Token_Identifier;
+ } else {
+ return Token_Unspecified;
+ }
+ }
+ }
+
+ return Token_EOF;
+}
+
+QByteArray addZAdjustment(const QByteArray &input)
+{
+ Tokenizer tok;
+ tok.initialize(input);
+
+ Tokenizer::Token lt = tok.next();
+ Tokenizer::Token t = tok.next();
+
+ // First find "void main() { ... "
+ const char* voidPos = input.constData();
+ while (t != Tokenizer::Token_EOF) {
+ if (lt == Tokenizer::Token_Void && t == Tokenizer::Token_Identifier) {
+ if (qstrncmp("main", tok.identifier, 4) == 0)
+ break;
+ }
+ voidPos = tok.pos - 4;
+ lt = t;
+ t = tok.next();
+ }
+
+ QByteArray result;
+ result.reserve(1024);
+ result += QByteArray::fromRawData(input.constData(), voidPos - input.constData());
+
+ result += QByteArrayLiteral("layout(location = 7) in float _qt_order;\n");
+
+ // Find first brace '{'
+ while (t != Tokenizer::Token_EOF && t != Tokenizer::Token_OpenBrace) t = tok.next();
+ int braceDepth = 1;
+ t = tok.next();
+
+ // Find matching brace and insert our code there...
+ while (t != Tokenizer::Token_EOF) {
+ switch (t) {
+ case Tokenizer::Token_CloseBrace:
+ braceDepth--;
+ if (braceDepth == 0) {
+ result += QByteArray::fromRawData(voidPos, tok.pos - 1 - voidPos);
+ result += QByteArrayLiteral(" gl_Position.z = _qt_order * gl_Position.w;\n");
+ result += QByteArray(tok.pos - 1);
+ return result;
+ }
+ break;
+ case Tokenizer::Token_OpenBrace:
+ ++braceDepth;
+ break;
+ default:
+ break;
+ }
+ t = tok.next();
+ }
+ return QByteArray();
+}
+
+} // namespace
+
+QT_END_NAMESPACE
diff --git a/src/shadertools/qshaderbatchablerewriter_p.h b/src/shadertools/qshaderbatchablerewriter_p.h
new file mode 100644
index 0000000..3ee8eca
--- /dev/null
+++ b/src/shadertools/qshaderbatchablerewriter_p.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSHADERBATCHABLEREWRITER_P_H
+#define QSHADERBATCHABLEREWRITER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/QByteArray>
+
+QT_BEGIN_NAMESPACE
+
+namespace QShaderBatchableRewriter {
+QByteArray addZAdjustment(const QByteArray &input);
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/shadertools/qspirvcompiler.cpp b/src/shadertools/qspirvcompiler.cpp
new file mode 100644
index 0000000..22547e9
--- /dev/null
+++ b/src/shadertools/qspirvcompiler.cpp
@@ -0,0 +1,390 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qspirvcompiler_p.h"
+#include "qshaderbatchablerewriter_p.h"
+#include <QFile>
+#include <QFileInfo>
+
+#include <glslang/Public/ShaderLang.h>
+#include <SPIRV/GlslangToSpv.h>
+
+QT_BEGIN_NAMESPACE
+
+static const TBuiltInResource resourceLimits =
+{
+ /* .MaxLights = */ 32,
+ /* .MaxClipPlanes = */ 6,
+ /* .MaxTextureUnits = */ 32,
+ /* .MaxTextureCoords = */ 32,
+ /* .MaxVertexAttribs = */ 64,
+ /* .MaxVertexUniformComponents = */ 4096,
+ /* .MaxVaryingFloats = */ 64,
+ /* .MaxVertexTextureImageUnits = */ 32,
+ /* .MaxCombinedTextureImageUnits = */ 80,
+ /* .MaxTextureImageUnits = */ 32,
+ /* .MaxFragmentUniformComponents = */ 4096,
+ /* .MaxDrawBuffers = */ 32,
+ /* .MaxVertexUniformVectors = */ 128,
+ /* .MaxVaryingVectors = */ 8,
+ /* .MaxFragmentUniformVectors = */ 16,
+ /* .MaxVertexOutputVectors = */ 16,
+ /* .MaxFragmentInputVectors = */ 15,
+ /* .MinProgramTexelOffset = */ -8,
+ /* .MaxProgramTexelOffset = */ 7,
+ /* .MaxClipDistances = */ 8,
+ /* .MaxComputeWorkGroupCountX = */ 65535,
+ /* .MaxComputeWorkGroupCountY = */ 65535,
+ /* .MaxComputeWorkGroupCountZ = */ 65535,
+ /* .MaxComputeWorkGroupSizeX = */ 1024,
+ /* .MaxComputeWorkGroupSizeY = */ 1024,
+ /* .MaxComputeWorkGroupSizeZ = */ 64,
+ /* .MaxComputeUniformComponents = */ 1024,
+ /* .MaxComputeTextureImageUnits = */ 16,
+ /* .MaxComputeImageUniforms = */ 8,
+ /* .MaxComputeAtomicCounters = */ 8,
+ /* .MaxComputeAtomicCounterBuffers = */ 1,
+ /* .MaxVaryingComponents = */ 60,
+ /* .MaxVertexOutputComponents = */ 64,
+ /* .MaxGeometryInputComponents = */ 64,
+ /* .MaxGeometryOutputComponents = */ 128,
+ /* .MaxFragmentInputComponents = */ 128,
+ /* .MaxImageUnits = */ 8,
+ /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 8,
+ /* .MaxCombinedShaderOutputResources = */ 8,
+ /* .MaxImageSamples = */ 0,
+ /* .MaxVertexImageUniforms = */ 0,
+ /* .MaxTessControlImageUniforms = */ 0,
+ /* .MaxTessEvaluationImageUniforms = */ 0,
+ /* .MaxGeometryImageUniforms = */ 0,
+ /* .MaxFragmentImageUniforms = */ 8,
+ /* .MaxCombinedImageUniforms = */ 8,
+ /* .MaxGeometryTextureImageUnits = */ 16,
+ /* .MaxGeometryOutputVertices = */ 256,
+ /* .MaxGeometryTotalOutputComponents = */ 1024,
+ /* .MaxGeometryUniformComponents = */ 1024,
+ /* .MaxGeometryVaryingComponents = */ 64,
+ /* .MaxTessControlInputComponents = */ 128,
+ /* .MaxTessControlOutputComponents = */ 128,
+ /* .MaxTessControlTextureImageUnits = */ 16,
+ /* .MaxTessControlUniformComponents = */ 1024,
+ /* .MaxTessControlTotalOutputComponents = */ 4096,
+ /* .MaxTessEvaluationInputComponents = */ 128,
+ /* .MaxTessEvaluationOutputComponents = */ 128,
+ /* .MaxTessEvaluationTextureImageUnits = */ 16,
+ /* .MaxTessEvaluationUniformComponents = */ 1024,
+ /* .MaxTessPatchComponents = */ 120,
+ /* .MaxPatchVertices = */ 32,
+ /* .MaxTessGenLevel = */ 64,
+ /* .MaxViewports = */ 16,
+ /* .MaxVertexAtomicCounters = */ 0,
+ /* .MaxTessControlAtomicCounters = */ 0,
+ /* .MaxTessEvaluationAtomicCounters = */ 0,
+ /* .MaxGeometryAtomicCounters = */ 0,
+ /* .MaxFragmentAtomicCounters = */ 8,
+ /* .MaxCombinedAtomicCounters = */ 8,
+ /* .MaxAtomicCounterBindings = */ 1,
+ /* .MaxVertexAtomicCounterBuffers = */ 0,
+ /* .MaxTessControlAtomicCounterBuffers = */ 0,
+ /* .MaxTessEvaluationAtomicCounterBuffers = */ 0,
+ /* .MaxGeometryAtomicCounterBuffers = */ 0,
+ /* .MaxFragmentAtomicCounterBuffers = */ 1,
+ /* .MaxCombinedAtomicCounterBuffers = */ 1,
+ /* .MaxAtomicCounterBufferSize = */ 16384,
+ /* .MaxTransformFeedbackBuffers = */ 4,
+ /* .MaxTransformFeedbackInterleavedComponents = */ 64,
+ /* .MaxCullDistances = */ 8,
+ /* .MaxCombinedClipAndCullDistances = */ 8,
+ /* .MaxSamples = */ 4,
+ /* .maxMeshOutputVerticesNV = */ 256,
+ /* .maxMeshOutputPrimitivesNV = */ 512,
+ /* .maxMeshWorkGroupSizeX_NV = */ 32,
+ /* .maxMeshWorkGroupSizeY_NV = */ 1,
+ /* .maxMeshWorkGroupSizeZ_NV = */ 1,
+ /* .maxTaskWorkGroupSizeX_NV = */ 32,
+ /* .maxTaskWorkGroupSizeY_NV = */ 1,
+ /* .maxTaskWorkGroupSizeZ_NV = */ 1,
+ /* .maxMeshViewCountNV = */ 4,
+
+ /* .limits = */ {
+ /* .nonInductiveForLoops = */ 1,
+ /* .whileLoops = */ 1,
+ /* .doWhileLoops = */ 1,
+ /* .generalUniformIndexing = */ 1,
+ /* .generalAttributeMatrixVectorIndexing = */ 1,
+ /* .generalVaryingIndexing = */ 1,
+ /* .generalSamplerIndexing = */ 1,
+ /* .generalVariableIndexing = */ 1,
+ /* .generalConstantMatrixVectorIndexing = */ 1,
+ }
+};
+
+struct QSpirvCompilerPrivate
+{
+ bool readFile(const QString &fn);
+ bool compile();
+
+ QString sourceFileName;
+ QByteArray source;
+ QByteArray batchableSource;
+ EShLanguage stage = EShLangVertex;
+ QSpirvCompiler::Flags flags = 0;
+ QByteArray spirv;
+ QString log;
+};
+
+bool QSpirvCompilerPrivate::readFile(const QString &fn)
+{
+ QFile f(fn);
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qWarning("QSpirvCompiler: Failed to open %s", qPrintable(fn));
+ return false;
+ }
+ source = f.readAll();
+ batchableSource.clear();
+ sourceFileName = fn;
+ f.close();
+ return true;
+}
+
+class Includer : public glslang::TShader::Includer
+{
+public:
+ IncludeResult *includeLocal(const char *headerName,
+ const char *includerName,
+ size_t inclusionDepth) override
+ {
+ Q_UNUSED(inclusionDepth);
+ return readFile(headerName, includerName);
+ }
+
+ IncludeResult *includeSystem(const char *headerName,
+ const char *includerName,
+ size_t inclusionDepth) override
+ {
+ Q_UNUSED(inclusionDepth);
+ return readFile(headerName, includerName);
+ }
+
+ void releaseInclude(IncludeResult *result) override
+ {
+ if (result) {
+ delete static_cast<QByteArray *>(result->userData);
+ delete result;
+ }
+ }
+
+private:
+ IncludeResult *readFile(const char *headerName, const char *includerName);
+};
+
+glslang::TShader::Includer::IncludeResult *Includer::readFile(const char *headerName, const char *includerName)
+{
+ // Just treat the included name as relative to the includer:
+ // Take the path from the includer, append the included name, remove redundancies.
+ // This should work also for qrc (source filenames with qrc:/ or :/ prefix).
+
+ QString includer = QString::fromUtf8(includerName);
+ if (includer.isEmpty())
+ includer = QLatin1String(".");
+ QString included = QFileInfo(includer).canonicalPath() + QLatin1Char('/') + QString::fromUtf8(headerName);
+ included = QFileInfo(included).canonicalFilePath();
+ if (included.isEmpty()) {
+ qWarning("QSpirvCompiler: Failed to find include file %s", headerName);
+ return nullptr;
+ }
+ QFile f(included);
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qWarning("QSpirvCompiler: Failed to read include file %s", qPrintable(included));
+ return nullptr;
+ }
+
+ QByteArray *data = new QByteArray;
+ *data = f.readAll();
+ return new IncludeResult(included.toStdString(), data->constData(), data->size(), data);
+}
+
+class GlobalInit
+{
+public:
+ GlobalInit() { glslang::InitializeProcess(); }
+ ~GlobalInit() { glslang::FinalizeProcess(); }
+};
+
+bool QSpirvCompilerPrivate::compile()
+{
+ log.clear();
+
+ const bool useBatchable = (stage == EShLangVertex && flags.testFlag(QSpirvCompiler::RewriteToMakeBatchableForSG));
+ const QByteArray *actualSource = useBatchable ? &batchableSource : &source;
+ if (actualSource->isEmpty())
+ return false;
+
+ static GlobalInit globalInit;
+
+ glslang::TShader shader(stage);
+ const QByteArray fn = sourceFileName.toUtf8();
+ const char *fnStr = fn.constData();
+ const char *srcStr = actualSource->constData();
+ const int size = actualSource->size();
+ shader.setStringsWithLengthsAndNames(&srcStr, &size, &fnStr, 1);
+
+ shader.setEnvInput(glslang::EShSourceGlsl, stage, glslang::EShClientVulkan, 100);
+ shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_0);
+ shader.setEnvTarget(glslang::EshTargetSpv, glslang::EShTargetSpv_1_0);
+
+ Includer includer;
+ if (!shader.parse(&resourceLimits, 100, false, EShMsgDefault, includer)) {
+ qWarning("QSpirvCompiler: Failed to parse shader");
+ log = QString::fromUtf8(shader.getInfoLog()).trimmed();
+ return false;
+ }
+
+ glslang::TProgram program;
+ program.addShader(&shader);
+ if (!program.link(EShMsgDefault)) {
+ qWarning("QSpirvCompiler: Link failed");
+ log = QString::fromUtf8(shader.getInfoLog()).trimmed();
+ return false;
+ }
+
+ std::vector<unsigned int> spv;
+ glslang::GlslangToSpv(*program.getIntermediate(stage), spv);
+ if (!spv.size()) {
+ qWarning("Failed to generate SPIR-V");
+ return false;
+ }
+
+ spirv.resize(int(spv.size() * 4));
+ memcpy(spirv.data(), spv.data(), spirv.size());
+
+ return true;
+}
+
+QSpirvCompiler::QSpirvCompiler()
+ : d(new QSpirvCompilerPrivate)
+{
+}
+
+QSpirvCompiler::~QSpirvCompiler()
+{
+ delete d;
+}
+
+void QSpirvCompiler::setSourceFileName(const QString &fileName)
+{
+ if (!d->readFile(fileName))
+ return;
+
+ const QString suffix = QFileInfo(fileName).suffix();
+ if (suffix == QStringLiteral("vert")) {
+ d->stage = EShLangVertex;
+ } else if (suffix == QStringLiteral("frag")) {
+ d->stage = EShLangFragment;
+ } else if (suffix == QStringLiteral("tesc")) {
+ d->stage = EShLangTessControl;
+ } else if (suffix == QStringLiteral("tese")) {
+ d->stage = EShLangTessEvaluation;
+ } else if (suffix == QStringLiteral("geom")) {
+ d->stage = EShLangGeometry;
+ } else if (suffix == QStringLiteral("comp")) {
+ d->stage = EShLangCompute;
+ } else {
+ qWarning("QSpirvCompiler: Unknown shader stage, defaulting to vertex");
+ d->stage = EShLangVertex;
+ }
+}
+
+static inline EShLanguage mapShaderStage(QRhiShader::ShaderStage stage)
+{
+ switch (stage) {
+ case QRhiShader::VertexStage:
+ return EShLangVertex;
+ case QRhiShader::TessControlStage:
+ return EShLangTessControl;
+ case QRhiShader::TessEvaluationStage:
+ return EShLangTessEvaluation;
+ case QRhiShader::GeometryStage:
+ return EShLangGeometry;
+ case QRhiShader::FragmentStage:
+ return EShLangFragment;
+ case QRhiShader::ComputeStage:
+ return EShLangCompute;
+ default:
+ return EShLangVertex;
+ }
+}
+
+void QSpirvCompiler::setSourceFileName(const QString &fileName, QRhiShader::ShaderStage stage)
+{
+ if (!d->readFile(fileName))
+ return;
+
+ d->stage = mapShaderStage(stage);
+}
+
+void QSpirvCompiler::setSourceDevice(QIODevice *device, QRhiShader::ShaderStage stage, const QString &fileName)
+{
+ setSourceString(device->readAll(), stage, fileName);
+}
+
+void QSpirvCompiler::setSourceString(const QByteArray &sourceString, QRhiShader::ShaderStage stage, const QString &fileName)
+{
+ d->sourceFileName = fileName; // for error messages, include handling, etc.
+ d->source = sourceString;
+ d->batchableSource.clear();
+ d->stage = mapShaderStage(stage);
+}
+
+void QSpirvCompiler::setFlags(Flags flags)
+{
+ d->flags = flags;
+}
+
+QByteArray QSpirvCompiler::compileToSpirv()
+{
+ if (d->stage == EShLangVertex && d->flags.testFlag(RewriteToMakeBatchableForSG) && d->batchableSource.isEmpty())
+ d->batchableSource = QShaderBatchableRewriter::addZAdjustment(d->source);
+
+ return d->compile() ? d->spirv : QByteArray();
+}
+
+QString QSpirvCompiler::errorMessage() const
+{
+ return d->log;
+}
+
+QT_END_NAMESPACE
diff --git a/src/shadertools/qspirvcompiler_p.h b/src/shadertools/qspirvcompiler_p.h
new file mode 100644
index 0000000..2af56b2
--- /dev/null
+++ b/src/shadertools/qspirvcompiler_p.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSPIRVCOMPILER_P_H
+#define QSPIRVCOMPILER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtShaderTools/private/qtshadertoolsglobal_p.h>
+#include <QtGui/qrhishader.h>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+
+struct QSpirvCompilerPrivate;
+class QIODevice;
+
+class Q_SHADERTOOLS_PRIVATE_EXPORT QSpirvCompiler
+{
+public:
+ QSpirvCompiler();
+ ~QSpirvCompiler();
+
+ enum Flag {
+ RewriteToMakeBatchableForSG = 0x01
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ void setSourceFileName(const QString &fileName);
+ void setSourceFileName(const QString &fileName, QRhiShader::ShaderStage stage);
+ void setSourceDevice(QIODevice *device, QRhiShader::ShaderStage stage, const QString &fileName = QString());
+ void setSourceString(const QByteArray &sourceString, QRhiShader::ShaderStage stage, const QString &fileName = QString());
+ void setFlags(Flags flags);
+
+ QByteArray compileToSpirv();
+ QString errorMessage() const;
+
+private:
+ Q_DISABLE_COPY(QSpirvCompiler)
+ QSpirvCompilerPrivate *d = nullptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSpirvCompiler::Flags)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/shadertools/qspirvshader.cpp b/src/shadertools/qspirvshader.cpp
new file mode 100644
index 0000000..7e0057e
--- /dev/null
+++ b/src/shadertools/qspirvshader.cpp
@@ -0,0 +1,468 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qspirvshader_p.h"
+#include <QtGui/private/qrhishaderdescription_p.h>
+#include <QFile>
+#include <QDebug>
+
+#include <SPIRV/SPVRemapper.h>
+
+#include <spirv_glsl.hpp>
+#include <spirv_hlsl.hpp>
+#include <spirv_msl.hpp>
+
+QT_BEGIN_NAMESPACE
+
+struct QSpirvShaderPrivate
+{
+ ~QSpirvShaderPrivate();
+
+ void createGLSLCompiler();
+ void processResources();
+
+ QRhiShaderDescription::InOutVariable inOutVar(const spirv_cross::Resource &r);
+ QRhiShaderDescription::BlockVariable blockVar(uint32_t typeId,
+ uint32_t memberIdx,
+ uint32_t memberTypeId);
+
+ void remapErrorHandler(const std::string &s);
+ void remapLogHandler(const std::string &s);
+
+ QByteArray ir;
+ QRhiShaderDescription shaderDescription;
+
+ spirv_cross::CompilerGLSL *glslGen = nullptr;
+ spirv_cross::CompilerHLSL *hlslGen = nullptr;
+ spirv_cross::CompilerMSL *mslGen = nullptr;
+
+ QString spirvCrossErrorMsg;
+ QString remapErrorMsg;
+};
+
+QSpirvShaderPrivate::~QSpirvShaderPrivate()
+{
+ delete mslGen;
+ delete hlslGen;
+ delete glslGen;
+}
+
+void QSpirvShaderPrivate::createGLSLCompiler()
+{
+ delete glslGen;
+ glslGen = new spirv_cross::CompilerGLSL(reinterpret_cast<const uint32_t *>(ir.constData()), ir.size() / 4);
+}
+
+static QRhiShaderDescription::VarType matVarType(const spirv_cross::SPIRType &t, QRhiShaderDescription::VarType compType)
+{
+ switch (t.columns) {
+ case 2:
+ return QRhiShaderDescription::VarType(compType + 4 + (t.vecsize == 3 ? 1 : t.vecsize == 4 ? 2 : 0));
+ case 3:
+ return QRhiShaderDescription::VarType(compType + 7 + (t.vecsize == 2 ? 1 : t.vecsize == 4 ? 2 : 0));
+ case 4:
+ return QRhiShaderDescription::VarType(compType + 10 + (t.vecsize == 2 ? 1 : t.vecsize == 3 ? 2 : 0));
+ default:
+ return QRhiShaderDescription::Unknown;
+ }
+}
+
+static QRhiShaderDescription::VarType vecVarType(const spirv_cross::SPIRType &t, QRhiShaderDescription::VarType compType)
+{
+ switch (t.vecsize) {
+ case 1:
+ return compType;
+ case 2:
+ return QRhiShaderDescription::VarType(compType + 1);
+ case 3:
+ return QRhiShaderDescription::VarType(compType + 2);
+ case 4:
+ return QRhiShaderDescription::VarType(compType + 3);
+ default:
+ return QRhiShaderDescription::Unknown;
+ }
+}
+
+static QRhiShaderDescription::VarType imageVarType(const spirv_cross::SPIRType &t)
+{
+ switch (t.image.dim) {
+ case spv::Dim1D:
+ return t.image.arrayed ? QRhiShaderDescription::Sampler1DArray : QRhiShaderDescription::Sampler1D;
+ case spv::Dim2D:
+ return t.image.arrayed
+ ? (t.image.ms ? QRhiShaderDescription::Sampler2DMSArray : QRhiShaderDescription::Sampler2DArray)
+ : (t.image.ms ? QRhiShaderDescription::Sampler2DMS : QRhiShaderDescription::Sampler2D);
+ case spv::Dim3D:
+ return t.image.arrayed ? QRhiShaderDescription::Sampler3DArray : QRhiShaderDescription::Sampler3D;
+ case spv::DimCube:
+ return t.image.arrayed ? QRhiShaderDescription::SamplerCubeArray : QRhiShaderDescription::SamplerCube;
+ default:
+ return QRhiShaderDescription::Unknown;
+ }
+}
+
+static QRhiShaderDescription::VarType varType(const spirv_cross::SPIRType &t)
+{
+ QRhiShaderDescription::VarType vt = QRhiShaderDescription::Unknown;
+ switch (t.basetype) {
+ case spirv_cross::SPIRType::Float:
+ vt = t.columns > 1 ? matVarType(t, QRhiShaderDescription::Float) : vecVarType(t, QRhiShaderDescription::Float);
+ break;
+ case spirv_cross::SPIRType::Double:
+ vt = t.columns > 1 ? matVarType(t, QRhiShaderDescription::Double) : vecVarType(t, QRhiShaderDescription::Double);
+ break;
+ case spirv_cross::SPIRType::UInt:
+ vt = vecVarType(t, QRhiShaderDescription::Uint);
+ break;
+ case spirv_cross::SPIRType::Int:
+ vt = vecVarType(t, QRhiShaderDescription::Int);
+ break;
+ case spirv_cross::SPIRType::Boolean:
+ vt = vecVarType(t, QRhiShaderDescription::Uint);
+ break;
+ case spirv_cross::SPIRType::SampledImage:
+ vt = imageVarType(t);
+ break;
+ case spirv_cross::SPIRType::Struct:
+ vt = QRhiShaderDescription::Struct;
+ break;
+ // ### separate image/sampler, atomic counter, ...
+ default:
+ break;
+ }
+ return vt;
+}
+
+QRhiShaderDescription::InOutVariable QSpirvShaderPrivate::inOutVar(const spirv_cross::Resource &r)
+{
+ QRhiShaderDescription::InOutVariable v;
+ v.name = QString::fromStdString(r.name);
+
+ const spirv_cross::SPIRType &t = glslGen->get_type(r.base_type_id);
+ v.type = varType(t);
+
+ if (glslGen->has_decoration(r.id, spv::DecorationLocation))
+ v.location = glslGen->get_decoration(r.id, spv::DecorationLocation);
+
+ if (glslGen->has_decoration(r.id, spv::DecorationBinding))
+ v.binding = glslGen->get_decoration(r.id, spv::DecorationBinding);
+
+ if (glslGen->has_decoration(r.id, spv::DecorationDescriptorSet))
+ v.descriptorSet = glslGen->get_decoration(r.id, spv::DecorationDescriptorSet);
+
+ return v;
+}
+
+QRhiShaderDescription::BlockVariable QSpirvShaderPrivate::blockVar(uint32_t typeId,
+ uint32_t memberIdx,
+ uint32_t memberTypeId)
+{
+ QRhiShaderDescription::BlockVariable v;
+ v.name = QString::fromStdString(glslGen->get_member_name(typeId, memberIdx));
+
+ const spirv_cross::SPIRType &memberType(glslGen->get_type(memberTypeId));
+ v.type = varType(memberType);
+
+ const spirv_cross::SPIRType &t = glslGen->get_type(typeId);
+ v.offset = glslGen->type_struct_member_offset(t, memberIdx);
+ v.size = int(glslGen->get_declared_struct_member_size(t, memberIdx));
+
+ for (uint32_t dimSize : memberType.array)
+ v.arrayDims.append(dimSize);
+
+ if (glslGen->has_member_decoration(typeId, memberIdx, spv::DecorationArrayStride))
+ v.arrayStride = glslGen->type_struct_member_array_stride(t, memberIdx);
+
+ if (glslGen->has_member_decoration(typeId, memberIdx, spv::DecorationMatrixStride))
+ v.matrixStride = glslGen->type_struct_member_matrix_stride(t, memberIdx);
+
+ if (glslGen->has_member_decoration(typeId, memberIdx, spv::DecorationRowMajor))
+ v.matrixIsRowMajor = true;
+
+ if (v.type == QRhiShaderDescription::Struct) {
+ uint32_t memberMemberIdx = 0;
+ for (uint32_t memberMemberType : memberType.member_types) {
+ v.structMembers.append(blockVar(memberType.self, memberMemberIdx, memberMemberType));
+ ++memberMemberIdx;
+ }
+ }
+
+ return v;
+}
+
+void QSpirvShaderPrivate::processResources()
+{
+ shaderDescription = QRhiShaderDescription();
+ QRhiShaderDescriptionPrivate *dd = QRhiShaderDescriptionPrivate::get(&shaderDescription);
+
+ spirv_cross::ShaderResources resources = glslGen->get_shader_resources();
+
+ /* ###
+ std::vector<Resource> uniform_buffers;
+ std::vector<Resource> storage_buffers;
+ std::vector<Resource> stage_inputs;
+ std::vector<Resource> stage_outputs;
+ std::vector<Resource> subpass_inputs;
+ std::vector<Resource> storage_images;
+ std::vector<Resource> sampled_images;
+ std::vector<Resource> atomic_counters;
+ std::vector<Resource> push_constant_buffers;
+ std::vector<Resource> separate_images;
+ std::vector<Resource> separate_samplers;
+ */
+
+ for (const spirv_cross::Resource &r : resources.stage_inputs) {
+ const QRhiShaderDescription::InOutVariable v = inOutVar(r);
+ if (v.type != QRhiShaderDescription::Unknown)
+ dd->inVars.append(v);
+ }
+
+ for (const spirv_cross::Resource &r : resources.stage_outputs) {
+ const QRhiShaderDescription::InOutVariable v = inOutVar(r);
+ if (v.type != QRhiShaderDescription::Unknown)
+ dd->outVars.append(v);
+ }
+
+ // uniform blocks map to either a uniform buffer or a plain struct
+ for (const spirv_cross::Resource &r : resources.uniform_buffers) {
+ const spirv_cross::SPIRType &t = glslGen->get_type(r.base_type_id);
+ QRhiShaderDescription::UniformBlock block;
+ block.blockName = QString::fromStdString(r.name);
+ block.structName = QString::fromStdString(glslGen->get_name(r.id));
+ block.size = int(glslGen->get_declared_struct_size(t));
+ if (glslGen->has_decoration(r.id, spv::DecorationBinding))
+ block.binding = glslGen->get_decoration(r.id, spv::DecorationBinding);
+ if (glslGen->has_decoration(r.id, spv::DecorationDescriptorSet))
+ block.descriptorSet = glslGen->get_decoration(r.id, spv::DecorationDescriptorSet);
+ uint32_t idx = 0;
+ for (uint32_t memberTypeId : t.member_types) {
+ const QRhiShaderDescription::BlockVariable v = blockVar(r.base_type_id, idx, memberTypeId);
+ ++idx;
+ if (v.type != QRhiShaderDescription::Unknown)
+ block.members.append(v);
+ }
+ dd->uniformBlocks.append(block);
+ }
+
+ // push constant blocks map to a plain GLSL struct regardless of version
+ for (const spirv_cross::Resource &r : resources.push_constant_buffers) {
+ const spirv_cross::SPIRType &t = glslGen->get_type(r.base_type_id);
+ QRhiShaderDescription::PushConstantBlock block;
+ block.name = QString::fromStdString(glslGen->get_name(r.id));
+ block.size = int(glslGen->get_declared_struct_size(t));
+ uint32_t idx = 0;
+ for (uint32_t memberTypeId : t.member_types) {
+ const QRhiShaderDescription::BlockVariable v = blockVar(r.base_type_id, idx, memberTypeId);
+ ++idx;
+ if (v.type != QRhiShaderDescription::Unknown)
+ block.members.append(v);
+ }
+ dd->pushConstantBlocks.append(block);
+ }
+
+ for (const spirv_cross::Resource &r : resources.sampled_images) {
+ const QRhiShaderDescription::InOutVariable v = inOutVar(r);
+ if (v.type != QRhiShaderDescription::Unknown)
+ dd->combinedImageSamplers.append(v);
+ }
+}
+
+QSpirvShader::QSpirvShader()
+ : d(new QSpirvShaderPrivate)
+{
+}
+
+QSpirvShader::~QSpirvShader()
+{
+ delete d;
+}
+
+void QSpirvShader::setFileName(const QString &fileName)
+{
+ QFile f(fileName);
+ if (!f.open(QIODevice::ReadOnly)) {
+ qWarning("QSpirvShader: Failed to open %s", qPrintable(fileName));
+ return;
+ }
+ setDevice(&f);
+}
+
+void QSpirvShader::setDevice(QIODevice *device)
+{
+ d->ir = device->readAll();
+ d->createGLSLCompiler();
+ d->processResources();
+}
+
+void QSpirvShader::setSpirvBinary(const QByteArray &spirv)
+{
+ d->ir = spirv;
+ d->createGLSLCompiler();
+ d->processResources();
+}
+
+QRhiShaderDescription QSpirvShader::shaderDescription() const
+{
+ return d->shaderDescription;
+}
+
+void QSpirvShaderPrivate::remapErrorHandler(const std::string &s)
+{
+ if (!remapErrorMsg.isEmpty())
+ remapErrorMsg.append(QLatin1Char('\n'));
+ remapErrorMsg.append(QString::fromStdString(s));
+}
+
+void QSpirvShaderPrivate::remapLogHandler(const std::string &)
+{
+}
+
+QByteArray QSpirvShader::strippedSpirvBinary(StripFlags flags, QString *errorMessage) const
+{
+ if (d->ir.isEmpty())
+ return QByteArray();
+
+ spv::spirvbin_t b;
+
+ d->remapErrorMsg.clear();
+ b.registerErrorHandler(std::bind(&QSpirvShaderPrivate::remapErrorHandler, d, std::placeholders::_1));
+ b.registerLogHandler(std::bind(&QSpirvShaderPrivate::remapLogHandler, d, std::placeholders::_1));
+
+ const uint32_t opts = flags.testFlag(Remap) ? spv::spirvbin_t::DO_EVERYTHING : spv::spirvbin_t::STRIP;
+
+ std::vector<uint32_t> v;
+ v.resize(d->ir.size() / 4);
+ memcpy(v.data(), d->ir.constData(), d->ir.size());
+
+ b.remap(v, opts);
+
+ if (!d->remapErrorMsg.isEmpty()) {
+ if (errorMessage)
+ *errorMessage = d->remapErrorMsg;
+ return QByteArray();
+ }
+
+ return QByteArray(reinterpret_cast<const char *>(v.data()), int(v.size()) * 4);
+}
+
+QByteArray QSpirvShader::translateToGLSL(int version, GlslFlags flags) const
+{
+ d->spirvCrossErrorMsg.clear();
+
+ try {
+ // create a new instance every time since option handling seem to be problematic
+ // (won't pick up new options on the second and subsequent compile())
+ d->createGLSLCompiler();
+
+ spirv_cross::CompilerGLSL::Options options;
+ options.version = version;
+ options.es = flags.testFlag(GlslEs);
+ options.vertex.fixup_clipspace = flags.testFlag(FixClipSpace);
+ options.fragment.default_float_precision = flags.testFlag(FragDefaultMediump)
+ ? spirv_cross::CompilerGLSL::Options::Mediump
+ : spirv_cross::CompilerGLSL::Options::Highp;
+ d->glslGen->set_common_options(options);
+
+ const std::string glsl = d->glslGen->compile();
+
+ QByteArray src = QByteArray::fromStdString(glsl);
+
+ // Fix it up by adding #extension GL_ARB_separate_shader_objects : require
+ // as well in order to make Mesa and perhaps others happy.
+ const QByteArray searchStr = QByteArrayLiteral("#extension GL_ARB_shading_language_420pack : require\n#endif\n");
+ int pos = src.indexOf(searchStr);
+ if (pos >= 0) {
+ src.insert(pos + searchStr.count(), QByteArrayLiteral("#ifdef GL_ARB_separate_shader_objects\n"
+ "#extension GL_ARB_separate_shader_objects : require\n"
+ "#endif\n"));
+ }
+
+ return src;
+ } catch (const std::runtime_error &e) {
+ d->spirvCrossErrorMsg = QString::fromUtf8(e.what());
+ return QByteArray();
+ }
+}
+
+QByteArray QSpirvShader::translateToHLSL(int version) const
+{
+ d->spirvCrossErrorMsg.clear();
+
+ try {
+ if (!d->hlslGen)
+ d->hlslGen = new spirv_cross::CompilerHLSL(reinterpret_cast<const uint32_t *>(d->ir.constData()), d->ir.size() / 4);
+
+ spirv_cross::CompilerHLSL::Options options;
+ options.shader_model = version;
+ d->hlslGen->set_hlsl_options(options);
+
+ const std::string hlsl = d->hlslGen->compile();
+
+ return QByteArray::fromStdString(hlsl);
+ } catch (const std::runtime_error &e) {
+ d->spirvCrossErrorMsg = QString::fromUtf8(e.what());
+ return QByteArray();
+ }
+}
+
+QByteArray QSpirvShader::translateToMSL(int version) const
+{
+ d->spirvCrossErrorMsg.clear();
+
+ try {
+ if (!d->mslGen)
+ d->mslGen = new spirv_cross::CompilerMSL(reinterpret_cast<const uint32_t *>(d->ir.constData()), d->ir.size() / 4);
+
+ spirv_cross::CompilerMSL::Options options;
+ options.msl_version = spirv_cross::CompilerMSL::Options::make_msl_version(version / 10, version % 10);
+ // leave platform set to macOS, it won't matter in practice (hopefully)
+ d->mslGen->set_msl_options(options);
+
+ const std::string msl = d->mslGen->compile();
+
+ return QByteArray::fromStdString(msl);
+ } catch (const std::runtime_error &e) {
+ d->spirvCrossErrorMsg = QString::fromUtf8(e.what());
+ return QByteArray();
+ }
+}
+
+QString QSpirvShader::translationErrorMessage() const
+{
+ return d->spirvCrossErrorMsg;
+}
+
+QT_END_NAMESPACE
diff --git a/src/shadertools/qspirvshader_p.h b/src/shadertools/qspirvshader_p.h
new file mode 100644
index 0000000..3191173
--- /dev/null
+++ b/src/shadertools/qspirvshader_p.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSPIRVSHADER_P_H
+#define QSPIRVSHADER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtShaderTools/private/qtshadertoolsglobal_p.h>
+#include <QtGui/qrhishaderdescription.h>
+
+QT_BEGIN_NAMESPACE
+
+class QIODevice;
+struct QSpirvShaderPrivate;
+
+class Q_SHADERTOOLS_PRIVATE_EXPORT QSpirvShader
+{
+public:
+ enum GlslFlag {
+ GlslEs = 0x01,
+ FixClipSpace = 0x02,
+ FragDefaultMediump = 0x04
+ };
+ Q_DECLARE_FLAGS(GlslFlags, GlslFlag)
+
+ enum StripFlag {
+ Remap = 0x01
+ };
+ Q_DECLARE_FLAGS(StripFlags, StripFlag)
+
+ QSpirvShader();
+ ~QSpirvShader();
+
+ void setFileName(const QString &fileName);
+ void setDevice(QIODevice *device);
+ void setSpirvBinary(const QByteArray &spirv);
+
+ QRhiShaderDescription shaderDescription() const;
+
+ QByteArray strippedSpirvBinary(StripFlags flags = StripFlags(), QString *errorMessage = nullptr) const;
+
+ QByteArray translateToGLSL(int version = 120, GlslFlags flags = GlslFlags()) const;
+ QByteArray translateToHLSL(int version = 50) const;
+ QByteArray translateToMSL(int version = 12) const;
+
+ QString translationErrorMessage() const;
+
+private:
+ Q_DISABLE_COPY(QSpirvShader)
+ QSpirvShaderPrivate *d = nullptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSpirvShader::GlslFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSpirvShader::StripFlags)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/shadertools/qtshadertoolsglobal.h b/src/shadertools/qtshadertoolsglobal.h
new file mode 100644
index 0000000..fda42ff
--- /dev/null
+++ b/src/shadertools/qtshadertoolsglobal.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTSHADERTOOLSGLOBAL_H
+#define QTSHADERTOOLSGLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef Q_SHADERTOOLS_EXPORT
+# if !defined(QT_STATIC)
+# if defined(QT_BUILD_SHADERTOOLS_LIB)
+# define Q_SHADERTOOLS_EXPORT Q_DECL_EXPORT
+# else
+# define Q_SHADERTOOLS_EXPORT Q_DECL_IMPORT
+# endif
+# else
+# define Q_SHADERTOOLS_EXPORT
+# endif
+#endif
+
+#ifdef Q_CLANG_QDOC
+#define Q_SHADERTOOLS_EXPORT
+#endif
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/shadertools/qtshadertoolsglobal_p.h b/src/shadertools/qtshadertoolsglobal_p.h
new file mode 100644
index 0000000..3516624
--- /dev/null
+++ b/src/shadertools/qtshadertoolsglobal_p.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Shader Tools module
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTSHADERTOOLSGLOBAL_P_H
+#define QTSHADERTOOLSGLOBAL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qtshadertoolsglobal.h"
+
+#define Q_SHADERTOOLS_PRIVATE_EXPORT Q_SHADERTOOLS_EXPORT
+
+#endif
diff --git a/src/shadertools/shadertools.pro b/src/shadertools/shadertools.pro
new file mode 100644
index 0000000..e9123b3
--- /dev/null
+++ b/src/shadertools/shadertools.pro
@@ -0,0 +1,41 @@
+TARGET = QtShaderTools
+
+QT += gui-private
+
+DEFINES += QT_BUILD_SHADERTOOLS_LIB
+
+HEADERS += \
+ $$PWD/qtshadertoolsglobal.h \
+ $$PWD/qshaderbaker.h \
+ $$PWD/qspirvshader_p.h \
+ $$PWD/qspirvcompiler_p.h \
+ $$PWD/qshaderbatchablerewriter_p.h
+
+SOURCES += \
+ $$PWD/qshaderbaker.cpp \
+ $$PWD/qspirvshader.cpp \
+ $$PWD/qspirvcompiler.cpp \
+ $$PWD/qshaderbatchablerewriter.cpp
+
+INCLUDEPATH += $$PWD/../3rdparty/SPIRV-Cross $$PWD/../3rdparty/glslang
+
+# Exceptions must be enabled since that is the only sane way to get errors reported from SPIRV-Cross.
+# They will not propagate outside of this module though so should be safe enough.
+CONFIG += exceptions
+
+!exists($$[QT_HOST_DATA]/.qmake.cache) {
+ LIBLOC = $$shadowed($$dirname(_QMAKE_CONF_))/lib
+} else {
+ LIBLOC = $$[QT_HOST_LIBS]
+}
+
+STATICLIBS = qtspirv-cross qtglslang-glslang qtglslang-spirv qtglslang-osdependent qtglslang-oglcompiler # qtglslang-hlsl
+for(libname, STATICLIBS) {
+ staticlib = $$LIBLOC/$${QMAKE_PREFIX_STATICLIB}$$qtLibraryTarget($$libname).$${QMAKE_EXTENSION_STATICLIB}
+ LIBS_PRIVATE += $$staticlib
+ PRE_TARGETDEPS += $$staticlib
+}
+
+include($$PWD/doc/doc.pri)
+
+load(qt_module)
diff --git a/src/src.pro b/src/src.pro
new file mode 100644
index 0000000..58a2f4c
--- /dev/null
+++ b/src/src.pro
@@ -0,0 +1,8 @@
+TEMPLATE = subdirs
+
+SUBDIRS += \
+ glslang \
+ SPIRV-Cross \
+ shadertools
+
+shadertools.depends = glslang SPIRV-Cross
diff --git a/sync.profile b/sync.profile
new file mode 100644
index 0000000..5e45fc2
--- /dev/null
+++ b/sync.profile
@@ -0,0 +1,14 @@
+%modules = ( # path to module name map
+ "QtShaderTools" => "$basedir/src/shadertools",
+);
+%moduleheaders = ( # restrict the module headers to those found in relative path
+);
+# Module dependencies.
+# Every module that is required to build this module should have one entry.
+# Each of the module version specifiers can take one of the following values:
+# - A specific Git revision.
+# - any git symbolic ref resolvable from the module's repository (e.g. "refs/heads/master" to track master branch)
+#
+%dependencies = (
+ "qtbase" => "",
+);
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
new file mode 100644
index 0000000..8e435be
--- /dev/null
+++ b/tests/auto/auto.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+SUBDIRS = \
+ qshaderbaker
diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt
new file mode 100644
index 0000000..3d77b9e
--- /dev/null
+++ b/tests/auto/cmake/CMakeLists.txt
@@ -0,0 +1,12 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(qmake_cmake_files)
+
+enable_testing()
+
+find_package(Qt5Core REQUIRED)
+
+include("${_Qt5CTestMacros}")
+
+test_module_includes(
+)
diff --git a/tests/auto/cmake/cmake.pro b/tests/auto/cmake/cmake.pro
new file mode 100644
index 0000000..929589a
--- /dev/null
+++ b/tests/auto/cmake/cmake.pro
@@ -0,0 +1,5 @@
+TEMPLATE = subdirs
+
+CMAKE_QT_MODULES_UNDER_TEST = gamepad
+
+CONFIG += ctest_testcase
diff --git a/tests/auto/qshaderbaker/data/color.frag b/tests/auto/qshaderbaker/data/color.frag
new file mode 100644
index 0000000..62ff1b0
--- /dev/null
+++ b/tests/auto/qshaderbaker/data/color.frag
@@ -0,0 +1,14 @@
+#version 440
+
+layout(location = 0) in vec3 v_color;
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ float opacity;
+} ubuf;
+
+void main()
+{
+ fragColor = vec4(v_color * ubuf.opacity, ubuf.opacity);
+}
diff --git a/tests/auto/qshaderbaker/data/color.vert b/tests/auto/qshaderbaker/data/color.vert
new file mode 100644
index 0000000..c92f71b
--- /dev/null
+++ b/tests/auto/qshaderbaker/data/color.vert
@@ -0,0 +1,18 @@
+#version 440
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec3 color;
+layout(location = 0) out vec3 v_color;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ float opacity;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ v_color = color;
+ gl_Position = ubuf.mvp * position;
+}
diff --git a/tests/auto/qshaderbaker/data/error.vert b/tests/auto/qshaderbaker/data/error.vert
new file mode 100644
index 0000000..3d2097b
--- /dev/null
+++ b/tests/auto/qshaderbaker/data/error.vert
@@ -0,0 +1,18 @@
+#version 440
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec3 color;
+layout(location = 0) out vec3 v_color;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ float opacity;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ v_color = cgrepijrgrewolor;
+ gl_Position = ubuf.mvp * position;
+}
diff --git a/tests/auto/qshaderbaker/data/hlsl_cbuf_error.frag b/tests/auto/qshaderbaker/data/hlsl_cbuf_error.frag
new file mode 100644
index 0000000..ba59ffa
--- /dev/null
+++ b/tests/auto/qshaderbaker/data/hlsl_cbuf_error.frag
@@ -0,0 +1,47 @@
+#version 440
+
+layout(location = 0) in vec3 vECVertNormal;
+layout(location = 1) in vec3 vECVertPos;
+layout(location = 2) flat in vec3 vDiffuseAdjust;
+
+#define MAX_LIGHTS 10
+
+struct Light {
+ vec3 ECLightPosition;
+ vec3 attenuation;
+ vec3 color;
+ float intensity;
+ float specularExp;
+ // this is not translatable to HLSL
+};
+
+layout(std140, binding = 1) uniform buf {
+ vec3 ECCameraPosition;
+ vec3 ka;
+ vec3 kd;
+ vec3 ks;
+ Light lights[MAX_LIGHTS];
+ int numLights;
+ layout(row_major) mat3 mm;
+} ubuf;
+
+layout(location = 0) out vec4 fragColor;
+
+void main()
+{
+ vec3 unnormL = ubuf.lights[0].ECLightPosition - vECVertPos;
+ float dist = length(unnormL);
+ float att = 1.0 / (ubuf.lights[0].attenuation.x + ubuf.lights[0].attenuation.y * dist + ubuf.lights[0].attenuation.z * dist * dist);
+
+ vec3 N = normalize(vECVertNormal);
+ vec3 L = normalize(unnormL);
+ float NL = max(0.0, dot(N, L));
+ vec3 dColor = att * ubuf.lights[0].intensity * ubuf.lights[0].color * NL;
+
+ vec3 R = reflect(-L, N);
+ vec3 V = normalize(ubuf.ECCameraPosition - vECVertPos);
+ float RV = max(0.0, dot(R, V));
+ vec3 sColor = att * ubuf.lights[0].intensity * ubuf.lights[0].color * pow(RV, ubuf.lights[0].specularExp);
+
+ fragColor = vec4(ubuf.ka + (ubuf.kd + vDiffuseAdjust) * dColor + ubuf.ks * sColor, 1.0);
+}
diff --git a/tests/auto/qshaderbaker/qshaderbaker.pro b/tests/auto/qshaderbaker/qshaderbaker.pro
new file mode 100644
index 0000000..22a81ba
--- /dev/null
+++ b/tests/auto/qshaderbaker/qshaderbaker.pro
@@ -0,0 +1,8 @@
+TARGET = tst_qshaderbaker
+CONFIG += testcase
+
+QT += testlib shadertools
+
+SOURCES += tst_qshaderbaker.cpp
+
+RESOURCES += qshaderbaker.qrc
diff --git a/tests/auto/qshaderbaker/qshaderbaker.qrc b/tests/auto/qshaderbaker/qshaderbaker.qrc
new file mode 100644
index 0000000..f161d8a
--- /dev/null
+++ b/tests/auto/qshaderbaker/qshaderbaker.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>data</file>
+ </qresource>
+</RCC>
diff --git a/tests/auto/qshaderbaker/tst_qshaderbaker.cpp b/tests/auto/qshaderbaker/tst_qshaderbaker.cpp
new file mode 100644
index 0000000..36085e5
--- /dev/null
+++ b/tests/auto/qshaderbaker/tst_qshaderbaker.cpp
@@ -0,0 +1,377 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QFile>
+#include <QtShaderTools/QShaderBaker>
+#include <QtGui/QRhiShaderDescription>
+#include <QtGui/QRhiShader>
+
+class tst_QShaderBaker : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanup();
+ void emptyCompile();
+ void noFileCompile();
+ void noTargetsCompile();
+ void noVariantsCompile();
+ void simpleCompile();
+ void simpleCompileNoSpirvSpecified();
+ void simpleCompileCheckResults();
+ void simpleCompileFromDevice();
+ void simpleCompileFromString();
+ void multiCompile();
+ void reuse();
+ void compileError();
+ void translateError();
+ void genVariants();
+};
+
+void tst_QShaderBaker::initTestCase()
+{
+}
+
+void tst_QShaderBaker::cleanup()
+{
+}
+
+void tst_QShaderBaker::emptyCompile()
+{
+ QShaderBaker baker;
+ QRhiShader s = baker.bake();
+ QVERIFY(!s.isValid());
+ QVERIFY(!baker.errorMessage().isEmpty());
+ qDebug() << baker.errorMessage();
+}
+
+void tst_QShaderBaker::noFileCompile()
+{
+ QShaderBaker baker;
+ baker.setSourceFileName(QLatin1String(":/data/nonexistant.vert"));
+ QRhiShader s = baker.bake();
+ QVERIFY(!s.isValid());
+ QVERIFY(!baker.errorMessage().isEmpty());
+ qDebug() << baker.errorMessage();
+}
+
+void tst_QShaderBaker::noTargetsCompile()
+{
+ QShaderBaker baker;
+ baker.setSourceFileName(QLatin1String(":/data/color.vert"));
+ QRhiShader s = baker.bake();
+ // an empty shader pack is invalid
+ QVERIFY(!s.isValid());
+ // not an error from the baker's point of view however
+ QVERIFY(baker.errorMessage().isEmpty());
+}
+
+void tst_QShaderBaker::noVariantsCompile()
+{
+ QShaderBaker baker;
+ baker.setSourceFileName(QLatin1String(":/data/color.vert"));
+ QVector<QShaderBaker::GeneratedShader> targets;
+ targets.append({ QRhiShaderKey::SpirvShader, QRhiShaderVersion(100) });
+ baker.setGeneratedShaders(targets);
+ QRhiShader s = baker.bake();
+ // an empty shader pack is invalid
+ QVERIFY(!s.isValid());
+ // not an error from the baker's point of view however
+ QVERIFY(baker.errorMessage().isEmpty());
+}
+
+void tst_QShaderBaker::simpleCompile()
+{
+ QShaderBaker baker;
+ baker.setSourceFileName(QLatin1String(":/data/color.vert"));
+ baker.setGeneratedShaderVariants({ QRhiShaderKey::StandardShader });
+ QVector<QShaderBaker::GeneratedShader> targets;
+ targets.append({ QRhiShaderKey::SpirvShader, QRhiShaderVersion(100) });
+ baker.setGeneratedShaders(targets);
+ QRhiShader s = baker.bake();
+ QVERIFY(s.isValid());
+ QVERIFY(baker.errorMessage().isEmpty());
+ QCOMPARE(s.availableShaders().count(), 1);
+ QVERIFY(s.availableShaders().contains(QRhiShaderKey(QRhiShaderKey::SpirvShader, QRhiShaderVersion(100))));
+}
+
+void tst_QShaderBaker::simpleCompileNoSpirvSpecified()
+{
+ QShaderBaker baker;
+ baker.setSourceFileName(QLatin1String(":/data/color.vert"));
+ baker.setGeneratedShaderVariants({ QRhiShaderKey::StandardShader });
+ QVector<QShaderBaker::GeneratedShader> targets;
+ targets.append({ QRhiShaderKey::GlslShader, QRhiShaderVersion(330) });
+ baker.setGeneratedShaders(targets);
+ QRhiShader s = baker.bake();
+ QVERIFY(s.isValid());
+ QVERIFY(baker.errorMessage().isEmpty());
+ QCOMPARE(s.availableShaders().count(), 1);
+ QVERIFY(s.availableShaders().contains(QRhiShaderKey(QRhiShaderKey::GlslShader, QRhiShaderVersion(330))));
+ QVERIFY(s.shader(s.availableShaders().first()).shader().contains(QByteArrayLiteral("#version 330")));
+}
+
+void tst_QShaderBaker::simpleCompileCheckResults()
+{
+ QShaderBaker baker;
+ baker.setSourceFileName(QLatin1String(":/data/color.vert"));
+ baker.setGeneratedShaderVariants({ QRhiShaderKey::StandardShader });
+ QVector<QShaderBaker::GeneratedShader> targets;
+ targets.append({ QRhiShaderKey::SpirvShader, QRhiShaderVersion(100) });
+ baker.setGeneratedShaders(targets);
+ QRhiShader s = baker.bake();
+ QVERIFY(s.isValid());
+ QVERIFY(baker.errorMessage().isEmpty());
+ QCOMPARE(s.availableShaders().count(), 1);
+
+ const QRhiShaderCode shader = s.shader(QRhiShaderKey(QRhiShaderKey::SpirvShader,
+ QRhiShaderVersion(100)));
+ QVERIFY(!shader.shader().isEmpty());
+ QCOMPARE(shader.entryPoint(), QByteArrayLiteral("main"));
+
+ const QRhiShaderDescription desc = s.description();
+ QVERIFY(desc.isValid());
+ QCOMPARE(desc.inputVariables().count(), 2);
+ for (const QRhiShaderDescription::InOutVariable &v : desc.inputVariables()) {
+ switch (v.location) {
+ case 0:
+ QCOMPARE(v.name, QLatin1String("position"));
+ QCOMPARE(v.type, QRhiShaderDescription::Vec4);
+ break;
+ case 1:
+ QCOMPARE(v.name, QLatin1String("color"));
+ QCOMPARE(v.type, QRhiShaderDescription::Vec3);
+ break;
+ default:
+ QVERIFY(false);
+ break;
+ }
+ }
+ QCOMPARE(desc.outputVariables().count(), 1);
+ for (const QRhiShaderDescription::InOutVariable &v : desc.outputVariables()) {
+ switch (v.location) {
+ case 0:
+ QCOMPARE(v.name, QLatin1String("v_color"));
+ QCOMPARE(v.type, QRhiShaderDescription::Vec3);
+ break;
+ default:
+ QVERIFY(false);
+ break;
+ }
+ }
+ QCOMPARE(desc.uniformBlocks().count(), 1);
+ const QRhiShaderDescription::UniformBlock blk = desc.uniformBlocks().first();
+ QCOMPARE(blk.blockName, QLatin1String("buf"));
+ QCOMPARE(blk.structName, QLatin1String("ubuf"));
+ QCOMPARE(blk.size, 68);
+ QCOMPARE(blk.binding, 0);
+ QCOMPARE(blk.descriptorSet, 0);
+ QCOMPARE(blk.members.count(), 2);
+ for (int i = 0; i < blk.members.count(); ++i) {
+ const QRhiShaderDescription::BlockVariable v = blk.members[i];
+ switch (i) {
+ case 0:
+ QCOMPARE(v.offset, 0);
+ QCOMPARE(v.size, 64);
+ QCOMPARE(v.name, QLatin1String("mvp"));
+ QCOMPARE(v.type, QRhiShaderDescription::Mat4);
+ QCOMPARE(v.matrixStride, 16);
+ break;
+ case 1:
+ QCOMPARE(v.offset, 64);
+ QCOMPARE(v.size, 4);
+ QCOMPARE(v.name, QLatin1String("opacity"));
+ QCOMPARE(v.type, QRhiShaderDescription::Float);
+ break;
+ default:
+ QVERIFY(false);
+ break;
+ }
+ }
+}
+
+void tst_QShaderBaker::simpleCompileFromDevice()
+{
+ QFile f(QLatin1String(":/data/color.vert"));
+ QVERIFY(f.open(QIODevice::ReadOnly | QIODevice::Text));
+
+ QShaderBaker baker;
+ baker.setSourceDevice(&f, QRhiShader::VertexStage);
+ baker.setGeneratedShaderVariants({ QRhiShaderKey::StandardShader });
+ QVector<QShaderBaker::GeneratedShader> targets;
+ targets.append({ QRhiShaderKey::SpirvShader, QRhiShaderVersion(100) });
+ baker.setGeneratedShaders(targets);
+ QRhiShader s = baker.bake();
+ QVERIFY(s.isValid());
+ QVERIFY(baker.errorMessage().isEmpty());
+ QCOMPARE(s.availableShaders().count(), 1);
+}
+
+void tst_QShaderBaker::simpleCompileFromString()
+{
+ QFile f(QLatin1String(":/data/color.vert"));
+ QVERIFY(f.open(QIODevice::ReadOnly | QIODevice::Text));
+ const QByteArray contents = f.readAll();
+ f.close();
+ QVERIFY(!contents.isEmpty());
+
+ QShaderBaker baker;
+ baker.setSourceString(contents, QRhiShader::VertexStage);
+ baker.setGeneratedShaderVariants({ QRhiShaderKey::StandardShader });
+ QVector<QShaderBaker::GeneratedShader> targets;
+ targets.append({ QRhiShaderKey::SpirvShader, QRhiShaderVersion(100) });
+ baker.setGeneratedShaders(targets);
+ QRhiShader s = baker.bake();
+ QVERIFY(s.isValid());
+ QVERIFY(baker.errorMessage().isEmpty());
+ QCOMPARE(s.availableShaders().count(), 1);
+}
+
+void tst_QShaderBaker::multiCompile()
+{
+ QShaderBaker baker;
+ baker.setSourceFileName(QLatin1String(":/data/color.vert"));
+ baker.setGeneratedShaderVariants({ QRhiShaderKey::StandardShader });
+ QVector<QShaderBaker::GeneratedShader> targets;
+ targets.append({ QRhiShaderKey::SpirvShader, QRhiShaderVersion(100) });
+ targets.append({ QRhiShaderKey::GlslShader, QRhiShaderVersion(100, QRhiShaderVersion::GlslEs) });
+ targets.append({ QRhiShaderKey::GlslShader, QRhiShaderVersion(120) });
+ targets.append({ QRhiShaderKey::HlslShader, QRhiShaderVersion(50) });
+ targets.append({ QRhiShaderKey::MslShader, QRhiShaderVersion(12) });
+ baker.setGeneratedShaders(targets);
+ QRhiShader s = baker.bake();
+ QVERIFY(s.isValid());
+ QVERIFY(baker.errorMessage().isEmpty());
+ QCOMPARE(s.availableShaders().count(), 5);
+
+ for (const QShaderBaker::GeneratedShader &genShader : targets) {
+ const QRhiShaderKey key(genShader.first, genShader.second);
+ const QRhiShaderCode shader = s.shader(key);
+ QVERIFY(!shader.shader().isEmpty());
+ if (genShader.first != QRhiShaderKey::MslShader)
+ QCOMPARE(shader.entryPoint(), QByteArrayLiteral("main"));
+ }
+}
+
+void tst_QShaderBaker::reuse()
+{
+ QShaderBaker baker;
+ baker.setSourceFileName(QLatin1String(":/data/color.vert"));
+ baker.setGeneratedShaderVariants({ QRhiShaderKey::StandardShader });
+ QVector<QShaderBaker::GeneratedShader> targets;
+ targets.append({ QRhiShaderKey::SpirvShader, QRhiShaderVersion(100) });
+ baker.setGeneratedShaders(targets);
+ QRhiShader s = baker.bake();
+ QVERIFY(s.isValid());
+ QVERIFY(baker.errorMessage().isEmpty());
+ QCOMPARE(s.availableShaders().count(), 1);
+
+ baker.setSourceFileName(QLatin1String(":/data/color.frag"));
+ targets.clear();
+ targets.append({ QRhiShaderKey::SpirvShader, QRhiShaderVersion(100) });
+ targets.append({ QRhiShaderKey::GlslShader, QRhiShaderVersion(100, QRhiShaderVersion::GlslEs) });
+ targets.append({ QRhiShaderKey::GlslShader, QRhiShaderVersion(120) });
+ targets.append({ QRhiShaderKey::HlslShader, QRhiShaderVersion(50) });
+ targets.append({ QRhiShaderKey::MslShader, QRhiShaderVersion(12) });
+ baker.setGeneratedShaders(targets);
+ s = baker.bake();
+ QVERIFY(s.isValid());
+ QVERIFY(baker.errorMessage().isEmpty());
+ QCOMPARE(s.availableShaders().count(), 5);
+}
+
+void tst_QShaderBaker::compileError()
+{
+ QShaderBaker baker;
+ baker.setSourceFileName(QLatin1String(":/data/error.vert"));
+ baker.setGeneratedShaderVariants({ QRhiShaderKey::StandardShader });
+ QVector<QShaderBaker::GeneratedShader> targets;
+ targets.append({ QRhiShaderKey::SpirvShader, QRhiShaderVersion(100) });
+ baker.setGeneratedShaders(targets);
+ QRhiShader s = baker.bake();
+ QVERIFY(!s.isValid());
+ QVERIFY(!baker.errorMessage().isEmpty());
+ qDebug() << baker.errorMessage();
+}
+
+void tst_QShaderBaker::translateError()
+{
+ // assume the shader here fails in SPIRV-Cross with "cbuffer cannot be expressed with either HLSL packing layout or packoffset"
+ QShaderBaker baker;
+ baker.setSourceFileName(QLatin1String(":/data/hlsl_cbuf_error.frag"));
+ baker.setGeneratedShaderVariants({ QRhiShaderKey::StandardShader });
+ QVector<QShaderBaker::GeneratedShader> targets;
+ targets.append({ QRhiShaderKey::HlslShader, QRhiShaderVersion(50) });
+ baker.setGeneratedShaders(targets);
+ QRhiShader s = baker.bake();
+ QVERIFY(!s.isValid());
+ QVERIFY(!baker.errorMessage().isEmpty());
+ qDebug() << baker.errorMessage();
+}
+
+void tst_QShaderBaker::genVariants()
+{
+ QShaderBaker baker;
+ baker.setSourceFileName(QLatin1String(":/data/color.vert"));
+ baker.setGeneratedShaderVariants({
+ QRhiShaderKey::StandardShader,
+ QRhiShaderKey::BatchableVertexShader
+ });
+ QVector<QShaderBaker::GeneratedShader> targets;
+ targets.append({ QRhiShaderKey::SpirvShader, QRhiShaderVersion(100) });
+ targets.append({ QRhiShaderKey::GlslShader, QRhiShaderVersion(100, QRhiShaderVersion::GlslEs) });
+ targets.append({ QRhiShaderKey::GlslShader, QRhiShaderVersion(330) });
+ targets.append({ QRhiShaderKey::GlslShader, QRhiShaderVersion(120) });
+ targets.append({ QRhiShaderKey::HlslShader, QRhiShaderVersion(50) });
+ targets.append({ QRhiShaderKey::MslShader, QRhiShaderVersion(12) });
+ baker.setGeneratedShaders(targets);
+ QRhiShader s = baker.bake();
+ QVERIFY(s.isValid());
+ QVERIFY(baker.errorMessage().isEmpty());
+ QCOMPARE(s.availableShaders().count(), 2 * 6);
+
+ int batchableVariantCount = 0;
+ int batchableGlslVariantCount = 0;
+ for (const QRhiShaderKey &key : s.availableShaders()) {
+ if (key.sourceVariant() == QRhiShaderKey::BatchableVertexShader) {
+ ++batchableVariantCount;
+ if (key.source() == QRhiShaderKey::GlslShader) {
+ ++batchableGlslVariantCount;
+ const QByteArray src = s.shader(key).shader();
+ QVERIFY(src.contains(QByteArrayLiteral("_qt_order * ")));
+ }
+ }
+ }
+ QCOMPARE(batchableVariantCount, 6);
+ QCOMPARE(batchableGlslVariantCount, 3);
+}
+
+#include <tst_qshaderbaker.moc>
+QTEST_MAIN(tst_QShaderBaker)
diff --git a/tests/playground/array.frag b/tests/playground/array.frag
new file mode 100644
index 0000000..ef3df3d
--- /dev/null
+++ b/tests/playground/array.frag
@@ -0,0 +1,49 @@
+#version 440
+
+layout(location = 0) in vec3 vECVertNormal;
+layout(location = 1) in vec3 vECVertPos;
+layout(location = 2) flat in vec3 vDiffuseAdjust;
+
+#define MAX_LIGHTS 10
+
+struct Light {
+ vec3 ECLightPosition;
+ vec3 attenuation;
+ vec3 color;
+ float intensity;
+ float specularExp;
+ // two dummies so that it stays translatable to HLSL with packoffset in the top-level block
+ float __dummy0;
+ float __dummy1;
+};
+
+layout(std140, binding = 1) uniform buf {
+ vec3 ECCameraPosition;
+ vec3 ka;
+ vec3 kd;
+ vec3 ks;
+ Light lights[MAX_LIGHTS];
+ int numLights;
+ layout(row_major) mat3 mm;
+} ubuf;
+
+layout(location = 0) out vec4 fragColor;
+
+void main()
+{
+ vec3 unnormL = ubuf.lights[0].ECLightPosition - vECVertPos;
+ float dist = length(unnormL);
+ float att = 1.0 / (ubuf.lights[0].attenuation.x + ubuf.lights[0].attenuation.y * dist + ubuf.lights[0].attenuation.z * dist * dist);
+
+ vec3 N = normalize(vECVertNormal);
+ vec3 L = normalize(unnormL);
+ float NL = max(0.0, dot(N, L));
+ vec3 dColor = att * ubuf.lights[0].intensity * ubuf.lights[0].color * NL;
+
+ vec3 R = reflect(-L, N);
+ vec3 V = normalize(ubuf.ECCameraPosition - vECVertPos);
+ float RV = max(0.0, dot(R, V));
+ vec3 sColor = att * ubuf.lights[0].intensity * ubuf.lights[0].color * pow(RV, ubuf.lights[0].specularExp);
+
+ fragColor = vec4(ubuf.ka + (ubuf.kd + vDiffuseAdjust) * dColor + ubuf.ks * sColor, 1.0);
+}
diff --git a/tests/playground/cbuf.frag b/tests/playground/cbuf.frag
new file mode 100644
index 0000000..0091443
--- /dev/null
+++ b/tests/playground/cbuf.frag
@@ -0,0 +1,21 @@
+#version 440
+
+layout(location = 0) in vec3 vECVertNormal;
+layout(location = 1) in vec3 vECVertPos;
+
+layout(std140, binding = 1) uniform buf {
+ vec3 v;
+ float f;
+} ubuf;
+
+layout(std140, binding = 2) uniform buf2 {
+ float f;
+ vec3 v;
+} ubuf2;
+
+layout(location = 0) out vec4 fragColor;
+
+void main()
+{
+ fragColor = vec4(ubuf.v, ubuf.f);
+}
diff --git a/tests/playground/color.frag b/tests/playground/color.frag
new file mode 100644
index 0000000..3755876
--- /dev/null
+++ b/tests/playground/color.frag
@@ -0,0 +1,10 @@
+#version 440
+
+layout(location = 0) in vec3 v_color;
+
+layout(location = 0) out vec4 fragColor;
+
+void main()
+{
+ fragColor = vec4(v_color, 1.0);
+}
diff --git a/tests/playground/color.vert b/tests/playground/color.vert
new file mode 100644
index 0000000..02492c0
--- /dev/null
+++ b/tests/playground/color.vert
@@ -0,0 +1,18 @@
+#version 440
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec3 color;
+
+layout(location = 0) out vec3 v_color;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ v_color = color;
+ gl_Position = ubuf.mvp * position;
+}
diff --git a/tests/playground/color_pc.frag b/tests/playground/color_pc.frag
new file mode 100644
index 0000000..3b04955
--- /dev/null
+++ b/tests/playground/color_pc.frag
@@ -0,0 +1,12 @@
+#version 440
+
+layout(push_constant) uniform PC {
+ layout(offset = 64) vec3 color;
+} pc;
+
+layout(location = 0) out vec4 fragColor;
+
+void main()
+{
+ fragColor = vec4(pc.color, 1.0);
+}
diff --git a/tests/playground/color_pc.vert b/tests/playground/color_pc.vert
new file mode 100644
index 0000000..19bf815
--- /dev/null
+++ b/tests/playground/color_pc.vert
@@ -0,0 +1,14 @@
+#version 440
+
+layout(location = 0) in vec4 position;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+layout(push_constant) uniform PC {
+ mat4 mvp;
+} pc;
+
+void main()
+{
+ gl_Position = pc.mvp * position;
+}
diff --git a/tests/playground/color_phong.frag b/tests/playground/color_phong.frag
new file mode 100644
index 0000000..8b0c715
--- /dev/null
+++ b/tests/playground/color_phong.frag
@@ -0,0 +1,39 @@
+#version 440
+
+layout(location = 0) in vec3 vECVertNormal;
+layout(location = 1) in vec3 vECVertPos;
+layout(location = 2) flat in vec3 vDiffuseAdjust;
+
+layout(std140, binding = 1) uniform buf {
+ vec3 ECCameraPosition;
+ vec3 ka;
+ vec3 kd;
+ vec3 ks;
+ // Have one light only for now.
+ vec3 ECLightPosition;
+ vec3 attenuation;
+ vec3 color;
+ float intensity;
+ float specularExp;
+} ubuf;
+
+layout(location = 0) out vec4 fragColor;
+
+void main()
+{
+ vec3 unnormL = ubuf.ECLightPosition - vECVertPos;
+ float dist = length(unnormL);
+ float att = 1.0 / (ubuf.attenuation.x + ubuf.attenuation.y * dist + ubuf.attenuation.z * dist * dist);
+
+ vec3 N = normalize(vECVertNormal);
+ vec3 L = normalize(unnormL);
+ float NL = max(0.0, dot(N, L));
+ vec3 dColor = att * ubuf.intensity * ubuf.color * NL;
+
+ vec3 R = reflect(-L, N);
+ vec3 V = normalize(ubuf.ECCameraPosition - vECVertPos);
+ float RV = max(0.0, dot(R, V));
+ vec3 sColor = att * ubuf.intensity * ubuf.color * pow(RV, ubuf.specularExp);
+
+ fragColor = vec4(ubuf.ka + (ubuf.kd + vDiffuseAdjust) * dColor + ubuf.ks * sColor, 1.0);
+}
diff --git a/tests/playground/color_phong.vert b/tests/playground/color_phong.vert
new file mode 100644
index 0000000..a1d1552
--- /dev/null
+++ b/tests/playground/color_phong.vert
@@ -0,0 +1,32 @@
+#version 440
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec3 normal;
+
+// Instanced attributes to variate the translation of the model and the diffuse
+// color of the material.
+layout(location = 2) in vec3 instTranslate;
+layout(location = 3) in vec3 instDiffuseAdjust;
+
+out gl_PerVertex { vec4 gl_Position; };
+layout(location = 0) out vec3 vECVertNormal;
+layout(location = 1) out vec3 vECVertPos;
+layout(location = 2) flat out vec3 vDiffuseAdjust;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 vp;
+ mat4 model;
+ mat3 modelNormal;
+} ubuf;
+
+void main()
+{
+ vECVertNormal = normalize(ubuf.modelNormal * normal);
+ mat4 t = mat4(1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ instTranslate.x, instTranslate.y, instTranslate.z, 1);
+ vECVertPos = vec3(t * ubuf.model * position);
+ vDiffuseAdjust = instDiffuseAdjust;
+ gl_Position = ubuf.vp * t * ubuf.model * position;
+}
diff --git a/tests/playground/fragcolor.inc b/tests/playground/fragcolor.inc
new file mode 100644
index 0000000..26ca216
--- /dev/null
+++ b/tests/playground/fragcolor.inc
@@ -0,0 +1 @@
+ fragColor = vec4(v_color, 1.0);
diff --git a/tests/playground/hlsl_cbuf_error.frag b/tests/playground/hlsl_cbuf_error.frag
new file mode 100644
index 0000000..ba59ffa
--- /dev/null
+++ b/tests/playground/hlsl_cbuf_error.frag
@@ -0,0 +1,47 @@
+#version 440
+
+layout(location = 0) in vec3 vECVertNormal;
+layout(location = 1) in vec3 vECVertPos;
+layout(location = 2) flat in vec3 vDiffuseAdjust;
+
+#define MAX_LIGHTS 10
+
+struct Light {
+ vec3 ECLightPosition;
+ vec3 attenuation;
+ vec3 color;
+ float intensity;
+ float specularExp;
+ // this is not translatable to HLSL
+};
+
+layout(std140, binding = 1) uniform buf {
+ vec3 ECCameraPosition;
+ vec3 ka;
+ vec3 kd;
+ vec3 ks;
+ Light lights[MAX_LIGHTS];
+ int numLights;
+ layout(row_major) mat3 mm;
+} ubuf;
+
+layout(location = 0) out vec4 fragColor;
+
+void main()
+{
+ vec3 unnormL = ubuf.lights[0].ECLightPosition - vECVertPos;
+ float dist = length(unnormL);
+ float att = 1.0 / (ubuf.lights[0].attenuation.x + ubuf.lights[0].attenuation.y * dist + ubuf.lights[0].attenuation.z * dist * dist);
+
+ vec3 N = normalize(vECVertNormal);
+ vec3 L = normalize(unnormL);
+ float NL = max(0.0, dot(N, L));
+ vec3 dColor = att * ubuf.lights[0].intensity * ubuf.lights[0].color * NL;
+
+ vec3 R = reflect(-L, N);
+ vec3 V = normalize(ubuf.ECCameraPosition - vECVertPos);
+ float RV = max(0.0, dot(R, V));
+ vec3 sColor = att * ubuf.lights[0].intensity * ubuf.lights[0].color * pow(RV, ubuf.lights[0].specularExp);
+
+ fragColor = vec4(ubuf.ka + (ubuf.kd + vDiffuseAdjust) * dColor + ubuf.ks * sColor, 1.0);
+}
diff --git a/tests/playground/includetest.frag b/tests/playground/includetest.frag
new file mode 100644
index 0000000..99dceae
--- /dev/null
+++ b/tests/playground/includetest.frag
@@ -0,0 +1,10 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+layout(location = 0) in vec3 v_color;
+layout(location = 0) out vec4 fragColor;
+
+void main()
+{
+#include "fragcolor.inc"
+}
diff --git a/tests/playground/texture.frag b/tests/playground/texture.frag
new file mode 100644
index 0000000..e6021fe
--- /dev/null
+++ b/tests/playground/texture.frag
@@ -0,0 +1,12 @@
+#version 440
+
+layout(location = 0) in vec2 v_texcoord;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D tex;
+
+void main()
+{
+ fragColor = texture(tex, v_texcoord);
+}
diff --git a/tests/playground/texture.vert b/tests/playground/texture.vert
new file mode 100644
index 0000000..de486cb
--- /dev/null
+++ b/tests/playground/texture.vert
@@ -0,0 +1,18 @@
+#version 440
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoord;
+
+layout(location = 0) out vec2 v_texcoord;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ v_texcoord = texcoord;
+ gl_Position = ubuf.mvp * position;
+}
diff --git a/tests/tests.pro b/tests/tests.pro
new file mode 100644
index 0000000..f887d53
--- /dev/null
+++ b/tests/tests.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+
+!package: SUBDIRS += auto
diff --git a/tools/qsb/qsb.cpp b/tools/qsb/qsb.cpp
new file mode 100644
index 0000000..66435c5
--- /dev/null
+++ b/tools/qsb/qsb.cpp
@@ -0,0 +1,490 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qcommandlineparser.h>
+#include <QtCore/qtextstream.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qtemporarydir.h>
+#include <QtCore/qprocess.h>
+#include <QtCore/qdebug.h>
+#include <QtShaderTools/qshaderbaker.h>
+
+static bool writeToFile(const QByteArray &buf, const QString &filename, bool text = false)
+{
+ QFile f(filename);
+ QIODevice::OpenMode flags = QIODevice::WriteOnly;
+ if (text)
+ flags |= QIODevice::Text;
+ if (!f.open(flags)) {
+ qWarning("Failed to open %s for writing", qPrintable(filename));
+ return false;
+ }
+ f.write(buf);
+ return true;
+}
+
+static QByteArray readFile(const QString &filename, bool text = false)
+{
+ QFile f(filename);
+ QIODevice::OpenMode flags = QIODevice::ReadOnly;
+ if (text)
+ flags |= QIODevice::Text;
+ if (!f.open(flags)) {
+ qWarning("Failed to open %s", qPrintable(filename));
+ return QByteArray();
+ }
+ return f.readAll();
+}
+
+static bool runProcess(const QString &cmd, QByteArray *output, QByteArray *errorOutput)
+{
+ QProcess p;
+ p.start(cmd);
+ if (!p.waitForFinished()) {
+ qWarning("Failed to run %s", qPrintable(cmd));
+ return false;
+ }
+
+ if (p.exitStatus() == QProcess::CrashExit) {
+ qWarning("%s crashed", qPrintable(cmd));
+ return false;
+ }
+
+ *output = p.readAllStandardOutput();
+ *errorOutput = p.readAllStandardError();
+
+ if (p.exitCode() != 0) {
+ qWarning("%s returned non-zero error code %d", qPrintable(cmd), p.exitCode());
+ return false;
+ }
+
+ return true;
+}
+
+static QString stageStr(QRhiShader::ShaderStage stage)
+{
+ switch (stage) {
+ case QRhiShader::VertexStage:
+ return QStringLiteral("Vertex");
+ case QRhiShader::TessControlStage:
+ return QStringLiteral("TessControl");
+ case QRhiShader::TessEvaluationStage:
+ return QStringLiteral("TessEval");
+ case QRhiShader::GeometryStage:
+ return QStringLiteral("Geometry");
+ case QRhiShader::FragmentStage:
+ return QStringLiteral("Fragment");
+ case QRhiShader::ComputeStage:
+ return QStringLiteral("Compute");
+ default:
+ Q_UNREACHABLE();
+ }
+}
+
+static QString sourceStr(QRhiShaderKey::ShaderSource source)
+{
+ switch (source) {
+ case QRhiShaderKey::SpirvShader:
+ return QStringLiteral("SPIR-V");
+ case QRhiShaderKey::GlslShader:
+ return QStringLiteral("GLSL");
+ case QRhiShaderKey::HlslShader:
+ return QStringLiteral("HLSL");
+ case QRhiShaderKey::DxbcShader:
+ return QStringLiteral("DXBC");
+ case QRhiShaderKey::MslShader:
+ return QStringLiteral("MSL");
+ case QRhiShaderKey::DxilShader:
+ return QStringLiteral("DXIL");
+ case QRhiShaderKey::MetalLibShader:
+ return QStringLiteral("metallib");
+ default:
+ Q_UNREACHABLE();
+ }
+}
+
+static QString sourceVersionStr(const QRhiShaderVersion &v)
+{
+ QString s = v.version() ? QString::number(v.version()) : QString();
+ if (v.flags().testFlag(QRhiShaderVersion::GlslEs))
+ s += QLatin1String(" es");
+
+ return s;
+}
+
+static QString sourceVariantStr(const QRhiShaderKey::ShaderVariant &v)
+{
+ switch (v) {
+ case QRhiShaderKey::StandardShader:
+ return QLatin1String("Standard");
+ case QRhiShaderKey::BatchableVertexShader:
+ return QLatin1String("Batchable");
+ default:
+ Q_UNREACHABLE();
+ }
+}
+
+static void dump(const QRhiShader &bs)
+{
+ QTextStream ts(stdout);
+ ts << "Stage: " << stageStr(bs.stage()) << "\n\n";
+ QList<QRhiShaderKey> s = bs.availableShaders();
+ ts << "Has " << s.count() << " shaders: (unordered list)\n";
+ for (int i = 0; i < s.count(); ++i) {
+ ts << " Shader " << i << ": " << sourceStr(s[i].source())
+ << " " << sourceVersionStr(s[i].sourceVersion())
+ << " [" << sourceVariantStr(s[i].sourceVariant()) << "]\n";
+ }
+ ts << "\n";
+ ts << "Reflection info: " << bs.description().toJson() << "\n\n";
+ for (int i = 0; i < s.count(); ++i) {
+ ts << "Shader " << i << ": " << sourceStr(s[i].source())
+ << " " << sourceVersionStr(s[i].sourceVersion())
+ << " [" << sourceVariantStr(s[i].sourceVariant()) << "]\n";
+ QRhiShaderCode shader = bs.shader(s[i]);
+ if (!shader.entryPoint().isEmpty())
+ ts << "Entry point: " << shader.entryPoint() << "\n";
+ ts << "Contents:\n";
+ switch (s[i].source()) {
+ case QRhiShaderKey::SpirvShader:
+ Q_FALLTHROUGH();
+ case QRhiShaderKey::DxbcShader:
+ Q_FALLTHROUGH();
+ case QRhiShaderKey::DxilShader:
+ Q_FALLTHROUGH();
+ case QRhiShaderKey::MetalLibShader:
+ ts << "Binary of " << shader.shader().size() << " bytes\n\n";
+ break;
+ default:
+ ts << shader.shader() << "\n";
+ break;
+ }
+ ts << "\n************************************\n\n";
+ }
+}
+
+static QByteArray fxcProfile(const QRhiShader &bs, const QRhiShaderKey &k)
+{
+ QByteArray t;
+
+ switch (bs.stage()) {
+ case QRhiShader::VertexStage:
+ t += QByteArrayLiteral("vs_");
+ break;
+ case QRhiShader::TessControlStage:
+ t += QByteArrayLiteral("hs_");
+ break;
+ case QRhiShader::TessEvaluationStage:
+ t += QByteArrayLiteral("ds_");
+ break;
+ case QRhiShader::GeometryStage:
+ t += QByteArrayLiteral("gs_");
+ break;
+ case QRhiShader::FragmentStage:
+ t += QByteArrayLiteral("ps_");
+ break;
+ case QRhiShader::ComputeStage:
+ t += QByteArrayLiteral("cs_");
+ break;
+ default:
+ break;
+ }
+
+ const int major = k.sourceVersion().version() / 10;
+ const int minor = k.sourceVersion().version() % 10;
+ t += QByteArray::number(major);
+ t += '_';
+ t += QByteArray::number(minor);
+
+ return t;
+}
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+
+ QCommandLineParser cmdLineParser;
+ cmdLineParser.setApplicationDescription(QObject::tr("Qt Shader Baker"));
+ cmdLineParser.addHelpOption();
+ cmdLineParser.addPositionalArgument(QLatin1String("file"), QObject::tr("Vulkan GLSL source file to compile"), QObject::tr("file"));
+ QCommandLineOption batchableOption({ "b", "batchable" }, QObject::tr("Also generates rewritten vertex shader for Qt Quick scene graph batching."));
+ cmdLineParser.addOption(batchableOption);
+ QCommandLineOption glslOption({ "g", "glsl" },
+ QObject::tr("Comma separated list of GLSL versions to generate. (for example, \"100 es,120,330\")"),
+ QObject::tr("glsl"));
+ cmdLineParser.addOption(glslOption);
+ QCommandLineOption hlslOption({ "l", "hlsl" },
+ QObject::tr("Comma separated list of HLSL (Shader Model) versions to generate. F.ex. 50 is 5.0, 51 is 5.1."),
+ QObject::tr("hlsl"));
+ cmdLineParser.addOption(hlslOption);
+ QCommandLineOption mslOption({ "m", "msl" },
+ QObject::tr("Comma separated list of Metal Shading Language versions to generate. F.ex. 12 is 1.2, 20 is 2.0."),
+ QObject::tr("msl"));
+ cmdLineParser.addOption(mslOption);
+ QCommandLineOption outputOption({ "o", "output" },
+ QObject::tr("Output file for the baked shader pack."),
+ QObject::tr("output"));
+ cmdLineParser.addOption(outputOption);
+ QCommandLineOption fxcOption({ "c", "fxc" }, QObject::tr("In combination with --hlsl invokes fxc to store DXBC instead of HLSL."));
+ cmdLineParser.addOption(fxcOption);
+ QCommandLineOption mtllibOption({ "t", "metallib" },
+ QObject::tr("In combination with --msl builds a Metal library with xcrun metal(lib) and stores that instead of the source."));
+ cmdLineParser.addOption(mtllibOption);
+ QCommandLineOption dumpOption({ "d", "dump" }, QObject::tr("Switches to dump mode. Input file is expected to be a baked shader pack."));
+ cmdLineParser.addOption(dumpOption);
+
+ cmdLineParser.process(app);
+
+ if (cmdLineParser.positionalArguments().isEmpty()) {
+ cmdLineParser.showHelp();
+ return 0;
+ }
+
+ QShaderBaker baker;
+ for (const QString &fn : cmdLineParser.positionalArguments()) {
+ if (cmdLineParser.isSet(dumpOption)) {
+ QByteArray buf = readFile(fn);
+ if (!buf.isEmpty()) {
+ QRhiShader bs = QRhiShader::fromSerialized(buf);
+ if (bs.isValid())
+ dump(bs);
+ else
+ qWarning("Failed to deserialize %s", qPrintable(fn));
+ }
+ continue;
+ }
+
+ baker.setSourceFileName(fn);
+
+ QVector<QRhiShaderKey::ShaderVariant> variants;
+ variants << QRhiShaderKey::StandardShader;
+ if (cmdLineParser.isSet(batchableOption))
+ variants << QRhiShaderKey::BatchableVertexShader;
+
+ baker.setGeneratedShaderVariants(variants);
+
+ QVector<QShaderBaker::GeneratedShader> genShaders;
+
+ genShaders << qMakePair(QRhiShaderKey::SpirvShader, QRhiShaderVersion(100));
+
+ if (cmdLineParser.isSet(glslOption)) {
+ const QStringList versions = cmdLineParser.value(glslOption).trimmed().split(',');
+ for (QString version : versions) {
+ QRhiShaderVersion::Flags flags = 0;
+ if (version.endsWith(QLatin1String(" es"))) {
+ version = version.left(version.count() - 3);
+ flags |= QRhiShaderVersion::GlslEs;
+ } else if (version.endsWith(QLatin1String("es"))) {
+ version = version.left(version.count() - 2);
+ flags |= QRhiShaderVersion::GlslEs;
+ }
+ bool ok = false;
+ int v = version.toInt(&ok);
+ if (ok)
+ genShaders << qMakePair(QRhiShaderKey::GlslShader, QRhiShaderVersion(v, flags));
+ else
+ qWarning("Ignoring invalid GLSL version %s", qPrintable(version));
+ }
+ }
+
+ if (cmdLineParser.isSet(hlslOption)) {
+ const QStringList versions = cmdLineParser.value(hlslOption).trimmed().split(',');
+ for (QString version : versions) {
+ bool ok = false;
+ int v = version.toInt(&ok);
+ if (ok)
+ genShaders << qMakePair(QRhiShaderKey::HlslShader, QRhiShaderVersion(v));
+ else
+ qWarning("Ignoring invalid HLSL (Shader Model) version %s", qPrintable(version));
+ }
+ }
+
+ if (cmdLineParser.isSet(mslOption)) {
+ const QStringList versions = cmdLineParser.value(mslOption).trimmed().split(',');
+ for (QString version : versions) {
+ bool ok = false;
+ int v = version.toInt(&ok);
+ if (ok)
+ genShaders << qMakePair(QRhiShaderKey::MslShader, QRhiShaderVersion(v));
+ else
+ qWarning("Ignoring invalid MSL version %s", qPrintable(version));
+ }
+ }
+
+ baker.setGeneratedShaders(genShaders);
+
+ QRhiShader bs = baker.bake();
+ if (!bs.isValid()) {
+ qWarning("Shader baking failed: %s", qPrintable(baker.errorMessage()));
+ return 1;
+ }
+
+ if (cmdLineParser.isSet(fxcOption)) {
+ QTemporaryDir tempDir;
+ if (!tempDir.isValid()) {
+ qWarning("Failed to create temporary directory");
+ return 1;
+ }
+ auto skeys = bs.availableShaders();
+ for (QRhiShaderKey &k : skeys) {
+ if (k.source() == QRhiShaderKey::HlslShader) {
+ QRhiShaderCode s = bs.shader(k);
+
+ const QString tmpIn = tempDir.path() + QLatin1String("/qsb_hlsl_temp");
+ const QString tmpOut = tempDir.path() + QLatin1String("/qsb_hlsl_temp_out");
+ QFile f(tmpIn);
+ if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ qWarning("Failed to create temporary file");
+ return 1;
+ }
+ f.write(s.shader());
+ f.close();
+
+ const QByteArray tempOutFileName = QDir::toNativeSeparators(tmpOut).toUtf8();
+ const QByteArray inFileName = QDir::toNativeSeparators(tmpIn).toUtf8();
+ const QByteArray typeArg = fxcProfile(bs, k);
+ const QByteArray entryPoint = s.entryPoint();
+ const QString cmd = QString::asprintf("fxc /nologo /E %s /T %s /Fo %s %s",
+ entryPoint.constData(),
+ typeArg.constData(),
+ tempOutFileName.constData(),
+ inFileName.constData());
+ qDebug("%s", qPrintable(cmd));
+ QByteArray output;
+ QByteArray errorOutput;
+ bool success = runProcess(cmd, &output, &errorOutput);
+ if (!success) {
+ if (!output.isEmpty() || !errorOutput.isEmpty()) {
+ qDebug("%s\n%s",
+ qPrintable(output.constData()),
+ qPrintable(errorOutput.constData()));
+ }
+ return 1;
+ }
+ f.setFileName(tmpOut);
+ if (!f.open(QIODevice::ReadOnly)) {
+ qWarning("Failed to open fxc output %s", qPrintable(tmpOut));
+ return 1;
+ }
+ const QByteArray bytecode = f.readAll();
+ f.close();
+
+ QRhiShaderKey dxbcKey = k;
+ dxbcKey.setSource(QRhiShaderKey::DxbcShader);
+ QRhiShaderCode dxbcShader(bytecode, s.entryPoint());
+ bs.setShader(dxbcKey, dxbcShader);
+ bs.removeShader(k);
+ }
+ }
+ }
+
+ if (cmdLineParser.isSet(mtllibOption)) {
+ QTemporaryDir tempDir;
+ if (!tempDir.isValid()) {
+ qWarning("Failed to create temporary directory");
+ return 1;
+ }
+ auto skeys = bs.availableShaders();
+ for (const QRhiShaderKey &k : skeys) {
+ if (k.source() == QRhiShaderKey::MslShader) {
+ QRhiShaderCode s = bs.shader(k);
+
+ const QString tmpIn = tempDir.path() + QLatin1String("/qsb_msl_temp.metal");
+ const QString tmpInterm = tempDir.path() + QLatin1String("/qsb_msl_temp_air");
+ const QString tmpOut = tempDir.path() + QLatin1String("/qsb_msl_temp_out");
+ QFile f(tmpIn);
+ if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ qWarning("Failed to create temporary file");
+ return 1;
+ }
+ f.write(s.shader());
+ f.close();
+
+ const QByteArray inFileName = QDir::toNativeSeparators(tmpIn).toUtf8();
+ const QByteArray tempIntermediateFileName = QDir::toNativeSeparators(tmpInterm).toUtf8();
+ qDebug("About to invoke xcrun with metal and metallib.\n"
+ " qsb is set up for XCode 10. For earlier versions the -c argument may need to be removed.\n"
+ " If getting unable to find utility \"metal\", do xcode-select --switch /Applications/Xcode.app/Contents/Developer");
+ QString cmd = QString::asprintf("xcrun -sdk macosx metal -c %s -o %s",
+ inFileName.constData(),
+ tempIntermediateFileName.constData());
+ qDebug("%s", qPrintable(cmd));
+ QByteArray output;
+ QByteArray errorOutput;
+ bool success = runProcess(cmd, &output, &errorOutput);
+ if (!success) {
+ if (!output.isEmpty() || !errorOutput.isEmpty()) {
+ qDebug("%s\n%s",
+ qPrintable(output.constData()),
+ qPrintable(errorOutput.constData()));
+ }
+ return 1;
+ }
+
+ const QByteArray tempOutFileName = QDir::toNativeSeparators(tmpOut).toUtf8();
+ cmd = QString::asprintf("xcrun -sdk macosx metallib %s -o %s",
+ tempIntermediateFileName.constData(),
+ tempOutFileName.constData());
+ qDebug("%s", qPrintable(cmd));
+ output.clear();
+ errorOutput.clear();
+ success = runProcess(cmd, &output, &errorOutput);
+ if (!success) {
+ if (!output.isEmpty() || !errorOutput.isEmpty()) {
+ qDebug("%s\n%s",
+ qPrintable(output.constData()),
+ qPrintable(errorOutput.constData()));
+ }
+ return 1;
+ }
+
+ f.setFileName(tmpOut);
+ if (!f.open(QIODevice::ReadOnly)) {
+ qWarning("Failed to open xcrun metallib output %s", qPrintable(tmpOut));
+ return 1;
+ }
+ const QByteArray bytecode = f.readAll();
+ f.close();
+
+ QRhiShaderKey mtlKey = k;
+ mtlKey.setSource(QRhiShaderKey::MetalLibShader);
+ QRhiShaderCode mtlShader(bytecode, s.entryPoint());
+ bs.setShader(mtlKey, mtlShader);
+ bs.removeShader(k);
+ }
+ }
+ }
+
+ if (cmdLineParser.isSet(outputOption))
+ writeToFile(bs.serialized(), cmdLineParser.value(outputOption));
+ }
+
+ return 0;
+}
diff --git a/tools/qsb/qsb.pro b/tools/qsb/qsb.pro
new file mode 100644
index 0000000..800e129
--- /dev/null
+++ b/tools/qsb/qsb.pro
@@ -0,0 +1,5 @@
+SOURCES += qsb.cpp
+
+QT += shadertools
+
+load(qt_tool)
diff --git a/tools/tools.pro b/tools/tools.pro
new file mode 100644
index 0000000..267dc98
--- /dev/null
+++ b/tools/tools.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+SUBDIRS += qsb