SPV: Implement Vulkan version of GLSL (KHR_vulkan_glsl).

This commit is contained in:
John Kessenich 2016-02-15 20:58:50 -07:00
parent 019f08fcd8
commit 6c292d3ba7
200 changed files with 7841 additions and 5577 deletions

View file

@ -130,6 +130,8 @@ enum TBuiltInVariable {
EbvLocalInvocationIndex,
EbvVertexId,
EbvInstanceId,
EbvVertexIndex,
EbvInstanceIndex,
EbvBaseVertex,
EbvBaseInstance,
EbvDrawId,
@ -221,6 +223,8 @@ __inline const char* GetBuiltInVariableString(TBuiltInVariable v)
case EbvLocalInvocationIndex: return "LocalInvocationIndex";
case EbvVertexId: return "VertexId";
case EbvInstanceId: return "InstanceId";
case EbvVertexIndex: return "VertexIndex";
case EbvInstanceIndex: return "InstanceIndex";
case EbvBaseVertex: return "BaseVertex";
case EbvBaseInstance: return "BaseInstance";
case EbvDrawId: return "DrawId";

View file

@ -1,6 +1,7 @@
//
//Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
//Copyright (C) 2012-2013 LunarG, Inc.
//Copyright (C) 2012-2015 LunarG, Inc.
//Copyright (C) 2015-2016 Google, Inc.
//
//All rights reserved.
//
@ -63,6 +64,7 @@ enum TSamplerDim {
EsdCube,
EsdRect,
EsdBuffer,
EsdSubpass, // goes only with non-sampled image (image is true)
EsdNumDims
};
@ -73,8 +75,16 @@ struct TSampler { // misnomer now; includes images, textures without sampler,
bool shadow : 1;
bool ms : 1;
bool image : 1; // image, combined should be false
bool combined : 1; // true means texture is combined with a sampler, false means texture with no sampler
bool sampler : 1; // true means a pure sampler, other fields should be clear()
bool external : 1; // GL_OES_EGL_image_external
bool isImage() const { return image && dim != EsdSubpass; }
bool isSubpass() const { return dim == EsdSubpass; }
bool isCombined() const { return combined; }
bool isPureSampler() const { return sampler; }
bool isTexture() const { return !sampler && !image; }
void clear()
{
type = EbtVoid;
@ -83,6 +93,8 @@ struct TSampler { // misnomer now; includes images, textures without sampler,
shadow = false;
ms = false;
image = false;
combined = false;
sampler = false;
external = false;
}
@ -95,6 +107,7 @@ struct TSampler { // misnomer now; includes images, textures without sampler,
arrayed = a;
shadow = s;
ms = m;
combined = true;
}
// make an image
@ -109,6 +122,35 @@ struct TSampler { // misnomer now; includes images, textures without sampler,
image = true;
}
// make a texture with no sampler
void setTexture(TBasicType t, TSamplerDim d, bool a = false, bool s = false, bool m = false)
{
clear();
type = t;
dim = d;
arrayed = a;
shadow = s;
ms = m;
}
// make a subpass input attachment
void setSubpass(TBasicType t, bool m = false)
{
clear();
type = t;
image = true;
dim = EsdSubpass;
ms = m;
}
// make a pure sampler, no texture, no image, nothing combined, the 'sampler' keyword
void setPureSampler(bool s)
{
clear();
sampler = true;
shadow = s;
}
bool operator==(const TSampler& right) const
{
return type == right.type &&
@ -117,6 +159,8 @@ struct TSampler { // misnomer now; includes images, textures without sampler,
shadow == right.shadow &&
ms == right.ms &&
image == right.image &&
combined == right.combined &&
sampler == right.sampler &&
external == right.external;
}
@ -129,6 +173,11 @@ struct TSampler { // misnomer now; includes images, textures without sampler,
{
TString s;
if (sampler) {
s.append("sampler");
return s;
}
switch (type) {
case EbtFloat: break;
case EbtInt: s.append("i"); break;
@ -136,9 +185,14 @@ struct TSampler { // misnomer now; includes images, textures without sampler,
default: break; // some compilers want this
}
if (image) {
s.append("image");
} else {
if (dim == EsdSubpass)
s.append("subpass");
else
s.append("image");
} else if (combined) {
s.append("sampler");
} else {
s.append("texture");
}
if (external) {
s.append("ExternalOES");
@ -151,6 +205,7 @@ struct TSampler { // misnomer now; includes images, textures without sampler,
case EsdCube: s.append("Cube"); break;
case EsdRect: s.append("2DRect"); break;
case EsdBuffer: s.append("Buffer"); break;
case EsdSubpass: s.append("Input"); break;
default: break; // some compilers want this
}
if (ms)
@ -352,6 +407,7 @@ public:
restrict = false;
readonly = false;
writeonly = false;
specConstant = false;
clearLayout();
}
@ -370,6 +426,7 @@ public:
bool restrict : 1;
bool readonly : 1;
bool writeonly : 1;
bool specConstant : 1; // having a constant_id is not sufficient: expressions have no id, but are still specConstant
bool isMemory() const
{
@ -505,8 +562,12 @@ public:
layoutXfbBuffer = layoutXfbBufferEnd;
layoutXfbStride = layoutXfbStrideEnd;
layoutXfbOffset = layoutXfbOffsetEnd;
layoutAttachment = layoutAttachmentEnd;
layoutSpecConstantId = layoutSpecConstantIdEnd;
layoutFormat = ElfNone;
layoutPushConstant = false;
}
bool hasLayout() const
{
@ -515,7 +576,8 @@ public:
hasBinding() ||
hasStream() ||
hasXfb() ||
hasFormat();
hasFormat() ||
layoutPushConstant;
}
TLayoutMatrix layoutMatrix : 3;
TLayoutPacking layoutPacking : 4;
@ -549,8 +611,16 @@ public:
unsigned int layoutXfbOffset : 10;
static const unsigned int layoutXfbOffsetEnd = 0x3FF;
unsigned int layoutAttachment : 8; // for input_attachment_index
static const unsigned int layoutAttachmentEnd = 0XFF;
unsigned int layoutSpecConstantId : 11;
static const unsigned int layoutSpecConstantIdEnd = 0x7FF;
TLayoutFormat layoutFormat : 8;
bool layoutPushConstant;
bool hasUniformLayout() const
{
return hasMatrix() ||
@ -627,6 +697,38 @@ public:
{
return layoutXfbOffset != layoutXfbOffsetEnd;
}
bool hasAttachment() const
{
return layoutAttachment != layoutAttachmentEnd;
}
bool hasSpecConstantId() const
{
// Not the same thing as being a specialization constant, this
// is just whether or not it was declared with an ID.
return layoutSpecConstantId != layoutSpecConstantIdEnd;
}
bool 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()
{
specConstant = true;
}
static const char* getLayoutPackingString(TLayoutPacking packing)
{
switch (packing) {
@ -781,6 +883,7 @@ struct TShaderQualifiers {
TVertexOrder order;
bool pointMode;
int localSize[3]; // compute shader
int localSizeSpecId[3]; // compute shader specialization id for gl_WorkGroupSize
bool earlyFragmentTests; // fragment input
TLayoutDepth layoutDepth;
bool blendEquation; // true if any blend equation was specified
@ -798,6 +901,9 @@ struct TShaderQualifiers {
localSize[0] = 1;
localSize[1] = 1;
localSize[2] = 1;
localSizeSpecId[0] = TQualifier::layoutNotSet;
localSizeSpecId[1] = TQualifier::layoutNotSet;
localSizeSpecId[2] = TQualifier::layoutNotSet;
earlyFragmentTests = false;
layoutDepth = EldNone;
blendEquation = false;
@ -827,6 +933,10 @@ struct TShaderQualifiers {
if (src.localSize[i] > 1)
localSize[i] = src.localSize[i];
}
for (int i = 0; i < 3; ++i) {
if (src.localSizeSpecId[i] != TQualifier::layoutNotSet)
localSizeSpecId[i] = src.localSizeSpecId[i];
}
if (src.earlyFragmentTests)
earlyFragmentTests = true;
if (src.layoutDepth)
@ -902,7 +1012,9 @@ public:
return matrixCols == 0 && vectorSize == 1 && arraySizes == nullptr && userDef == nullptr;
}
bool isImage() const { return basicType == EbtSampler && sampler.image; }
// "Image" is a superset of "Subpass"
bool isImage() const { return basicType == EbtSampler && sampler.isImage(); }
bool isSubpass() const { return basicType == EbtSampler && sampler.isSubpass(); }
};
//
@ -1112,7 +1224,9 @@ public:
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; }
// "Image" is a superset of "Subpass"
virtual bool isImage() const { return basicType == EbtSampler && getSampler().isImage(); }
virtual bool isSubpass() const { return basicType == EbtSampler && getSampler().isSubpass(); }
// Recursively checks if the type contains the given basic type
virtual bool containsBasicType(TBasicType checkType) const
@ -1181,6 +1295,42 @@ public:
return false;
}
virtual bool containsNonOpaque() const
{
// list all non-opaque types
switch (basicType) {
case EbtVoid:
case EbtFloat:
case EbtDouble:
case EbtInt:
case EbtUint:
case EbtBool:
return true;
default:
break;
}
if (! structure)
return false;
for (unsigned int i = 0; i < structure->size(); ++i) {
if ((*structure)[i].type->containsNonOpaque())
return true;
}
return false;
}
virtual bool containsSpecializationSize() const
{
if (isArray() && arraySizes->containsNode())
return true;
if (! structure)
return false;
for (unsigned int i = 0; i < structure->size(); ++i) {
if ((*structure)[i].type->containsSpecializationSize())
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
@ -1218,7 +1368,7 @@ public:
arraySizes->addOuterSizes(s);
}
void changeOuterArraySize(int s) { arraySizes->changeOuterSize(s); }
void setImplicitArraySize (int s) { arraySizes->setImplicitSize(s); }
void setImplicitArraySize(int s) { arraySizes->setImplicitSize(s); }
// Recursively make the implicit array size the explicit array size, through the type tree.
void adoptImplicitArraySizes()
@ -1296,6 +1446,12 @@ public:
p += snprintf(p, end - p, "xfb_offset=%d ", qualifier.layoutXfbOffset);
if (qualifier.hasXfbStride())
p += snprintf(p, end - p, "xfb_stride=%d ", qualifier.layoutXfbStride);
if (qualifier.hasAttachment())
p += snprintf(p, end - p, "input_attachment_index=%d ", qualifier.layoutAttachment);
if (qualifier.hasSpecConstantId())
p += snprintf(p, end - p, "constant_id=%d ", qualifier.layoutSpecConstantId);
if (qualifier.layoutPushConstant)
p += snprintf(p, end - p, "push_constant ");
p += snprintf(p, end - p, ") ");
}
}
@ -1324,6 +1480,8 @@ public:
p += snprintf(p, end - p, "readonly ");
if (qualifier.writeonly)
p += snprintf(p, end - p, "writeonly ");
if (qualifier.specConstant)
p += snprintf(p, end - p, "specialization-constant ");
p += snprintf(p, end - p, "%s ", getStorageQualifierString());
if (arraySizes) {
for(int i = 0; i < (int)arraySizes->getNumDims(); ++i) {

View file

@ -46,6 +46,26 @@ namespace glslang {
// This is used to mean there is no size yet (unsized), it is waiting to get a size from somewhere else.
const int UnsizedArraySize = 0;
class TIntermTyped;
extern bool SameSpecializationConstants(TIntermTyped*, TIntermTyped*);
// Specialization constants need both a nominal size and a node that defines
// the specialization constant being used. Array types are the same when their
// size and specialization constant nodes are the same.
struct TArraySize {
unsigned int size;
TIntermTyped* node; // nullptr means no specialization constant node
bool operator==(const TArraySize& rhs) const
{
if (size != rhs.size)
return false;
if (node == nullptr || rhs.node == nullptr)
return node == rhs.node;
return SameSpecializationConstants(node, rhs.node);
}
};
//
// TSmallArrayVector is used as the container for the set of sizes in TArraySizes.
// It has generic-container semantics, while TArraySizes has array-of-array semantics.
@ -83,22 +103,31 @@ struct TSmallArrayVector {
return (int)sizes->size();
}
unsigned int front() const
unsigned int frontSize() const
{
assert(sizes != nullptr && sizes->size() > 0);
return sizes->front();
return sizes->front().size;
}
TIntermTyped* frontNode() const
{
assert(sizes != nullptr && sizes->size() > 0);
return sizes->front().node;
}
void changeFront(unsigned int s)
{
assert(sizes != nullptr);
sizes->front() = s;
// this should only happen for implicitly sized arrays, not specialization constants
assert(sizes->front().node == nullptr);
sizes->front().size = s;
}
void push_back(unsigned int e)
void push_back(unsigned int e, TIntermTyped* n)
{
alloc();
sizes->push_back(e);
TArraySize pair = { e, n };
sizes->push_back(pair);
}
void push_front(const TSmallArrayVector& newDims)
@ -129,16 +158,23 @@ struct TSmallArrayVector {
}
}
unsigned int operator[](int i) const
unsigned int getDimSize(int i) const
{
assert(sizes != nullptr && (int)sizes->size() > i);
return (*sizes)[i];
assert(sizes != nullptr && (int)sizes->size() > i);
return (*sizes)[i].size;
}
unsigned int& operator[](int i)
void setDimSize(int i, unsigned int size) const
{
assert(sizes != nullptr && (int)sizes->size() > i);
return (*sizes)[i];
assert(sizes != nullptr && (int)sizes->size() > i);
assert((*sizes)[i].node == nullptr);
(*sizes)[i].size = size;
}
TIntermTyped* getDimNode(int i) const
{
assert(sizes != nullptr && (int)sizes->size() > i);
return (*sizes)[i].node;
}
bool operator==(const TSmallArrayVector& rhs) const
@ -157,7 +193,7 @@ protected:
void alloc()
{
if (sizes == nullptr)
sizes = new TVector<unsigned int>;
sizes = new TVector<TArraySize>;
}
void dealloc()
{
@ -165,7 +201,7 @@ protected:
sizes = nullptr;
}
TVector<unsigned int>* sizes; // will either hold such a pointer, or in the future, hold the two array sizes
TVector<TArraySize>* sizes; // will either hold such a pointer, or in the future, hold the two array sizes
};
//
@ -197,28 +233,32 @@ struct TArraySizes {
// translate from array-of-array semantics to container semantics
int getNumDims() const { return sizes.size(); }
int getDimSize(int dim) const { return sizes[dim]; }
void setDimSize(int dim, int size) { sizes[dim] = size; }
int getOuterSize() const { return sizes.front(); }
int getDimSize(int dim) const { return sizes.getDimSize(dim); }
TIntermTyped* getDimNode(int dim) const { return sizes.getDimNode(dim); }
void setDimSize(int dim, int size) { sizes.setDimSize(dim, size); }
int getOuterSize() const { return sizes.frontSize(); }
TIntermTyped* getOuterNode() const { return sizes.frontNode(); }
int getCumulativeSize() const
{
int size = 1;
for (int d = 0; d < sizes.size(); ++d) {
// this only makes sense in paths that have a known array size
assert(sizes[d] != UnsizedArraySize);
size *= sizes[d];
assert(sizes.getDimSize(d) != UnsizedArraySize);
size *= sizes.getDimSize(d);
}
return size;
}
void addInnerSize() { sizes.push_back((unsigned)UnsizedArraySize); }
void addInnerSize(int s) { sizes.push_back((unsigned)s); }
void addInnerSize() { addInnerSize((unsigned)UnsizedArraySize); }
void addInnerSize(int s) { addInnerSize((unsigned)s, nullptr); }
void addInnerSize(int s, TIntermTyped* n) { sizes.push_back((unsigned)s, n); }
void addInnerSize(TArraySize pair) { sizes.push_back(pair.size, pair.node); }
void changeOuterSize(int s) { sizes.changeFront((unsigned)s); }
int getImplicitSize() const { return (int)implicitArraySize; }
void setImplicitSize(int s) { implicitArraySize = s; }
bool isInnerImplicit() const
{
for (int d = 1; d < sizes.size(); ++d) {
if (sizes[d] == (unsigned)UnsizedArraySize)
if (sizes.getDimSize(d) == (unsigned)UnsizedArraySize)
return true;
}
@ -240,13 +280,26 @@ struct TArraySizes {
return false;
for (int d = 1; d < sizes.size(); ++d) {
if (sizes[d] != rhs.sizes[d])
if (sizes.getDimSize(d) != rhs.sizes.getDimSize(d) ||
sizes.getDimNode(d) != rhs.sizes.getDimNode(d))
return false;
}
return true;
}
// Returns true if any of the dimensions of the array is sized with a node
// instead of a front-end compile-time constant.
bool containsNode()
{
for (int d = 0; d < sizes.size(); ++d) {
if (sizes.getDimNode(d) != nullptr)
return true;
}
return false;
}
bool operator==(const TArraySizes& rhs) { return sizes == rhs.sizes; }
bool operator!=(const TArraySizes& rhs) { return sizes != rhs.sizes; }

View file

@ -324,6 +324,7 @@ enum TOperator {
EOpConstructDMat4x3,
EOpConstructDMat4x4,
EOpConstructStruct,
EOpConstructTextureSampler,
EOpConstructGuardEnd,
//
@ -371,6 +372,8 @@ enum TOperator {
EOpImageAtomicExchange,
EOpImageAtomicCompSwap,
EOpSubpassLoad,
EOpSubpassLoadMS,
EOpSparseImageLoad,
EOpImageGuardEnd,
@ -606,7 +609,7 @@ protected:
//
class TIntermSymbol : public TIntermTyped {
public:
// if symbol is initialized as symbol(sym), the memory comes from the poolallocator of sym. If sym comes from
// if symbol is initialized as symbol(sym), the memory comes from the pool allocator of sym. If sym comes from
// per process threadPoolAllocator, then it causes increased memory usage per compile
// it is essential to use "symbol = sym" to assign to symbol
TIntermSymbol(int i, const TString& n, const TType& t) :
@ -619,9 +622,9 @@ public:
void setConstArray(const TConstUnionArray& c) { unionArray = c; }
const TConstUnionArray& getConstArray() const { return unionArray; }
protected:
int id;
TString name;
TConstUnionArray unionArray;
int id; // the unique id of the symbol this node represents
TString name; // the name of the symbol this node represents
TConstUnionArray unionArray; // if the symbol is a front-end compile-time constant, this is its value
};
class TIntermConstantUnion : public TIntermTyped {
@ -651,6 +654,7 @@ struct TCrackedTextureOp {
bool offsets;
bool gather;
bool grad;
bool subpass;
bool lodClamp;
};
@ -681,6 +685,7 @@ public:
cracked.offsets = false;
cracked.gather = false;
cracked.grad = false;
cracked.subpass = false;
cracked.lodClamp = false;
switch (op) {
@ -790,6 +795,10 @@ public:
cracked.gather = true;
cracked.offsets = true;
break;
case EOpSubpassLoad:
case EOpSubpassLoadMS:
cracked.subpass = true;
break;
default:
break;
}
@ -937,7 +946,7 @@ enum TVisit
//
// Explicitly set postVisit to true if you want post visiting, otherwise,
// filled in methods will only be called at pre-visit time (before processing
// the subtree). Similary for inVisit for in-order visiting of nodes with
// the subtree). Similarly for inVisit for in-order visiting of nodes with
// multiple children.
//
// If you only want post-visits, explicitly turn off preVisit (and inVisit)
@ -970,7 +979,7 @@ public:
void incrementDepth(TIntermNode *current)
{
depth++;
maxDepth = std::max(maxDepth, depth);
maxDepth = (std::max)(maxDepth, depth);
path.push_back(current);
}
@ -1000,6 +1009,14 @@ protected:
TVector<TIntermNode *> path;
};
// KHR_vulkan_glsl says "Two arrays sized with specialization constants are the same type only if
// sized with the same symbol, involving no operations"
inline bool SameSpecializationConstants(TIntermTyped* node1, TIntermTyped* node2)
{
return node1->getAsSymbolNode() && node2->getAsSymbolNode() &&
node1->getAsSymbolNode()->getId() == node2->getAsSymbolNode()->getId();
}
} // end namespace glslang
#endif // __INTERMEDIATE_H

View file

@ -2,5 +2,5 @@
// For the version, it uses the latest git tag followed by the number of commits.
// For the date, it uses the current date (when then script is run).
#define GLSLANG_REVISION "SPIRV99.866"
#define GLSLANG_DATE "24-Dec-2015"
#define GLSLANG_REVISION "SPIRV99.947"
#define GLSLANG_DATE "15-Feb-2016"

View file

@ -1,6 +1,7 @@
//
//Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
//Copyright (C) 2012-2013 LunarG, Inc.
//Copyright (C) 2012-2015 LunarG, Inc.
//Copyright (C) 2015-2016 Google, Inc.
//
//All rights reserved.
//
@ -85,6 +86,7 @@ TBuiltIns::TBuiltIns()
dimMap[Esd3D] = 3;
dimMap[EsdCube] = 3;
dimMap[EsdBuffer] = 1;
dimMap[EsdSubpass] = 2; // potientially unused for now
}
TBuiltIns::~TBuiltIns()
@ -99,7 +101,7 @@ TBuiltIns::~TBuiltIns()
// Most built-ins variables can be added as simple text strings. Some need to
// be added programmatically, which is done later in IdentifyBuiltIns() below.
//
void TBuiltIns::initialize(int version, EProfile profile, int spv)
void TBuiltIns::initialize(int version, EProfile profile, int spv, int vulkan)
{
//============================================================================
//
@ -1080,17 +1082,19 @@ void TBuiltIns::initialize(int version, EProfile profile, int spv)
"\n");
}
//
// Atomic counter functions.
//
if ((profile != EEsProfile && version >= 300) ||
(profile == EEsProfile && version >= 310)) {
commonBuiltins.append(
"uint atomicCounterIncrement(atomic_uint x);"
"uint atomicCounterDecrement(atomic_uint x);"
"uint atomicCounter(atomic_uint x);"
if (vulkan == 0) {
//
// Atomic counter functions.
//
if ((profile != EEsProfile && version >= 300) ||
(profile == EEsProfile && version >= 310)) {
commonBuiltins.append(
"uint atomicCounterIncrement(atomic_uint x);"
"uint atomicCounterDecrement(atomic_uint x);"
"uint atomicCounter(atomic_uint x);"
"\n");
"\n");
}
}
// Bitfield
@ -1434,28 +1438,31 @@ void TBuiltIns::initialize(int version, EProfile profile, int spv)
//
// Depth range in window coordinates, p. 33
//
commonBuiltins.append(
"struct gl_DepthRangeParameters {"
);
if (profile == EEsProfile) {
if (vulkan == 0) {
commonBuiltins.append(
"highp float near;" // n
"highp float far;" // f
"highp float diff;" // f - n
"struct gl_DepthRangeParameters {"
);
} else {
commonBuiltins.append(
"float near;" // n
"float far;" // f
"float diff;" // f - n
);
}
commonBuiltins.append(
"};"
"uniform gl_DepthRangeParameters gl_DepthRange;"
"\n");
if (profile == EEsProfile) {
commonBuiltins.append(
"highp float near;" // n
"highp float far;" // f
"highp float diff;" // f - n
);
} else {
commonBuiltins.append(
"float near;" // n
"float far;" // f
"float diff;" // f - n
);
}
if (IncludeLegacy(version, profile, spv)) {
commonBuiltins.append(
"};"
"uniform gl_DepthRangeParameters gl_DepthRange;"
"\n");
}
if (vulkan == 0 && IncludeLegacy(version, profile, spv)) {
//
// Matrix state. p. 31, 32, 37, 39, 40.
//
@ -1693,14 +1700,19 @@ void TBuiltIns::initialize(int version, EProfile profile, int spv)
"};"
"\n");
}
if (version >= 130)
if (version >= 130 && vulkan == 0)
stageBuiltins[EShLangVertex].append(
"int gl_VertexID;" // needs qualifier fixed later
);
if (version >= 140)
if (version >= 140 && vulkan == 0)
stageBuiltins[EShLangVertex].append(
"int gl_InstanceID;" // needs qualifier fixed later
);
if (spv > 0 && version >= 140)
stageBuiltins[EShLangVertex].append(
"in int gl_VertexIndex;"
"in int gl_InstanceIndex;"
);
if (version >= 440) {
stageBuiltins[EShLangVertex].append(
"in int gl_BaseVertexARB;"
@ -1716,10 +1728,16 @@ void TBuiltIns::initialize(int version, EProfile profile, int spv)
"mediump float gl_PointSize;" // needs qualifier fixed later
);
} else {
stageBuiltins[EShLangVertex].append(
"highp int gl_VertexID;" // needs qualifier fixed later
"highp int gl_InstanceID;" // needs qualifier fixed later
);
if (vulkan == 0)
stageBuiltins[EShLangVertex].append(
"in highp int gl_VertexID;" // needs qualifier fixed later
"in highp int gl_InstanceID;" // needs qualifier fixed later
);
if (spv > 0)
stageBuiltins[EShLangVertex].append(
"in highp int gl_VertexIndex;"
"in highp int gl_InstanceIndex;"
);
if (version < 310)
stageBuiltins[EShLangVertex].append(
"highp vec4 gl_Position;" // needs qualifier fixed later
@ -2071,7 +2089,7 @@ void TBuiltIns::initialize(int version, EProfile profile, int spv)
stageBuiltins[EShLangFragment].append("\n");
if (version >= 130)
add2ndGenerationSamplingImaging(version, profile, spv);
add2ndGenerationSamplingImaging(version, profile, spv, vulkan);
//printf("%s\n", commonBuiltins.c_str());
//printf("%s\n", stageBuiltins[EShLangFragment].c_str());
@ -2081,7 +2099,7 @@ void TBuiltIns::initialize(int version, EProfile profile, int spv)
// Helper function for initialize(), to add the second set of names for texturing,
// when adding context-independent built-in functions.
//
void TBuiltIns::add2ndGenerationSamplingImaging(int version, EProfile profile, int spv)
void TBuiltIns::add2ndGenerationSamplingImaging(int version, EProfile profile, int /*spv*/, int vulkan)
{
//
// In this function proper, enumerate the types, then calls the next set of functions
@ -2108,9 +2126,13 @@ void TBuiltIns::add2ndGenerationSamplingImaging(int version, EProfile profile, i
for (int arrayed = 0; arrayed <= 1; ++arrayed) { // loop over "bool" arrayed or not
for (int dim = Esd1D; dim < EsdNumDims; ++dim) { // 1D, 2D, ..., buffer
if (dim == EsdSubpass && vulkan == 0)
continue;
if (dim == EsdSubpass && (image || shadow || arrayed))
continue;
if ((dim == Esd1D || dim == EsdRect) && profile == EEsProfile)
continue;
if (dim != Esd2D && ms)
if (dim != Esd2D && dim != EsdSubpass && ms)
continue;
if ((dim == Esd3D || dim == EsdRect) && arrayed)
continue;
@ -2138,7 +2160,9 @@ void TBuiltIns::add2ndGenerationSamplingImaging(int version, EProfile profile, i
//
TSampler sampler;
if (image) {
if (dim == EsdSubpass) {
sampler.setSubpass(bTypes[bType], ms ? true : false);
} else if (image) {
sampler.setImage(bTypes[bType], (TSamplerDim)dim, arrayed ? true : false,
shadow ? true : false,
ms ? true : false);
@ -2150,6 +2174,11 @@ void TBuiltIns::add2ndGenerationSamplingImaging(int version, EProfile profile, i
TString typeName = sampler.getString();
if (dim == EsdSubpass) {
addSubpassSampling(sampler, typeName, version, profile);
continue;
}
addQueryFunctions(sampler, typeName, version, profile);
if (image)
@ -2342,6 +2371,23 @@ void TBuiltIns::addImageFunctions(TSampler sampler, TString& typeName, int versi
}
}
//
// Helper function for initialize(),
// when adding context-independent built-in functions.
//
// Add all the subpass access functions for the given type.
//
void TBuiltIns::addSubpassSampling(TSampler sampler, TString& typeName, int /*version*/, EProfile /*profile*/)
{
stageBuiltins[EShLangFragment].append(prefixes[sampler.type]);
stageBuiltins[EShLangFragment].append("vec4 subpassLoad");
stageBuiltins[EShLangFragment].append("(");
stageBuiltins[EShLangFragment].append(typeName.c_str());
if (sampler.ms)
stageBuiltins[EShLangFragment].append(", int");
stageBuiltins[EShLangFragment].append(");\n");
}
//
// Helper function for add2ndGenerationSamplingImaging(),
// when adding context-independent built-in functions.
@ -2682,7 +2728,7 @@ void TBuiltIns::addGatherFunctions(TSampler sampler, TString& typeName, int vers
// add stage-specific entries to the commonBuiltins, and only if that stage
// was requested.
//
void TBuiltIns::initialize(const TBuiltInResource &resources, int version, EProfile profile, int spv, EShLanguage language)
void TBuiltIns::initialize(const TBuiltInResource &resources, int version, EProfile profile, int spv, int vulkan, EShLanguage language)
{
//
// Initialize the context-dependent (resource-dependent) built-in strings for parsing.
@ -2845,7 +2891,7 @@ void TBuiltIns::initialize(const TBuiltInResource &resources, int version, EProf
snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentUniformComponents = %d;", resources.maxFragmentUniformComponents);
s.append(builtInConstant);
if (IncludeLegacy(version, profile, spv)) {
if (vulkan == 0 && IncludeLegacy(version, profile, spv)) {
//
// OpenGL'uniform' state. Page numbers are in reference to version
// 1.4 of the OpenGL specification.
@ -3189,7 +3235,7 @@ void BuiltInVariable(const char* blockName, const char* name, TBuiltInVariable b
// 3) Tag extension-related symbols added to their base version with their extensions, so
// that if an early version has the extension turned off, there is an error reported on use.
//
void IdentifyBuiltIns(int version, EProfile profile, int spv, EShLanguage language, TSymbolTable& symbolTable)
void IdentifyBuiltIns(int version, EProfile profile, int spv, int vulkan, EShLanguage language, TSymbolTable& symbolTable)
{
//
// Tag built-in variables and functions with additional qualifier and extension information
@ -3254,6 +3300,14 @@ void IdentifyBuiltIns(int version, EProfile profile, int spv, EShLanguage langua
symbolTable.setFunctionExtensions("imageAtomicCompSwap", 1, &E_GL_OES_shader_image_atomic);
}
if (vulkan == 0) {
SpecialQualifier("gl_VertexID", EvqVertexId, EbvVertexId, symbolTable);
SpecialQualifier("gl_InstanceID", EvqInstanceId, EbvInstanceId, symbolTable);
}
BuiltInVariable("gl_VertexIndex", EbvVertexIndex, symbolTable);
BuiltInVariable("gl_InstanceIndex", EbvInstanceIndex, symbolTable);
// Fall through
case EShLangTessControl:
@ -3269,8 +3323,6 @@ void IdentifyBuiltIns(int version, EProfile profile, int spv, EShLanguage langua
SpecialQualifier("gl_Position", EvqPosition, EbvPosition, symbolTable);
SpecialQualifier("gl_PointSize", EvqPointSize, EbvPointSize, symbolTable);
SpecialQualifier("gl_ClipVertex", EvqClipVertex, EbvClipVertex, symbolTable);
SpecialQualifier("gl_VertexID", EvqVertexId, EbvVertexId, symbolTable);
SpecialQualifier("gl_InstanceID", EvqInstanceId, EbvInstanceId, symbolTable);
BuiltInVariable("gl_in", "gl_Position", EbvPosition, symbolTable);
BuiltInVariable("gl_in", "gl_PointSize", EbvPointSize, symbolTable);
@ -3674,6 +3726,9 @@ void IdentifyBuiltIns(int version, EProfile profile, int spv, EShLanguage langua
symbolTable.relateToOperator("imageAtomicExchange", EOpImageAtomicExchange);
symbolTable.relateToOperator("imageAtomicCompSwap", EOpImageAtomicCompSwap);
symbolTable.relateToOperator("subpassLoad", EOpSubpassLoad);
symbolTable.relateToOperator("subpassLoadMS", EOpSubpassLoadMS);
symbolTable.relateToOperator("textureSize", EOpTextureQuerySize);
symbolTable.relateToOperator("textureQueryLod", EOpTextureQueryLod);
symbolTable.relateToOperator("textureQueryLevels", EOpTextureQueryLevels);
@ -3834,7 +3889,7 @@ void IdentifyBuiltIns(int version, EProfile profile, int spv, EShLanguage langua
// 2) Tag extension-related symbols added to their base version with their extensions, so
// that if an early version has the extension turned off, there is an error reported on use.
//
void IdentifyBuiltIns(int version, EProfile profile, int spv, EShLanguage language, TSymbolTable& symbolTable, const TBuiltInResource &resources)
void IdentifyBuiltIns(int version, EProfile profile, int spv, int /*vulkan*/, EShLanguage language, TSymbolTable& symbolTable, const TBuiltInResource &resources)
{
if (profile != EEsProfile && version >= 430 && version < 440) {
symbolTable.setVariableExtensions("gl_MaxTransformFeedbackBuffers", 1, &E_GL_ARB_enhanced_layouts);

View file

@ -59,13 +59,14 @@ public:
POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator())
TBuiltIns();
virtual ~TBuiltIns();
void initialize(int version, EProfile, int spv);
void initialize(const TBuiltInResource& resources, int version, EProfile, int spv, EShLanguage);
void initialize(int version, EProfile, int spv, int vulkan);
void initialize(const TBuiltInResource& resources, int version, EProfile, int spv, int vulkan, EShLanguage);
const TString& getCommonString() const { return commonBuiltins; }
const TString& getStageString(EShLanguage language) const { return stageBuiltins[language]; }
protected:
void add2ndGenerationSamplingImaging(int version, EProfile profile, int spv);
void add2ndGenerationSamplingImaging(int version, EProfile profile, int spv, int vulkan);
void addSubpassSampling(TSampler, TString& typeName, int version, EProfile profile);
void addQueryFunctions(TSampler, TString& typeName, int version, EProfile profile);
void addImageFunctions(TSampler, TString& typeName, int version, EProfile profile);
void addSamplingFunctions(TSampler, TString& typeName, int version, EProfile profile);
@ -81,8 +82,8 @@ protected:
int dimMap[EsdNumDims];
};
void IdentifyBuiltIns(int version, EProfile profile, int spv, EShLanguage, TSymbolTable&);
void IdentifyBuiltIns(int version, EProfile profile, int spv, EShLanguage, TSymbolTable&, const TBuiltInResource &resources);
void IdentifyBuiltIns(int version, EProfile profile, int spv, int vulkan, EShLanguage, TSymbolTable&);
void IdentifyBuiltIns(int version, EProfile profile, int spv, int vulkan, EShLanguage, TSymbolTable&, const TBuiltInResource &resources);
} // end namespace glslang

View file

@ -54,7 +54,7 @@ namespace glslang {
//
//
// Traversal functions for terminals are straighforward....
// Traversal functions for terminals are straightforward....
//
void TIntermMethod::traverse(TIntermTraverser*)
{

View file

@ -1,6 +1,7 @@
//
//Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
//Copyright (C) 2012-2013 LunarG, Inc.
//Copyright (C) 2012-2015 LunarG, Inc.
//Copyright (C) 2015-2016 Google, Inc.
//
//All rights reserved.
//
@ -59,6 +60,7 @@ namespace glslang {
//
// Returns the added node.
//
TIntermSymbol* TIntermediate::addSymbol(int id, const TString& name, const TType& type, const TSourceLoc& loc)
{
TIntermSymbol* node = new TIntermSymbol(id, name, type);
@ -67,9 +69,17 @@ TIntermSymbol* TIntermediate::addSymbol(int id, const TString& name, const TType
return node;
}
TIntermSymbol* TIntermediate::addSymbol(int id, const TString& name, const TType& type, const TConstUnionArray& constArray, const TSourceLoc& loc)
{
TIntermSymbol* node = addSymbol(id, name, type, loc);
node->setConstArray(constArray);
return node;
}
TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable, const TSourceLoc& loc)
{
return addSymbol(variable.getUniqueId(), variable.getName(), variable.getType(), loc);
return addSymbol(variable.getUniqueId(), variable.getName(), variable.getType(), variable.getConstArray(), loc);
}
//
@ -112,10 +122,9 @@ TIntermTyped* TIntermediate::addBinaryMath(TOperator op, TIntermTyped* left, TIn
node->updatePrecision();
//
// If they are both constants, they must be folded.
// If they are both (non-specialization) constants, they must be folded.
// (Unless it's the sequence (comma) operator, but that's handled in addComma().)
//
TIntermConstantUnion *leftTempConstant = left->getAsConstantUnion();
TIntermConstantUnion *rightTempConstant = right->getAsConstantUnion();
if (leftTempConstant && rightTempConstant) {
@ -124,6 +133,13 @@ TIntermTyped* TIntermediate::addBinaryMath(TOperator op, TIntermTyped* left, TIn
return folded;
}
// If either is a specialization constant, while the other is
// a constant (or specialization constant), the result is still
// a specialization constant.
if (( left->getType().getQualifier().isSpecConstant() && right->getType().getQualifier().isConstant()) ||
(right->getType().getQualifier().isSpecConstant() && left->getType().getQualifier().isConstant()))
node->getWritableType().getQualifier().makeSpecConstant();
return node;
}
@ -261,9 +277,14 @@ TIntermTyped* TIntermediate::addUnaryMath(TOperator op, TIntermTyped* child, TSo
node->updatePrecision();
// If it's a (non-specialization) constant, it must be folded.
if (child->getAsConstantUnion())
return child->getAsConstantUnion()->fold(op, node->getType());
// If it's a specialiation constant, the result is too.
if (child->getType().getQualifier().isSpecConstant())
node->getWritableType().getQualifier().makeSpecConstant();
return node;
}
@ -379,35 +400,37 @@ TIntermTyped* TIntermediate::setAggregateOperator(TIntermNode* node, TOperator o
TIntermTyped* TIntermediate::addConversion(TOperator op, const TType& type, TIntermTyped* node) const
{
//
// Does the base type allow operation?
// Does the base type even allow the operation?
//
switch (node->getBasicType()) {
case EbtVoid:
return 0;
case EbtAtomicUint:
case EbtSampler:
if (op != EOpFunctionCall)
return 0;
break;
// opaque types can be passed to functions
if (op == EOpFunction)
break;
// samplers can get assigned via a sampler constructor
// (well, not yet, but code in the rest of this function is ready for it)
if (node->getBasicType() == EbtSampler && op == EOpAssign &&
node->getAsOperator() != nullptr && node->getAsOperator()->getOp() == EOpConstructTextureSampler)
break;
// otherwise, opaque types can't even be operated on, let alone converted
return 0;
default:
break;
}
//
// Otherwise, if types are identical, no problem
//
if (type == node->getType())
return node;
//
// If one's a structure, then no conversions.
//
if (type.isStruct() || node->isStruct())
return 0;
//
// If one's an array, then no conversions.
//
if (type.isArray() || node->getType().isArray())
return 0;
@ -1144,13 +1167,19 @@ bool TIntermBinary::promote()
setType(left->getType());
type.getQualifier().clear();
// Finish all array and structure operations.
if (left->isArray() || left->getBasicType() == EbtStruct) {
// Composite and opaque types don't having pending operator changes, e.g.,
// array, structure, and samplers. Just establish final type and correctness.
if (left->isArray() || left->getBasicType() == EbtStruct || left->getBasicType() == EbtSampler) {
switch (op) {
case EOpEqual:
case EOpNotEqual:
// Promote to conditional
setType(TType(EbtBool));
if (left->getBasicType() == EbtSampler) {
// can't compare samplers
return false;
} else {
// Promote to conditional
setType(TType(EbtBool));
}
return true;

View file

@ -1,6 +1,7 @@
//
//Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
//Copyright (C) 2012-2013 LunarG, Inc.
//Copyright (C) 2012-2015 LunarG, Inc.
//Copyright (C) 2015-2016 Google, Inc.
//
//All rights reserved.
//
@ -47,10 +48,10 @@ extern int yyparse(glslang::TParseContext*);
namespace glslang {
TParseContext::TParseContext(TSymbolTable& symt, TIntermediate& interm, bool pb, int v, EProfile p, int spv, EShLanguage L, TInfoSink& is,
TParseContext::TParseContext(TSymbolTable& symt, TIntermediate& interm, bool pb, int v, EProfile p, int spv, int vulkan, EShLanguage L, TInfoSink& is,
bool fc, EShMessages m) :
intermediate(interm), symbolTable(symt), infoSink(is), language(L),
version(v), profile(p), spv(spv), forwardCompatible(fc),
version(v), profile(p), spv(spv), vulkan(vulkan), forwardCompatible(fc),
contextPragma(true, false), loopNestingLevel(0), structNestingLevel(0), controlFlowNestingLevel(0), statementNestingLevel(0),
postMainReturn(false),
tokensBeforeEOF(false), limits(resources.limits), messages(m), currentScanner(nullptr),
@ -97,11 +98,11 @@ TParseContext::TParseContext(TSymbolTable& symt, TIntermediate& interm, bool pb,
globalUniformDefaults.clear();
globalUniformDefaults.layoutMatrix = ElmColumnMajor;
globalUniformDefaults.layoutPacking = ElpShared;
globalUniformDefaults.layoutPacking = vulkan > 0 ? ElpStd140 : ElpShared;
globalBufferDefaults.clear();
globalBufferDefaults.layoutMatrix = ElmColumnMajor;
globalBufferDefaults.layoutPacking = ElpShared;
globalBufferDefaults.layoutPacking = vulkan > 0 ? ElpStd430 : ElpShared;
globalInputDefaults.clear();
globalOutputDefaults.clear();
@ -463,7 +464,7 @@ TIntermTyped* TParseContext::handleVariable(const TSourceLoc& loc, TSymbol* symb
if (! variable)
variable = new TVariable(string, TType(EbtVoid));
if (variable->getType().getQualifier().storage == EvqConst)
if (variable->getType().getQualifier().isFrontEndConstant())
node = intermediate.addConstantUnion(variable->getConstArray(), variable->getType(), loc);
else
node = intermediate.addSymbol(*variable, loc);
@ -610,6 +611,16 @@ void TParseContext::makeEditable(TSymbol*& symbol)
intermediate.addSymbolLinkageNode(linkage, *symbol);
}
TVariable* TParseContext::getEditableVariable(const char* name)
{
bool builtIn;
TSymbol* symbol = symbolTable.find(name, &builtIn);
if (builtIn)
makeEditable(symbol);
return symbol->getAsVariable();
}
// Return true if this is a geometry shader input array or tessellation control output array.
bool TParseContext::isIoResizeArray(const TType& type) const
{
@ -813,7 +824,7 @@ TIntermTyped* TParseContext::handleDotDereference(const TSourceLoc& loc, TInterm
}
}
if (base->getType().getQualifier().storage == EvqConst)
if (base->getType().getQualifier().isFrontEndConstant())
result = intermediate.foldSwizzle(base, fields, loc);
else {
if (fields.num == 1) {
@ -1682,6 +1693,8 @@ TOperator TParseContext::mapTypeToConstructorOp(const TType& type) const
op = EOpConstructStruct;
break;
case EbtSampler:
if (type.getSampler().combined)
op = EOpConstructTextureSampler;
break;
case EbtFloat:
if (type.isMatrix()) {
@ -2154,6 +2167,8 @@ bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, T
bool constructingMatrix = false;
switch(op) {
case EOpConstructTextureSampler:
return constructorTextureSamplerError(loc, function);
case EOpConstructMat2x2:
case EOpConstructMat2x3:
case EOpConstructMat2x4:
@ -2309,6 +2324,66 @@ bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, T
return false;
}
// Verify all the correct semantics for constructing a combined texture/sampler.
// Return true if the semantics are incorrect.
bool TParseContext::constructorTextureSamplerError(const TSourceLoc& loc, const TFunction& function)
{
TString constructorName = function.getType().getBasicTypeString(); // TODO: performance: should not be making copy; interface needs to change
const char* token = constructorName.c_str();
// exactly two arguments needed
if (function.getParamCount() != 2) {
error(loc, "sampler-constructor requires two arguments", token, "");
return true;
}
// For now, not allowing arrayed constructors, the rest of this function
// is set up to allow them, if this test is removed:
if (function.getType().isArray()) {
error(loc, "sampler-constructor cannot make an array of samplers", token, "");
return true;
}
// first argument
// * the constructor's first argument must be a texture type
// * the dimensionality (1D, 2D, 3D, Cube, Rect, Buffer, MS, and Array)
// of the texture type must match that of the constructed sampler type
// (that is, the suffixes of the type of the first argument and the
// type of the constructor will be spelled the same way)
if (function[0].type->getBasicType() != EbtSampler ||
! function[0].type->getSampler().isTexture() ||
function[0].type->isArray()) {
error(loc, "sampler-constructor first argument must be a scalar textureXXX type", token, "");
return true;
}
// simulate the first argument's impact on the result type, so it can be compared with the encapsulated operator!=()
TSampler texture = function.getType().getSampler();
texture.combined = false;
texture.shadow = false;
if (texture != function[0].type->getSampler()) {
error(loc, "sampler-constructor first argument must match type and dimensionality of constructor type", token, "");
return true;
}
// second argument
// * the constructor's second argument must be a scalar of type
// *sampler* or *samplerShadow*
// * the presence or absence of depth comparison (Shadow) must match
// between the constructed sampler type and the type of the second argument
if ( function[1].type->getBasicType() != EbtSampler ||
! function[1].type->getSampler().isPureSampler() ||
function[1].type->isArray()) {
error(loc, "sampler-constructor second argument must be a scalar type 'sampler'", token, "");
return true;
}
if (function.getType().getSampler().shadow != function[1].type->getSampler().shadow) {
error(loc, "sampler-constructor second argument presence of shadow must match constructor presence of shadow", token, "");
return true;
}
return false;
}
// Checks to see if a void variable has been declared and raise an error message for such a case
//
// returns true in case of an error
@ -2337,7 +2412,7 @@ void TParseContext::boolCheck(const TSourceLoc& loc, const TPublicType& pType)
error(loc, "boolean expression expected", "", "");
}
void TParseContext::samplerCheck(const TSourceLoc& loc, const TType& type, const TString& identifier)
void TParseContext::samplerCheck(const TSourceLoc& loc, const TType& type, const TString& identifier, TIntermTyped* /*initializer*/)
{
if (type.getQualifier().storage == EvqUniform)
return;
@ -2345,6 +2420,9 @@ void TParseContext::samplerCheck(const TSourceLoc& loc, const TType& type, const
if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtSampler))
error(loc, "non-uniform struct contains a sampler or image:", type.getBasicTypeString().c_str(), identifier.c_str());
else if (type.getBasicType() == EbtSampler && type.getQualifier().storage != EvqUniform) {
// non-uniform sampler
// not yet: okay if it has an initializer
// if (! initializer)
error(loc, "sampler/image types can only be used in uniform variables or function parameters:", type.getBasicTypeString().c_str(), identifier.c_str());
}
}
@ -2360,6 +2438,19 @@ void TParseContext::atomicUintCheck(const TSourceLoc& loc, const TType& type, co
error(loc, "atomic_uints can only be used in uniform variables or function parameters:", type.getBasicTypeString().c_str(), identifier.c_str());
}
void TParseContext::transparentCheck(const TSourceLoc& loc, const TType& type, const TString& /*identifier*/)
{
// double standard due to gl_NumSamples
if (parsingBuiltins)
return;
// Vulkan doesn't allow transparent uniforms outside of blocks
if (vulkan == 0 || type.getQualifier().storage != EvqUniform)
return;
if (type.containsNonOpaque())
vulkanRemoved(loc, "non-opaque uniforms outside a block");
}
//
// Check/fix just a full qualifier (no variables or types yet, but qualifier is complete) at global level.
//
@ -2605,6 +2696,7 @@ void TParseContext::mergeQualifiers(const TSourceLoc& loc, TQualifier& dst, cons
MERGE_SINGLETON(restrict);
MERGE_SINGLETON(readonly);
MERGE_SINGLETON(writeonly);
MERGE_SINGLETON(specConstant);
if (repeated)
error(loc, "replicated qualifiers", "", "");
@ -2707,22 +2799,35 @@ bool TParseContext::containsFieldWithBasicType(const TType& type, TBasicType bas
//
// Do size checking for an array type's size.
//
void TParseContext::arraySizeCheck(const TSourceLoc& loc, TIntermTyped* expr, int& size)
void TParseContext::arraySizeCheck(const TSourceLoc& loc, TIntermTyped* expr, TArraySize& sizePair)
{
TIntermConstantUnion* constant = expr->getAsConstantUnion();
if (constant == nullptr || (constant->getBasicType() != EbtInt && constant->getBasicType() != EbtUint)) {
error(loc, "array size must be a constant integer expression", "", "");
size = 1;
bool isConst = false;
sizePair.size = 1;
sizePair.node = nullptr;
TIntermConstantUnion* constant = expr->getAsConstantUnion();
if (constant) {
// handle true (non-specialization) constant
sizePair.size = constant->getConstArray()[0].getIConst();
isConst = true;
} else {
// see if it's a specialization constant instead
if (expr->getQualifier().isSpecConstant()) {
isConst = true;
sizePair.node = expr;
TIntermSymbol* symbol = expr->getAsSymbolNode();
if (symbol && symbol->getConstArray().size() > 0)
sizePair.size = symbol->getConstArray()[0].getIConst();
}
}
if (! isConst || (expr->getBasicType() != EbtInt && expr->getBasicType() != EbtUint)) {
error(loc, "array size must be a constant integer expression", "", "");
return;
}
size = constant->getConstArray()[0].getIConst();
if (size <= 0) {
if (sizePair.size <= 0) {
error(loc, "array size must be a positive integer", "", "");
size = 1;
return;
}
}
@ -3390,6 +3495,12 @@ void TParseContext::opaqueCheck(const TSourceLoc& loc, const TType& type, const
error(loc, "can't use with samplers or structs containing samplers", op, "");
}
void TParseContext::specializationCheck(const TSourceLoc& loc, const TType& type, const char* op)
{
if (type.containsSpecializationSize())
error(loc, "can't use with types containing arrays sized with a specialization constant", op, "");
}
void TParseContext::structTypeCheck(const TSourceLoc& /*loc*/, TPublicType& publicType)
{
const TTypeList& typeList = *publicType.userDef->getStruct();
@ -3605,10 +3716,14 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi
return;
}
if (id == TQualifier::getLayoutPackingString(ElpPacked)) {
if (vulkan > 0)
vulkanRemoved(loc, "packed");
publicType.qualifier.layoutPacking = ElpPacked;
return;
}
if (id == TQualifier::getLayoutPackingString(ElpShared)) {
if (vulkan > 0)
vulkanRemoved(loc, "shared");
publicType.qualifier.layoutPacking = ElpShared;
return;
}
@ -3636,6 +3751,11 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi
return;
}
}
if (id == "push_constant") {
requireVulkan(loc, "push_constant");
publicType.qualifier.layoutPushConstant = true;
return;
}
if (language == EShLangGeometry || language == EShLangTessEvaluation) {
if (id == TQualifier::getGeometryString(ElgTriangles)) {
publicType.shaderQualifiers.geometry = ElgTriangles;
@ -3874,6 +3994,27 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi
}
}
if (id == "input_attachment_index") {
requireVulkan(loc, "input_attachment_index");
if (value >= (int)TQualifier::layoutAttachmentEnd)
error(loc, "attachment index is too large", id.c_str(), "");
else
publicType.qualifier.layoutAttachment = value;
return;
}
if (id == "constant_id") {
requireSpv(loc, "constant_id");
if (value >= (int)TQualifier::layoutSpecConstantIdEnd) {
error(loc, "specialization-constant id is too large", id.c_str(), "");
} else {
publicType.qualifier.layoutSpecConstantId = value;
publicType.qualifier.specConstant = true;
if (! intermediate.addUsedConstantId(value))
error(loc, "specialization-constant id already used", id.c_str(), "");
}
return;
}
switch (language) {
case EShLangVertex:
break;
@ -3924,17 +4065,33 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi
break;
case EShLangCompute:
if (id == "local_size_x") {
publicType.shaderQualifiers.localSize[0] = value;
return;
}
if (id == "local_size_y") {
publicType.shaderQualifiers.localSize[1] = value;
return;
}
if (id == "local_size_z") {
publicType.shaderQualifiers.localSize[2] = value;
return;
if (id.compare(0, 11, "local_size_") == 0) {
if (id == "local_size_x") {
publicType.shaderQualifiers.localSize[0] = value;
return;
}
if (id == "local_size_y") {
publicType.shaderQualifiers.localSize[1] = value;
return;
}
if (id == "local_size_z") {
publicType.shaderQualifiers.localSize[2] = value;
return;
}
if (spv > 0) {
if (id == "local_size_x_id") {
publicType.shaderQualifiers.localSizeSpecId[0] = value;
return;
}
if (id == "local_size_y_id") {
publicType.shaderQualifiers.localSizeSpecId[1] = value;
return;
}
if (id == "local_size_z_id") {
publicType.shaderQualifiers.localSizeSpecId[2] = value;
return;
}
}
}
break;
@ -3999,6 +4156,13 @@ void TParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifie
dst.layoutXfbStride = src.layoutXfbStride;
if (src.hasXfbOffset())
dst.layoutXfbOffset = src.layoutXfbOffset;
if (src.hasAttachment())
dst.layoutAttachment = src.layoutAttachment;
if (src.hasSpecConstantId())
dst.layoutSpecConstantId = src.layoutSpecConstantId;
if (src.layoutPushConstant)
dst.layoutPushConstant = true;
}
}
@ -4041,6 +4205,8 @@ void TParseContext::layoutObjectCheck(const TSourceLoc& loc, const TSymbol& symb
// "The align qualifier can only be used on blocks or block members..."
if (qualifier.hasAlign())
error(loc, "cannot specify on a variable declaration", "align", "");
if (qualifier.layoutPushConstant)
error(loc, "can only specify on a uniform block", "push_constant", "");
}
break;
default:
@ -4055,7 +4221,7 @@ void TParseContext::layoutTypeCheck(const TSourceLoc& loc, const TType& type)
{
const TQualifier& qualifier = type.getQualifier();
// first, intra layout qualifier-only error checking
// first, intra-layout qualifier-only error checking
layoutQualifierCheck(loc, qualifier);
// now, error checking combining type and qualifier
@ -4087,7 +4253,7 @@ void TParseContext::layoutTypeCheck(const TSourceLoc& loc, const TType& type)
case EvqBuffer:
break;
default:
error(loc, "can only appy to uniform, buffer, in, or out storage qualifiers", "location", "");
error(loc, "can only apply to uniform, buffer, in, or out storage qualifiers", "location", "");
break;
}
@ -4181,6 +4347,38 @@ void TParseContext::layoutTypeCheck(const TSourceLoc& loc, const TType& type)
}
} else if (type.isImage() && ! qualifier.writeonly)
error(loc, "image variables not declared 'writeonly' must have a format layout qualifier", "", "");
if (qualifier.layoutPushConstant && type.getBasicType() != EbtBlock)
error(loc, "can only be used with a block", "push_constant", "");
// input attachment
if (type.isSubpass()) {
if (! qualifier.hasAttachment())
error(loc, "requires an input_attachment_index layout qualifier", "subpass", "");
} else {
if (qualifier.hasAttachment())
error(loc, "can only be used with a subpass", "input_attachment_index", "");
}
// specialization-constant id
if (qualifier.hasSpecConstantId()) {
if (type.getQualifier().storage != EvqConst)
error(loc, "can only be applied to 'const'-qualified scalar", "constant_id", "");
if (! type.isScalar())
error(loc, "can only be applied to a scalar", "constant_id", "");
switch (type.getBasicType())
{
case EbtInt:
case EbtUint:
case EbtBool:
case EbtFloat:
case EbtDouble:
break;
default:
error(loc, "cannot be applied to this type", "constant_id", "");
break;
}
}
}
// Do layout error checking that can be done within a layout qualifier proper, not needing to know
@ -4275,6 +4473,10 @@ void TParseContext::layoutQualifierCheck(const TSourceLoc& loc, const TQualifier
error(loc, "offset/align can only be used on a uniform or buffer", "layout", "");
}
}
if (qualifier.layoutPushConstant) {
if (qualifier.storage != EvqUniform)
error(loc, "can only be used with a uniform", "push_constant", "");
}
}
// For places that can't have shader-level layout qualifiers
@ -4297,6 +4499,8 @@ void TParseContext::checkNoShaderLayouts(const TSourceLoc& loc, const TShaderQua
for (int i = 0; i < 3; ++i) {
if (shaderQualifiers.localSize[i] > 1)
error(loc, message, "local_size", "");
if (shaderQualifiers.localSizeSpecId[i] != TQualifier::layoutNotSet)
error(loc, message, "local_size id", "");
}
if (shaderQualifiers.blendEquation)
error(loc, message, "blend equation", "");
@ -4490,8 +4694,9 @@ TIntermNode* TParseContext::declareVariable(const TSourceLoc& loc, TString& iden
else
nonInitConstCheck(loc, identifier, type);
samplerCheck(loc, type, identifier);
samplerCheck(loc, type, identifier, initializer);
atomicUintCheck(loc, type, identifier);
transparentCheck(loc, type, identifier);
if (identifier != "gl_FragCoord" && (publicType.shaderQualifiers.originUpperLeft || publicType.shaderQualifiers.pixelCenterInteger))
error(loc, "can only apply origin_upper_left and pixel_center_origin to gl_FragCoord", "layout qualifier", "");
@ -4690,7 +4895,7 @@ TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyp
}
if (qualifier == EvqConst || qualifier == EvqUniform) {
// Compile-time tagging of the variable with it's constant value...
// Compile-time tagging of the variable with its constant value...
initializer = intermediate.addConversion(EOpAssign, variable->getType(), initializer);
if (! initializer || ! initializer->getAsConstantUnion() || variable->getType() != initializer->getType()) {
@ -4703,6 +4908,7 @@ TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyp
variable->setConstArray(initializer->getAsConstantUnion()->getConstArray());
} else {
// normal assigning of a value to a variable...
specializationCheck(loc, initializer->getType(), "initializer");
TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc);
TIntermNode* initNode = intermediate.addAssign(EOpAssign, intermSymbol, initializer, loc);
if (! initNode)
@ -4716,7 +4922,7 @@ TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyp
//
// Reprocess any initializer-list { ... } parts of the initializer.
// Need to heirarchically assign correct types and implicit
// Need to hierarchically assign correct types and implicit
// conversions. Will do this mimicking the same process used for
// creating a constructor-style initializer, ensuring we get the
// same form.
@ -4811,6 +5017,11 @@ TIntermTyped* TParseContext::addConstructor(const TSourceLoc& loc, TIntermNode*
TIntermAggregate* aggrNode = node->getAsAggregate();
// Combined texture-sampler constructors are completely semantic checked
// in constructorTextureSamplerError()
if (op == EOpConstructTextureSampler)
return intermediate.setAggregateOperator(aggrNode, op, type, loc);
TTypeList::const_iterator memberTypes;
if (op == EOpConstructStruct)
memberTypes = type.getStruct()->begin();
@ -4997,7 +5208,7 @@ TIntermTyped* TParseContext::constructAggregate(TIntermNode* node, const TType&
void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, const TString* instanceName, TArraySizes* arraySizes)
{
blockStageIoCheck(loc, currentBlockQualifier);
blockQualifierCheck(loc, currentBlockQualifier);
blockQualifierCheck(loc, currentBlockQualifier, instanceName != nullptr);
if (arraySizes) {
arrayUnsizedCheck(loc, currentBlockQualifier, arraySizes, false, false);
arrayDimCheck(loc, arraySizes, 0);
@ -5052,6 +5263,11 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con
default: defaultQualification.clear(); break;
}
// Special case for "push_constant uniform", which has a default of std430,
// contrary to normal uniform defaults, and can't have a default tracked for it.
if (currentBlockQualifier.layoutPushConstant && !currentBlockQualifier.hasPacking())
currentBlockQualifier.layoutPacking = ElpStd430;
// fix and check for member layout qualifiers
mergeObjectLayoutQualifiers(defaultQualification, currentBlockQualifier, true);
@ -5197,7 +5413,7 @@ void TParseContext::blockStageIoCheck(const TSourceLoc& loc, const TQualifier& q
case EvqUniform:
profileRequires(loc, EEsProfile, 300, nullptr, "uniform block");
profileRequires(loc, ENoProfile, 140, nullptr, "uniform block");
if (currentBlockQualifier.layoutPacking == ElpStd430)
if (currentBlockQualifier.layoutPacking == ElpStd430 && ! currentBlockQualifier.layoutPushConstant)
error(loc, "requires the 'buffer' storage qualifier", "std430", "");
break;
case EvqBuffer:
@ -5227,7 +5443,7 @@ void TParseContext::blockStageIoCheck(const TSourceLoc& loc, const TQualifier& q
}
// Do all block-declaration checking regarding its qualifers.
void TParseContext::blockQualifierCheck(const TSourceLoc& loc, const TQualifier& qualifier)
void TParseContext::blockQualifierCheck(const TSourceLoc& loc, const TQualifier& qualifier, bool instanceName)
{
// The 4.5 specification says:
//
@ -5254,6 +5470,11 @@ void TParseContext::blockQualifierCheck(const TSourceLoc& loc, const TQualifier&
error(loc, "cannot use sample qualifier on an interface block", "sample", "");
if (qualifier.invariant)
error(loc, "cannot use invariant qualifier on an interface block", "invariant", "");
if (qualifier.layoutPushConstant) {
intermediate.addPushConstantCount();
if (! instanceName)
error(loc, "requires an instance name", "push_constant", "");
}
}
//
@ -5541,16 +5762,22 @@ void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, con
error(loc, "too large; see gl_MaxComputeWorkGroupSize", "local_size", "");
// Fix the existing constant gl_WorkGroupSize with this new information.
bool builtIn;
TSymbol* symbol = symbolTable.find("gl_WorkGroupSize", &builtIn);
if (builtIn)
makeEditable(symbol);
TVariable* workGroupSize = symbol->getAsVariable();
TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize");
workGroupSize->getWritableConstArray()[i].setUConst(intermediate.getLocalSize(i));
}
} else
error(loc, "can only apply to 'in'", "local_size", "");
}
if (publicType.shaderQualifiers.localSizeSpecId[i] != TQualifier::layoutNotSet) {
if (publicType.qualifier.storage == EvqVaryingIn) {
if (! intermediate.setLocalSizeSpecId(i, publicType.shaderQualifiers.localSizeSpecId[i]))
error(loc, "cannot change previously set size", "local_size", "");
} else
error(loc, "can only apply to 'in'", "local_size id", "");
// Set the workgroup built-in variable as a specialization constant
TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize");
workGroupSize->getWritableType().getQualifier().specConstant = true;
}
}
if (publicType.shaderQualifiers.earlyFragmentTests) {
if (publicType.qualifier.storage == EvqVaryingIn)
@ -5614,6 +5841,10 @@ void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, con
error(loc, "cannot declare a default, use a full declaration", "location/component/index", "");
if (qualifier.hasXfbOffset())
error(loc, "cannot declare a default, use a full declaration", "xfb_offset", "");
if (qualifier.layoutPushConstant)
error(loc, "cannot declare a default, can only be used on a block", "push_constant", "");
if (qualifier.hasSpecConstantId())
error(loc, "cannot declare a default, can only be used on a scalar", "constant_id", "");
}
//

View file

@ -65,7 +65,7 @@ typedef std::set<int> TIdSetType;
//
class TParseContext {
public:
TParseContext(TSymbolTable&, TIntermediate&, bool parsingBuiltins, int version, EProfile, int spv, EShLanguage, TInfoSink&,
TParseContext(TSymbolTable&, TIntermediate&, bool parsingBuiltins, int version, EProfile, int spv, int vulkan, EShLanguage, TInfoSink&,
bool forwardCompatible = false, EShMessages messages = EShMsgDefault);
virtual ~TParseContext();
@ -98,6 +98,7 @@ public:
void handleIndexLimits(const TSourceLoc&, TIntermTyped* base, TIntermTyped* index);
void makeEditable(TSymbol*&);
TVariable* getEditableVariable(const char* name);
bool isIoResizeArray(const TType&) const;
void fixIoArraySize(const TSourceLoc&, TType&);
void ioArrayCheck(const TSourceLoc&, const TType&, const TString& identifier);
@ -132,7 +133,8 @@ public:
void integerCheck(const TIntermTyped* node, const char* token);
void globalCheck(const TSourceLoc&, const char* token);
bool constructorError(const TSourceLoc&, TIntermNode*, TFunction&, TOperator, TType&);
void arraySizeCheck(const TSourceLoc&, TIntermTyped* expr, int& size);
bool constructorTextureSamplerError(const TSourceLoc&, const TFunction&);
void arraySizeCheck(const TSourceLoc&, TIntermTyped* expr, TArraySize&);
bool arrayQualifierError(const TSourceLoc&, const TQualifier&);
bool arrayError(const TSourceLoc&, const TType&);
void arraySizeRequiredCheck(const TSourceLoc&, const TArraySizes&);
@ -145,8 +147,9 @@ public:
bool voidErrorCheck(const TSourceLoc&, const TString&, TBasicType);
void boolCheck(const TSourceLoc&, const TIntermTyped*);
void boolCheck(const TSourceLoc&, const TPublicType&);
void samplerCheck(const TSourceLoc&, const TType&, const TString& identifier);
void samplerCheck(const TSourceLoc&, const TType&, const TString& identifier, TIntermTyped* initializer);
void atomicUintCheck(const TSourceLoc&, const TType&, const TString& identifier);
void transparentCheck(const TSourceLoc&, const TType&, const TString& identifier);
void globalQualifierFixCheck(const TSourceLoc&, TQualifier&);
void globalQualifierTypeCheck(const TSourceLoc&, const TQualifier&, const TPublicType&);
bool structQualifierErrorCheck(const TSourceLoc&, const TPublicType& pType);
@ -165,6 +168,7 @@ public:
void nestedStructCheck(const TSourceLoc&);
void arrayObjectCheck(const TSourceLoc&, const TType&, const char* op);
void opaqueCheck(const TSourceLoc&, const TType&, const char* op);
void specializationCheck(const TSourceLoc&, const TType&, const char* op);
void structTypeCheck(const TSourceLoc&, TPublicType&);
void inductiveLoopCheck(const TSourceLoc&, TIntermNode* init, TIntermLoop* loop);
void arrayLimitCheck(const TSourceLoc&, const TString&, int size);
@ -193,7 +197,7 @@ public:
TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermTyped*, const TSourceLoc&, bool subset);
void declareBlock(const TSourceLoc&, TTypeList& typeList, const TString* instanceName = 0, TArraySizes* arraySizes = 0);
void blockStageIoCheck(const TSourceLoc&, const TQualifier&);
void blockQualifierCheck(const TSourceLoc&, const TQualifier&);
void blockQualifierCheck(const TSourceLoc&, const TQualifier&, bool instanceName);
void fixBlockLocations(const TSourceLoc&, TQualifier&, TTypeList&, bool memberWithLocation, bool memberWithoutLocation);
void fixBlockXfbOffsets(TQualifier&, TTypeList&);
void fixBlockUniformOffsets(TQualifier&, TTypeList&);
@ -244,6 +248,10 @@ public:
void updateExtensionBehavior(int line, const char* const extension, const char* behavior);
void fullIntegerCheck(const TSourceLoc&, const char* op);
void doubleCheck(const TSourceLoc&, const char* op);
void spvRemoved(const TSourceLoc&, const char* op);
void vulkanRemoved(const TSourceLoc&, const char* op);
void requireVulkan(const TSourceLoc&, const char* op);
void requireSpv(const TSourceLoc&, const char* op);
void setVersionCallback(const std::function<void(int, int, const char*)>& func) { versionCallback = func; }
void setPragmaCallback(const std::function<void(int, const TVector<TString>&)>& func) { pragmaCallback = func; }
@ -281,6 +289,7 @@ public:
int version; // version, updated by #version in the shader
EProfile profile; // the declared profile in the shader (core by default)
int spv; // SPIR-V version; 0 means not SPIR-V
int vulkan; // Vulkan version; 0 means not vulkan
bool forwardCompatible; // true if errors are to be given for use of deprecated features
// Current state of parsing

View file

@ -494,6 +494,50 @@ void TScanContext::fillInKeywordMap()
(*KeywordMap)["samplerExternalOES"] = SAMPLEREXTERNALOES; // GL_OES_EGL_image_external
(*KeywordMap)["sampler"] = SAMPLER;
(*KeywordMap)["samplerShadow"] = SAMPLERSHADOW;
(*KeywordMap)["texture2D"] = TEXTURE2D;
(*KeywordMap)["textureCube"] = TEXTURECUBE;
(*KeywordMap)["textureCubeArray"] = TEXTURECUBEARRAY;
(*KeywordMap)["itextureCubeArray"] = ITEXTURECUBEARRAY;
(*KeywordMap)["utextureCubeArray"] = UTEXTURECUBEARRAY;
(*KeywordMap)["itexture1DArray"] = ITEXTURE1DARRAY;
(*KeywordMap)["utexture1D"] = UTEXTURE1D;
(*KeywordMap)["itexture1D"] = ITEXTURE1D;
(*KeywordMap)["utexture1DArray"] = UTEXTURE1DARRAY;
(*KeywordMap)["textureBuffer"] = TEXTUREBUFFER;
(*KeywordMap)["texture2DArray"] = TEXTURE2DARRAY;
(*KeywordMap)["itexture2D"] = ITEXTURE2D;
(*KeywordMap)["itexture3D"] = ITEXTURE3D;
(*KeywordMap)["itextureCube"] = ITEXTURECUBE;
(*KeywordMap)["itexture2DArray"] = ITEXTURE2DARRAY;
(*KeywordMap)["utexture2D"] = UTEXTURE2D;
(*KeywordMap)["utexture3D"] = UTEXTURE3D;
(*KeywordMap)["utextureCube"] = UTEXTURECUBE;
(*KeywordMap)["utexture2DArray"] = UTEXTURE2DARRAY;
(*KeywordMap)["itexture2DRect"] = ITEXTURE2DRECT;
(*KeywordMap)["utexture2DRect"] = UTEXTURE2DRECT;
(*KeywordMap)["itextureBuffer"] = ITEXTUREBUFFER;
(*KeywordMap)["utextureBuffer"] = UTEXTUREBUFFER;
(*KeywordMap)["texture2DMS"] = TEXTURE2DMS;
(*KeywordMap)["itexture2DMS"] = ITEXTURE2DMS;
(*KeywordMap)["utexture2DMS"] = UTEXTURE2DMS;
(*KeywordMap)["texture2DMSArray"] = TEXTURE2DMSARRAY;
(*KeywordMap)["itexture2DMSArray"] = ITEXTURE2DMSARRAY;
(*KeywordMap)["utexture2DMSArray"] = UTEXTURE2DMSARRAY;
(*KeywordMap)["texture1D"] = TEXTURE1D;
(*KeywordMap)["texture3D"] = TEXTURE3D;
(*KeywordMap)["texture2DRect"] = TEXTURE2DRECT;
(*KeywordMap)["texture1DArray"] = TEXTURE1DARRAY;
(*KeywordMap)["subpassInput"] = SUBPASSINPUT;
(*KeywordMap)["subpassInputMS"] = SUBPASSINPUTMS;
(*KeywordMap)["isubpassInput"] = ISUBPASSINPUT;
(*KeywordMap)["isubpassInputMS"] = ISUBPASSINPUTMS;
(*KeywordMap)["usubpassInput"] = USUBPASSINPUT;
(*KeywordMap)["usubpassInputMS"] = USUBPASSINPUTMS;
(*KeywordMap)["noperspective"] = NOPERSPECTIVE;
(*KeywordMap)["smooth"] = SMOOTH;
(*KeywordMap)["flat"] = FLAT;
@ -987,6 +1031,57 @@ int TScanContext::tokenizeIdentifier()
return keyword;
return identifierOrType();
case TEXTURE2D:
case TEXTURECUBE:
case TEXTURECUBEARRAY:
case ITEXTURECUBEARRAY:
case UTEXTURECUBEARRAY:
case ITEXTURE1DARRAY:
case UTEXTURE1D:
case ITEXTURE1D:
case UTEXTURE1DARRAY:
case TEXTUREBUFFER:
case TEXTURE2DARRAY:
case ITEXTURE2D:
case ITEXTURE3D:
case ITEXTURECUBE:
case ITEXTURE2DARRAY:
case UTEXTURE2D:
case UTEXTURE3D:
case UTEXTURECUBE:
case UTEXTURE2DARRAY:
case ITEXTURE2DRECT:
case UTEXTURE2DRECT:
case ITEXTUREBUFFER:
case UTEXTUREBUFFER:
case TEXTURE2DMS:
case ITEXTURE2DMS:
case UTEXTURE2DMS:
case TEXTURE2DMSARRAY:
case ITEXTURE2DMSARRAY:
case UTEXTURE2DMSARRAY:
case TEXTURE1D:
case TEXTURE3D:
case TEXTURE2DRECT:
case TEXTURE1DARRAY:
case SAMPLER:
case SAMPLERSHADOW:
if (parseContext.spv > 0)
return keyword;
else
return identifierOrType();
case SUBPASSINPUT:
case SUBPASSINPUTMS:
case ISUBPASSINPUT:
case ISUBPASSINPUTMS:
case USUBPASSINPUT:
case USUBPASSINPUTMS:
if (parseContext.spv > 0)
return keyword;
else
return identifierOrType();
case NOPERSPECTIVE:
return es30ReservedFromGLSL(130);

View file

@ -1,6 +1,7 @@
//
//Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
//Copyright (C) 2013 LunarG, Inc.
//Copyright (C) 2013-2015 LunarG, Inc.
//Copyright (C) 2015-2016 Google, Inc.
//
//All rights reserved.
//
@ -124,12 +125,12 @@ TPoolAllocator* PerProcessGPA = 0;
//
// Parse and add to the given symbol table the content of the given shader string.
//
bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profile, int spv, EShLanguage language, TInfoSink& infoSink,
bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profile, int spv, int vulkan, EShLanguage language, TInfoSink& infoSink,
TSymbolTable& symbolTable)
{
TIntermediate intermediate(language, version, profile);
TParseContext parseContext(symbolTable, intermediate, true, version, profile, spv, language, infoSink);
TParseContext parseContext(symbolTable, intermediate, true, version, profile, spv, vulkan, language, infoSink);
TPpContext ppContext(parseContext, TShader::ForbidInclude());
TScanContext scanContext(parseContext);
parseContext.setScanContext(&scanContext);
@ -168,11 +169,11 @@ int CommonIndex(EProfile profile, EShLanguage language)
//
// To initialize per-stage shared tables, with the common table already complete.
//
void InitializeStageSymbolTable(TBuiltIns& builtIns, int version, EProfile profile, int spv, EShLanguage language, TInfoSink& infoSink, TSymbolTable** commonTable, TSymbolTable** symbolTables)
void InitializeStageSymbolTable(TBuiltIns& builtIns, int version, EProfile profile, int spv, int vulkan, EShLanguage language, TInfoSink& infoSink, TSymbolTable** commonTable, TSymbolTable** symbolTables)
{
(*symbolTables[language]).adoptLevels(*commonTable[CommonIndex(profile, language)]);
InitializeSymbolTable(builtIns.getStageString(language), version, profile, spv, language, infoSink, *symbolTables[language]);
IdentifyBuiltIns(version, profile, spv, language, *symbolTables[language]);
InitializeSymbolTable(builtIns.getStageString(language), version, profile, spv, vulkan, language, infoSink, *symbolTables[language]);
IdentifyBuiltIns(version, profile, spv, vulkan, language, *symbolTables[language]);
if (profile == EEsProfile && version >= 300)
(*symbolTables[language]).setNoBuiltInRedeclarations();
if (version == 110)
@ -183,49 +184,49 @@ void InitializeStageSymbolTable(TBuiltIns& builtIns, int version, EProfile profi
// Initialize the full set of shareable symbol tables;
// The common (cross-stage) and those shareable per-stage.
//
bool InitializeSymbolTables(TInfoSink& infoSink, TSymbolTable** commonTable, TSymbolTable** symbolTables, int version, EProfile profile, int spv)
bool InitializeSymbolTables(TInfoSink& infoSink, TSymbolTable** commonTable, TSymbolTable** symbolTables, int version, EProfile profile, int spv, int vulkan)
{
TBuiltIns builtIns;
builtIns.initialize(version, profile, spv);
builtIns.initialize(version, profile, spv, vulkan);
// do the common tables
InitializeSymbolTable(builtIns.getCommonString(), version, profile, spv, EShLangVertex, infoSink, *commonTable[EPcGeneral]);
InitializeSymbolTable(builtIns.getCommonString(), version, profile, spv, vulkan, EShLangVertex, infoSink, *commonTable[EPcGeneral]);
if (profile == EEsProfile)
InitializeSymbolTable(builtIns.getCommonString(), version, profile, spv, EShLangFragment, infoSink, *commonTable[EPcFragment]);
InitializeSymbolTable(builtIns.getCommonString(), version, profile, spv, vulkan, EShLangFragment, infoSink, *commonTable[EPcFragment]);
// do the per-stage tables
// always have vertex and fragment
InitializeStageSymbolTable(builtIns, version, profile, spv, EShLangVertex, infoSink, commonTable, symbolTables);
InitializeStageSymbolTable(builtIns, version, profile, spv, EShLangFragment, infoSink, commonTable, symbolTables);
InitializeStageSymbolTable(builtIns, version, profile, spv, vulkan, EShLangVertex, infoSink, commonTable, symbolTables);
InitializeStageSymbolTable(builtIns, version, profile, spv, vulkan, EShLangFragment, infoSink, commonTable, symbolTables);
// check for tessellation
if ((profile != EEsProfile && version >= 150) ||
(profile == EEsProfile && version >= 310)) {
InitializeStageSymbolTable(builtIns, version, profile, spv, EShLangTessControl, infoSink, commonTable, symbolTables);
InitializeStageSymbolTable(builtIns, version, profile, spv, EShLangTessEvaluation, infoSink, commonTable, symbolTables);
InitializeStageSymbolTable(builtIns, version, profile, spv, vulkan, EShLangTessControl, infoSink, commonTable, symbolTables);
InitializeStageSymbolTable(builtIns, version, profile, spv, vulkan, EShLangTessEvaluation, infoSink, commonTable, symbolTables);
}
// check for geometry
if ((profile != EEsProfile && version >= 150) ||
(profile == EEsProfile && version >= 310))
InitializeStageSymbolTable(builtIns, version, profile, spv, EShLangGeometry, infoSink, commonTable, symbolTables);
InitializeStageSymbolTable(builtIns, version, profile, spv, vulkan, EShLangGeometry, infoSink, commonTable, symbolTables);
// check for compute
if ((profile != EEsProfile && version >= 430) ||
(profile == EEsProfile && version >= 310))
InitializeStageSymbolTable(builtIns, version, profile, spv, EShLangCompute, infoSink, commonTable, symbolTables);
InitializeStageSymbolTable(builtIns, version, profile, spv, vulkan, EShLangCompute, infoSink, commonTable, symbolTables);
return true;
}
bool AddContextSpecificSymbols(const TBuiltInResource* resources, TInfoSink& infoSink, TSymbolTable& symbolTable, int version, EProfile profile, int spv, EShLanguage language)
bool AddContextSpecificSymbols(const TBuiltInResource* resources, TInfoSink& infoSink, TSymbolTable& symbolTable, int version, EProfile profile, int spv, int vulkan, EShLanguage language)
{
TBuiltIns builtIns;
builtIns.initialize(*resources, version, profile, spv, language);
InitializeSymbolTable(builtIns.getCommonString(), version, profile, spv, language, infoSink, symbolTable);
IdentifyBuiltIns(version, profile, spv, language, symbolTable, *resources);
builtIns.initialize(*resources, version, profile, spv, vulkan, language);
InitializeSymbolTable(builtIns.getCommonString(), version, profile, spv, vulkan, language, infoSink, symbolTable);
IdentifyBuiltIns(version, profile, spv, vulkan, language, symbolTable, *resources);
return true;
}
@ -242,7 +243,7 @@ bool AddContextSpecificSymbols(const TBuiltInResource* resources, TInfoSink& inf
// This only gets done the first time any thread needs a particular symbol table
// (lazy evaluation).
//
void SetupBuiltinSymbolTable(int version, EProfile profile, int spv)
void SetupBuiltinSymbolTable(int version, EProfile profile, int spv, int vulkan)
{
TInfoSink infoSink;
@ -272,7 +273,7 @@ void SetupBuiltinSymbolTable(int version, EProfile profile, int spv)
stageTables[stage] = new TSymbolTable;
// Generate the local symbol tables using the new pool
InitializeSymbolTables(infoSink, commonTable, stageTables, version, profile, spv);
InitializeSymbolTables(infoSink, commonTable, stageTables, version, profile, spv, vulkan);
// Switch to the process-global pool
SetThreadPoolAllocator(*PerProcessGPA);
@ -471,7 +472,7 @@ bool ProcessDeferred(
const char* customPreamble,
const EShOptimizationLevel optLevel,
const TBuiltInResource* resources,
int defaultVersion, // use 100 for ES environment, 110 for desktop
int defaultVersion, // use 100 for ES environment, 110 for desktop; this is the GLSL version, not SPIR-V or Vulkan
EProfile defaultProfile,
// set version/profile to defaultVersion/defaultProfile regardless of the #version
// directive in the source code
@ -550,7 +551,7 @@ bool ProcessDeferred(
profile = defaultProfile;
}
int spv = (messages & EShMsgSpvRules) ? 100 : 0;
int spv = (messages & EShMsgSpvRules) ? 100 : 0; // TODO find path to get real version number here, for now non-0 is what matters
bool goodVersion = DeduceVersionProfile(compiler->infoSink, compiler->getLanguage(), versionNotFirst, defaultVersion, version, profile, spv);
bool versionWillBeError = (versionNotFound || (profile == EEsProfile && version >= 300 && versionNotFirst));
bool warnVersionNotFirst = false;
@ -561,10 +562,13 @@ bool ProcessDeferred(
versionWillBeError = true;
}
int vulkan = (messages & EShMsgVulkanRules) ? 100 : 0; // TODO find path to get real version number here, for now non-0 is what matters
intermediate.setVersion(version);
intermediate.setProfile(profile);
intermediate.setSpv(spv);
SetupBuiltinSymbolTable(version, profile, spv);
if (vulkan)
intermediate.setOriginUpperLeft();
SetupBuiltinSymbolTable(version, profile, spv, vulkan);
TSymbolTable* cachedTable = SharedSymbolTables[MapVersionToIndex(version)]
[MapProfileToIndex(profile)]
@ -578,13 +582,13 @@ bool ProcessDeferred(
// Add built-in symbols that are potentially context dependent;
// they get popped again further down.
AddContextSpecificSymbols(resources, compiler->infoSink, symbolTable, version, profile, spv, compiler->getLanguage());
AddContextSpecificSymbols(resources, compiler->infoSink, symbolTable, version, profile, spv, vulkan, compiler->getLanguage());
//
// Now we can process the full shader under proper symbols and rules.
//
TParseContext parseContext(symbolTable, intermediate, false, version, profile, spv, compiler->getLanguage(), compiler->infoSink, forwardCompatible, messages);
TParseContext parseContext(symbolTable, intermediate, false, version, profile, spv, vulkan, compiler->getLanguage(), compiler->infoSink, forwardCompatible, messages);
glslang::TScanContext scanContext(parseContext);
TPpContext ppContext(parseContext, includer);
parseContext.setScanContext(&scanContext);

View file

@ -87,6 +87,7 @@ void TType::buildMangledName(TString& mangledName)
case EsdCube: mangledName += "C"; break;
case EsdRect: mangledName += "R2"; break;
case EsdBuffer: mangledName += "B"; break;
case EsdSubpass: mangledName += "P"; break;
default: break; // some compilers want this
}
if (sampler.ms)
@ -115,7 +116,13 @@ void TType::buildMangledName(TString& mangledName)
const int maxSize = 11;
char buf[maxSize];
for (int i = 0; i < arraySizes->getNumDims(); ++i) {
snprintf(buf, maxSize, "%d", arraySizes->getDimSize(i));
if (arraySizes->getDimNode(i)) {
if (arraySizes->getDimNode(i)->getAsSymbolNode())
snprintf(buf, maxSize, "s%d", arraySizes->getDimNode(i)->getAsSymbolNode()->getId());
else
snprintf(buf, maxSize, "s%x", arraySizes->getDimNode(i));
} else
snprintf(buf, maxSize, "%d", arraySizes->getDimSize(i));
mangledName += '[';
mangledName += buf;
mangledName += ']';

View file

@ -173,6 +173,7 @@ void TParseContext::initializeExtensionBehavior()
extensionBehavior[E_GL_ARB_derivative_control] = EBhDisable;
extensionBehavior[E_GL_ARB_shader_texture_image_samples] = EBhDisable;
extensionBehavior[E_GL_ARB_viewport_array] = EBhDisable;
extensionBehavior[E_GL_ARB_gl_spirv] = EBhDisable;
extensionBehavior[E_GL_ARB_sparse_texture2] = EBhDisable;
extensionBehavior[E_GL_ARB_sparse_texture_clamp] = EBhDisable;
// extensionBehavior[E_GL_ARB_cull_distance] = EBhDisable; // present for 4.5, but need extension control over block members
@ -276,6 +277,7 @@ const char* TParseContext::getPreamble()
"#define GL_ARB_derivative_control 1\n"
"#define GL_ARB_shader_texture_image_samples 1\n"
"#define GL_ARB_viewport_array 1\n"
"#define GL_ARB_gl_spirv 1\n"
"#define GL_ARB_sparse_texture2 1\n"
"#define GL_ARB_sparse_texture_clamp 1\n"
@ -564,6 +566,9 @@ void TParseContext::updateExtensionBehavior(int line, const char* extension, con
updateExtensionBehavior(line, "GL_OES_shader_io_blocks", behaviorString);
else if (strcmp(extension, "GL_GOOGLE_include_directive") == 0)
updateExtensionBehavior(line, "GL_GOOGLE_cpp_style_line_directive", behaviorString);
// SPIR-V
else if (strcmp(extension, "GL_ARB_gl_spirv") == 0)
spv = 100;
}
void TParseContext::updateExtensionBehavior(const char* extension, TExtensionBehavior behavior)
@ -606,18 +611,14 @@ void TParseContext::updateExtensionBehavior(const char* extension, TExtensionBeh
}
}
//
// Call for any operation needing full GLSL integer data-type support.
//
void TParseContext::fullIntegerCheck(const TSourceLoc& loc, const char* op)
{
profileRequires(loc, ENoProfile, 130, nullptr, op);
profileRequires(loc, EEsProfile, 300, nullptr, op);
}
//
// Call for any operation needing GLSL double data-type support.
//
void TParseContext::doubleCheck(const TSourceLoc& loc, const char* op)
{
requireProfile(loc, ECoreProfile | ECompatibilityProfile, op);
@ -625,4 +626,32 @@ void TParseContext::doubleCheck(const TSourceLoc& loc, const char* op)
profileRequires(loc, ECompatibilityProfile, 400, nullptr, op);
}
// Call for any operation removed because SPIR-V is in use.
void TParseContext::spvRemoved(const TSourceLoc& loc, const char* op)
{
if (spv > 0)
error(loc, "not allowed when generating SPIR-V", op, "");
}
// Call for any operation removed because Vulkan SPIR-V is being generated.
void TParseContext::vulkanRemoved(const TSourceLoc& loc, const char* op)
{
if (vulkan > 0)
error(loc, "not allowed when using GLSL for Vulkan", op, "");
}
// Call for any operation that requires Vulkan.
void TParseContext::requireVulkan(const TSourceLoc& loc, const char* op)
{
if (vulkan == 0)
error(loc, "only allowed when using GLSL for Vulkan", op, "");
}
// Call for any operation that requires SPIR-V.
void TParseContext::requireSpv(const TSourceLoc& loc, const char* op)
{
if (spv == 0)
error(loc, "only allowed when generating SPIR-V", op, "");
}
} // end namespace glslang

View file

@ -111,6 +111,7 @@ const char* const E_GL_ARB_shader_draw_parameters = "GL_ARB_shader_draw_pa
const char* const E_GL_ARB_derivative_control = "GL_ARB_derivative_control";
const char* const E_GL_ARB_shader_texture_image_samples = "GL_ARB_shader_texture_image_samples";
const char* const E_GL_ARB_viewport_array = "GL_ARB_viewport_array";
const char* const E_GL_ARB_gl_spirv = "GL_ARB_gl_spirv";
const char* const E_GL_ARB_sparse_texture2 = "GL_ARB_sparse_texture2";
const char* const E_GL_ARB_sparse_texture_clamp = "GL_ARB_sparse_texture_clamp";
//const char* const E_GL_ARB_cull_distance = "GL_ARB_cull_distance"; // present for 4.5, but need extension control over block members

View file

@ -148,6 +148,24 @@ extern int yylex(YYSTYPE*, TParseContext&);
%token <lex> SAMPLER2DMSARRAY ISAMPLER2DMSARRAY USAMPLER2DMSARRAY
%token <lex> SAMPLEREXTERNALOES
// pure sampler
%token <lex> SAMPLER SAMPLERSHADOW
// texture without sampler
%token <lex> TEXTURE1D TEXTURE2D TEXTURE3D TEXTURECUBE
%token <lex> TEXTURE1DARRAY TEXTURE2DARRAY
%token <lex> ITEXTURE1D ITEXTURE2D ITEXTURE3D ITEXTURECUBE
%token <lex> ITEXTURE1DARRAY ITEXTURE2DARRAY UTEXTURE1D UTEXTURE2D UTEXTURE3D
%token <lex> UTEXTURECUBE UTEXTURE1DARRAY UTEXTURE2DARRAY
%token <lex> TEXTURE2DRECT ITEXTURE2DRECT UTEXTURE2DRECT
%token <lex> TEXTUREBUFFER ITEXTUREBUFFER UTEXTUREBUFFER
%token <lex> TEXTURECUBEARRAY ITEXTURECUBEARRAY UTEXTURECUBEARRAY
%token <lex> TEXTURE2DMS ITEXTURE2DMS UTEXTURE2DMS
%token <lex> TEXTURE2DMSARRAY ITEXTURE2DMSARRAY UTEXTURE2DMSARRAY
// input attachments
%token <lex> SUBPASSINPUT SUBPASSINPUTMS ISUBPASSINPUT ISUBPASSINPUTMS USUBPASSINPUT USUBPASSINPUTMS
%token <lex> IMAGE1D IIMAGE1D UIMAGE1D IMAGE2D IIMAGE2D
%token <lex> UIMAGE2D IMAGE3D IIMAGE3D UIMAGE3D
%token <lex> IMAGE2DRECT IIMAGE2DRECT UIMAGE2DRECT
@ -503,6 +521,7 @@ equality_expression
| equality_expression EQ_OP relational_expression {
parseContext.arrayObjectCheck($2.loc, $1->getType(), "array comparison");
parseContext.opaqueCheck($2.loc, $1->getType(), "==");
parseContext.specializationCheck($2.loc, $1->getType(), "==");
$$ = parseContext.handleBinaryMath($2.loc, "==", EOpEqual, $1, $3);
if ($$ == 0)
$$ = parseContext.intermediate.addConstantUnion(false, $2.loc);
@ -510,6 +529,7 @@ equality_expression
| equality_expression NE_OP relational_expression {
parseContext.arrayObjectCheck($2.loc, $1->getType(), "array comparison");
parseContext.opaqueCheck($2.loc, $1->getType(), "!=");
parseContext.specializationCheck($2.loc, $1->getType(), "!=");
$$ = parseContext.handleBinaryMath($2.loc, "!=", EOpNotEqual, $1, $3);
if ($$ == 0)
$$ = parseContext.intermediate.addConstantUnion(false, $2.loc);
@ -597,6 +617,7 @@ assignment_expression
| unary_expression assignment_operator assignment_expression {
parseContext.arrayObjectCheck($2.loc, $1->getType(), "array assignment");
parseContext.opaqueCheck($2.loc, $1->getType(), "=");
parseContext.specializationCheck($2.loc, $1->getType(), "=");
parseContext.lValueErrorCheck($2.loc, "assign", $1);
parseContext.rValueErrorCheck($2.loc, "assign", $3);
$$ = parseContext.intermediate.addAssign($2.op, $1, $3, $2.loc);
@ -1201,11 +1222,13 @@ storage_qualifier
$$.qualifier.writeonly = true;
}
| SUBROUTINE {
parseContext.spvRemoved($1.loc, "subroutine");
parseContext.globalCheck($1.loc, "subroutine");
$$.init($1.loc);
$$.qualifier.storage = EvqUniform;
}
| SUBROUTINE LEFT_PAREN type_name_list RIGHT_PAREN {
parseContext.spvRemoved($1.loc, "subroutine");
parseContext.globalCheck($1.loc, "subroutine");
$$.init($1.loc);
$$.qualifier.storage = EvqUniform;
@ -1242,11 +1265,11 @@ array_specifier
$$.arraySizes = new TArraySizes;
$$.arraySizes->addInnerSize();
}
| LEFT_BRACKET constant_expression RIGHT_BRACKET {
| LEFT_BRACKET conditional_expression RIGHT_BRACKET {
$$.loc = $1.loc;
$$.arraySizes = new TArraySizes;
int size;
TArraySize size;
parseContext.arraySizeCheck($2->getLoc(), $2, size);
$$.arraySizes->addInnerSize(size);
}
@ -1254,10 +1277,10 @@ array_specifier
$$ = $1;
$$.arraySizes->addInnerSize();
}
| array_specifier LEFT_BRACKET constant_expression RIGHT_BRACKET {
| array_specifier LEFT_BRACKET conditional_expression RIGHT_BRACKET {
$$ = $1;
int size;
TArraySize size;
parseContext.arraySizeCheck($3->getLoc(), $3, size);
$$.arraySizes->addInnerSize(size);
}
@ -1504,6 +1527,7 @@ type_specifier_nonarray
$$.setMatrix(4, 4);
}
| ATOMIC_UINT {
parseContext.vulkanRemoved($1.loc, "atomic counter types");
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtAtomicUint;
}
@ -1707,6 +1731,181 @@ type_specifier_nonarray
$$.basicType = EbtSampler;
$$.sampler.set(EbtUint, Esd2D, true, false, true);
}
| SAMPLER {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setPureSampler(false);
}
| SAMPLERSHADOW {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setPureSampler(true);
}
| TEXTURE1D {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtFloat, Esd1D);
}
| TEXTURE2D {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtFloat, Esd2D);
}
| TEXTURE3D {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtFloat, Esd3D);
}
| TEXTURECUBE {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtFloat, EsdCube);
}
| TEXTURE1DARRAY {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtFloat, Esd1D, true);
}
| TEXTURE2DARRAY {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtFloat, Esd2D, true);
}
| TEXTURECUBEARRAY {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtFloat, EsdCube, true);
}
| ITEXTURE1D {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtInt, Esd1D);
}
| ITEXTURE2D {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtInt, Esd2D);
}
| ITEXTURE3D {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtInt, Esd3D);
}
| ITEXTURECUBE {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtInt, EsdCube);
}
| ITEXTURE1DARRAY {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtInt, Esd1D, true);
}
| ITEXTURE2DARRAY {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtInt, Esd2D, true);
}
| ITEXTURECUBEARRAY {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtInt, EsdCube, true);
}
| UTEXTURE1D {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtUint, Esd1D);
}
| UTEXTURE2D {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtUint, Esd2D);
}
| UTEXTURE3D {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtUint, Esd3D);
}
| UTEXTURECUBE {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtUint, EsdCube);
}
| UTEXTURE1DARRAY {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtUint, Esd1D, true);
}
| UTEXTURE2DARRAY {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtUint, Esd2D, true);
}
| UTEXTURECUBEARRAY {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtUint, EsdCube, true);
}
| TEXTURE2DRECT {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtFloat, EsdRect);
}
| ITEXTURE2DRECT {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtInt, EsdRect);
}
| UTEXTURE2DRECT {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtUint, EsdRect);
}
| TEXTUREBUFFER {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtFloat, EsdBuffer);
}
| ITEXTUREBUFFER {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtInt, EsdBuffer);
}
| UTEXTUREBUFFER {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtUint, EsdBuffer);
}
| TEXTURE2DMS {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtFloat, Esd2D, false, false, true);
}
| ITEXTURE2DMS {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtInt, Esd2D, false, false, true);
}
| UTEXTURE2DMS {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtUint, Esd2D, false, false, true);
}
| TEXTURE2DMSARRAY {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtFloat, Esd2D, true, false, true);
}
| ITEXTURE2DMSARRAY {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtInt, Esd2D, true, false, true);
}
| UTEXTURE2DMSARRAY {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setTexture(EbtUint, Esd2D, true, false, true);
}
| IMAGE1D {
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
@ -1878,6 +2077,42 @@ type_specifier_nonarray
$$.sampler.set(EbtFloat, Esd2D);
$$.sampler.external = true;
}
| SUBPASSINPUT {
parseContext.requireStage($1.loc, EShLangFragment, "subpass input");
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setSubpass(EbtFloat);
}
| SUBPASSINPUTMS {
parseContext.requireStage($1.loc, EShLangFragment, "subpass input");
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setSubpass(EbtFloat, true);
}
| ISUBPASSINPUT {
parseContext.requireStage($1.loc, EShLangFragment, "subpass input");
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setSubpass(EbtInt);
}
| ISUBPASSINPUTMS {
parseContext.requireStage($1.loc, EShLangFragment, "subpass input");
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setSubpass(EbtInt, true);
}
| USUBPASSINPUT {
parseContext.requireStage($1.loc, EShLangFragment, "subpass input");
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setSubpass(EbtUint);
}
| USUBPASSINPUTMS {
parseContext.requireStage($1.loc, EShLangFragment, "subpass input");
$$.init($1.loc, parseContext.symbolTable.atGlobalLevel());
$$.basicType = EbtSampler;
$$.sampler.setSubpass(EbtUint, true);
}
| struct_specifier {
$$ = $1;
$$.qualifier.storage = parseContext.symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary;

View file

@ -385,6 +385,7 @@ bool TOutputTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node
case EOpConstructDMat4x3: out.debug << "Construct dmat4x3"; break;
case EOpConstructDMat4x4: out.debug << "Construct dmat4"; break;
case EOpConstructStruct: out.debug << "Construct structure"; break;
case EOpConstructTextureSampler: out.debug << "Construct combined texture-sampler"; break;
case EOpLessThan: out.debug << "Compare Less Than"; break;
case EOpGreaterThan: out.debug << "Compare Greater Than"; break;
@ -755,6 +756,20 @@ void TIntermediate::output(TInfoSink& infoSink, bool tree)
case EShLangCompute:
infoSink.debug << "local_size = (" << localSize[0] << ", " << localSize[1] << ", " << localSize[2] << ")\n";
{
bool dumpSpecIds = false;
for (auto c : localSizeSpecId) {
if (c != TQualifier::layoutNotSet)
dumpSpecIds = true;
}
if (dumpSpecIds) {
infoSink.debug << "local_size ids = (" <<
localSizeSpecId[0] << ", " <<
localSizeSpecId[1] << ", " <<
localSizeSpecId[2] << ")\n";
}
}
break;
default:

View file

@ -71,6 +71,7 @@ void TIntermediate::merge(TInfoSink& infoSink, TIntermediate& unit)
{
numMains += unit.numMains;
numErrors += unit.numErrors;
numPushConstants += unit.numPushConstants;
callGraph.insert(callGraph.end(), unit.callGraph.begin(), unit.callGraph.end());
if ((profile != EEsProfile && unit.profile == EEsProfile) ||
@ -129,6 +130,11 @@ void TIntermediate::merge(TInfoSink& infoSink, TIntermediate& unit)
localSize[i] = unit.localSize[i];
else if (localSize[i] != unit.localSize[i])
error(infoSink, "Contradictory local size");
if (localSizeSpecId[i] != TQualifier::layoutNotSet)
localSizeSpecId[i] = unit.localSizeSpecId[i];
else if (localSizeSpecId[i] != unit.localSizeSpecId[i])
error(infoSink, "Contradictory local size specialization ids");
}
if (unit.xfbMode)
@ -353,6 +359,9 @@ void TIntermediate::finalCheck(TInfoSink& infoSink)
if (numMains < 1)
error(infoSink, "Missing entry point: Each stage requires one \"void main()\" entry point");
if (numPushConstants > 1)
error(infoSink, "Only one push_constant block is allowed per stage");
// recursion checking
checkCallGraphCycles(infoSink);
@ -690,6 +699,19 @@ int TIntermediate::addUsedOffsets(int binding, int offset, int numOffsets)
return -1; // no collision
}
// Accumulate used constant_id values.
//
// Return false is one was already used.
bool TIntermediate::addUsedConstantId(int id)
{
if (usedConstantId.find(id) != usedConstantId.end())
return false;
usedConstantId.insert(id);
return true;
}
// Recursively figure out how many locations are used up by an input or output type.
// Return the size of type, as measured by "locations".
int TIntermediate::computeTypeLocationSize(const TType& type) const

View file

@ -93,7 +93,7 @@ struct TIoRange {
int index;
};
// An IO range is a 2-D rectangle; the set of (binding, offset) pairs all lying
// An offset range is a 2-D rectangle; the set of (binding, offset) pairs all lying
// within the same binding and offset range.
struct TOffsetRange {
TOffsetRange(TRange binding, TRange offset)
@ -125,7 +125,7 @@ class TVariable;
class TIntermediate {
public:
explicit TIntermediate(EShLanguage l, int v = 0, EProfile p = ENoProfile) : language(l), treeRoot(0), profile(p), version(v), spv(0),
numMains(0), numErrors(0), recursive(false),
numMains(0), numErrors(0), numPushConstants(0), recursive(false),
invocations(TQualifier::layoutNotSet), vertices(TQualifier::layoutNotSet), inputPrimitive(ElgNone), outputPrimitive(ElgNone),
pixelCenterInteger(false), originUpperLeft(false),
vertexSpacing(EvsNone), vertexOrder(EvoNone), pointMode(false), earlyFragmentTests(false), depthLayout(EldNone), depthReplacing(false), blendEquations(0), xfbMode(false)
@ -133,6 +133,9 @@ public:
localSize[0] = 1;
localSize[1] = 1;
localSize[2] = 1;
localSizeSpecId[0] = TQualifier::layoutNotSet;
localSizeSpecId[1] = TQualifier::layoutNotSet;
localSizeSpecId[2] = TQualifier::layoutNotSet;
xfbBuffers.resize(TQualifier::layoutXfbBufferEnd);
}
void setLimits(const TBuiltInResource& r) { resources = r; }
@ -156,8 +159,10 @@ public:
void addMainCount() { ++numMains; }
int getNumMains() const { return numMains; }
int getNumErrors() const { return numErrors; }
void addPushConstantCount() { ++numPushConstants; }
bool isRecursive() const { return recursive; }
TIntermSymbol* addSymbol(int Id, const TString&, const TType&, const TConstUnionArray&, const TSourceLoc&);
TIntermSymbol* addSymbol(int Id, const TString&, const TType&, const TSourceLoc&);
TIntermSymbol* addSymbol(const TVariable&, const TSourceLoc&);
TIntermTyped* addConversion(TOperator, const TType&, TIntermTyped*) const;
@ -255,6 +260,15 @@ public:
}
unsigned int getLocalSize(int dim) const { return localSize[dim]; }
bool setLocalSizeSpecId(int dim, int id)
{
if (localSizeSpecId[dim] != TQualifier::layoutNotSet)
return id == localSizeSpecId[dim];
localSizeSpecId[dim] = id;
return true;
}
unsigned int getLocalSizeSpecId(int dim) const { return localSizeSpecId[dim]; }
void setXfbMode() { xfbMode = true; }
bool getXfbMode() const { return xfbMode; }
bool setOutputPrimitive(TLayoutGeometry p)
@ -294,6 +308,7 @@ public:
int addUsedLocation(const TQualifier&, const TType&, bool& typeCollision);
int addUsedOffsets(int binding, int offset, int numOffsets);
bool addUsedConstantId(int id);
int computeTypeLocationSize(const TType&) const;
bool setXfbBufferStride(int buffer, unsigned stride)
@ -328,6 +343,7 @@ protected:
TBuiltInResource resources;
int numMains;
int numErrors;
int numPushConstants;
bool recursive;
int invocations;
int vertices;
@ -339,6 +355,7 @@ protected:
TVertexOrder vertexOrder;
bool pointMode;
int localSize[3];
int localSizeSpecId[3];
bool earlyFragmentTests;
TLayoutDepth depthLayout;
bool depthReplacing;
@ -352,6 +369,7 @@ protected:
std::vector<TIoRange> usedIo[4]; // sets of used locations, one for each of in, out, uniform, and buffers
std::vector<TOffsetRange> usedAtomics; // sets of bindings used by atomic counters
std::vector<TXfbBuffer> xfbBuffers; // all the data we need to track per xfb buffer
std::unordered_set<int> usedConstantId; // specialization constant ids used
private:
void operator=(TIntermediate&); // prevent assignments

View file

@ -303,7 +303,7 @@ public:
// Returns an error message for any #include directive.
class ForbidInclude : public Includer {
public:
std::pair<std::string, std::string> include(const char* filename) const override
std::pair<std::string, std::string> include(const char* /*filename*/) const override
{
return std::make_pair<std::string, std::string>("", "unexpected include directive");
}