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"