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);
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);
// 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
// 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());
// 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);
}
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,
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;
}
// 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;
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();
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)
--- /dev/null
+spv.specConstantComposite.vert
+Warning, version 450 is not yet complete; most version-specific features are present, but some are missing.
+
+
+Linked vertex stage:
+
+
+// Module Version 10000
+// Generated by (magic number): 80001
+// Id's are bound by 106
+
+ Capability Shader
+ Capability Float64
+ 1: ExtInstImport "GLSL.std.450"
+ MemoryModel Logical GLSL450
+ EntryPoint Vertex 4 "main" 27 105
+ Source GLSL 450
+ Name 4 "main"
+ Name 6 "refer_primary_spec_const("
+ Name 8 "refer_composite_spec_const("
+ Name 10 "refer_copmosite_dot_dereference("
+ Name 12 "refer_composite_bracket_dereference("
+ Name 16 "refer_spec_const_array_length("
+ Name 18 "declare_spec_const_in_func("
+ Name 27 "color"
+ Name 41 "flat_struct"
+ MemberName 41(flat_struct) 0 "i"
+ MemberName 41(flat_struct) 1 "f"
+ MemberName 41(flat_struct) 2 "d"
+ MemberName 41(flat_struct) 3 "b"
+ Name 42 "nesting_struct"
+ MemberName 42(nesting_struct) 0 "nested"
+ MemberName 42(nesting_struct) 1 "v"
+ MemberName 42(nesting_struct) 2 "i"
+ Name 72 "indexable"
+ Name 76 "indexable"
+ Name 83 "len"
+ Name 105 "global_vec4_array_with_spec_length"
+ Decorate 21 SpecId 203
+ Decorate 28 SpecId 200
+ Decorate 32 SpecId 201
+ Decorate 43 SpecId 202
+ 2: TypeVoid
+ 3: TypeFunction 2
+ 14: TypeInt 32 1
+ 15: TypeFunction 14(int)
+ 20: TypeBool
+ 21: 20(bool) SpecConstantTrue
+ 24: TypeFloat 32
+ 25: TypeVector 24(float) 4
+ 26: TypePointer Output 25(fvec4)
+ 27(color): 26(ptr) Variable Output
+ 28: 14(int) SpecConstant 3
+ 32: 24(float) SpecConstant 1078523331
+ 33: 25(fvec4) SpecConstantComposite 32 32 32 32
+ 36: 24(float) Constant 1133908460
+ 37: 25(fvec4) SpecConstantComposite 32 32 36 36
+ 40: TypeFloat 64
+ 41(flat_struct): TypeStruct 14(int) 24(float) 40(float) 20(bool)
+42(nesting_struct): TypeStruct 41(flat_struct) 25(fvec4) 14(int)
+ 43: 40(float) SpecConstant 1413754136 1074340347
+ 44:41(flat_struct) SpecConstantComposite 28 32 43 21
+ 45:42(nesting_struct) SpecConstantComposite 44 33 28
+ 46: 14(int) Constant 2
+ 51: TypeInt 32 0
+ 52: 51(int) Constant 0
+ 57: 51(int) Constant 5
+ 58: TypeArray 24(float) 57
+ 59: 24(float) Constant 1065353216
+ 60: 24(float) Constant 1073741824
+ 61: 24(float) Constant 1077936128
+ 62: 58 SpecConstantComposite 32 32 59 60 61
+ 63: 14(int) Constant 1
+ 68: TypeArray 14(int) 57
+ 69: 14(int) Constant 30
+ 70: 68 SpecConstantComposite 28 28 63 46 69
+ 71: TypePointer Function 68
+ 73: TypePointer Function 14(int)
+ 87: 24(float) Constant 1106321080
+ 88:41(flat_struct) SpecConstantComposite 69 87 43 21
+ 89: 14(int) Constant 10
+ 90:42(nesting_struct) SpecConstantComposite 88 37 89
+ 96: 20(bool) ConstantFalse
+ 97:41(flat_struct) SpecConstantComposite 28 32 43 96
+ 98: 24(float) Constant 1036831949
+ 99: 25(fvec4) ConstantComposite 98 98 98 98
+ 100:42(nesting_struct) SpecConstantComposite 97 99 28
+ 101: 14(int) Constant 3000
+ 102:42(nesting_struct) SpecConstantComposite 88 37 101
+ 103: TypeArray 25(fvec4) 28
+ 104: TypePointer Input 103
+105(global_vec4_array_with_spec_length): 104(ptr) Variable Input
+ 4(main): 2 Function None 3
+ 5: Label
+ Return
+ FunctionEnd
+6(refer_primary_spec_const(): 2 Function None 3
+ 7: Label
+ SelectionMerge 23 None
+ BranchConditional 21 22 23
+ 22: Label
+ 29: 24(float) ConvertSToF 28
+ 30: 25(fvec4) Load 27(color)
+ 31: 25(fvec4) VectorTimesScalar 30 29
+ Store 27(color) 31
+ Branch 23
+ 23: Label
+ Return
+ FunctionEnd
+8(refer_composite_spec_const(): 2 Function None 3
+ 9: Label
+ 34: 25(fvec4) Load 27(color)
+ 35: 25(fvec4) FAdd 34 33
+ Store 27(color) 35
+ 38: 25(fvec4) Load 27(color)
+ 39: 25(fvec4) FSub 38 37
+ Store 27(color) 39
+ Return
+ FunctionEnd
+10(refer_copmosite_dot_dereference(): 2 Function None 3
+ 11: Label
+ 47: 14(int) CompositeExtract 45 2
+ 48: 24(float) ConvertSToF 47
+ 49: 25(fvec4) Load 27(color)
+ 50: 25(fvec4) VectorTimesScalar 49 48
+ Store 27(color) 50
+ 53: 24(float) CompositeExtract 33 0
+ 54: 25(fvec4) Load 27(color)
+ 55: 25(fvec4) CompositeConstruct 53 53 53 53
+ 56: 25(fvec4) FAdd 54 55
+ Store 27(color) 56
+ Return
+ FunctionEnd
+12(refer_composite_bracket_dereference(): 2 Function None 3
+ 13: Label
+ 72(indexable): 71(ptr) Variable Function
+ 76(indexable): 71(ptr) Variable Function
+ 64: 24(float) CompositeExtract 62 1
+ 65: 25(fvec4) Load 27(color)
+ 66: 25(fvec4) CompositeConstruct 64 64 64 64
+ 67: 25(fvec4) FSub 65 66
+ Store 27(color) 67
+ Store 72(indexable) 70
+ 74: 73(ptr) AccessChain 72(indexable) 28
+ 75: 14(int) Load 74
+ Store 76(indexable) 70
+ 77: 73(ptr) AccessChain 76(indexable) 75
+ 78: 14(int) Load 77
+ 79: 24(float) ConvertSToF 78
+ 80: 25(fvec4) Load 27(color)
+ 81: 25(fvec4) CompositeConstruct 79 79 79 79
+ 82: 25(fvec4) FDiv 80 81
+ Store 27(color) 82
+ Return
+ FunctionEnd
+16(refer_spec_const_array_length(): 14(int) Function None 15
+ 17: Label
+ 83(len): 73(ptr) Variable Function
+ Store 83(len) 28
+ 84: 14(int) Load 83(len)
+ ReturnValue 84
+ FunctionEnd
+18(declare_spec_const_in_func(): 2 Function None 3
+ 19: Label
+ 91: 14(int) CompositeExtract 90 2
+ 92: 24(float) ConvertSToF 91
+ 93: 25(fvec4) Load 27(color)
+ 94: 25(fvec4) CompositeConstruct 92 92 92 92
+ 95: 25(fvec4) FDiv 93 94
+ Store 27(color) 95
+ Return
+ FunctionEnd
--- /dev/null
+#version 450
+
+// constant_id specified scalar spec constants
+layout(constant_id = 200) const int spec_int = 3;
+layout(constant_id = 201) const float spec_float = 3.14;
+layout(constant_id = 202) const
+ double spec_double = 3.1415926535897932384626433832795;
+layout(constant_id = 203) const bool spec_bool = true;
+
+const float cast_spec_float = float(spec_float);
+
+// Flat struct
+struct flat_struct {
+ int i;
+ float f;
+ double d;
+ bool b;
+};
+
+// Nesting struct
+struct nesting_struct {
+ flat_struct nested;
+ vec4 v;
+ int i;
+};
+
+// Expect OpSpecConstantComposite
+// Flat struct initializer
+const flat_struct spec_flat_struct_all_spec = {spec_int, spec_float,
+ spec_double, spec_bool};
+const flat_struct spec_flat_struct_partial_spec = {30, 30.14, spec_double,
+ spec_bool};
+
+// Nesting struct initializer
+const nesting_struct nesting_struct_ctor = {
+ {spec_int, spec_float, spec_double, false},
+ vec4(0.1, 0.1, 0.1, 0.1),
+ spec_int};
+
+// Vector constructor
+const vec4 spec_vec4_all_spec =
+ vec4(spec_float, spec_float, spec_float, spec_float);
+const vec4 spec_vec4_partial_spec =
+ vec4(spec_float, spec_float, 300.14, 300.14);
+
+// Struct nesting constructor
+const nesting_struct spec_nesting_struct_all_spec = {
+ spec_flat_struct_all_spec, spec_vec4_all_spec, spec_int};
+const nesting_struct spec_nesting_struct_partial_spec = {
+ spec_flat_struct_partial_spec, spec_vec4_partial_spec, 3000};
+
+const float spec_float_array[5] = {spec_float, spec_float, 1.0, 2.0, 3.0};
+const int spec_int_array[5] = {spec_int, spec_int, 1, 2, 30};
+
+// global_vec4_array_with_spec_length is not a spec constant, but its array
+// size is. When calling global_vec4_array_with_spec_length.length(), A
+// TIntermSymbol Node shoule be returned, instead of a TIntermConstantUnion
+// node which represents a known constant value.
+in vec4 global_vec4_array_with_spec_length[spec_int];
+
+out vec4 color;
+
+void refer_primary_spec_const() {
+ if (spec_bool) color *= spec_int;
+}
+
+void refer_composite_spec_const() {
+ color += spec_vec4_all_spec;
+ color -= spec_vec4_partial_spec;
+}
+
+void refer_copmosite_dot_dereference() {
+ color *= spec_nesting_struct_all_spec.i;
+ color += spec_vec4_all_spec.x;
+}
+
+void refer_composite_bracket_dereference() {
+ color -= spec_float_array[1];
+ color /= spec_int_array[spec_int_array[spec_int]];
+}
+
+int refer_spec_const_array_length() {
+ int len = global_vec4_array_with_spec_length.length();
+ return len;
+}
+
+void declare_spec_const_in_func() {
+ const nesting_struct spec_const_declared_in_func = {
+ spec_flat_struct_partial_spec, spec_vec4_partial_spec, 10};
+ color /= spec_const_declared_in_func.i;
+}
+
+void main() {}
spv.subpass.frag
spv.specConstant.vert
spv.specConstant.comp
+spv.specConstantComposite.vert
# GLSL-level semantics
vulkan.frag
vulkan.vert
virtual int getMatrixCols() const { return matrixCols; }
virtual int getMatrixRows() const { return matrixRows; }
virtual int getOuterArraySize() const { return arraySizes->getOuterSize(); }
+ virtual TIntermTyped* getOuterArrayNode() const { return arraySizes->getOuterNode(); }
virtual int getCumulativeArraySize() const { return arraySizes->getCumulativeSize(); }
virtual bool isArrayOfArrays() const { return arraySizes != nullptr && arraySizes->getNumDims() > 1; }
virtual int getImplicitArraySize() const { return arraySizes->getImplicitSize(); }
TIntermTyped* result = nullptr;
int indexValue = 0;
- if (index->getQualifier().isConstant()) {
+ if (index->getQualifier().isFrontEndConstant()) {
indexValue = index->getAsConstantUnion()->getConstArray()[0].getIConst();
checkIndex(loc, base->getType(), indexValue);
}
if (base->getAsSymbolNode() && isIoResizeArray(base->getType()))
handleIoResizeArrayAccess(loc, base);
- if (index->getQualifier().isConstant()) {
+ if (index->getQualifier().isFrontEndConstant()) {
if (base->getType().isImplicitlySizedArray())
updateImplicitArraySize(loc, base, indexValue);
result = intermediate.addIndex(EOpIndexDirect, base, index, loc);
} else {
// Insert valid dereferenced result
TType newType(base->getType(), 0); // dereferenced type
- if (base->getType().getQualifier().isFrontEndConstant() && index->getQualifier().isFrontEndConstant())
+ if (base->getType().getQualifier().isConstant() && index->getQualifier().isConstant()) {
newType.getQualifier().storage = EvqConst;
- else
+ // If base or index is a specialization constant, the result should also be a specialization constant.
+ if (base->getType().getQualifier().isSpecConstant() || index->getQualifier().isSpecConstant()) {
+ newType.getQualifier().makeSpecConstant();
+ }
+ } else {
newType.getQualifier().makePartialTemporary();
+ }
result->setType(newType);
if (anyIndexLimits)
else
error(loc, "", function->getName().c_str(), "array must be declared with a size before using this method");
}
+ } else if (type.getOuterArrayNode()) {
+ // If the array's outer size is specified by an intermediate node, it means the array's length
+ // was specified by a specialization constant. In such a case, we should return the node of the
+ // specialization constants to represent the length.
+ return type.getOuterArrayNode();
} else
length = type.getOuterArraySize();
} else if (type.isMatrix())
// We don't know "top down" whether type is a specialization constant,
// but a const becomes a specialization constant if any of its children are.
- bool specConst = false;
+ bool hasSpecConst = false;
+ bool isConstConstrutor = true;
for (TIntermSequence::iterator p = sequenceVector.begin();
p != sequenceVector.end(); p++, paramCount++) {
if (newNode) {
*p = newNode;
+ if (! newNode->getType().getQualifier().isConstant())
+ isConstConstrutor = false;
if (newNode->getType().getQualifier().isSpecConstant())
- specConst = true;
+ hasSpecConst = true;
} else
return nullptr;
}
TIntermTyped* constructor = intermediate.setAggregateOperator(aggrNode, op, type, loc);
- if (constructor->getType().getQualifier().isConstant() && specConst)
+ if (isConstConstrutor && hasSpecConst)
constructor->getWritableType().getQualifier().makeSpecConstant();
return constructor;