| // |
| // 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 |
| #ifdef ENABLE_HLSL |
| unsigned int vectorSize : 3; // vector return type size. |
| unsigned int getVectorSize() const { return vectorSize; } |
| void clearReturnStruct() { structReturnIndex = noReturnStruct; } |
| bool hasReturnStruct() const { return structReturnIndex != noReturnStruct; } |
| unsigned getStructReturnIndex() const { return structReturnIndex; } |
| |
| 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; |
| #else |
| unsigned int getVectorSize() const { return 4; } |
| void clearReturnStruct() const { } |
| bool hasReturnStruct() const { return false; } |
| unsigned getStructReturnIndex() const { return 0; } |
| #endif |
| |
| #ifdef GLSLANG_WEB |
| bool is1D() const { return false; } |
| bool isBuffer() const { return false; } |
| bool isRect() const { return false; } |
| bool isSubpass() const { return false; } |
| bool isCombined() const { return true; } |
| bool isPureSampler() const { return false; } |
| bool isTexture() const { return false; } |
| bool isImage() const { return false; } |
| bool isImageClass() const { return false; } |
| bool isMultiSample() const { return false; } |
| bool isExternal() const { return false; } |
| void setExternal(bool e) { } |
| bool isYuv() const { return false; } |
| #else |
| 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 |
| // 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. |
| |
| bool is1D() const { return dim == Esd1D; } |
| bool isBuffer() const { return dim == EsdBuffer; } |
| bool isRect() const { return dim == EsdRect; } |
| bool isSubpass() const { return dim == EsdSubpass; } |
| bool isCombined() const { return combined; } |
| bool isPureSampler() const { return sampler; } |
| bool isTexture() const { return !sampler && !image; } |
| bool isImage() const { return image && !isSubpass(); } |
| bool isImageClass() const { return image; } |
| bool isMultiSample() const { return ms; } |
| bool isExternal() const { return external; } |
| void setExternal(bool e) { external = e; } |
| bool isYuv() const { return yuv; } |
| #endif |
| void setCombined(bool c) { combined = c; } |
| bool isShadow() const { return shadow; } |
| bool isArrayed() const { return arrayed; } |
| |
| void clear() |
| { |
| type = EbtVoid; |
| dim = EsdNone; |
| arrayed = false; |
| shadow = false; |
| ms = false; |
| image = false; |
| combined = false; |
| #ifndef GLSLANG_WEB |
| sampler = false; |
| external = false; |
| yuv = false; |
| #endif |
| |
| #ifdef ENABLE_HLSL |
| clearReturnStruct(); |
| // by default, returns a single vec4; |
| vectorSize = 4; |
| #endif |
| } |
| |
| // 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; |
| } |
| |
| #ifndef GLSLANG_WEB |
| // 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; |
| } |
| #endif |
| |
| bool operator==(const TSampler& right) const |
| { |
| return type == right.type && |
| dim == right.dim && |
| arrayed == right.arrayed && |
| shadow == right.shadow && |
| isMultiSample() == right.isMultiSample() && |
| isImageClass() == right.isImageClass() && |
| isCombined() == right.isCombined() && |
| isPureSampler() == right.isPureSampler() && |
| isExternal() == right.isExternal() && |
| isYuv() == right.isYuv() && |
| getVectorSize() == right.getVectorSize() && |
| getStructReturnIndex() == right.getStructReturnIndex(); |
| } |
| |
| bool operator!=(const TSampler& right) const |
| { |
| return ! operator==(right); |
| } |
| |
| TString getString() const |
| { |
| TString s; |
| |
| if (isPureSampler()) { |
| s.append("sampler"); |
| return s; |
| } |
| |
| switch (type) { |
| case EbtInt: s.append("i"); break; |
| case EbtUint: s.append("u"); break; |
| #ifndef GLSLANG_WEB |
| case EbtFloat16: s.append("f16"); break; |
| 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 EbtInt64: s.append("i64"); break; |
| case EbtUint64: s.append("u64"); break; |
| #endif |
| default: break; |
| } |
| if (isImageClass()) { |
| if (isSubpass()) |
| s.append("subpass"); |
| else |
| s.append("image"); |
| } else if (isCombined()) { |
| s.append("sampler"); |
| } else { |
| s.append("texture"); |
| } |
| if (isExternal()) { |
| s.append("ExternalOES"); |
| return s; |
| } |
| if (isYuv()) { |
| return "__" + s + "External2DY2YEXT"; |
| } |
| switch (dim) { |
| case Esd2D: s.append("2D"); break; |
| case Esd3D: s.append("3D"); break; |
| case EsdCube: s.append("Cube"); break; |
| #ifndef GLSLANG_WEB |
| case Esd1D: s.append("1D"); break; |
| case EsdRect: s.append("2DRect"); break; |
| case EsdBuffer: s.append("Buffer"); break; |
| case EsdSubpass: s.append("Input"); break; |
| #endif |
| default: break; // some compilers want this |
| } |
| if (isMultiSample()) |
| 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 |
| }; |
| |
| enum TInterlockOrdering { |
| EioNone, |
| EioPixelInterlockOrdered, |
| EioPixelInterlockUnordered, |
| EioSampleInterlockOrdered, |
| EioSampleInterlockUnordered, |
| EioShadingRateInterlockOrdered, |
| EioShadingRateInterlockUnordered, |
| |
| EioCount, |
| }; |
| |
| class TQualifier { |
| public: |
| static const int layoutNotSet = -1; |
| |
| void clear() |
| { |
| precision = EpqNone; |
| invariant = false; |
| makeTemporary(); |
| declaredBuiltIn = EbvNone; |
| #ifndef GLSLANG_WEB |
| noContraction = false; |
| #endif |
| } |
| |
| // 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(); |
| #ifndef GLSLANG_WEB |
| patch = false; |
| sample = false; |
| #endif |
| } |
| |
| void clearInterpolation() |
| { |
| centroid = false; |
| smooth = false; |
| flat = false; |
| #ifndef GLSLANG_WEB |
| nopersp = false; |
| explicitInterp = false; |
| 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; |
| } |
| |
| const char* semanticName; |
| TStorageQualifier storage : 6; |
| TBuiltInVariable builtIn : 9; |
| TBuiltInVariable declaredBuiltIn : 9; |
| static_assert(EbvLast < 256, "need to increase size of TBuiltInVariable bitfields!"); |
| TPrecisionQualifier precision : 3; |
| bool invariant : 1; // require canonical treatment for cross-shader invariance |
| bool centroid : 1; |
| bool smooth : 1; |
| bool flat : 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; |
| // having a constant_id is not sufficient: expressions have no id, but are still specConstant |
| bool specConstant : 1; |
| bool nonUniform : 1; |
| |
| #ifdef GLSLANG_WEB |
| bool isWriteOnly() const { return false; } |
| bool isReadOnly() const { return false; } |
| bool isSample() const { return false; } |
| bool isMemory() const { return false; } |
| bool isMemoryQualifierImageAndSSBOOnly() const { return false; } |
| bool bufferReferenceNeedsVulkanMemoryModel() const { return false; } |
| bool isInterpolation() const { return flat || smooth; } |
| bool isExplicitInterpolation() const { return false; } |
| bool isAuxiliary() const { return centroid; } |
| bool isPatch() const { return false; } |
| bool isNoContraction() const { return false; } |
| void setNoContraction() { } |
| bool isPervertexNV() const { return false; } |
| #else |
| bool noContraction: 1; // prevent contraction and reassociation, e.g., for 'precise' keyword, and expressions it affects |
| bool nopersp : 1; |
| bool explicitInterp : 1; |
| bool pervertexNV : 1; |
| bool perPrimitiveNV : 1; |
| bool perViewNV : 1; |
| bool perTaskNV : 1; |
| bool patch : 1; |
| bool sample : 1; |
| bool isWriteOnly() const { return writeonly; } |
| bool isReadOnly() const { return readonly; } |
| bool isSample() const { return sample; } |
| 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 |
| { |
| return flat || smooth || nopersp || explicitInterp; |
| } |
| bool isExplicitInterpolation() const |
| { |
| return explicitInterp; |
| } |
| bool isAuxiliary() const |
| { |
| return centroid || patch || sample || pervertexNV; |
| } |
| bool isPatch() const { return patch; } |
| bool isNoContraction() const { return noContraction; } |
| void setNoContraction() { noContraction = true; } |
| bool isPervertexNV() const { return pervertexNV; } |
| #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 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; |
| } |
| } |
| |
| #ifdef GLSLANG_WEB |
| bool isPerView() const { return false; } |
| bool isTaskMemory() const { return false; } |
| bool isArrayedIo(EShLanguage language) const { return false; } |
| #else |
| bool isPerPrimitive() const { return perPrimitiveNV; } |
| bool isPerView() const { return perViewNV; } |
| bool isTaskMemory() const { return perTaskNV; } |
| |
| // 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(); |
| case EShLangFragment: |
| return pervertexNV && isPipeInput(); |
| case EShLangMeshNV: |
| return ! perTaskNV && isPipeOutput(); |
| |
| default: |
| return false; |
| } |
| } |
| #endif |
| |
| // Implementing an embedded layout-qualifier class here, since C++ can't have a real class bitfield |
| void clearLayout() // all layout |
| { |
| clearUniformLayout(); |
| |
| #ifndef GLSLANG_WEB |
| layoutPushConstant = false; |
| layoutBufferReference = false; |
| layoutPassthrough = false; |
| layoutViewportRelative = false; |
| // -2048 as the default value indicating layoutSecondaryViewportRelative is not set |
| layoutSecondaryViewportRelativeOffset = -2048; |
| layoutShaderRecordNV = false; |
| layoutBufferReferenceAlign = layoutBufferReferenceAlignEnd; |
| layoutFormat = ElfNone; |
| #endif |
| |
| clearInterstageLayout(); |
| |
| layoutSpecConstantId = layoutSpecConstantIdEnd; |
| } |
| void clearInterstageLayout() |
| { |
| layoutLocation = layoutLocationEnd; |
| layoutComponent = layoutComponentEnd; |
| layoutIndex = layoutIndexEnd; |
| #ifndef GLSLANG_WEB |
| clearStreamLayout(); |
| clearXfbLayout(); |
| #endif |
| } |
| |
| #ifndef GLSLANG_WEB |
| void clearStreamLayout() |
| { |
| layoutStream = layoutStreamEnd; |
| } |
| void clearXfbLayout() |
| { |
| layoutXfbBuffer = layoutXfbBufferEnd; |
| layoutXfbStride = layoutXfbStrideEnd; |
| layoutXfbOffset = layoutXfbOffsetEnd; |
| } |
| #endif |
| |
| bool hasNonXfbLayout() const |
| { |
| return hasUniformLayout() || |
| hasAnyLocation() || |
| hasStream() || |
| hasFormat() || |
| isShaderRecordNV() || |
| isPushConstant() || |
| hasBufferReference(); |
| } |
| 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; |
| |
| #ifndef GLSLANG_WEB |
| // 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; |
| 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 hasAlign() const |
| { |
| return layoutAlign != layoutNotSet; |
| } |
| bool hasAnyLocation() const |
| { |
| return hasLocation() || |
| hasComponent() || |
| hasIndex(); |
| } |
| bool hasLocation() const |
| { |
| return layoutLocation != layoutLocationEnd; |
| } |
| bool hasSet() const |
| { |
| return layoutSet != layoutSetEnd; |
| } |
| bool hasBinding() const |
| { |
| return layoutBinding != layoutBindingEnd; |
| } |
| #ifdef GLSLANG_WEB |
| bool hasOffset() const { return false; } |
| bool isNonPerspective() const { return false; } |
| bool hasIndex() const { return false; } |
| bool hasComponent() const { return false; } |
| bool hasStream() const { return false; } |
| bool hasFormat() const { return false; } |
| bool hasXfb() const { return false; } |
| bool hasXfbBuffer() const { return false; } |
| bool hasXfbStride() const { return false; } |
| bool hasXfbOffset() const { return false; } |
| bool hasAttachment() const { return false; } |
| TLayoutFormat getFormat() const { return ElfNone; } |
| bool isPushConstant() const { return false; } |
| bool isShaderRecordNV() const { return false; } |
| bool hasBufferReference() const { return false; } |
| bool hasBufferReferenceAlign() const { return false; } |
| bool isNonUniform() const { return false; } |
| #else |
| bool hasOffset() const |
| { |
| return layoutOffset != layoutNotSet; |
| } |
| bool isNonPerspective() const { return nopersp; } |
| bool hasIndex() const |
| { |
| return layoutIndex != layoutIndexEnd; |
| } |
| bool hasComponent() const |
| { |
| return layoutComponent != layoutComponentEnd; |
| } |
| 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; |
| } |
| TLayoutFormat getFormat() const { return layoutFormat; } |
| bool isPushConstant() const { return layoutPushConstant; } |
| bool isShaderRecordNV() const { return layoutShaderRecordNV; } |
| bool hasBufferReference() const { return layoutBufferReference; } |
| bool hasBufferReferenceAlign() const |
| { |
| return layoutBufferReferenceAlign != layoutBufferReferenceAlignEnd; |
| } |
| bool isNonUniform() const |
| { |
| return nonUniform; |
| } |
| #endif |
| 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 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 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 ElpStd140: return "std140"; |
| #ifndef GLSLANG_WEB |
| case ElpPacked: return "packed"; |
| case ElpShared: return "shared"; |
| case ElpStd430: return "std430"; |
| case ElpScalar: return "scalar"; |
| #endif |
| default: return "none"; |
| } |
| } |
| static const char* getLayoutMatrixString(TLayoutMatrix m) |
| { |
| switch (m) { |
| case ElmColumnMajor: return "column_major"; |
| case ElmRowMajor: return "row_major"; |
| default: return "none"; |
| } |
| } |
| #ifdef GLSLANG_WEB |
| static const char* getLayoutFormatString(TLayoutFormat f) { return "none"; } |
| #else |
| 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; |
| } |
| } |
| static const char* getInterlockOrderingString(TInterlockOrdering order) |
| { |
| switch (order) { |
| case EioPixelInterlockOrdered: return "pixel_interlock_ordered"; |
| case EioPixelInterlockUnordered: return "pixel_interlock_unordered"; |
| case EioSampleInterlockOrdered: return "sample_interlock_ordered"; |
| case EioSampleInterlockUnordered: return "sample_interlock_unordered"; |
| case EioShadingRateInterlockOrdered: return "shading_rate_interlock_ordered"; |
| case EioShadingRateInterlockUnordered: return "shading_rate_interlock_unordered"; |
| default: return "none"; |
| } |
| } |
| #endif |
| }; |
| |
| // 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 |
| bool localSizeNotDefault[3]; // compute shader |
| int localSizeSpecId[3]; // compute shader specialization id for gl_WorkGroupSize |
| #ifndef GLSLANG_WEB |
| bool earlyFragmentTests; // fragment input |
| bool postDepthCoverage; // fragment input |
| TLayoutDepth layoutDepth; |
| bool blendEquation; // true if any blend equation was specified |
| int numViews; // multiview extenstions |
| TInterlockOrdering interlockOrdering; |
| 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 |
| TLayoutDepth getDepth() const { return layoutDepth; } |
| #else |
| TLayoutDepth getDepth() const { return EldNone; } |
| #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; |
| localSizeNotDefault[0] = false; |
| localSizeNotDefault[1] = false; |
| localSizeNotDefault[2] = false; |
| localSizeSpecId[0] = TQualifier::layoutNotSet; |
| localSizeSpecId[1] = TQualifier::layoutNotSet; |
| localSizeSpecId[2] = TQualifier::layoutNotSet; |
| #ifndef GLSLANG_WEB |
| earlyFragmentTests = false; |
| postDepthCoverage = false; |
| layoutDepth = EldNone; |
| blendEquation = false; |
| numViews = TQualifier::layoutNotSet; |
| layoutOverrideCoverage = false; |
| layoutDerivativeGroupQuads = false; |
| layoutDerivativeGroupLinear = false; |
| primitives = TQualifier::layoutNotSet; |
| interlockOrdering = EioNone; |
| #endif |
| } |
| |
| #ifdef GLSLANG_WEB |
| bool hasBlendEquation() const { return false; } |
| #else |
| bool hasBlendEquation() const { return blendEquation; } |
| #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) { |
| localSizeNotDefault[i] = src.localSizeNotDefault[i] || localSizeNotDefault[i]; |
| } |
| for (int i = 0; i < 3; ++i) { |
| if (src.localSizeSpecId[i] != TQualifier::layoutNotSet) |
| localSizeSpecId[i] = src.localSizeSpecId[i]; |
| } |
| #ifndef GLSLANG_WEB |
| 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; |
| 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; |
| if (src.interlockOrdering != EioNone) |
| interlockOrdering = src.interlockOrdering; |
| #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; |
| |
| #ifdef GLSLANG_WEB |
| bool isCoopmat() const { return false; } |
| #else |
| bool isCoopmat() const { return coopmat; } |
| #endif |
| |
| 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.isCoopmat() && p.typeParameters && p.typeParameters->getNumDims() > 0) { |
| int numBits = p.typeParameters->getDimSize(0); |
| if (p.basicType == EbtFloat && numBits == 16) { |
| basicType = EbtFloat16; |
| qualifier.precision = EpqNone; |
| } else if (p.basicType == EbtUint && numBits == 8) { |
| basicType = EbtUint8; |
| qualifier.precision = EpqNone; |
| } else if (p.basicType == EbtInt && numBits == 8) { |
| basicType = EbtInt8; |
| 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.isCoopMat(); |
| } |
| |
| // 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(); } |
| #ifdef GLSLANG_WEB |
| bool isArrayOfArrays() const { return false; } |
| #else |
| bool isArrayOfArrays() const { return arraySizes != nullptr && arraySizes->getNumDims() > 1; } |
| #endif |
| 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 |
| #ifndef GLSLANG_WEB |
| || basicType == EbtAtomicUint || 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(); } |
| // Check the block-name convention of creating a block without populating it's members: |
| virtual bool isUnusableName() const { return isStruct() && structure == nullptr; } |
| virtual bool isParameterized() const { return typeParameters != nullptr; } |
| #ifdef GLSLANG_WEB |
| bool isAtomic() const { return false; } |
| bool isCoopMat() const { return false; } |
| bool isReference() const { return false; } |
| #else |
| bool isAtomic() const { return basicType == EbtAtomicUint; } |
| bool isCoopMat() const { return coopmat; } |
| bool isReference() const { return getBasicType() == EbtReference; } |
| #endif |
| |
| // 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(); } ); |
| } |
| |
| #ifdef GLSLANG_WEB |
| bool containsDouble() const { return false; } |
| bool contains16BitFloat() const { return false; } |
| bool contains64BitInt() const { return false; } |
| bool contains16BitInt() const { return false; } |
| bool contains8BitInt() const { return false; } |
| bool containsCoopMat() const { return false; } |
| bool containsReference() const { return false; } |
| #else |
| bool containsDouble() const |
| { |
| return containsBasicType(EbtDouble); |
| } |
| bool contains16BitFloat() const |
| { |
| return containsBasicType(EbtFloat16); |
| } |
| bool contains64BitInt() const |
| { |
| return containsBasicType(EbtInt64) || containsBasicType(EbtUint64); |
| } |
| bool contains16BitInt() const |
| { |
| return containsBasicType(EbtInt16) || containsBasicType(EbtUint16); |
| } |
| bool contains8BitInt() const |
| { |
| return containsBasicType(EbtInt8) || containsBasicType(EbtUint8); |
| } |
| bool containsCoopMat() const |
| { |
| return contains([](const TType* t) { return t->coopmat; } ); |
| } |
| bool containsReference() const |
| { |
| return containsBasicType(EbtReference); |
| } |
| #endif |
| |
| // 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()); |
| // For multi-dim per-view arrays, set unsized inner dimension size to 1 |
| if (qualifier.isPerView() && arraySizes && arraySizes->isInnerUnsized()) |
| arraySizes->clearInnerUnsized(); |
| 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 EbtFloat: return "float"; |
| case EbtInt: return "int"; |
| case EbtUint: return "uint"; |
| #ifndef GLSLANG_WEB |
| case EbtVoid: return "void"; |
| 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 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"; |
| case EbtAccStructNV: return "accelerationStructureNV"; |
| case EbtReference: return "reference"; |
| #endif |
| default: return "unknown type"; |
| } |
| } |
| |
| #ifdef GLSLANG_WEB |
| TString getCompleteString() const { return ""; } |
| const char* getStorageQualifierString() const { return ""; } |
| const char* getBuiltInVariableString() const { return ""; } |
| const char* getPrecisionQualifierString() const { return ""; } |
| TString getBasicTypeString() const { return ""; } |
| #else |
| 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); |
| } |
| |
| if (qualifier.layoutPassthrough) |
| appendStr(" passthrough"); |
| if (qualifier.layoutViewportRelative) |
| appendStr(" layoutViewportRelative"); |
| if (qualifier.layoutSecondaryViewportRelativeOffset != -2048) { |
| appendStr(" layoutSecondaryViewportRelativeOffset="); |
| appendInt(qualifier.layoutSecondaryViewportRelativeOffset); |
| } |
| if (qualifier.layoutShaderRecordNV) |
| appendStr(" shaderRecordNV"); |
| |
| 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"); |
| if (qualifier.explicitInterp) |
| appendStr(" __explicitInterpAMD"); |
| if (qualifier.pervertexNV) |
| appendStr(" pervertexNV"); |
| if (qualifier.perPrimitiveNV) |
| appendStr(" perprimitiveNV"); |
| if (qualifier.perViewNV) |
| appendStr(" perviewNV"); |
| if (qualifier.perTaskNV) |
| appendStr(" taskNV"); |
| 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() && structure) { |
| appendStr("{"); |
| bool hasHiddenMember = true; |
| for (size_t i = 0; i < structure->size(); ++i) { |
| if (! (*structure)[i].type->hiddenMember()) { |
| if (!hasHiddenMember) |
| appendStr(", "); |
| typeString.append((*structure)[i].type->getCompleteString()); |
| typeString.append(" "); |
| typeString.append((*structure)[i].type->getFieldName()); |
| hasHiddenMember = false; |
| } |
| } |
| 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); } |
| #endif |
| |
| 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 (isReference() != right.isReference()) |
| return false; |
| |
| if (!isReference() && !right.isReference()) |
| 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 && |
| isCoopMat() == right.isCoopMat() && |
| 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 isCoopMat() && right.isCoopMat() && (getBasicType() == right.getBasicType()) && |
| typeParameters == nullptr && right.typeParameters != nullptr; |
| } |
| |
| bool sameCoopMatBaseType(const TType &right) const { |
| bool rv = coopmat && right.coopmat; |
| if (getBasicType() == EbtFloat || getBasicType() == EbtFloat16) |
| rv = right.getBasicType() == EbtFloat || right.getBasicType() == EbtFloat16; |
| else if (getBasicType() == EbtUint || getBasicType() == EbtUint8) |
| rv = right.getBasicType() == EbtUint || right.getBasicType() == EbtUint8; |
| else if (getBasicType() == EbtInt || getBasicType() == EbtInt8) |
| rv = right.getBasicType() == EbtInt || right.getBasicType() == EbtInt8; |
| else |
| rv = false; |
| return rv; |
| } |
| |
| |
| // 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 |
| { |
| #ifndef GLSLANG_WEB |
| if (getBasicType() == glslang::EbtReference) { |
| return getReferentType()->getQualifier().hasBufferReferenceAlign() ? |
| (1u << getReferentType()->getQualifier().layoutBufferReferenceAlign) : 16u; |
| } |
| #endif |
| 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_ |