Support specialization composite constants
Fix issue #163, support creation and reference of composite type
specialization constants.
e.g.:
```
layout(constant_id = 200) const float myfloat = 1.25;
layout(constant_id = 201) const int myint = 14;
struct structtype {
float f;
int i;
};
const structtype outer_struct_var = {myfloat, myint};
void main(){}
```
generated code (use glslangValidator):
```
// Module Version 10000
// Generated by (magic number): 80001
// Id's are bound by 12
Capability Shader
1: ExtInstImport "GLSL.std.450"
MemoryModel Logical GLSL450
EntryPoint Vertex 4 "main"
Source GLSL 450
Name 4 "main"
Name 10 "structtype"
MemberName 10(structtype) 0 "f"
MemberName 10(structtype) 1 "i"
Decorate 7 SpecId 200
Decorate 9 SpecId 201
2: TypeVoid
3: TypeFunction 2
6: TypeFloat 32
7: 6(float) SpecConstant 1067450368
8: TypeInt 32 1
9: 8(int) SpecConstant 14
10(structtype): TypeStruct 6(float) 8(int)
11:10(structtype) SpecConstantComposite 7 9
4(main): 2 Function None 3
5: Label
Return
FunctionEnd
```
Rname two function names to match their functionalities.
1) Rename `GlslangToSpvTraverser::createSpvSpecConstant()` to
`createSpvConstant()`;
2) Rename `GlslangToSpvTraverser::createSpvConstant()` to
`createSpvConstantFromConstUnionArray()`
Add function `GlslangToSpvTraverser::createSpvConstantFromSubTree()` to
handle constant creation from sub trees (e.g.: specialization constants).
Related PR: #208
diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp
index 6f5697e..dd5afd4 100755
--- a/SPIRV/GlslangToSpv.cpp
+++ b/SPIRV/GlslangToSpv.cpp
@@ -128,8 +128,9 @@
void addDecoration(spv::Id id, spv::Decoration dec, unsigned value);
void addMemberDecoration(spv::Id id, int member, spv::Decoration dec);
void addMemberDecoration(spv::Id id, int member, spv::Decoration dec, unsigned value);
- spv::Id createSpvSpecConstant(const glslang::TIntermTyped&);
- spv::Id createSpvConstant(const glslang::TType& type, const glslang::TConstUnionArray&, int& nextConst, bool specConstant);
+ spv::Id createSpvConstant(const glslang::TIntermTyped&);
+ spv::Id createSpvConstantFromConstUnionArray(const glslang::TType& type, const glslang::TConstUnionArray&, int& nextConst, bool specConstant);
+ spv::Id createSpvConstantFromConstSubTree(const glslang::TIntermTyped* subTree);
bool isTrivialLeaf(const glslang::TIntermTyped* node);
bool isTrivial(const glslang::TIntermTyped* node);
spv::Id createShortCircuit(glslang::TOperator, glslang::TIntermTyped& left, glslang::TIntermTyped& right);
@@ -1520,7 +1521,7 @@
void TGlslangToSpvTraverser::visitConstantUnion(glslang::TIntermConstantUnion* node)
{
int nextConst = 0;
- spv::Id constant = createSpvConstant(node->getType(), node->getConstArray(), nextConst, false);
+ spv::Id constant = createSpvConstantFromConstUnionArray(node->getType(), node->getConstArray(), nextConst, false);
builder.clearAccessChain();
builder.setAccessChainRValue(constant);
@@ -1630,7 +1631,7 @@
// can still have a mapping to a SPIR-V Id.
// This includes specialization constants.
if (node->getQualifier().isConstant()) {
- return createSpvSpecConstant(*node);
+ return createSpvConstant(*node);
}
// Now, handle actual variables
@@ -3730,7 +3731,7 @@
// recursively walks. So, this function walks the "top" of the tree:
// - emit specialization constant-building instructions for specConstant
// - when running into a non-spec-constant, switch to createSpvConstant()
-spv::Id TGlslangToSpvTraverser::createSpvSpecConstant(const glslang::TIntermTyped& node)
+spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TIntermTyped& node)
{
assert(node.getQualifier().isConstant());
@@ -3738,7 +3739,7 @@
// hand off to the non-spec-constant path
assert(node.getAsConstantUnion() != nullptr || node.getAsSymbolNode() != nullptr);
int nextConst = 0;
- return createSpvConstant(node.getType(), node.getAsConstantUnion() ? node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(),
+ return createSpvConstantFromConstUnionArray(node.getType(), node.getAsConstantUnion() ? node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(),
nextConst, false);
}
@@ -3747,7 +3748,7 @@
if (node.getAsSymbolNode() && node.getQualifier().hasSpecConstantId()) {
// this is a direct literal assigned to a layout(constant_id=) declaration
int nextConst = 0;
- return createSpvConstant(node.getType(), node.getAsConstantUnion() ? node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(),
+ return createSpvConstantFromConstUnionArray(node.getType(), node.getAsConstantUnion() ? node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(),
nextConst, true);
} else {
// gl_WorkgroupSize is a special case until the front-end handles hierarchical specialization constants,
@@ -3761,8 +3762,10 @@
addDecoration(dimConstId.back(), spv::DecorationSpecId, glslangIntermediate->getLocalSizeSpecId(dim));
}
return builder.makeCompositeConstant(builder.makeVectorType(builder.makeUintType(32), 3), dimConstId, true);
+ } else if (auto* sn = node.getAsSymbolNode()){
+ return createSpvConstantFromConstSubTree(sn->getConstSubtree());
} else {
- spv::MissingFunctionality("specialization-constant expression trees");
+ spv::MissingFunctionality("Neither a front-end constant nor a spec constant.");
exit(1);
return spv::NoResult;
}
@@ -3775,7 +3778,7 @@
// If there are not enough elements present in 'consts', 0 will be substituted;
// an empty 'consts' can be used to create a fully zeroed SPIR-V constant.
//
-spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TType& glslangType, const glslang::TConstUnionArray& consts, int& nextConst, bool specConstant)
+spv::Id TGlslangToSpvTraverser::createSpvConstantFromConstUnionArray(const glslang::TType& glslangType, const glslang::TConstUnionArray& consts, int& nextConst, bool specConstant)
{
// vector of constants for SPIR-V
std::vector<spv::Id> spvConsts;
@@ -3786,15 +3789,15 @@
if (glslangType.isArray()) {
glslang::TType elementType(glslangType, 0);
for (int i = 0; i < glslangType.getOuterArraySize(); ++i)
- spvConsts.push_back(createSpvConstant(elementType, consts, nextConst, false));
+ spvConsts.push_back(createSpvConstantFromConstUnionArray(elementType, consts, nextConst, false));
} else if (glslangType.isMatrix()) {
glslang::TType vectorType(glslangType, 0);
for (int col = 0; col < glslangType.getMatrixCols(); ++col)
- spvConsts.push_back(createSpvConstant(vectorType, consts, nextConst, false));
+ spvConsts.push_back(createSpvConstantFromConstUnionArray(vectorType, consts, nextConst, false));
} else if (glslangType.getStruct()) {
glslang::TVector<glslang::TTypeLoc>::const_iterator iter;
for (iter = glslangType.getStruct()->begin(); iter != glslangType.getStruct()->end(); ++iter)
- spvConsts.push_back(createSpvConstant(*iter->type, consts, nextConst, false));
+ spvConsts.push_back(createSpvConstantFromConstUnionArray(*iter->type, consts, nextConst, false));
} else if (glslangType.isVector()) {
for (unsigned int i = 0; i < (unsigned int)glslangType.getVectorSize(); ++i) {
bool zero = nextConst >= consts.size();
@@ -3851,6 +3854,66 @@
return builder.makeCompositeConstant(typeId, spvConsts);
}
+// Create constant ID from const initializer sub tree.
+spv::Id TGlslangToSpvTraverser::createSpvConstantFromConstSubTree(
+ const glslang::TIntermTyped* subTree) {
+ const glslang::TType& glslangType = subTree->getType();
+ spv::Id typeId = convertGlslangToSpvType(glslangType);
+ bool is_spec_const = subTree->getType().getQualifier().isSpecConstant();
+ if (const glslang::TIntermAggregate* an = subTree->getAsAggregate()) {
+ // Aggregate node, we should generate OpConstantComposite or
+ // OpSpecConstantComposite instruction.
+ std::vector<spv::Id> const_constituents;
+ for (auto NI = an->getSequence().begin(); NI != an->getSequence().end();
+ NI++) {
+ const_constituents.push_back(
+ createSpvConstantFromConstSubTree((*NI)->getAsTyped()));
+ }
+ // Note that constructors are aggregate nodes, so expressions like:
+ // float x = float(y) will become an aggregate node. If 'x' is declared
+ // as a constant, the aggregate node representing 'float(y)' will be
+ // processed here.
+ if (builder.isVectorType(typeId) || builder.isMatrixType(typeId) ||
+ builder.isAggregateType(typeId)) {
+ return builder.makeCompositeConstant(typeId, const_constituents, is_spec_const);
+ } else {
+ assert(builder.isScalarType(typeId) && const_constituents.size() == 1);
+ return const_constituents.front();
+ }
+
+ } else if (const glslang::TIntermBinary* bn = subTree->getAsBinaryNode()) {
+ // Binary operation node, we should generate OpSpecConstantOp <binary op>
+ // This case should only happen when Specialization Constants are involved.
+ spv::MissingFunctionality("OpSpecConstantOp <binary op> not implemented");
+ return spv::NoResult;
+
+ } else if (const glslang::TIntermUnary* un = subTree->getAsUnaryNode()) {
+ // Unary operation node, similar to binary operation node, should only
+ // happen when specialization constants are involved.
+ spv::MissingFunctionality("OpSpecConstantOp <unary op> not implemented");
+ return spv::NoResult;
+
+ } else if (const glslang::TIntermConstantUnion* cn = subTree->getAsConstantUnion()) {
+ // ConstantUnion node, should redirect to
+ // createSpvConstantFromConstUnionArray
+ int nextConst = 0;
+ return createSpvConstantFromConstUnionArray(
+ glslangType, cn->getConstArray(), nextConst, is_spec_const);
+
+ } else if (const glslang::TIntermSymbol* sn = subTree->getAsSymbolNode()) {
+ // Symbol node. Call getSymbolId(). This should cover both cases 1) the
+ // symbol has already been assigned an ID, 2) need a new ID for this
+ // symbol.
+ return getSymbolId(sn);
+
+ } else {
+ spv::MissingFunctionality(
+ "createSpvConstantFromConstSubTree() not covered TIntermTyped* const "
+ "initializer subtree.");
+ return spv::NoResult;
+ }
+}
+
// Return true if the node is a constant or symbol whose reading has no
// non-trivial observable cost or effect.
bool TGlslangToSpvTraverser::isTrivialLeaf(const glslang::TIntermTyped* node)