diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp index ddad636b..84c9e734 100755 --- a/SPIRV/GlslangToSpv.cpp +++ b/SPIRV/GlslangToSpv.cpp @@ -130,6 +130,7 @@ protected: spv::Id makeArraySizeId(const glslang::TArraySizes&, int dim); spv::Id accessChainLoad(const glslang::TType& type); void accessChainStore(const glslang::TType& type, spv::Id rvalue); + void multiTypeStore(const glslang::TType&, spv::Id rValue); glslang::TLayoutPacking getExplicitLayout(const glslang::TType& type) const; int getArrayStride(const glslang::TType& arrayType, glslang::TLayoutPacking, glslang::TLayoutMatrix); int getMatrixStride(const glslang::TType& matrixType, glslang::TLayoutPacking, glslang::TLayoutMatrix); @@ -188,7 +189,7 @@ protected: std::unordered_map extBuiltinMap; std::unordered_map symbolValues; - std::unordered_set constReadOnlyParameters; // set of formal function parameters that have glslang qualifier constReadOnly, so we know they are not local function "const" that are write-once + std::unordered_set rValueParameters; // set of formal function parameters passed as rValues, rather than a pointer std::unordered_map functionMap; std::unordered_map structMap[glslang::ElpCount][glslang::ElmCount]; std::unordered_map > memberRemapper; // for mapping glslang block indices to spv indices (e.g., due to hidden members) @@ -897,13 +898,12 @@ void TGlslangToSpvTraverser::visitSymbol(glslang::TIntermSymbol* symbol) // For now, we consider all user variables as being in memory, so they are pointers, // except for - // A) "const in" arguments to a function, which are an intermediate object. + // A) R-Value arguments to a function, which are an intermediate object. // See comments in handleUserFunctionCall(). - // B) Specialization constants (normal constant don't even come in as a variable), + // B) Specialization constants (normal constants don't even come in as a variable), // These are also pure R-values. glslang::TQualifier qualifier = symbol->getQualifier(); - if ((qualifier.storage == glslang::EvqConstReadOnly && constReadOnlyParameters.find(symbol->getId()) != constReadOnlyParameters.end()) || - qualifier.isSpecConstant()) + if (qualifier.isSpecConstant() || rValueParameters.find(symbol->getId()) != rValueParameters.end()) builder.setAccessChainRValue(id); else builder.setAccessChainLValue(id); @@ -965,16 +965,7 @@ bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::T // store the result builder.setAccessChain(lValue); - if (builder.isStructType(builder.getTypeId(rValue))) { - //spv::Id lType = builder.getContainedTypeId(builder.getTypeId(builder.accessChainGetLValue())); - //spv::Id rType = builder.getTypeId(rValue); - //if (lType != rType) { - // TODO: do member-wise copy instead, this is current issue - // https://github.com/KhronosGroup/glslang/issues/304 - //} else - accessChainStore(node->getType(), rValue); - } else - accessChainStore(node->getType(), rValue); + multiTypeStore(node->getType(), rValue); // assignments are expressions having an rValue after they are evaluated... builder.clearAccessChain(); @@ -2248,6 +2239,8 @@ spv::Id TGlslangToSpvTraverser::accessChainLoad(const glslang::TType& type) // Wrap the builder's accessChainStore to: // - do conversion of concrete to abstract type +// +// Implicitly uses the existing builder.accessChain as the storage target. void TGlslangToSpvTraverser::accessChainStore(const glslang::TType& type, spv::Id rvalue) { // Need to convert to abstract types when necessary @@ -2277,6 +2270,57 @@ void TGlslangToSpvTraverser::accessChainStore(const glslang::TType& type, spv::I builder.accessChainStore(rvalue); } +// For storing when types match at the glslang level, but not might match at the +// SPIR-V level. +// +// This especially happens when a single glslang type expands to multiple +// SPIR-V types, like a struct that is used in an member-undecorated way as well +// as in a member-decorated way. +// +// NOTE: This function can handle any store request; if it's not special it +// simplifies to a simple OpStore. +// +// Implicitly uses the existing builder.accessChain as the storage target. +void TGlslangToSpvTraverser::multiTypeStore(const glslang::TType& type, spv::Id rValue) +{ + // we only do the complex path here if it's a structure + if (! type.isStruct()) { + accessChainStore(type, rValue); + return; + } + + // and, it has to be a case of structure type aliasing + spv::Id rType = builder.getTypeId(rValue); + spv::Id lValue = builder.accessChainGetLValue(); + spv::Id lType = builder.getContainedTypeId(builder.getTypeId(lValue)); + if (lType == rType) { + accessChainStore(type, rValue); + return; + } + + // Recursively (as needed) copy a struct type to a different struct type, + // where the two types were the same type in GLSL. This requires member + // by member copy, recursively. + + // loop over members + const glslang::TTypeList& members = *type.getStruct(); + for (int m = 0; m < (int)members.size(); ++m) { + const glslang::TType& glslangMemberType = *members[m].type; + + // get the source member + spv::Id memberRType = builder.getContainedTypeId(rType, m); + spv::Id memberRValue = builder.createCompositeExtract(rValue, memberRType, m); + + // set up the target storage + builder.clearAccessChain(); + builder.setAccessChainLValue(lValue); + builder.accessChainPush(builder.makeIntConstant(m)); + + // store the member + multiTypeStore(glslangMemberType, memberRValue); + } +} + // Decide whether or not this type should be // decorated with offsets and strides, and if so // whether std140 or std430 rules should be applied. @@ -2403,19 +2447,18 @@ void TGlslangToSpvTraverser::makeFunctions(const glslang::TIntermSequence& glslF continue; // We're on a user function. Set up the basic interface for the function now, - // so that it's available to call. - // Translating the body will happen later. + // so that it's available to call. Translating the body will happen later. // // Typically (except for a "const in" parameter), an address will be passed to the // function. What it is an address of varies: // - // - "in" parameters not marked as "const" can be written to without modifying the argument, - // so that write needs to be to a copy, hence the address of a copy works. + // - "in" parameters not marked as "const" can be written to without modifying the calling + // argument so that write needs to be to a copy, hence the address of a copy works. // // - "const in" parameters can just be the r-value, as no writes need occur. // - // - "out" and "inout" arguments can't be done as direct pointers, because GLSL has - // copy-in/copy-out semantics. They can be handled though with a pointer to a copy. + // - "out" and "inout" arguments can't be done as pointers to the calling argument, because + // GLSL has copy-in/copy-out semantics. They can be handled though with a pointer to a copy. std::vector paramTypes; std::vector paramPrecisions; @@ -2429,7 +2472,7 @@ void TGlslangToSpvTraverser::makeFunctions(const glslang::TIntermSequence& glslF else if (paramType.getQualifier().storage != glslang::EvqConstReadOnly) typeId = builder.makePointer(spv::StorageClassFunction, typeId); else - constReadOnlyParameters.insert(parameters[p]->getAsSymbolNode()->getId()); + rValueParameters.insert(parameters[p]->getAsSymbolNode()->getId()); paramPrecisions.push_back(TranslatePrecisionDecoration(paramType)); paramTypes.push_back(typeId); } @@ -2914,7 +2957,9 @@ spv::Id TGlslangToSpvTraverser::handleUserFunctionCall(const glslang::TIntermAgg // need to copy the input into output space builder.setAccessChain(lValues[lValueCount]); spv::Id copy = accessChainLoad(*argTypes[a]); - builder.createStore(copy, arg); + builder.clearAccessChain(); + builder.setAccessChainLValue(arg); + multiTypeStore(paramType, copy); } ++lValueCount; } else { @@ -2931,11 +2976,12 @@ spv::Id TGlslangToSpvTraverser::handleUserFunctionCall(const glslang::TIntermAgg // 4. Copy back out an "out" arguments. lValueCount = 0; for (int a = 0; a < (int)glslangArgs.size(); ++a) { + const glslang::TType& paramType = glslangArgs[a]->getAsTyped()->getType(); if (qualifiers[a] != glslang::EvqConstReadOnly) { if (qualifiers[a] == glslang::EvqOut || qualifiers[a] == glslang::EvqInOut) { spv::Id copy = builder.createLoad(spvArgs[a]); builder.setAccessChain(lValues[lValueCount]); - accessChainStore(glslangArgs[a]->getAsTyped()->getType(), copy); + multiTypeStore(paramType, copy); } ++lValueCount; } diff --git a/Test/baseResults/spv.multiStruct.comp.out b/Test/baseResults/spv.multiStruct.comp.out new file mode 100755 index 00000000..a2ab8f81 --- /dev/null +++ b/Test/baseResults/spv.multiStruct.comp.out @@ -0,0 +1,192 @@ +spv.multiStruct.comp +Warning, version 450 is not yet complete; most version-specific features are present, but some are missing. + + +Linked compute stage: + + +// Module Version 10000 +// Generated by (magic number): 80001 +// Id's are bound by 97 + + Capability Shader + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint GLCompute 4 "main" + ExecutionMode 4 LocalSize 1 1 1 + Source GLSL 450 + Name 4 "main" + Name 9 "MyStruct" + MemberName 9(MyStruct) 0 "foo" + MemberName 9(MyStruct) 1 "sb" + Name 11 "t" + Name 13 "MyStruct" + MemberName 13(MyStruct) 0 "foo" + MemberName 13(MyStruct) 1 "sb" + Name 14 "SSBO0" + MemberName 14(SSBO0) 0 "a" + Name 16 "inBuf" + Name 29 "SSBO1" + MemberName 29(SSBO1) 0 "b" + Name 31 "outBuf" + Name 43 "MyStruct" + MemberName 43(MyStruct) 0 "foo" + MemberName 43(MyStruct) 1 "sb" + Name 44 "UBO" + MemberName 44(UBO) 0 "c" + Name 46 "uBuf" + Name 61 "Nested" + MemberName 61(Nested) 0 "f" + MemberName 61(Nested) 1 "S" + Name 63 "n" + Name 64 "Nested" + MemberName 64(Nested) 0 "f" + MemberName 64(Nested) 1 "S" + Name 65 "UBON" + MemberName 65(UBON) 0 "N1" + Name 67 "uBufN" + Name 80 "Nested" + MemberName 80(Nested) 0 "f" + MemberName 80(Nested) 1 "S" + Name 81 "SSBO1N" + MemberName 81(SSBO1N) 0 "N2" + Name 83 "outBufN" + MemberDecorate 13(MyStruct) 0 Offset 0 + MemberDecorate 13(MyStruct) 1 Offset 16 + MemberDecorate 14(SSBO0) 0 Offset 0 + Decorate 14(SSBO0) BufferBlock + Decorate 16(inBuf) DescriptorSet 0 + Decorate 16(inBuf) Binding 0 + MemberDecorate 29(SSBO1) 0 Offset 0 + Decorate 29(SSBO1) BufferBlock + Decorate 31(outBuf) DescriptorSet 0 + Decorate 31(outBuf) Binding 1 + MemberDecorate 43(MyStruct) 0 Offset 0 + MemberDecorate 43(MyStruct) 1 Offset 16 + MemberDecorate 44(UBO) 0 Offset 0 + Decorate 44(UBO) Block + Decorate 46(uBuf) DescriptorSet 0 + Decorate 46(uBuf) Binding 2 + MemberDecorate 64(Nested) 0 Offset 0 + MemberDecorate 64(Nested) 1 Offset 16 + MemberDecorate 65(UBON) 0 Offset 0 + Decorate 65(UBON) Block + Decorate 67(uBufN) DescriptorSet 0 + Decorate 67(uBufN) Binding 2 + MemberDecorate 80(Nested) 0 Offset 0 + MemberDecorate 80(Nested) 1 Offset 16 + MemberDecorate 81(SSBO1N) 0 Offset 0 + Decorate 81(SSBO1N) BufferBlock + Decorate 83(outBufN) DescriptorSet 0 + Decorate 83(outBufN) Binding 1 + 2: TypeVoid + 3: TypeFunction 2 + 6: TypeFloat 32 + 7: TypeVector 6(float) 4 + 8: TypeBool + 9(MyStruct): TypeStruct 7(fvec4) 8(bool) + 10: TypePointer Function 9(MyStruct) + 12: TypeInt 32 0 + 13(MyStruct): TypeStruct 7(fvec4) 12(int) + 14(SSBO0): TypeStruct 13(MyStruct) + 15: TypePointer Uniform 14(SSBO0) + 16(inBuf): 15(ptr) Variable Uniform + 17: TypeInt 32 1 + 18: 17(int) Constant 0 + 19: TypePointer Uniform 13(MyStruct) + 23: TypePointer Function 7(fvec4) + 26: 17(int) Constant 1 + 27: TypePointer Function 8(bool) + 29(SSBO1): TypeStruct 13(MyStruct) + 30: TypePointer Uniform 29(SSBO1) + 31(outBuf): 30(ptr) Variable Uniform + 35: TypePointer Uniform 7(fvec4) + 38: 12(int) Constant 0 + 39: 12(int) Constant 1 + 41: TypePointer Uniform 12(int) + 43(MyStruct): TypeStruct 7(fvec4) 12(int) + 44(UBO): TypeStruct 43(MyStruct) + 45: TypePointer Uniform 44(UBO) + 46(uBuf): 45(ptr) Variable Uniform + 47: TypePointer Uniform 43(MyStruct) + 61(Nested): TypeStruct 6(float) 9(MyStruct) + 62: TypePointer Function 61(Nested) + 64(Nested): TypeStruct 6(float) 43(MyStruct) + 65(UBON): TypeStruct 64(Nested) + 66: TypePointer Uniform 65(UBON) + 67(uBufN): 66(ptr) Variable Uniform + 68: TypePointer Uniform 64(Nested) + 72: TypePointer Function 6(float) + 80(Nested): TypeStruct 6(float) 13(MyStruct) + 81(SSBO1N): TypeStruct 80(Nested) + 82: TypePointer Uniform 81(SSBO1N) + 83(outBufN): 82(ptr) Variable Uniform + 85: TypePointer Uniform 80(Nested) + 88: TypePointer Uniform 6(float) + 4(main): 2 Function None 3 + 5: Label + 11(t): 10(ptr) Variable Function + 63(n): 62(ptr) Variable Function + 20: 19(ptr) AccessChain 16(inBuf) 18 + 21:13(MyStruct) Load 20 + 22: 7(fvec4) CompositeExtract 21 0 + 24: 23(ptr) AccessChain 11(t) 18 + Store 24 22 + 25: 12(int) CompositeExtract 21 1 + 28: 27(ptr) AccessChain 11(t) 26 + Store 28 25 + 32: 9(MyStruct) Load 11(t) + 33: 19(ptr) AccessChain 31(outBuf) 18 + 34: 7(fvec4) CompositeExtract 32 0 + 36: 35(ptr) AccessChain 33 18 + Store 36 34 + 37: 8(bool) CompositeExtract 32 1 + 40: 12(int) Select 37 39 38 + 42: 41(ptr) AccessChain 33 26 + Store 42 40 + 48: 47(ptr) AccessChain 46(uBuf) 18 + 49:43(MyStruct) Load 48 + 50: 7(fvec4) CompositeExtract 49 0 + 51: 23(ptr) AccessChain 11(t) 18 + Store 51 50 + 52: 12(int) CompositeExtract 49 1 + 53: 27(ptr) AccessChain 11(t) 26 + Store 53 52 + 54: 9(MyStruct) Load 11(t) + 55: 19(ptr) AccessChain 31(outBuf) 18 + 56: 7(fvec4) CompositeExtract 54 0 + 57: 35(ptr) AccessChain 55 18 + Store 57 56 + 58: 8(bool) CompositeExtract 54 1 + 59: 12(int) Select 58 39 38 + 60: 41(ptr) AccessChain 55 26 + Store 60 59 + 69: 68(ptr) AccessChain 67(uBufN) 18 + 70: 64(Nested) Load 69 + 71: 6(float) CompositeExtract 70 0 + 73: 72(ptr) AccessChain 63(n) 18 + Store 73 71 + 74:43(MyStruct) CompositeExtract 70 1 + 75: 10(ptr) AccessChain 63(n) 26 + 76: 7(fvec4) CompositeExtract 74 0 + 77: 23(ptr) AccessChain 75 18 + Store 77 76 + 78: 12(int) CompositeExtract 74 1 + 79: 27(ptr) AccessChain 75 26 + Store 79 78 + 84: 61(Nested) Load 63(n) + 86: 85(ptr) AccessChain 83(outBufN) 18 + 87: 6(float) CompositeExtract 84 0 + 89: 88(ptr) AccessChain 86 18 + Store 89 87 + 90: 9(MyStruct) CompositeExtract 84 1 + 91: 19(ptr) AccessChain 86 26 + 92: 7(fvec4) CompositeExtract 90 0 + 93: 35(ptr) AccessChain 91 18 + Store 93 92 + 94: 8(bool) CompositeExtract 90 1 + 95: 12(int) Select 94 39 38 + 96: 41(ptr) AccessChain 91 26 + Store 96 95 + Return + FunctionEnd diff --git a/Test/baseResults/spv.multiStructFuncall.frag.out b/Test/baseResults/spv.multiStructFuncall.frag.out new file mode 100755 index 00000000..dcbcfa00 --- /dev/null +++ b/Test/baseResults/spv.multiStructFuncall.frag.out @@ -0,0 +1,120 @@ +spv.multiStructFuncall.frag +Warning, version 450 is not yet complete; most version-specific features are present, but some are missing. + + +Linked fragment stage: + + +// Module Version 10000 +// Generated by (magic number): 80001 +// Id's are bound by 63 + + Capability Shader + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Fragment 4 "main" + ExecutionMode 4 OriginUpperLeft + Source GLSL 450 + Name 4 "main" + Name 9 "S" + MemberName 9(S) 0 "m" + Name 12 "fooConst(struct-S-mf441;" + Name 11 "s" + Name 17 "foo(struct-S-mf441;" + Name 16 "s" + Name 20 "fooOut(struct-S-mf441;" + Name 19 "s" + Name 22 "S" + MemberName 22(S) 0 "m" + Name 23 "blockName" + MemberName 23(blockName) 0 "s1" + Name 25 "" + Name 33 "s2" + Name 36 "S" + MemberName 36(S) 0 "m" + Name 38 "param" + Name 45 "param" + Name 48 "param" + Name 59 "param" + MemberDecorate 22(S) 0 ColMajor + MemberDecorate 22(S) 0 Offset 0 + MemberDecorate 22(S) 0 MatrixStride 16 + MemberDecorate 23(blockName) 0 Offset 0 + Decorate 23(blockName) BufferBlock + Decorate 25 DescriptorSet 0 + MemberDecorate 36(S) 0 ColMajor + 2: TypeVoid + 3: TypeFunction 2 + 6: TypeFloat 32 + 7: TypeVector 6(float) 4 + 8: TypeMatrix 7(fvec4) 4 + 9(S): TypeStruct 8 + 10: TypeFunction 2 9(S) + 14: TypePointer Function 9(S) + 15: TypeFunction 2 14(ptr) + 22(S): TypeStruct 8 + 23(blockName): TypeStruct 22(S) + 24: TypePointer Uniform 23(blockName) + 25: 24(ptr) Variable Uniform + 26: TypeInt 32 1 + 27: 26(int) Constant 0 + 28: TypePointer Uniform 22(S) + 32: TypePointer Private 9(S) + 33(s2): 32(ptr) Variable Private + 36(S): TypeStruct 8 + 37: TypePointer Function 36(S) + 42: TypePointer Function 8 + 57: TypePointer Uniform 8 + 4(main): 2 Function None 3 + 5: Label + 38(param): 37(ptr) Variable Function + 45(param): 14(ptr) Variable Function + 48(param): 37(ptr) Variable Function + 59(param): 14(ptr) Variable Function + 29: 28(ptr) AccessChain 25 27 + 30: 22(S) Load 29 + 31: 2 FunctionCall 12(fooConst(struct-S-mf441;) 30 + 34: 9(S) Load 33(s2) + 35: 2 FunctionCall 12(fooConst(struct-S-mf441;) 34 + 39: 28(ptr) AccessChain 25 27 + 40: 22(S) Load 39 + 41: 8 CompositeExtract 40 0 + 43: 42(ptr) AccessChain 38(param) 27 + Store 43 41 + 44: 2 FunctionCall 17(foo(struct-S-mf441;) 38(param) + 46: 9(S) Load 33(s2) + Store 45(param) 46 + 47: 2 FunctionCall 17(foo(struct-S-mf441;) 45(param) + 49: 28(ptr) AccessChain 25 27 + 50: 22(S) Load 49 + 51: 8 CompositeExtract 50 0 + 52: 42(ptr) AccessChain 48(param) 27 + Store 52 51 + 53: 2 FunctionCall 20(fooOut(struct-S-mf441;) 48(param) + 54: 36(S) Load 48(param) + 55: 28(ptr) AccessChain 25 27 + 56: 8 CompositeExtract 54 0 + 58: 57(ptr) AccessChain 55 27 + Store 58 56 + 60: 9(S) Load 33(s2) + Store 59(param) 60 + 61: 2 FunctionCall 20(fooOut(struct-S-mf441;) 59(param) + 62: 9(S) Load 59(param) + Store 33(s2) 62 + Return + FunctionEnd +12(fooConst(struct-S-mf441;): 2 Function None 10 + 11(s): 9(S) FunctionParameter + 13: Label + Return + FunctionEnd +17(foo(struct-S-mf441;): 2 Function None 15 + 16(s): 14(ptr) FunctionParameter + 18: Label + Return + FunctionEnd +20(fooOut(struct-S-mf441;): 2 Function None 15 + 19(s): 14(ptr) FunctionParameter + 21: Label + Return + FunctionEnd diff --git a/Test/spv.multiStruct.comp b/Test/spv.multiStruct.comp new file mode 100644 index 00000000..de27ccb7 --- /dev/null +++ b/Test/spv.multiStruct.comp @@ -0,0 +1,48 @@ +#version 450 core + +struct MyStruct +{ + vec4 foo; + bool sb; +}; + +layout(binding = 0, std430) buffer SSBO0 +{ + MyStruct a; +} inBuf; + +layout(binding = 1, std430) buffer SSBO1 +{ + MyStruct b; +} outBuf; + +layout(binding = 2, std140) uniform UBO +{ + MyStruct c; +} uBuf; + +struct Nested { + float f; + MyStruct S; +}; + +layout(binding = 2, std140) uniform UBON +{ + Nested N1; +} uBufN; + +layout(binding = 1, std430) buffer SSBO1N +{ + Nested N2; +} outBufN; + +void main() +{ + MyStruct t = inBuf.a; + outBuf.b = t; + t = uBuf.c; + outBuf.b = t; + + Nested n = uBufN.N1; + outBufN.N2 = n; +} diff --git a/Test/spv.multiStructFuncall.frag b/Test/spv.multiStructFuncall.frag new file mode 100755 index 00000000..7f9968dc --- /dev/null +++ b/Test/spv.multiStructFuncall.frag @@ -0,0 +1,21 @@ +#version 450 + +struct S { mat4 m; }; +buffer blockName { S s1; }; // need an S with decoration +S s2; // no decorations on S + +void fooConst(const in S s) { } +void foo(in S s) { } +void fooOut(inout S s) { } + +void main() +{ + fooConst(s1); + fooConst(s2); + + foo(s1); + foo(s2); + + fooOut(s1); + fooOut(s2); +} \ No newline at end of file diff --git a/glslang/Include/revision.h b/glslang/Include/revision.h index 91b40ad5..eac03933 100644 --- a/glslang/Include/revision.h +++ b/glslang/Include/revision.h @@ -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 "Overload400-PrecQual.1456" -#define GLSLANG_DATE "01-Sep-2016" +#define GLSLANG_REVISION "Overload400-PrecQual.1460" +#define GLSLANG_DATE "02-Sep-2016" diff --git a/gtests/Spv.FromFile.cpp b/gtests/Spv.FromFile.cpp index a8f3dd86..45fed044 100644 --- a/gtests/Spv.FromFile.cpp +++ b/gtests/Spv.FromFile.cpp @@ -164,6 +164,8 @@ INSTANTIATE_TEST_CASE_P( "spv.matrix2.frag", "spv.memoryQualifier.frag", "spv.merge-unreachable.frag", + "spv.multiStruct.comp", + "spv.multiStructFuncall.frag", "spv.newTexture.frag", "spv.noDeadDecorations.vert", "spv.nonSquare.vert",