diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp index 966acef4..a2bd8484 100755 --- a/SPIRV/GlslangToSpv.cpp +++ b/SPIRV/GlslangToSpv.cpp @@ -101,7 +101,7 @@ private: // class TGlslangToSpvTraverser : public glslang::TIntermTraverser { public: - TGlslangToSpvTraverser(const glslang::TIntermediate*, spv::SpvBuildLogger* logger); + TGlslangToSpvTraverser(const glslang::TIntermediate*, spv::SpvBuildLogger* logger, glslang::SpvOptions& options); virtual ~TGlslangToSpvTraverser() { } bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate*); @@ -179,6 +179,7 @@ protected: spv::Id createShortCircuit(glslang::TOperator, glslang::TIntermTyped& left, glslang::TIntermTyped& right); spv::Id getExtBuiltins(const char* name); + glslang::SpvOptions& options; spv::Function* shaderEntry; spv::Function* currentFunction; spv::Instruction* entryPoint; @@ -851,8 +852,11 @@ bool HasNonLayoutQualifiers(const glslang::TType& type, const glslang::TQualifie // Implement the TGlslangToSpvTraverser class. // -TGlslangToSpvTraverser::TGlslangToSpvTraverser(const glslang::TIntermediate* glslangIntermediate, spv::SpvBuildLogger* buildLogger) - : TIntermTraverser(true, false, true), shaderEntry(nullptr), currentFunction(nullptr), +TGlslangToSpvTraverser::TGlslangToSpvTraverser(const glslang::TIntermediate* glslangIntermediate, + spv::SpvBuildLogger* buildLogger, glslang::SpvOptions& options) + : TIntermTraverser(true, false, true), + options(options), + shaderEntry(nullptr), currentFunction(nullptr), sequenceDepth(0), logger(buildLogger), builder((glslang::GetKhronosToolId() << 16) | GeneratorVersion, logger), inEntryPoint(false), entryPointTerminated(false), linkageOnly(false), @@ -862,6 +866,10 @@ TGlslangToSpvTraverser::TGlslangToSpvTraverser(const glslang::TIntermediate* gls builder.clearAccessChain(); builder.setSource(TranslateSourceLanguage(glslangIntermediate->getSource(), glslangIntermediate->getProfile()), glslangIntermediate->getVersion()); + if (options.generateDebugInfo) { + builder.setSourceFile(glslangIntermediate->getSourceFile()); + builder.setSourceText(glslangIntermediate->getSourceText()); + } stdBuiltins = builder.import("GLSL.std.450"); builder.setMemoryModel(spv::AddressingModelLogical, spv::MemoryModelGLSL450); shaderEntry = builder.makeEntryPoint(glslangIntermediate->getEntryPointName().c_str()); @@ -5561,22 +5569,27 @@ void OutputSpvHex(const std::vector& spirv, const char* baseName, // // Set up the glslang traversal // -void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv) +void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv, SpvOptions* options) { spv::SpvBuildLogger logger; - GlslangToSpv(intermediate, spirv, &logger); + GlslangToSpv(intermediate, spirv, &logger, options); } -void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv, spv::SpvBuildLogger* logger) +void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger* logger, SpvOptions* options) { TIntermNode* root = intermediate.getTreeRoot(); if (root == 0) return; + glslang::SpvOptions defaultOptions; + if (options == nullptr) + options = &defaultOptions; + glslang::GetThreadPoolAllocator().push(); - TGlslangToSpvTraverser it(&intermediate, logger); + TGlslangToSpvTraverser it(&intermediate, logger, *options); root->traverse(&it); it.finishSpv(); it.dumpSpv(spirv); diff --git a/SPIRV/GlslangToSpv.h b/SPIRV/GlslangToSpv.h index aba48a38..0dad4d21 100644 --- a/SPIRV/GlslangToSpv.h +++ b/SPIRV/GlslangToSpv.h @@ -47,9 +47,16 @@ namespace glslang { +struct SpvOptions { + SpvOptions() : generateDebugInfo(false) { } + bool generateDebugInfo; +}; + void GetSpirvVersion(std::string&); -void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv); -void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv, spv::SpvBuildLogger* logger); +void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv, + SpvOptions* options = nullptr); +void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger* logger, SpvOptions* options = nullptr); void OutputSpvBin(const std::vector& spirv, const char* baseName); void OutputSpvHex(const std::vector& spirv, const char* baseName, const char* varName); diff --git a/SPIRV/SpvBuilder.cpp b/SPIRV/SpvBuilder.cpp index 0add1db4..37c4cb5b 100644 --- a/SPIRV/SpvBuilder.cpp +++ b/SPIRV/SpvBuilder.cpp @@ -59,6 +59,7 @@ namespace spv { Builder::Builder(unsigned int magicNumber, SpvBuildLogger* buildLogger) : source(SourceLanguageUnknown), sourceVersion(0), + sourceFileStringId(NoResult), addressModel(AddressingModelLogical), memoryModel(MemoryModelGLSL450), builderNumber(magicNumber), @@ -2411,12 +2412,8 @@ void Builder::dump(std::vector& out) const dumpInstructions(out, executionModes); // Debug instructions - if (source != SourceLanguageUnknown) { - Instruction sourceInst(0, 0, OpSource); - sourceInst.addImmediateOperand(source); - sourceInst.addImmediateOperand(sourceVersion); - sourceInst.dump(out); - } + dumpInstructions(out, strings); + dumpSourceInstructions(out); for (int e = 0; e < (int)sourceExtensions.size(); ++e) { Instruction sourceExtInst(0, 0, OpSourceExtension); sourceExtInst.addStringOperand(sourceExtensions[e]); @@ -2574,6 +2571,48 @@ void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* els elseBlock->addPredecessor(buildPoint); } +// OpSource +// [OpSourceContinued] +// ... +void Builder::dumpSourceInstructions(std::vector& out) const +{ + const int maxWordCount = 0xFFFF; + const int opSourceWordCount = 4; + const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1; + + if (source != SourceLanguageUnknown) { + // OpSource Language Version File Source + Instruction sourceInst(NoResult, NoType, OpSource); + sourceInst.addImmediateOperand(source); + sourceInst.addImmediateOperand(sourceVersion); + // File operand + if (sourceFileStringId != NoResult) { + sourceInst.addIdOperand(sourceFileStringId); + // Source operand + if (sourceText.size() > 0) { + int nextByte = 0; + std::string subString; + while ((int)sourceText.size() - nextByte > 0) { + subString = sourceText.substr(nextByte, nonNullBytesPerInstruction); + if (nextByte == 0) { + // OpSource + sourceInst.addStringOperand(subString.c_str()); + sourceInst.dump(out); + } else { + // OpSourcContinued + Instruction sourceContinuedInst(OpSourceContinued); + sourceContinuedInst.addStringOperand(subString.c_str()); + sourceContinuedInst.dump(out); + } + nextByte += nonNullBytesPerInstruction; + } + } else + sourceInst.dump(out); + } else + sourceInst.dump(out); + } +} + void Builder::dumpInstructions(std::vector& out, const std::vector >& instructions) const { for (int i = 0; i < (int)instructions.size(); ++i) { diff --git a/SPIRV/SpvBuilder.h b/SPIRV/SpvBuilder.h index d93174ed..42c18b7d 100755 --- a/SPIRV/SpvBuilder.h +++ b/SPIRV/SpvBuilder.h @@ -70,6 +70,14 @@ public: source = lang; sourceVersion = version; } + void setSourceFile(const std::string& file) + { + Instruction* fileString = new Instruction(getUniqueId(), NoType, OpString); + fileString->addStringOperand(file.c_str()); + sourceFileStringId = fileString->getResultId(); + strings.push_back(std::unique_ptr(fileString)); + } + void setSourceText(const std::string& text) { sourceText = text; } void addSourceExtension(const char* ext) { sourceExtensions.push_back(ext); } void addExtension(const char* ext) { extensions.insert(ext); } Id import(const char*); @@ -561,10 +569,13 @@ public: void simplifyAccessChainSwizzle(); void createAndSetNoPredecessorBlock(const char*); void createSelectionMerge(Block* mergeBlock, unsigned int control); + void dumpSourceInstructions(std::vector&) const; void dumpInstructions(std::vector&, const std::vector >&) const; SourceLanguage source; int sourceVersion; + spv::Id sourceFileStringId; + std::string sourceText; std::set extensions; std::vector sourceExtensions; AddressingModel addressModel; @@ -579,6 +590,7 @@ public: AccessChain accessChain; // special blocks of instructions for output + std::vector > strings; std::vector > imports; std::vector > entryPoints; std::vector > executionModes; @@ -599,7 +611,7 @@ public: // Our loop stack. std::stack loops; - // The stream for outputing warnings and errors. + // The stream for outputting warnings and errors. SpvBuildLogger* logger; }; // end Builder class diff --git a/SPIRV/spvIR.h b/SPIRV/spvIR.h index ce8b4b8a..087a53f2 100755 --- a/SPIRV/spvIR.h +++ b/SPIRV/spvIR.h @@ -87,7 +87,6 @@ public: void addImmediateOperand(unsigned int immediate) { operands.push_back(immediate); } void addStringOperand(const char* str) { - originalString = str; unsigned int word; char* wordString = (char*)&word; char* wordPtr = wordString; @@ -120,7 +119,6 @@ public: Id getTypeId() const { return typeId; } Id getIdOperand(int op) const { return operands[op]; } unsigned int getImmediateOperand(int op) const { return operands[op]; } - const char* getStringOperand() const { return originalString.c_str(); } // Write out the binary form. void dump(std::vector& out) const @@ -151,7 +149,6 @@ protected: Id typeId; Op opCode; std::vector operands; - std::string originalString; // could be optimized away; convenience for getting string operand Block* block; }; diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp index 8a6b12dc..ef47662c 100644 --- a/StandAlone/StandAlone.cpp +++ b/StandAlone/StandAlone.cpp @@ -91,6 +91,7 @@ enum TOptions { EOptionHlslOffsets = (1 << 23), EOptionHlslIoMapping = (1 << 24), EOptionAutoMapLocations = (1 << 25), + EOptionDebug = (1 << 26), }; // @@ -448,6 +449,9 @@ void ProcessArguments(std::vector>& workItem } else Error("no provided for -e"); break; + case 'g': + Options |= EOptionDebug; + break; case 'h': usage(); break; @@ -539,6 +543,8 @@ void SetMessageOptions(EShMessages& messages) messages = (EShMessages)(messages | EShMsgKeepUncalled); if (Options & EOptionHlslOffsets) messages = (EShMessages)(messages | EShMsgHlslOffsets); + if (Options & EOptionDebug) + messages = (EShMessages)(messages | EShMsgDebugInfo); } // @@ -722,7 +728,10 @@ void CompileAndLinkShaderUnits(std::vector compUnits) std::vector spirv; std::string warningsErrors; spv::SpvBuildLogger logger; - glslang::GlslangToSpv(*program.getIntermediate((EShLanguage)stage), spirv, &logger); + glslang::SpvOptions spvOptions; + if (Options & EOptionDebug) + spvOptions.generateDebugInfo = true; + glslang::GlslangToSpv(*program.getIntermediate((EShLanguage)stage), spirv, &logger, &spvOptions); // Dump the spv to a file or stdout, etc., but only if not doing // memory/perf testing, as it's not internal to programmatic use. @@ -1031,6 +1040,7 @@ void usage() " (default is ES version 100)\n" " -D input is HLSL\n" " -e specify entry-point name\n" + " -g generate debug information\n" " -h print this usage message\n" " -i intermediate tree (glslang AST) is printed out\n" " -l link all input files together to form a single module\n" diff --git a/Test/baseResults/spv.debugInfo.frag.out b/Test/baseResults/spv.debugInfo.frag.out new file mode 100644 index 00000000..6e9e2082 --- /dev/null +++ b/Test/baseResults/spv.debugInfo.frag.out @@ -0,0 +1,99 @@ +spv.debugInfo.frag +Warning, version 450 is not yet complete; most version-specific features are present, but some are missing. + +// Module Version 10000 +// Generated by (magic number): 80001 +// Id's are bound by 40 + + Capability Shader + 2: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Fragment 5 "main" 17 24 + ExecutionMode 5 OriginUpperLeft + 1: String "spv.debugInfo.frag" + Source GLSL 450 1 "#version 450 + +struct S { + int a; +}; + +uniform ubuf { + S s; +}; + +layout(location = 0) in vec4 inv; +layout(location = 0) out vec4 outv; + +void foo(S s) +{ + outv = s.a * inv; +} + +void main() +{ + foo(s); +}" + Name 5 "main" + Name 8 "S" + MemberName 8(S) 0 "a" + Name 12 "foo(struct-S-i11;" + Name 11 "s" + Name 17 "outv" + Name 24 "inv" + Name 27 "S" + MemberName 27(S) 0 "a" + Name 28 "ubuf" + MemberName 28(ubuf) 0 "s" + Name 30 "" + Name 31 "S" + MemberName 31(S) 0 "a" + Name 33 "param" + Decorate 17(outv) Location 0 + Decorate 24(inv) Location 0 + MemberDecorate 27(S) 0 Offset 0 + MemberDecorate 28(ubuf) 0 Offset 0 + Decorate 28(ubuf) Block + Decorate 30 DescriptorSet 0 + 3: TypeVoid + 4: TypeFunction 3 + 7: TypeInt 32 1 + 8(S): TypeStruct 7(int) + 9: TypePointer Function 8(S) + 10: TypeFunction 3 9(ptr) + 14: TypeFloat 32 + 15: TypeVector 14(float) 4 + 16: TypePointer Output 15(fvec4) + 17(outv): 16(ptr) Variable Output + 18: 7(int) Constant 0 + 19: TypePointer Function 7(int) + 23: TypePointer Input 15(fvec4) + 24(inv): 23(ptr) Variable Input + 27(S): TypeStruct 7(int) + 28(ubuf): TypeStruct 27(S) + 29: TypePointer Uniform 28(ubuf) + 30: 29(ptr) Variable Uniform + 31(S): TypeStruct 7(int) + 32: TypePointer Function 31(S) + 34: TypePointer Uniform 27(S) + 5(main): 3 Function None 4 + 6: Label + 33(param): 32(ptr) Variable Function + 35: 34(ptr) AccessChain 30 18 + 36: 27(S) Load 35 + 37: 7(int) CompositeExtract 36 0 + 38: 19(ptr) AccessChain 33(param) 18 + Store 38 37 + 39: 3 FunctionCall 12(foo(struct-S-i11;) 33(param) + Return + FunctionEnd +12(foo(struct-S-i11;): 3 Function None 10 + 11(s): 9(ptr) FunctionParameter + 13: Label + 20: 19(ptr) AccessChain 11(s) 18 + 21: 7(int) Load 20 + 22: 14(float) ConvertSToF 21 + 25: 15(fvec4) Load 24(inv) + 26: 15(fvec4) VectorTimesScalar 25 22 + Store 17(outv) 26 + Return + FunctionEnd diff --git a/Test/runtests b/Test/runtests index a3c89e04..ac117fb1 100755 --- a/Test/runtests +++ b/Test/runtests @@ -90,14 +90,21 @@ diff -b $BASEDIR/hlsl.hlslOffset.vert.out $TARGETDIR/hlsl.hlslOffset.vert.out || # echo Configuring HLSL descriptor set and binding number manually $EXE -V -D -e main -H hlsl.multiDescriptorSet.frag --rsb frag t0 0 0 t1 1 0 s0 0 1 s1 1 1 b0 2 0 b1 2 1 b2 2 2 > $TARGETDIR/hlsl.multiDescriptorSet.frag.out -diff -b $BASEDIR/hlsl.multiDescriptorSet.frag.out $TARGETDIR/hlsl.multiDescriptorSet.frag.out +diff -b $BASEDIR/hlsl.multiDescriptorSet.frag.out $TARGETDIR/hlsl.multiDescriptorSet.frag.out || HASERROR=1 # # Testing location error # echo Testing SPV no location $EXE -V -C spv.noLocation.vert > $TARGETDIR/spv.noLocation.vert.out -diff -b $BASEDIR/spv.noLocation.vert.out $TARGETDIR/spv.noLocation.vert.out +diff -b $BASEDIR/spv.noLocation.vert.out $TARGETDIR/spv.noLocation.vert.out || HASERROR=1 + +# +# Testing debug information +# +echo Testing SPV Debug Information +$EXE -g -H spv.debugInfo.frag > $TARGETDIR/spv.debugInfo.frag.out +diff -b $BASEDIR/spv.debugInfo.frag.out $TARGETDIR/spv.debugInfo.frag.out || HASERROR=1 # # Final checking diff --git a/Test/spv.debugInfo.frag b/Test/spv.debugInfo.frag new file mode 100644 index 00000000..e2fc257f --- /dev/null +++ b/Test/spv.debugInfo.frag @@ -0,0 +1,22 @@ +#version 450 + +struct S { + int a; +}; + +uniform ubuf { + S s; +}; + +layout(location = 0) in vec4 inv; +layout(location = 0) out vec4 outv; + +void foo(S s) +{ + outv = s.a * inv; +} + +void main() +{ + foo(s); +} \ No newline at end of file diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index d563bc2c..297edfd8 100644 --- a/glslang/MachineIndependent/ShaderLang.cpp +++ b/glslang/MachineIndependent/ShaderLang.cpp @@ -728,6 +728,11 @@ bool ProcessDeferred( intermediate.setOriginUpperLeft(); if ((messages & EShMsgHlslOffsets) || (messages & EShMsgReadHlsl)) intermediate.setHlslOffsets(); + if (messages & EShMsgDebugInfo) { + intermediate.setSourceFile(names[numPre]); + for (int s = 0; s < numStrings; ++s) + intermediate.addSourceText(strings[numPre]); + } SetupBuiltinSymbolTable(version, profile, spvVersion, source); TSymbolTable* cachedTable = SharedSymbolTables[MapVersionToIndex(version)] diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h index 8f6fdffe..56319dc6 100644 --- a/glslang/MachineIndependent/localintermediate.h +++ b/glslang/MachineIndependent/localintermediate.h @@ -451,6 +451,11 @@ public: return semanticNameSet.insert(name).first->c_str(); } + void setSourceFile(const char* file) { sourceFile = file; } + const std::string& getSourceFile() const { return sourceFile; } + void addSourceText(const char* text) { sourceText = sourceText + text; } + const std::string& getSourceText() const { return sourceText; } + const char* const implicitThisName = "@this"; protected: @@ -541,6 +546,10 @@ protected: EShTextureSamplerTransformMode textureSamplerTransformMode; + // source code of shader, useful as part of debug information + std::string sourceFile; + std::string sourceText; + private: void operator=(TIntermediate&); // prevent assignments }; diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h index 9cd999ba..2d366d1f 100644 --- a/glslang/Public/ShaderLang.h +++ b/glslang/Public/ShaderLang.h @@ -157,6 +157,7 @@ enum EShMessages { EShMsgCascadingErrors = (1 << 7), // get cascading errors; risks error-recovery issues, instead of an early exit EShMsgKeepUncalled = (1 << 8), // for testing, don't eliminate uncalled functions EShMsgHlslOffsets = (1 << 9), // allow block offsets to follow HLSL rules instead of GLSL rules + EShMsgDebugInfo = (1 << 10), // save debug information }; //