diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp index 606f2476..063e416d 100644 --- a/StandAlone/StandAlone.cpp +++ b/StandAlone/StandAlone.cpp @@ -1,6 +1,6 @@ // //Copyright (C) 2002-2005 3Dlabs Inc. Ltd. -//Copyright (C) 2013 LunarG, Inc. +//Copyright (C) 2013-2016 LunarG, Inc. // //All rights reserved. // @@ -48,7 +48,9 @@ #include "../SPIRV/disassemble.h" #include #include +#include #include +#include #include "../glslang/OSDependent/osinclude.h" @@ -78,6 +80,7 @@ enum TOptions { EOptionOutputHexadecimal = (1 << 16), EOptionReadHlsl = (1 << 17), EOptionCascadingErrors = (1 << 18), + EOptionAutoMapBindings = (1 << 19), }; // @@ -96,7 +99,7 @@ enum TFailCode { // // Forward declarations. // -EShLanguage FindLanguage(const std::string& name); +EShLanguage FindLanguage(const std::string& name, bool parseSuffix=true); void CompileFile(const char* fileName, ShHandle); void usage(); void FreeFileData(char** data); @@ -157,6 +160,10 @@ const char* binaryFileName = nullptr; const char* entryPointName = nullptr; const char* shaderStageName = nullptr; +std::array baseSamplerBinding; +std::array baseTextureBinding; +std::array baseUboBinding; + // // Create the default name for saving a binary if -o is not provided. // @@ -204,6 +211,35 @@ void Error(const char* message) exit(EFailUsage); } +// +// Process an optional binding base of the form: +// --argname [stage] base +// Where stage is one of the forms accepted by FindLanguage, and base is an integer +// +void ProcessBindingBase(int& argc, char**& argv, std::array& base) +{ + if (argc < 2) + usage(); + + if (!isdigit(argv[1][0])) { + if (argc < 3) // this form needs one more argument + usage(); + + // Parse form: --argname stage base + const EShLanguage lang = FindLanguage(argv[1], false); + base[lang] = atoi(argv[2]); + argc-= 2; + argv+= 2; + } else { + // Parse form: --argname base + for (int lang=0; lang= 1; argc--, argv++) { if (argv[0][0] == '-') { switch (argv[0][1]) { + case '-': + { + std::string lowerword(argv[0]+2); + std::transform(lowerword.begin(), lowerword.end(), lowerword.begin(), ::tolower); + + // handle --word style options + if (lowerword == "shift-sampler-bindings" || // synonyms + lowerword == "shift-sampler-binding" || + lowerword == "ssb") { + ProcessBindingBase(argc, argv, baseSamplerBinding); + } else if (lowerword == "shift-texture-bindings" || // synonyms + lowerword == "shift-texture-binding" || + lowerword == "stb") { + ProcessBindingBase(argc, argv, baseTextureBinding); + } else if (lowerword == "shift-ubo-bindings" || // synonyms + lowerword == "shift-ubo-binding" || + lowerword == "sub") { + ProcessBindingBase(argc, argv, baseUboBinding); + } else if (lowerword == "auto-map-bindings" || // synonyms + lowerword == "auto-map-binding" || + lowerword == "amb") { + Options |= EOptionAutoMapBindings; + } else { + usage(); + } + } + break; case 'H': Options |= EOptionHumanReadableSpv; if ((Options & EOptionSpv) == 0) { @@ -461,6 +528,14 @@ void CompileAndLinkShaderUnits(std::vector compUnits) shader->setStringsWithLengthsAndNames(compUnit.text, NULL, compUnit.fileNameList, 1); if (entryPointName) // HLSL todo: this needs to be tracked per compUnits shader->setEntryPoint(entryPointName); + + shader->setShiftSamplerBinding(baseSamplerBinding[compUnit.stage]); + shader->setShiftTextureBinding(baseTextureBinding[compUnit.stage]); + shader->setShiftUboBinding(baseUboBinding[compUnit.stage]); + + if (Options & EOptionAutoMapBindings) + shader->setAutoMapBindings(true); + shaders.push_back(shader); const int defaultVersion = Options & EOptionDefaultDesktop? 110: 100; @@ -506,6 +581,10 @@ void CompileAndLinkShaderUnits(std::vector compUnits) PutsIfNonEmpty(program.getInfoDebugLog()); } + // Map IO + if (Options & EOptionSpv) + program.mapIO(); + // Reflect if (Options & EOptionDumpReflection) { program.buildReflection(); @@ -705,15 +784,22 @@ int C_DECL main(int argc, char* argv[]) // .frag = fragment // .comp = compute // -EShLanguage FindLanguage(const std::string& name) +EShLanguage FindLanguage(const std::string& name, bool parseSuffix) { - size_t ext = name.rfind('.'); - if (ext == std::string::npos) { - usage(); - return EShLangVertex; + size_t ext = 0; + + // Search for a suffix on a filename: e.g, "myfile.frag". If given + // the suffix directly, we skip looking the '.' + if (parseSuffix) { + ext = name.rfind('.'); + if (ext == std::string::npos) { + usage(); + return EShLangVertex; + } + ++ext; } - std::string suffix = name.substr(ext + 1, std::string::npos); + std::string suffix = name.substr(ext, std::string::npos); if (shaderStageName) suffix = shaderStageName; @@ -831,6 +917,19 @@ void usage() " -v print version strings\n" " -w suppress warnings (except as required by #extension : warn)\n" " -x save 32-bit hexadecimal numbers as text, requires a binary option (e.g., -V)\n" + "\n" + " --shift-sampler-binding [stage] num set base binding number for samplers\n" + " --ssb [stage] num synonym for --shift-sampler-binding\n" + "\n" + " --shift-texture-binding [stage] num set base binding number for textures\n" + " --stb [stage] num synonym for --shift-texture-binding\n" + "\n" + " --shift-UBO-binding [stage] num set base binding number for UBOs\n" + " --sub [stage] num synonym for --shift-UBO-binding\n" + "\n" + " --auto-map-bindings automatically bind uniform variables without\n" + " explicit bindings.\n" + " --amb synonym for --auto-map-bindings\n" ); exit(EFailUsage); diff --git a/Test/baseResults/spv.register.autoassign.frag.out b/Test/baseResults/spv.register.autoassign.frag.out new file mode 100644 index 00000000..c2cd59a3 --- /dev/null +++ b/Test/baseResults/spv.register.autoassign.frag.out @@ -0,0 +1,230 @@ +spv.register.autoassign.frag + +Linked fragment stage: + + +// Module Version 10000 +// Generated by (magic number): 80001 +// Id's are bound by 154 + + Capability Shader + Capability Sampled1D + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Fragment 4 "main_ep" 143 + ExecutionMode 4 OriginUpperLeft + Name 4 "main_ep" + Name 9 "Func1(" + Name 11 "Func2(" + Name 13 "Func2_unused(" + Name 17 "g_tTex1" + Name 21 "g_sSamp1" + Name 27 "g_tTex2" + Name 29 "g_sSamp2" + Name 39 "g_tTex3" + Name 46 "g_sSamp3" + Name 64 "g_tTex4" + Name 69 "g_sSamp4" + Name 84 "g_tTex5" + Name 86 "g_sSamp5" + Name 93 "MyStruct_t" + MemberName 93(MyStruct_t) 0 "a" + MemberName 93(MyStruct_t) 1 "b" + MemberName 93(MyStruct_t) 2 "c" + Name 95 "mystruct" + Name 117 "g_tTex_unused1" + Name 119 "g_sSamp_unused1" + Name 124 "g_tTex_unused2" + Name 126 "g_sSamp_unused2" + Name 134 "PS_OUTPUT" + MemberName 134(PS_OUTPUT) 0 "Color" + Name 136 "psout" + Name 143 "Color" + Name 147 "g_tTex_unused3" + Name 149 "myfloat4_a" + Name 150 "myfloat4_b" + Name 153 "myint4_a" + Decorate 17(g_tTex1) DescriptorSet 0 + Decorate 17(g_tTex1) Binding 11 + Decorate 21(g_sSamp1) DescriptorSet 0 + Decorate 21(g_sSamp1) Binding 5 + Decorate 27(g_tTex2) DescriptorSet 0 + Decorate 27(g_tTex2) Binding 1 + Decorate 29(g_sSamp2) DescriptorSet 0 + Decorate 29(g_sSamp2) Binding 2 + Decorate 39(g_tTex3) DescriptorSet 0 + Decorate 39(g_tTex3) Binding 13 + Decorate 46(g_sSamp3) DescriptorSet 0 + Decorate 46(g_sSamp3) Binding 7 + Decorate 64(g_tTex4) DescriptorSet 0 + Decorate 64(g_tTex4) Binding 3 + Decorate 69(g_sSamp4) DescriptorSet 0 + Decorate 69(g_sSamp4) Binding 4 + Decorate 84(g_tTex5) DescriptorSet 0 + Decorate 84(g_tTex5) Binding 6 + Decorate 86(g_sSamp5) DescriptorSet 0 + Decorate 86(g_sSamp5) Binding 8 + Decorate 95(mystruct) Binding 19 + Decorate 117(g_tTex_unused1) DescriptorSet 0 + Decorate 117(g_tTex_unused1) Binding 10 + Decorate 119(g_sSamp_unused1) DescriptorSet 0 + Decorate 124(g_tTex_unused2) DescriptorSet 0 + Decorate 124(g_tTex_unused2) Binding 12 + Decorate 126(g_sSamp_unused2) DescriptorSet 0 + Decorate 143(Color) Location 0 + Decorate 147(g_tTex_unused3) DescriptorSet 0 + 2: TypeVoid + 3: TypeFunction 2 + 6: TypeFloat 32 + 7: TypeVector 6(float) 4 + 8: TypeFunction 7(fvec4) + 15: TypeImage 6(float) 1D sampled format:Unknown + 16: TypePointer UniformConstant 15 + 17(g_tTex1): 16(ptr) Variable UniformConstant + 19: TypeSampler + 20: TypePointer UniformConstant 19 + 21(g_sSamp1): 20(ptr) Variable UniformConstant + 23: TypeSampledImage 15 + 25: 6(float) Constant 1036831949 + 27(g_tTex2): 16(ptr) Variable UniformConstant + 29(g_sSamp2): 20(ptr) Variable UniformConstant + 32: 6(float) Constant 1045220557 + 35: TypeInt 32 0 + 36: 35(int) Constant 2 + 37: TypeArray 15 36 + 38: TypePointer UniformConstant 37 + 39(g_tTex3): 38(ptr) Variable UniformConstant + 40: TypeInt 32 1 + 41: 40(int) Constant 0 + 44: TypeArray 19 36 + 45: TypePointer UniformConstant 44 + 46(g_sSamp3): 45(ptr) Variable UniformConstant + 50: 6(float) Constant 1050253722 + 53: 40(int) Constant 1 + 61: 35(int) Constant 3 + 62: TypeArray 15 61 + 63: TypePointer UniformConstant 62 + 64(g_tTex4): 63(ptr) Variable UniformConstant + 67: TypeArray 19 61 + 68: TypePointer UniformConstant 67 + 69(g_sSamp4): 68(ptr) Variable UniformConstant + 73: 6(float) Constant 1053609165 + 76: 40(int) Constant 2 + 84(g_tTex5): 16(ptr) Variable UniformConstant + 86(g_sSamp5): 20(ptr) Variable UniformConstant + 89: 6(float) Constant 1056964608 + 92: TypeVector 6(float) 3 + 93(MyStruct_t): TypeStruct 40(int) 6(float) 92(fvec3) + 94: TypePointer UniformConstant 93(MyStruct_t) + 95(mystruct): 94(ptr) Variable UniformConstant + 96: 35(int) Constant 1 + 97: TypePointer UniformConstant 6(float) +117(g_tTex_unused1): 16(ptr) Variable UniformConstant +119(g_sSamp_unused1): 20(ptr) Variable UniformConstant + 122: 6(float) Constant 1066192077 +124(g_tTex_unused2): 16(ptr) Variable UniformConstant +126(g_sSamp_unused2): 20(ptr) Variable UniformConstant + 129: 6(float) Constant 1067030938 + 134(PS_OUTPUT): TypeStruct 7(fvec4) + 135: TypePointer Function 134(PS_OUTPUT) + 140: TypePointer Function 7(fvec4) + 142: TypePointer Output 7(fvec4) + 143(Color): 142(ptr) Variable Output +147(g_tTex_unused3): 16(ptr) Variable UniformConstant + 148: TypePointer UniformConstant 7(fvec4) + 149(myfloat4_a): 148(ptr) Variable UniformConstant + 150(myfloat4_b): 148(ptr) Variable UniformConstant + 151: TypeVector 40(int) 4 + 152: TypePointer UniformConstant 151(ivec4) + 153(myint4_a): 152(ptr) Variable UniformConstant + 4(main_ep): 2 Function None 3 + 5: Label + 136(psout): 135(ptr) Variable Function + 137: 7(fvec4) FunctionCall 9(Func1() + 138: 7(fvec4) FunctionCall 11(Func2() + 139: 7(fvec4) FAdd 137 138 + 141: 140(ptr) AccessChain 136(psout) 41 + Store 141 139 + 144: 140(ptr) AccessChain 136(psout) 41 + 145: 7(fvec4) Load 144 + Store 143(Color) 145 + Return + FunctionEnd + 9(Func1(): 7(fvec4) Function None 8 + 10: Label + 18: 15 Load 17(g_tTex1) + 22: 19 Load 21(g_sSamp1) + 24: 23 SampledImage 18 22 + 26: 7(fvec4) ImageSampleImplicitLod 24 25 + 28: 15 Load 27(g_tTex2) + 30: 19 Load 29(g_sSamp2) + 31: 23 SampledImage 28 30 + 33: 7(fvec4) ImageSampleImplicitLod 31 32 + 34: 7(fvec4) FAdd 26 33 + 42: 16(ptr) AccessChain 39(g_tTex3) 41 + 43: 15 Load 42 + 47: 20(ptr) AccessChain 46(g_sSamp3) 41 + 48: 19 Load 47 + 49: 23 SampledImage 43 48 + 51: 7(fvec4) ImageSampleImplicitLod 49 50 + 52: 7(fvec4) FAdd 34 51 + 54: 16(ptr) AccessChain 39(g_tTex3) 53 + 55: 15 Load 54 + 56: 20(ptr) AccessChain 46(g_sSamp3) 53 + 57: 19 Load 56 + 58: 23 SampledImage 55 57 + 59: 7(fvec4) ImageSampleImplicitLod 58 50 + 60: 7(fvec4) FAdd 52 59 + 65: 16(ptr) AccessChain 64(g_tTex4) 53 + 66: 15 Load 65 + 70: 20(ptr) AccessChain 69(g_sSamp4) 53 + 71: 19 Load 70 + 72: 23 SampledImage 66 71 + 74: 7(fvec4) ImageSampleImplicitLod 72 73 + 75: 7(fvec4) FAdd 60 74 + 77: 16(ptr) AccessChain 64(g_tTex4) 76 + 78: 15 Load 77 + 79: 20(ptr) AccessChain 69(g_sSamp4) 76 + 80: 19 Load 79 + 81: 23 SampledImage 78 80 + 82: 7(fvec4) ImageSampleImplicitLod 81 73 + 83: 7(fvec4) FAdd 75 82 + 85: 15 Load 84(g_tTex5) + 87: 19 Load 86(g_sSamp5) + 88: 23 SampledImage 85 87 + 90: 7(fvec4) ImageSampleImplicitLod 88 89 + 91: 7(fvec4) FAdd 83 90 + 98: 97(ptr) AccessChain 95(mystruct) 76 96 + 99: 6(float) Load 98 + 100: 7(fvec4) CompositeConstruct 99 99 99 99 + 101: 7(fvec4) FAdd 91 100 + ReturnValue 101 + FunctionEnd + 11(Func2(): 7(fvec4) Function None 8 + 12: Label + 104: 15 Load 17(g_tTex1) + 105: 19 Load 21(g_sSamp1) + 106: 23 SampledImage 104 105 + 107: 7(fvec4) ImageSampleImplicitLod 106 25 + 108: 16(ptr) AccessChain 39(g_tTex3) 53 + 109: 15 Load 108 + 110: 20(ptr) AccessChain 46(g_sSamp3) 53 + 111: 19 Load 110 + 112: 23 SampledImage 109 111 + 113: 7(fvec4) ImageSampleImplicitLod 112 50 + 114: 7(fvec4) FAdd 107 113 + ReturnValue 114 + FunctionEnd +13(Func2_unused(): 7(fvec4) Function None 8 + 14: Label + 118: 15 Load 117(g_tTex_unused1) + 120: 19 Load 119(g_sSamp_unused1) + 121: 23 SampledImage 118 120 + 123: 7(fvec4) ImageSampleImplicitLod 121 122 + 125: 15 Load 124(g_tTex_unused2) + 127: 19 Load 126(g_sSamp_unused2) + 128: 23 SampledImage 125 127 + 130: 7(fvec4) ImageSampleImplicitLod 128 129 + 131: 7(fvec4) FAdd 123 130 + ReturnValue 131 + FunctionEnd diff --git a/Test/baseResults/spv.register.noautoassign.frag.out b/Test/baseResults/spv.register.noautoassign.frag.out new file mode 100644 index 00000000..3a8ab1a3 --- /dev/null +++ b/Test/baseResults/spv.register.noautoassign.frag.out @@ -0,0 +1,224 @@ +spv.register.noautoassign.frag + +Linked fragment stage: + + +// Module Version 10000 +// Generated by (magic number): 80001 +// Id's are bound by 154 + + Capability Shader + Capability Sampled1D + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Fragment 4 "main_ep" 143 + ExecutionMode 4 OriginUpperLeft + Name 4 "main_ep" + Name 9 "Func1(" + Name 11 "Func2(" + Name 13 "Func2_unused(" + Name 17 "g_tTex1" + Name 21 "g_sSamp1" + Name 27 "g_tTex2" + Name 29 "g_sSamp2" + Name 39 "g_tTex3" + Name 46 "g_sSamp3" + Name 64 "g_tTex4" + Name 69 "g_sSamp4" + Name 84 "g_tTex5" + Name 86 "g_sSamp5" + Name 93 "MyStruct_t" + MemberName 93(MyStruct_t) 0 "a" + MemberName 93(MyStruct_t) 1 "b" + MemberName 93(MyStruct_t) 2 "c" + Name 95 "mystruct" + Name 117 "g_tTex_unused1" + Name 119 "g_sSamp_unused1" + Name 124 "g_tTex_unused2" + Name 126 "g_sSamp_unused2" + Name 134 "PS_OUTPUT" + MemberName 134(PS_OUTPUT) 0 "Color" + Name 136 "psout" + Name 143 "Color" + Name 147 "g_tTex_unused3" + Name 149 "myfloat4_a" + Name 150 "myfloat4_b" + Name 153 "myint4_a" + Decorate 17(g_tTex1) DescriptorSet 0 + Decorate 17(g_tTex1) Binding 11 + Decorate 21(g_sSamp1) DescriptorSet 0 + Decorate 21(g_sSamp1) Binding 5 + Decorate 27(g_tTex2) DescriptorSet 0 + Decorate 29(g_sSamp2) DescriptorSet 0 + Decorate 39(g_tTex3) DescriptorSet 0 + Decorate 39(g_tTex3) Binding 13 + Decorate 46(g_sSamp3) DescriptorSet 0 + Decorate 46(g_sSamp3) Binding 7 + Decorate 64(g_tTex4) DescriptorSet 0 + Decorate 69(g_sSamp4) DescriptorSet 0 + Decorate 84(g_tTex5) DescriptorSet 0 + Decorate 86(g_sSamp5) DescriptorSet 0 + Decorate 95(mystruct) Binding 19 + Decorate 117(g_tTex_unused1) DescriptorSet 0 + Decorate 117(g_tTex_unused1) Binding 10 + Decorate 119(g_sSamp_unused1) DescriptorSet 0 + Decorate 124(g_tTex_unused2) DescriptorSet 0 + Decorate 124(g_tTex_unused2) Binding 12 + Decorate 126(g_sSamp_unused2) DescriptorSet 0 + Decorate 143(Color) Location 0 + Decorate 147(g_tTex_unused3) DescriptorSet 0 + 2: TypeVoid + 3: TypeFunction 2 + 6: TypeFloat 32 + 7: TypeVector 6(float) 4 + 8: TypeFunction 7(fvec4) + 15: TypeImage 6(float) 1D sampled format:Unknown + 16: TypePointer UniformConstant 15 + 17(g_tTex1): 16(ptr) Variable UniformConstant + 19: TypeSampler + 20: TypePointer UniformConstant 19 + 21(g_sSamp1): 20(ptr) Variable UniformConstant + 23: TypeSampledImage 15 + 25: 6(float) Constant 1036831949 + 27(g_tTex2): 16(ptr) Variable UniformConstant + 29(g_sSamp2): 20(ptr) Variable UniformConstant + 32: 6(float) Constant 1045220557 + 35: TypeInt 32 0 + 36: 35(int) Constant 2 + 37: TypeArray 15 36 + 38: TypePointer UniformConstant 37 + 39(g_tTex3): 38(ptr) Variable UniformConstant + 40: TypeInt 32 1 + 41: 40(int) Constant 0 + 44: TypeArray 19 36 + 45: TypePointer UniformConstant 44 + 46(g_sSamp3): 45(ptr) Variable UniformConstant + 50: 6(float) Constant 1050253722 + 53: 40(int) Constant 1 + 61: 35(int) Constant 3 + 62: TypeArray 15 61 + 63: TypePointer UniformConstant 62 + 64(g_tTex4): 63(ptr) Variable UniformConstant + 67: TypeArray 19 61 + 68: TypePointer UniformConstant 67 + 69(g_sSamp4): 68(ptr) Variable UniformConstant + 73: 6(float) Constant 1053609165 + 76: 40(int) Constant 2 + 84(g_tTex5): 16(ptr) Variable UniformConstant + 86(g_sSamp5): 20(ptr) Variable UniformConstant + 89: 6(float) Constant 1056964608 + 92: TypeVector 6(float) 3 + 93(MyStruct_t): TypeStruct 40(int) 6(float) 92(fvec3) + 94: TypePointer UniformConstant 93(MyStruct_t) + 95(mystruct): 94(ptr) Variable UniformConstant + 96: 35(int) Constant 1 + 97: TypePointer UniformConstant 6(float) +117(g_tTex_unused1): 16(ptr) Variable UniformConstant +119(g_sSamp_unused1): 20(ptr) Variable UniformConstant + 122: 6(float) Constant 1066192077 +124(g_tTex_unused2): 16(ptr) Variable UniformConstant +126(g_sSamp_unused2): 20(ptr) Variable UniformConstant + 129: 6(float) Constant 1067030938 + 134(PS_OUTPUT): TypeStruct 7(fvec4) + 135: TypePointer Function 134(PS_OUTPUT) + 140: TypePointer Function 7(fvec4) + 142: TypePointer Output 7(fvec4) + 143(Color): 142(ptr) Variable Output +147(g_tTex_unused3): 16(ptr) Variable UniformConstant + 148: TypePointer UniformConstant 7(fvec4) + 149(myfloat4_a): 148(ptr) Variable UniformConstant + 150(myfloat4_b): 148(ptr) Variable UniformConstant + 151: TypeVector 40(int) 4 + 152: TypePointer UniformConstant 151(ivec4) + 153(myint4_a): 152(ptr) Variable UniformConstant + 4(main_ep): 2 Function None 3 + 5: Label + 136(psout): 135(ptr) Variable Function + 137: 7(fvec4) FunctionCall 9(Func1() + 138: 7(fvec4) FunctionCall 11(Func2() + 139: 7(fvec4) FAdd 137 138 + 141: 140(ptr) AccessChain 136(psout) 41 + Store 141 139 + 144: 140(ptr) AccessChain 136(psout) 41 + 145: 7(fvec4) Load 144 + Store 143(Color) 145 + Return + FunctionEnd + 9(Func1(): 7(fvec4) Function None 8 + 10: Label + 18: 15 Load 17(g_tTex1) + 22: 19 Load 21(g_sSamp1) + 24: 23 SampledImage 18 22 + 26: 7(fvec4) ImageSampleImplicitLod 24 25 + 28: 15 Load 27(g_tTex2) + 30: 19 Load 29(g_sSamp2) + 31: 23 SampledImage 28 30 + 33: 7(fvec4) ImageSampleImplicitLod 31 32 + 34: 7(fvec4) FAdd 26 33 + 42: 16(ptr) AccessChain 39(g_tTex3) 41 + 43: 15 Load 42 + 47: 20(ptr) AccessChain 46(g_sSamp3) 41 + 48: 19 Load 47 + 49: 23 SampledImage 43 48 + 51: 7(fvec4) ImageSampleImplicitLod 49 50 + 52: 7(fvec4) FAdd 34 51 + 54: 16(ptr) AccessChain 39(g_tTex3) 53 + 55: 15 Load 54 + 56: 20(ptr) AccessChain 46(g_sSamp3) 53 + 57: 19 Load 56 + 58: 23 SampledImage 55 57 + 59: 7(fvec4) ImageSampleImplicitLod 58 50 + 60: 7(fvec4) FAdd 52 59 + 65: 16(ptr) AccessChain 64(g_tTex4) 53 + 66: 15 Load 65 + 70: 20(ptr) AccessChain 69(g_sSamp4) 53 + 71: 19 Load 70 + 72: 23 SampledImage 66 71 + 74: 7(fvec4) ImageSampleImplicitLod 72 73 + 75: 7(fvec4) FAdd 60 74 + 77: 16(ptr) AccessChain 64(g_tTex4) 76 + 78: 15 Load 77 + 79: 20(ptr) AccessChain 69(g_sSamp4) 76 + 80: 19 Load 79 + 81: 23 SampledImage 78 80 + 82: 7(fvec4) ImageSampleImplicitLod 81 73 + 83: 7(fvec4) FAdd 75 82 + 85: 15 Load 84(g_tTex5) + 87: 19 Load 86(g_sSamp5) + 88: 23 SampledImage 85 87 + 90: 7(fvec4) ImageSampleImplicitLod 88 89 + 91: 7(fvec4) FAdd 83 90 + 98: 97(ptr) AccessChain 95(mystruct) 76 96 + 99: 6(float) Load 98 + 100: 7(fvec4) CompositeConstruct 99 99 99 99 + 101: 7(fvec4) FAdd 91 100 + ReturnValue 101 + FunctionEnd + 11(Func2(): 7(fvec4) Function None 8 + 12: Label + 104: 15 Load 17(g_tTex1) + 105: 19 Load 21(g_sSamp1) + 106: 23 SampledImage 104 105 + 107: 7(fvec4) ImageSampleImplicitLod 106 25 + 108: 16(ptr) AccessChain 39(g_tTex3) 53 + 109: 15 Load 108 + 110: 20(ptr) AccessChain 46(g_sSamp3) 53 + 111: 19 Load 110 + 112: 23 SampledImage 109 111 + 113: 7(fvec4) ImageSampleImplicitLod 112 50 + 114: 7(fvec4) FAdd 107 113 + ReturnValue 114 + FunctionEnd +13(Func2_unused(): 7(fvec4) Function None 8 + 14: Label + 118: 15 Load 117(g_tTex_unused1) + 120: 19 Load 119(g_sSamp_unused1) + 121: 23 SampledImage 118 120 + 123: 7(fvec4) ImageSampleImplicitLod 121 122 + 125: 15 Load 124(g_tTex_unused2) + 127: 19 Load 126(g_sSamp_unused2) + 128: 23 SampledImage 125 127 + 130: 7(fvec4) ImageSampleImplicitLod 128 129 + 131: 7(fvec4) FAdd 123 130 + ReturnValue 131 + FunctionEnd diff --git a/Test/spv.register.autoassign.frag b/Test/spv.register.autoassign.frag new file mode 100644 index 00000000..0d6f0b2d --- /dev/null +++ b/Test/spv.register.autoassign.frag @@ -0,0 +1,71 @@ + +SamplerState g_sSamp1 : register(s0); +SamplerState g_sSamp2; +SamplerState g_sSamp3[2] : register(s2); +SamplerState g_sSamp4[3]; +SamplerState g_sSamp5; + +SamplerState g_sSamp_unused1; +SamplerState g_sSamp_unused2; + +Texture1D g_tTex1 : register(t1); +const uniform Texture1D g_tTex2; +Texture1D g_tTex3[2] : register(t3); +Texture1D g_tTex4[3]; +Texture1D g_tTex5; + +Texture1D g_tTex_unused1 : register(t0); +Texture1D g_tTex_unused2 : register(t2); +Texture1D g_tTex_unused3; + +struct MyStruct_t { + int a; + float b; + float3 c; +}; + +uniform MyStruct_t mystruct : register(b4); + +struct PS_OUTPUT +{ + float4 Color : SV_Target0; +}; + +uniform float4 myfloat4_a; +uniform float4 myfloat4_b; +uniform int4 myint4_a; + +float4 Func1() +{ + return + g_tTex1 . Sample(g_sSamp1, 0.1) + + g_tTex2 . Sample(g_sSamp2, 0.2) + + g_tTex3[0] . Sample(g_sSamp3[0], 0.3) + + g_tTex3[1] . Sample(g_sSamp3[1], 0.3) + + g_tTex4[1] . Sample(g_sSamp4[1], 0.4) + + g_tTex4[2] . Sample(g_sSamp4[2], 0.4) + + g_tTex5 . Sample(g_sSamp5, 0.5) + + mystruct.c[1]; +} + +float4 Func2() +{ + return + g_tTex1 . Sample(g_sSamp1, 0.1) + + g_tTex3[1] . Sample(g_sSamp3[1], 0.3); +} + +// Not called from entry point: +float4 Func2_unused() +{ + return + g_tTex_unused1 . Sample(g_sSamp_unused1, 1.1) + + g_tTex_unused2 . Sample(g_sSamp_unused2, 1.2); +} + +PS_OUTPUT main_ep() +{ + PS_OUTPUT psout; + psout.Color = Func1() + Func2(); + return psout; +} diff --git a/Test/spv.register.noautoassign.frag b/Test/spv.register.noautoassign.frag new file mode 100644 index 00000000..0d6f0b2d --- /dev/null +++ b/Test/spv.register.noautoassign.frag @@ -0,0 +1,71 @@ + +SamplerState g_sSamp1 : register(s0); +SamplerState g_sSamp2; +SamplerState g_sSamp3[2] : register(s2); +SamplerState g_sSamp4[3]; +SamplerState g_sSamp5; + +SamplerState g_sSamp_unused1; +SamplerState g_sSamp_unused2; + +Texture1D g_tTex1 : register(t1); +const uniform Texture1D g_tTex2; +Texture1D g_tTex3[2] : register(t3); +Texture1D g_tTex4[3]; +Texture1D g_tTex5; + +Texture1D g_tTex_unused1 : register(t0); +Texture1D g_tTex_unused2 : register(t2); +Texture1D g_tTex_unused3; + +struct MyStruct_t { + int a; + float b; + float3 c; +}; + +uniform MyStruct_t mystruct : register(b4); + +struct PS_OUTPUT +{ + float4 Color : SV_Target0; +}; + +uniform float4 myfloat4_a; +uniform float4 myfloat4_b; +uniform int4 myint4_a; + +float4 Func1() +{ + return + g_tTex1 . Sample(g_sSamp1, 0.1) + + g_tTex2 . Sample(g_sSamp2, 0.2) + + g_tTex3[0] . Sample(g_sSamp3[0], 0.3) + + g_tTex3[1] . Sample(g_sSamp3[1], 0.3) + + g_tTex4[1] . Sample(g_sSamp4[1], 0.4) + + g_tTex4[2] . Sample(g_sSamp4[2], 0.4) + + g_tTex5 . Sample(g_sSamp5, 0.5) + + mystruct.c[1]; +} + +float4 Func2() +{ + return + g_tTex1 . Sample(g_sSamp1, 0.1) + + g_tTex3[1] . Sample(g_sSamp3[1], 0.3); +} + +// Not called from entry point: +float4 Func2_unused() +{ + return + g_tTex_unused1 . Sample(g_sSamp_unused1, 1.1) + + g_tTex_unused2 . Sample(g_sSamp_unused2, 1.2); +} + +PS_OUTPUT main_ep() +{ + PS_OUTPUT psout; + psout.Color = Func1() + Func2(); + return psout; +} diff --git a/glslang/CMakeLists.txt b/glslang/CMakeLists.txt index 6325fd4a..95d4bdd8 100644 --- a/glslang/CMakeLists.txt +++ b/glslang/CMakeLists.txt @@ -10,6 +10,7 @@ set(SOURCES MachineIndependent/glslang.y MachineIndependent/glslang_tab.cpp MachineIndependent/Constant.cpp + MachineIndependent/iomapper.cpp MachineIndependent/InfoSink.cpp MachineIndependent/Initialize.cpp MachineIndependent/IntermTraverse.cpp @@ -55,6 +56,7 @@ set(HEADERS MachineIndependent/glslang_tab.cpp.h MachineIndependent/gl_types.h MachineIndependent/Initialize.h + MachineIndependent/iomapper.h MachineIndependent/LiveTraverser.h MachineIndependent/localintermediate.h MachineIndependent/ParseHelper.h diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index 414a62b3..dc0c01b6 100644 --- a/glslang/MachineIndependent/ShaderLang.cpp +++ b/glslang/MachineIndependent/ShaderLang.cpp @@ -60,6 +60,7 @@ #define SH_EXPORTING #include "../Public/ShaderLang.h" #include "reflection.h" +#include "iomapper.h" #include "Initialize.h" namespace { // anonymous namespace for file-local functions and symbols @@ -1488,6 +1489,10 @@ void TShader::setEntryPoint(const char* entryPoint) intermediate->setEntryPointName(entryPoint); } +void TShader::setShiftSamplerBinding(unsigned int base) { intermediate->setShiftSamplerBinding(base); } +void TShader::setShiftTextureBinding(unsigned int base) { intermediate->setShiftTextureBinding(base); } +void TShader::setShiftUboBinding(unsigned int base) { intermediate->setShiftUboBinding(base); } +void TShader::setAutoMapBindings(bool map) { intermediate->setAutoMapBindings(map); } // // Turn the shader strings into a parse tree in the TIntermediate. // @@ -1548,7 +1553,7 @@ const char* TShader::getInfoDebugLog() return infoSink->debug.c_str(); } -TProgram::TProgram() : pool(0), reflection(0), linked(false) +TProgram::TProgram() : pool(0), reflection(0), ioMapper(nullptr), linked(false) { infoSink = new TInfoSink; for (int s = 0; s < EShLangCount; ++s) { @@ -1700,4 +1705,24 @@ int TProgram::getAttributeType(int index) { return reflection->getAtt void TProgram::dumpReflection() { reflection->dump(); } +// +// I/O mapping implementation. +// +bool TProgram::mapIO() +{ + if (! linked || ioMapper) + return false; + + ioMapper = new TIoMapper; + + for (int s = 0; s < EShLangCount; ++s) { + if (intermediate[s]) { + if (! ioMapper->addStage((EShLanguage)s, *intermediate[s])) + return false; + } + } + + return true; +} + } // end namespace glslang diff --git a/glslang/MachineIndependent/iomapper.cpp b/glslang/MachineIndependent/iomapper.cpp new file mode 100644 index 00000000..d02fb5ea --- /dev/null +++ b/glslang/MachineIndependent/iomapper.cpp @@ -0,0 +1,249 @@ +// +//Copyright (C) 2016 LunarG, Inc. +// +//All rights reserved. +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions +//are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +//POSSIBILITY OF SUCH DAMAGE. +// + +#include "../Include/Common.h" +#include "iomapper.h" +#include "LiveTraverser.h" +#include "localintermediate.h" + +#include "gl_types.h" + +#include +#include + +// +// Map IO bindings. +// +// High-level algorithm for one stage: +// +// 1. Traverse all code (live+dead) to find the explicitly provided bindings. +// +// 2. Traverse (just) the live code to determine which non-provided bindings +// require auto-numbering. We do not auto-number dead ones. +// +// 3. Traverse all the code to apply the bindings: +// a. explicitly given bindings are offset according to their type +// b. implicit live bindings are auto-numbered into the holes, using +// any open binding slot. +// c. implicit dead bindings are left un-bound. +// + + +namespace glslang { + +// Map of IDs to bindings +typedef std::unordered_map TBindingMap; +typedef std::unordered_set TUsedBindings; + + +// This traverses the AST to determine which bindings are used, and which are implicit +// (for subsequent auto-numbering) +class TBindingTraverser : public TLiveTraverser { +public: + TBindingTraverser(const TIntermediate& i, TBindingMap& bindingMap, TUsedBindings& usedBindings, + bool traverseDeadCode = false) : + TLiveTraverser(i, traverseDeadCode), + bindingMap(bindingMap), + usedBindings(usedBindings) + { } + +protected: + virtual void visitSymbol(TIntermSymbol* base) { + if (base->getQualifier().storage == EvqUniform) + addUniform(*base); + } + + // Return the right binding base given the variable type. + int getBindingBase(const TType& type) { + if (type.getBasicType() == EbtSampler) { + const TSampler& sampler = type.getSampler(); + if (sampler.isPureSampler()) + return intermediate.getShiftSamplerBinding(); + if (sampler.isTexture()) + return intermediate.getShiftTextureBinding(); + } + + if (type.getQualifier().isUniformOrBuffer()) + return intermediate.getShiftUboBinding(); + + return -1; // not a type with a binding + } + + // Mark a given base symbol ID as being bound to 'binding' + void markBinding(const TIntermSymbol& base, int binding) { + bindingMap[base.getId()] = binding; + + if (binding >= 0) { + // const TType& type = base.getType(); + const unsigned int size = 1; // type.isArray() ? type.getCumulativeArraySize() : 1; + + for (unsigned int offset=0; offsettraverse(&it_binding_all); + + // Traverse just live code to find things that need implicit bindings. + it_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str()); + + while (! it_binding_live.functions.empty()) { + TIntermNode* function = it_binding_live.functions.back(); + it_binding_live.functions.pop_back(); + function->traverse(&it_binding_live); + } + + // Bind everything that needs a binding and doesn't have one. + root->traverse(&it_iomap); + + return true; +} + +} // end namespace glslang diff --git a/glslang/MachineIndependent/iomapper.h b/glslang/MachineIndependent/iomapper.h new file mode 100644 index 00000000..69ed4c5b --- /dev/null +++ b/glslang/MachineIndependent/iomapper.h @@ -0,0 +1,61 @@ +// +//Copyright (C) 2016 LunarG, Inc. +// +//All rights reserved. +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions +//are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +//POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _IOMAPPER_INCLUDED +#define _IOMAPPER_INCLUDED + +#include "../Public/ShaderLang.h" + +// +// A reflection database and its interface, consistent with the OpenGL API reflection queries. +// + +namespace glslang { + +class TIntermediate; + +// I/O mapper +class TIoMapper { +public: + TIoMapper() {} + virtual ~TIoMapper() {} + + // grow the reflection stage by stage + bool addStage(EShLanguage, TIntermediate&); +}; + +} // end namespace glslang + +#endif // _IOMAPPER_INCLUDED diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h index 3ce615ee..4de811cf 100644 --- a/glslang/MachineIndependent/localintermediate.h +++ b/glslang/MachineIndependent/localintermediate.h @@ -141,7 +141,11 @@ public: 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), - multiStream(false), xfbMode(false) + multiStream(false), xfbMode(false), + shiftSamplerBinding(0), + shiftTextureBinding(0), + shiftUboBinding(0), + autoMapBindings(false) { localSize[0] = 1; localSize[1] = 1; @@ -163,6 +167,16 @@ public: void setEntryPointMangledName(const char* ep) { entryPointMangledName = ep; } const std::string& getEntryPointName() const { return entryPointName; } const std::string& getEntryPointMangledName() const { return entryPointMangledName; } + + void setShiftSamplerBinding(unsigned int shift) { shiftSamplerBinding = shift; } + unsigned int getShiftSamplerBinding() const { return shiftSamplerBinding; } + void setShiftTextureBinding(unsigned int shift) { shiftTextureBinding = shift; } + unsigned int getShiftTextureBinding() const { return shiftTextureBinding; } + void setShiftUboBinding(unsigned int shift) { shiftUboBinding = shift; } + unsigned int getShiftUboBinding() const { return shiftUboBinding; } + void setAutoMapBindings(bool map) { autoMapBindings = map; } + bool getAutoMapBindings() const { return autoMapBindings; } + void setVersion(int v) { version = v; } int getVersion() const { return version; } void setProfile(EProfile p) { profile = p; } @@ -367,6 +381,11 @@ protected: EShSource source; // source language, known a bit later std::string entryPointName; std::string entryPointMangledName; + unsigned int shiftSamplerBinding; + unsigned int shiftTextureBinding; + unsigned int shiftUboBinding; + bool autoMapBindings; + EProfile profile; int version; SpvVersion spvVersion; diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h index d1121595..80605ba3 100644 --- a/glslang/Public/ShaderLang.h +++ b/glslang/Public/ShaderLang.h @@ -300,6 +300,10 @@ public: const char* const* s, const int* l, const char* const* names, int n); void setPreamble(const char* s) { preamble = s; } void setEntryPoint(const char* entryPoint); + void setShiftSamplerBinding(unsigned int base); + void setShiftTextureBinding(unsigned int base); + void setShiftUboBinding(unsigned int base); + void setAutoMapBindings(bool map); // Interface to #include handlers. // @@ -433,6 +437,7 @@ private: }; class TReflection; +class TIoMapper; // Make one TProgram per set of shaders that will get linked together. Add all // the shaders that are to be linked together. After calling shader.parse() @@ -470,6 +475,9 @@ public: int getAttributeType(int index); // can be used for glGetActiveAttrib() void dumpReflection(); + // I/O mapping: apply base offsets and map live unbound variables + bool mapIO(); + protected: bool linkStage(EShLanguage, EShMessages); @@ -479,6 +487,7 @@ protected: bool newedIntermediate[EShLangCount]; // track which intermediate were "new" versus reusing a singleton unit in a stage TInfoSink* infoSink; TReflection* reflection; + TIoMapper* ioMapper; bool linked; private: diff --git a/gtests/Spv.FromFile.cpp b/gtests/Spv.FromFile.cpp index 45fed044..5ea46c44 100644 --- a/gtests/Spv.FromFile.cpp +++ b/gtests/Spv.FromFile.cpp @@ -41,11 +41,30 @@ namespace glslangtest { namespace { +struct IoMapData { + const char* fileName; + const char* entryPoint; + int baseSamplerBinding; + int baseTextureBinding; + int baseUboBinding; + bool autoMapBindings; +}; + +std::string FileNameAsCustomTestSuffixIoMap( + const ::testing::TestParamInfo& info) { + std::string name = info.param.fileName; + // A valid test case suffix cannot have '.' and '-' inside. + std::replace(name.begin(), name.end(), '.', '_'); + std::replace(name.begin(), name.end(), '-', '_'); + return name; +} + using CompileVulkanToSpirvTest = GlslangTest<::testing::TestWithParam>; using CompileOpenGLToSpirvTest = GlslangTest<::testing::TestWithParam>; using VulkanSemantics = GlslangTest<::testing::TestWithParam>; using OpenGLSemantics = GlslangTest<::testing::TestWithParam>; using VulkanAstSemantics = GlslangTest<::testing::TestWithParam>; +using HlslSemantics = GlslangTest<::testing::TestWithParam>; // Compiling GLSL to SPIR-V under Vulkan semantics. Expected to successfully // generate SPIR-V. @@ -91,6 +110,18 @@ TEST_P(VulkanAstSemantics, FromFile) Target::AST); } +// HLSL-level Vulkan semantics tests. +TEST_P(HlslSemantics, FromFile) +{ + loadFileCompileIoMapAndCheck(GLSLANG_TEST_DIRECTORY, GetParam().fileName, + Source::HLSL, Semantics::Vulkan, + Target::Spv, GetParam().entryPoint, + GetParam().baseSamplerBinding, + GetParam().baseTextureBinding, + GetParam().baseUboBinding, + GetParam().autoMapBindings); +} + // clang-format off INSTANTIATE_TEST_CASE_P( Glsl, CompileVulkanToSpirvTest, @@ -216,6 +247,16 @@ INSTANTIATE_TEST_CASE_P( FileNameAsCustomTestSuffix ); +// clang-format off +INSTANTIATE_TEST_CASE_P( + Hlsl, HlslSemantics, + ::testing::ValuesIn(std::vector{ + { "spv.register.autoassign.frag", "main_ep", 5, 10, 15, true }, + { "spv.register.noautoassign.frag", "main_ep", 5, 10, 15, false }, + }), + FileNameAsCustomTestSuffixIoMap +); + // clang-format off INSTANTIATE_TEST_CASE_P( Glsl, CompileOpenGLToSpirvTest, diff --git a/gtests/TestFixture.h b/gtests/TestFixture.h index 81930125..47e2703a 100644 --- a/gtests/TestFixture.h +++ b/gtests/TestFixture.h @@ -234,6 +234,54 @@ public: } } + // Compiles and links the given source |code| of the given shader + // |stage| into the target under the semantics specified via |controls|. + // Returns a GlslangResult instance containing all the information generated + // during the process. If the target includes SPIR-V, also disassembles + // the result and returns disassembly text. + GlslangResult compileLinkIoMap( + const std::string shaderName, const std::string& code, + const std::string& entryPointName, EShMessages controls, + int baseSamplerBinding, + int baseTextureBinding, + int baseUboBinding, + bool autoMapBindings) + { + const EShLanguage kind = GetShaderStage(GetSuffix(shaderName)); + + glslang::TShader shader(kind); + shader.setShiftSamplerBinding(baseSamplerBinding); + shader.setShiftTextureBinding(baseTextureBinding); + shader.setShiftUboBinding(baseUboBinding); + shader.setAutoMapBindings(autoMapBindings); + + bool success = compile(&shader, code, entryPointName, controls); + + glslang::TProgram program; + program.addShader(&shader); + + success &= program.link(controls); + success &= program.mapIO(); + + spv::SpvBuildLogger logger; + + if (success && (controls & EShMsgSpvRules)) { + std::vector spirv_binary; + glslang::GlslangToSpv(*program.getIntermediate(kind), + spirv_binary, &logger); + + std::ostringstream disassembly_stream; + spv::Parameterize(); + spv::Disassemble(disassembly_stream, spirv_binary); + return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, + program.getInfoLog(), program.getInfoDebugLog(), + logger.getAllMessages(), disassembly_stream.str()}; + } else { + return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, + program.getInfoLog(), program.getInfoDebugLog(), "", ""}; + } + } + // This is like compileAndLink but with remapping of the SPV binary // through spirvbin_t::remap(). While technically this could be merged // with compileAndLink() above (with the remap step optionally being a no-op) @@ -347,6 +395,38 @@ public: expectedOutputFname); } + void loadFileCompileIoMapAndCheck(const std::string& testDir, + const std::string& testName, + Source source, + Semantics semantics, + Target target, + const std::string& entryPointName, + int baseSamplerBinding, + int baseTextureBinding, + int baseUboBinding, + bool autoMapBindings) + { + const std::string inputFname = testDir + "/" + testName; + const std::string expectedOutputFname = + testDir + "/baseResults/" + testName + ".out"; + std::string input, expectedOutput; + + tryLoadFile(inputFname, "input", &input); + tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); + + const EShMessages controls = DeriveOptions(source, semantics, target); + GlslangResult result = compileLinkIoMap(testName, input, entryPointName, controls, + baseSamplerBinding, baseTextureBinding, baseUboBinding, + autoMapBindings); + + // Generate the hybrid output in the way of glslangValidator. + std::ostringstream stream; + outputResultToStream(&stream, result, controls); + + checkEqAndUpdateIfRequested(expectedOutput, stream.str(), + expectedOutputFname); + } + void loadFileCompileRemapAndCheck(const std::string& testDir, const std::string& testName, Source source,