glslang-zig/glslang/Include/Types.h
Greg Fischer 02132406bc Do not propagate packing qualifiers to scalars or vectors
Packing qualifiers have no practical effect on scalars or vectors
so this is unnecessary and its confusing tools downstream that
consume the AST.
2021-04-06 15:40:22 -06:00

2589 lines
90 KiB
C++

//
// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
// Copyright (C) 2012-2016 LunarG, Inc.
// Copyright (C) 2015-2016 Google, Inc.
// Copyright (C) 2017 ARM Limited.
// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
#ifndef _TYPES_INCLUDED
#define _TYPES_INCLUDED
#include "../Include/Common.h"
#include "../Include/BaseTypes.h"
#include "../Public/ShaderLang.h"
#include "arrays.h"
#include <algorithm>
namespace glslang {
const int GlslangMaxTypeLength = 200; // TODO: need to print block/struct one member per line, so this can stay bounded
const char* const AnonymousPrefix = "anon@"; // for something like a block whose members can be directly accessed
inline bool IsAnonymous(const TString& name)
{
return name.compare(0, 5, AnonymousPrefix) == 0;
}
//
// Details within a sampler type
//
enum TSamplerDim {
EsdNone,
Esd1D,
Esd2D,
Esd3D,
EsdCube,
EsdRect,
EsdBuffer,
EsdSubpass, // goes only with non-sampled image (image is true)
EsdNumDims
};
struct TSampler { // misnomer now; includes images, textures without sampler, and textures with sampler
TBasicType type : 8; // type returned by sampler
TSamplerDim dim : 8;
bool arrayed : 1;
bool shadow : 1;
bool ms : 1;
bool image : 1; // image, combined should be false
bool combined : 1; // true means texture is combined with a sampler, false means texture with no sampler
bool sampler : 1; // true means a pure sampler, other fields should be clear()
#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 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
unsigned int vectorSize : 3; // vector return type size.
// Some languages support structures as sample results. Storing the whole structure in the
// TSampler is too large, so there is an index to a separate table.
static const unsigned structReturnIndexBits = 4; // number of index bits to use.
static const unsigned structReturnSlots = (1<<structReturnIndexBits)-1; // number of valid values
static const unsigned noReturnStruct = structReturnSlots; // value if no return struct type.
// Index into a language specific table of texture return structures.
unsigned int structReturnIndex : structReturnIndexBits;
bool external : 1; // GL_OES_EGL_image_external
bool yuv : 1; // GL_EXT_YUV_target
#ifdef ENABLE_HLSL
unsigned int getVectorSize() const { return vectorSize; }
void clearReturnStruct() { structReturnIndex = noReturnStruct; }
bool hasReturnStruct() const { return structReturnIndex != noReturnStruct; }
unsigned getStructReturnIndex() const { return structReturnIndex; }
#endif
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 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
bool isTexture() const { return !sampler && !image; }
bool isPureSampler() const { return sampler; }
void setCombined(bool c) { combined = c; }
void setBasicType(TBasicType t) { type = t; }
TBasicType getBasicType() const { return type; }
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;
sampler = false;
#ifndef GLSLANG_WEB
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;
}
// make a pure sampler, no texture, no image, nothing combined, the 'sampler' keyword
void setPureSampler(bool s)
{
clear();
sampler = true;
shadow = s;
}
#ifndef GLSLANG_WEB
// make a subpass input attachment
void setSubpass(TBasicType t, bool m = false)
{
clear();
type = t;
image = true;
dim = EsdSubpass;
ms = m;
}
#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()
#ifdef ENABLE_HLSL
&& getVectorSize() == right.getVectorSize() &&
getStructReturnIndex() == right.getStructReturnIndex()
#endif
;
}
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,
ElfR64i,
ElfIntGuard, // to help with comparisons
// Uint image
ElfRgba32ui,
ElfRgba16ui,
ElfRgba8ui,
ElfR32ui,
ElfEsUintGuard, // to help with comparisons
ElfRg32ui,
ElfRg16ui,
ElfRgb10a2ui,
ElfRg8ui,
ElfR16ui,
ElfR8ui,
ElfR64ui,
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,
};
enum TShaderInterface
{
// Includes both uniform blocks and buffer blocks
EsiUniform = 0,
EsiInput,
EsiOutput,
EsiNone,
EsiCount
};
class TQualifier {
public:
static const int layoutNotSet = -1;
void clear()
{
precision = EpqNone;
invariant = false;
makeTemporary();
declaredBuiltIn = EbvNone;
#ifndef GLSLANG_WEB
noContraction = false;
nullInit = false;
#endif
defaultBlock = false;
}
// drop qualifiers that don't belong in a temporary variable
void makeTemporary()
{
semanticName = nullptr;
storage = EvqTemporary;
builtIn = EbvNone;
clearInterstage();
clearMemory();
specConstant = false;
nonUniform = false;
nullInit = false;
defaultBlock = 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()
{
#ifndef GLSLANG_WEB
coherent = false;
devicecoherent = false;
queuefamilycoherent = false;
workgroupcoherent = false;
subgroupcoherent = false;
shadercallcoherent = false;
nonprivate = false;
volatil = false;
restrict = false;
readonly = false;
writeonly = false;
#endif
}
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;
// having a constant_id is not sufficient: expressions have no id, but are still specConstant
bool specConstant : 1;
bool nonUniform : 1;
bool explicitOffset : 1;
bool defaultBlock : 1; // default blocks with matching names have structures merged when linking
#ifdef GLSLANG_WEB
bool isWriteOnly() const { return false; }
bool isReadOnly() const { return false; }
bool isRestrict() const { return false; }
bool isCoherent() const { return false; }
bool isVolatile() 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; }
void setNullInit() { }
bool isNullInit() 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 restrict : 1;
bool readonly : 1;
bool writeonly : 1;
bool coherent : 1;
bool volatil : 1;
bool devicecoherent : 1;
bool queuefamilycoherent : 1;
bool workgroupcoherent : 1;
bool subgroupcoherent : 1;
bool shadercallcoherent : 1;
bool nonprivate : 1;
bool nullInit : 1;
bool isWriteOnly() const { return writeonly; }
bool isReadOnly() const { return readonly; }
bool isRestrict() const { return restrict; }
bool isCoherent() const { return coherent; }
bool isVolatile() const { return volatil; }
bool isSample() const { return sample; }
bool isMemory() const
{
return shadercallcoherent || subgroupcoherent || workgroupcoherent || queuefamilycoherent || devicecoherent || coherent || volatil || restrict || readonly || writeonly || nonprivate;
}
bool isMemoryQualifierImageAndSSBOOnly() const
{
return shadercallcoherent || 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; }
void setNullInit() { nullInit = true; }
bool isNullInit() const { return nullInit; }
#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;
}
}
TBlockStorageClass getBlockStorage() const {
if (storage == EvqUniform && !isPushConstant()) {
return EbsUniform;
}
else if (storage == EvqUniform) {
return EbsPushConstant;
}
else if (storage == EvqBuffer) {
return EbsStorageBuffer;
}
return EbsNone;
}
void setBlockStorage(TBlockStorageClass newBacking) {
#ifndef GLSLANG_WEB
layoutPushConstant = (newBacking == EbsPushConstant);
#endif
switch (newBacking) {
case EbsUniform :
if (layoutPacking == ElpStd430) {
// std430 would not be valid
layoutPacking = ElpStd140;
}
storage = EvqUniform;
break;
case EbsStorageBuffer :
storage = EvqBuffer;
break;
#ifndef GLSLANG_WEB
case EbsPushConstant :
storage = EvqUniform;
layoutSet = TQualifier::layoutSetEnd;
layoutBinding = TQualifier::layoutBindingEnd;
break;
#endif
default:
break;
}
}
#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; }
bool isAnyPayload() const {
return storage == EvqPayload || storage == EvqPayloadIn;
}
bool isAnyCallable() const {
return storage == EvqCallableData || storage == EvqCallableDataIn;
}
// 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;
layoutShaderRecord = false;
layoutBufferReferenceAlign = layoutBufferReferenceAlignEnd;
layoutFormat = ElfNone;
#endif
clearInterstageLayout();
layoutSpecConstantId = layoutSpecConstantIdEnd;
}
void clearInterstageLayout()
{
layoutLocation = layoutLocationEnd;
layoutComponent = layoutComponentEnd;
#ifndef GLSLANG_WEB
layoutIndex = layoutIndexEnd;
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() ||
isShaderRecord() ||
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 layoutShaderRecord;
#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;
#ifndef GLSLANG_WEB
layoutAttachment = layoutAttachmentEnd;
#endif
}
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; }
unsigned getIndex() const { return 0; }
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 isShaderRecord() 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;
}
unsigned getIndex() const { return layoutIndex; }
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 isShaderRecord() const { return layoutShaderRecord; }
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";
case ElfR64ui: return "r64ui";
case ElfR64i: return "r64i";
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
bool layoutPrimitiveCulling; // true if layout primitive_culling 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;
layoutPrimitiveCulling = 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;
if (src.layoutPrimitiveCulling)
layoutPrimitiveCulling = src.layoutPrimitiveCulling;
#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;
}
TShaderInterface getShaderInterface() const
{
if (basicType != EbtBlock)
return EsiNone;
switch (qualifier.storage) {
default:
return EsiNone;
case EvqVaryingIn:
return EsiInput;
case EvqVaryingOut:
return EsiOutput;
case EvqUniform:
case EvqBuffer:
return EsiUniform;
}
}
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 isScalarOrVector() const { return !isMatrix() && !isStruct() && !isArray(); }
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 == EbtAccStruct || basicType == EbtRayQuery
#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";
case EbtSampler: return "sampler/image";
#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 EbtStruct: return "structure";
case EbtBlock: return "block";
case EbtAccStruct: return "accelerationStructureNV";
case EbtRayQuery: return "rayQueryEXT";
case EbtReference: return "reference";
case EbtString: return "string";
#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.layoutShaderRecord)
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.shadercallcoherent)
appendStr(" shadercallcoherent");
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");
if (qualifier.isNullInit())
appendStr(" null-init");
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
void setBasicType(const TBasicType& t) { basicType = t; }
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 += ';' ;
}
// These variables are inconsistently declared inside and outside of gl_PerVertex in glslang right now.
// They are declared inside of 'in gl_PerVertex', but sitting as standalone when they are 'out'puts.
bool isInconsistentGLPerVertexMember(const TString& name) const
{
if (name == "gl_SecondaryPositionNV" ||
name == "gl_PositionPerViewNV")
return true;
return false;
}
// 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;
// Structure names have to match
if (*typeName != *right.typeName)
return false;
// There are inconsistencies with how gl_PerVertex is setup. For now ignore those as errors if they
// are known inconsistencies.
bool isGLPerVertex = *typeName == "gl_PerVertex";
// 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() && !isGLPerVertex))
return false;
// Compare the names and types of all the members, which have to match
for (size_t li = 0, ri = 0; li < structure->size() || ri < right.structure->size(); ++li, ++ri) {
if (li < structure->size() && ri < right.structure->size()) {
if ((*structure)[li].type->getFieldName() == (*right.structure)[ri].type->getFieldName()) {
if (*(*structure)[li].type != *(*right.structure)[ri].type)
return false;
} else {
// If one of the members is something that's inconsistently declared, skip over it
// for now.
if (isGLPerVertex) {
if (isInconsistentGLPerVertexMember((*structure)[li].type->getFieldName())) {
ri--;
continue;
} else if (isInconsistentGLPerVertexMember((*right.structure)[ri].type->getFieldName())) {
li--;
continue;
}
} else {
return false;
}
}
// If we get here, then there should only be inconsistently declared members left
} else if (li < structure->size()) {
if (!isInconsistentGLPerVertexMember((*structure)[li].type->getFieldName()))
return false;
} else {
if (!isInconsistentGLPerVertexMember((*right.structure)[ri].type->getFieldName()))
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_