// //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 _TYPES_INCLUDED #define _TYPES_INCLUDED #include "../Include/Common.h" #include "../Include/BaseTypes.h" #include "../Public/ShaderLang.h" #include "arrays.h" 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, 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 external : 1; // GL_OES_EGL_image_external void clear() { type = EbtVoid; dim = EsdNone; arrayed = false; shadow = false; ms = false; image = false; external = false; } // 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; } // 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; } 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 && external == right.external; } bool operator!=(const TSampler& right) const { return ! operator==(right); } TString getString() const { TString s; switch (type) { case EbtFloat: break; case EbtInt: s.append("i"); break; case EbtUint: s.append("u"); break; default: break; // some compilers want this } if (image) { s.append("image"); } else { s.append("sampler"); } if (external) { s.append("ExternalOES"); return s; } 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; 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 TTypeList; typedef TVector 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, 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; makeTemporary(); } // drop qualifiers that don't belong in a temporary variable void makeTemporary() { storage = EvqTemporary; builtIn = EbvNone; centroid = false; smooth = false; flat = false; nopersp = false; patch = false; sample = false; coherent = false; volatil = false; restrict = false; readonly = false; writeonly = false; clearLayout(); } TStorageQualifier storage : 6; TBuiltInVariable builtIn : 8; TPrecisionQualifier precision : 3; bool invariant : 1; bool centroid : 1; bool smooth : 1; bool flat : 1; bool nopersp : 1; bool patch : 1; bool sample : 1; bool coherent : 1; bool volatil : 1; bool restrict : 1; bool readonly : 1; bool writeonly : 1; bool isMemory() const { return coherent || volatil || restrict || readonly || writeonly; } bool isInterpolation() const { return flat || smooth || nopersp; } bool isAuxiliary() const { return centroid || patch || sample; } 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; } } // 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(); default: return false; } } // Implementing an embedded layout-qualifier class here, since C++ can't have a real class bitfield void clearLayout() { layoutMatrix = ElmNone; layoutPacking = ElpNone; layoutOffset = layoutNotSet; layoutAlign = layoutNotSet; layoutLocation = layoutLocationEnd; layoutComponent = layoutComponentEnd; layoutSet = layoutSetEnd; layoutBinding = layoutBindingEnd; layoutIndex = layoutIndexEnd; layoutStream = layoutStreamEnd; layoutXfbBuffer = layoutXfbBufferEnd; layoutXfbStride = layoutXfbStrideEnd; layoutXfbOffset = layoutXfbOffsetEnd; layoutFormat = ElfNone; } bool hasLayout() const { return hasUniformLayout() || hasAnyLocation() || hasBinding() || hasStream() || hasXfb() || hasFormat(); } 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 : 8; static const unsigned int layoutBindingEnd = 0xFF; 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 : 10; static const unsigned int layoutXfbStrideEnd = 0x3FF; unsigned int layoutXfbOffset : 10; static const unsigned int layoutXfbOffsetEnd = 0x3FF; TLayoutFormat layoutFormat : 8; bool hasUniformLayout() const { return hasMatrix() || hasPacking() || hasOffset() || hasBinding() || hasAlign(); } 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; } static const char* getLayoutPackingString(TLayoutPacking packing) { switch (packing) { case ElpPacked: return "packed"; case ElpShared: return "shared"; case ElpStd140: return "std140"; case ElpStd430: return "std430"; 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 "rgb10a2ui"; 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; // both for tessellation "vertices" and geometry "max_vertices" TVertexSpacing spacing; TVertexOrder order; bool pointMode; int localSize[3]; // compute shader bool earlyFragmentTests; // fragment input TLayoutDepth layoutDepth; bool blendEquation; // true if any blend equation was specified 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; earlyFragmentTests = false; layoutDepth = EldNone; blendEquation = false; } // 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]; } if (src.earlyFragmentTests) earlyFragmentTests = true; if (src.layoutDepth) layoutDepth = src.layoutDepth; if (src.blendEquation) blendEquation = src.blendEquation; } }; // // 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; TArraySizes* arraySizes; const TType* userDef; TSourceLoc loc; void initType(const TSourceLoc& l) { basicType = EbtVoid; vectorSize = 1; matrixRows = 0; matrixCols = 0; arraySizes = nullptr; userDef = nullptr; loc = l; } void initQualifiers(bool global = false) { qualifier.clear(); if (global) qualifier.storage = EvqGlobal; } void init(const TSourceLoc& loc, bool global = false) { initType(loc); 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; } bool isImage() const { return basicType == EbtSampler && sampler.image; } }; // // 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) : basicType(t), vectorSize(vs), matrixCols(mc), matrixRows(mr), arraySizes(nullptr), structure(nullptr), fieldName(nullptr), typeName(nullptr) { sampler.clear(); qualifier.clear(); qualifier.storage = q; } // for explicit precision qualifier TType(TBasicType t, TStorageQualifier q, TPrecisionQualifier p, int vs = 1, int mc = 0, int mr = 0) : basicType(t), vectorSize(vs), matrixCols(mc), matrixRows(mr), arraySizes(nullptr), structure(nullptr), fieldName(nullptr), typeName(nullptr) { sampler.clear(); qualifier.clear(); qualifier.storage = q; qualifier.precision = p; assert(p >= 0 && p <= EpqHigh); } // 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), arraySizes(p.arraySizes), structure(nullptr), fieldName(nullptr), typeName(nullptr) { if (basicType == EbtSampler) sampler = p.sampler; else sampler.clear(); qualifier = p.qualifier; if (p.userDef) { structure = p.userDef->getWritableStruct(); // public type is short-lived; there are no sharing issues typeName = NewPoolTString(p.userDef->getTypeName().c_str()); } } // 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) { if (rowMajor) vectorSize = matrixCols; else vectorSize = matrixRows; matrixCols = 0; matrixRows = 0; } else if (vectorSize > 1) vectorSize = 1; } } // for making structures, ... TType(TTypeList* userDef, const TString& n) : basicType(EbtStruct), vectorSize(1), matrixCols(0), matrixRows(0), arraySizes(nullptr), structure(userDef), fieldName(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), qualifier(q), arraySizes(nullptr), structure(userDef), fieldName(nullptr) { sampler.clear(); typeName = NewPoolTString(n.c_str()); } 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; arraySizes = copyOf.arraySizes; // copying the pointer only, not the contents structure = copyOf.structure; fieldName = copyOf.fieldName; typeName = copyOf.typeName; } void deepCopy(const TType& copyOf) { shallowCopy(copyOf); if (copyOf.arraySizes) { arraySizes = new TArraySizes; *arraySizes = *copyOf.arraySizes; } if (copyOf.structure) { structure = new TTypeList; 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); structure->push_back(typeLoc); } } if (copyOf.fieldName) fieldName = NewPoolTString(copyOf.fieldName->c_str()); if (copyOf.typeName) typeName = NewPoolTString(copyOf.typeName->c_str()); } TType* clone() { TType *newType = new TType(); newType->deepCopy(*this); return newType; } // Merge type from parent, where a parentType is at the beginning of a declaration, // establishing some characteristics for all subsequent names, while this type // is on the individual names. void mergeType(const TPublicType& parentType) { // arrayness is currently the only child aspect that has to be preserved basicType = parentType.basicType; vectorSize = parentType.vectorSize; matrixCols = parentType.matrixCols; matrixRows = parentType.matrixRows; qualifier = parentType.qualifier; sampler = parentType.sampler; if (parentType.arraySizes) newArraySizes(*parentType.arraySizes); if (parentType.userDef) { structure = parentType.userDef->getWritableStruct(); setTypeName(parentType.userDef->getTypeName()); } } virtual void hideMember() { basicType = EbtVoid; vectorSize = 1; } virtual bool hiddenMember() const { return basicType == EbtVoid; } virtual void setTypeName(const TString& n) { typeName = NewPoolTString(n.c_str()); } 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 TQualifier& getQualifier() { return qualifier; } virtual const TQualifier& getQualifier() const { return qualifier; } virtual int getVectorSize() const { return vectorSize; } virtual int getMatrixCols() const { return matrixCols; } virtual int getMatrixRows() const { return matrixRows; } virtual int getOuterArraySize() const { return arraySizes->getOuterSize(); } 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() { assert(arraySizes != nullptr); return *arraySizes; } virtual bool isScalar() const { return vectorSize == 1 && ! isStruct() && ! isArray(); } virtual bool isVector() const { return vectorSize > 1; } virtual bool isMatrix() const { return matrixCols ? true : false; } virtual bool isArray() const { return arraySizes != nullptr; } virtual bool isExplicitlySizedArray() const { return isArray() && getOuterArraySize() != UnsizedArraySize; } virtual bool isImplicitlySizedArray() const { return isArray() && getOuterArraySize() == UnsizedArraySize && qualifier.storage != EvqBuffer; } virtual bool isRuntimeSizedArray() const { return isArray() && getOuterArraySize() == UnsizedArraySize && qualifier.storage == EvqBuffer; } virtual bool isStruct() const { return structure != nullptr; } virtual bool isImage() const { return basicType == EbtSampler && getSampler().image; } // Recursively checks if the type contains the given basic type virtual bool containsBasicType(TBasicType checkType) const { if (basicType == checkType) return true; if (! structure) return false; for (unsigned int i = 0; i < structure->size(); ++i) { if ((*structure)[i].type->containsBasicType(checkType)) return true; } return false; } // Recursively check the structure for any arrays, needed for some error checks virtual bool containsArray() const { if (isArray()) return true; if (structure == nullptr) return false; for (unsigned int i = 0; i < structure->size(); ++i) { if ((*structure)[i].type->containsArray()) return true; } return false; } // Check the structure for any structures, needed for some error checks virtual bool containsStructure() const { if (structure == nullptr) return false; for (unsigned int i = 0; i < structure->size(); ++i) { if ((*structure)[i].type->structure) return true; } return false; } // Recursively check the structure for any implicitly-sized arrays, needed for triggering a copyUp(). virtual bool containsImplicitlySizedArray() const { if (isImplicitlySizedArray()) return true; if (structure == nullptr) return false; for (unsigned int i = 0; i < structure->size(); ++i) { if ((*structure)[i].type->containsImplicitlySizedArray()) return true; } return false; } virtual bool containsOpaque() const { if (basicType == EbtSampler || basicType == EbtAtomicUint) return true; if (! structure) return false; for (unsigned int i = 0; i < structure->size(); ++i) { if ((*structure)[i].type->containsOpaque()) return true; } return false; } // 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 newArraySizes(const TArraySizes& s) { // For setting a fresh new set of array sizes, not yet worrying about sharing. arraySizes = new TArraySizes; *arraySizes = s; } void clearArraySizes() { arraySizes = 0; } void addArrayOuterSizes(const TArraySizes& s) { if (arraySizes == nullptr) newArraySizes(s); else arraySizes->addOuterSizes(s); } void changeOuterArraySize(int s) { arraySizes->changeOuterSize(s); } void setImplicitArraySize (int s) { arraySizes->setImplicitSize(s); } // Recursively make the implicit array size the explicit array size, through the type tree. void adoptImplicitArraySizes() { if (isImplicitlySizedArray()) changeOuterArraySize(getImplicitArraySize()); if (isStruct()) { for (int i = 0; i < (int)structure->size(); ++i) (*structure)[i].type->adoptImplicitArraySizes(); } } 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 EbtInt: return "int"; case EbtUint: return "uint"; case EbtBool: return "bool"; case EbtAtomicUint: return "atomic_uint"; case EbtSampler: return "sampler/image"; case EbtStruct: return "structure"; case EbtBlock: return "block"; default: return "unknown type"; } } TString getCompleteString() const { const int maxSize = GlslangMaxTypeLength; char buf[maxSize]; char* p = &buf[0]; char* end = &buf[maxSize]; 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()) { p += snprintf(p, end - p, "layout("); if (qualifier.hasAnyLocation()) { p += snprintf(p, end - p, "location=%d ", qualifier.layoutLocation); if (qualifier.hasComponent()) p += snprintf(p, end - p, "component=%d ", qualifier.layoutComponent); if (qualifier.hasIndex()) p += snprintf(p, end - p, "index=%d ", qualifier.layoutIndex); } if (qualifier.hasSet()) p += snprintf(p, end - p, "set=%d ", qualifier.layoutSet); if (qualifier.hasBinding()) p += snprintf(p, end - p, "binding=%d ", qualifier.layoutBinding); if (qualifier.hasStream()) p += snprintf(p, end - p, "stream=%d ", qualifier.layoutStream); if (qualifier.hasMatrix()) p += snprintf(p, end - p, "%s ", TQualifier::getLayoutMatrixString(qualifier.layoutMatrix)); if (qualifier.hasPacking()) p += snprintf(p, end - p, "%s ", TQualifier::getLayoutPackingString(qualifier.layoutPacking)); if (qualifier.hasOffset()) p += snprintf(p, end - p, "offset=%d ", qualifier.layoutOffset); if (qualifier.hasAlign()) p += snprintf(p, end - p, "align=%d ", qualifier.layoutAlign); if (qualifier.hasFormat()) p += snprintf(p, end - p, "%s ", TQualifier::getLayoutFormatString(qualifier.layoutFormat)); if (qualifier.hasXfbBuffer() && qualifier.hasXfbOffset()) p += snprintf(p, end - p, "xfb_buffer=%d ", qualifier.layoutXfbBuffer); if (qualifier.hasXfbOffset()) p += snprintf(p, end - p, "xfb_offset=%d ", qualifier.layoutXfbOffset); if (qualifier.hasXfbStride()) p += snprintf(p, end - p, "xfb_stride=%d ", qualifier.layoutXfbStride); p += snprintf(p, end - p, ") "); } } if (qualifier.invariant) p += snprintf(p, end - p, "invariant "); if (qualifier.centroid) p += snprintf(p, end - p, "centroid "); if (qualifier.smooth) p += snprintf(p, end - p, "smooth "); if (qualifier.flat) p += snprintf(p, end - p, "flat "); if (qualifier.nopersp) p += snprintf(p, end - p, "noperspective "); if (qualifier.patch) p += snprintf(p, end - p, "patch "); if (qualifier.sample) p += snprintf(p, end - p, "sample "); if (qualifier.coherent) p += snprintf(p, end - p, "coherent "); if (qualifier.volatil) p += snprintf(p, end - p, "volatile "); if (qualifier.restrict) p += snprintf(p, end - p, "restrict "); if (qualifier.readonly) p += snprintf(p, end - p, "readonly "); if (qualifier.writeonly) p += snprintf(p, end - p, "writeonly "); p += snprintf(p, end - p, "%s ", getStorageQualifierString()); if (arraySizes) { for(int i = 0; i < (int)arraySizes->getNumDims(); ++i) { int size = arraySizes->getDimSize(i); if (size == 0) p += snprintf(p, end - p, "implicitly-sized array of "); else p += snprintf(p, end - p, "%d-element array of ", arraySizes->getDimSize(i)); } } if (qualifier.precision != EpqNone) p += snprintf(p, end - p, "%s ", getPrecisionQualifierString()); if (matrixCols > 0) p += snprintf(p, end - p, "%dX%d matrix of ", matrixCols, matrixRows); else if (vectorSize > 1) p += snprintf(p, end - p, "%d-component vector of ", vectorSize); *p = 0; TString s(buf); s.append(getBasicTypeString()); if (qualifier.builtIn != EbvNone) { s.append(" "); s.append(getBuiltInVariableString()); } // Add struct/block members if (structure) { s.append("{"); for (size_t i = 0; i < structure->size(); ++i) { if (! (*structure)[i].type->hiddenMember()) { s.append((*structure)[i].type->getCompleteString()); s.append(" "); s.append((*structure)[i].type->getFieldName()); if (i < structure->size() - 1) s.append(", "); } } s.append("}"); } return s; } 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 { return structure; } TTypeList* getWritableStruct() const { 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) { 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 (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 (structure == nullptr || right.structure == nullptr || 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; } // 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 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 && sameStructType(right); } // 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); } bool operator!=(const TType& right) const { return ! operator==(right); } protected: // Require consumer to pick between deep copy and shallow copy. TType(const TType& type); TType& operator=(const TType& type); void buildMangledName(TString&); TBasicType basicType : 8; int vectorSize : 4; int matrixCols : 4; int matrixRows : 4; TSampler sampler; TQualifier qualifier; TArraySizes* arraySizes; // nullptr unless an array; can be shared across types TTypeList* structure; // nullptr unless this is a struct; can be shared across types TString *fieldName; // for structure field names TString *typeName; // for structure type name }; } // end namespace glslang #endif // _TYPES_INCLUDED_