diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp index 966acef4941ca1b589fc5db26daafc979f6ecc96..e175bfc6516f055ac1080063047d9dce55588e17 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,11 @@ 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()); + builder.setEmitOpLines(); + } stdBuiltins = builder.import("GLSL.std.450"); builder.setMemoryModel(spv::AddressingModelLogical, spv::MemoryModelGLSL450); shaderEntry = builder.makeEntryPoint(glslangIntermediate->getEntryPointName().c_str()); @@ -1069,6 +1078,8 @@ void TGlslangToSpvTraverser::visitSymbol(glslang::TIntermSymbol* symbol) bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::TIntermBinary* node) { + builder.setLine(node->getLoc().line); + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); if (node->getType().getQualifier().isSpecConstant()) spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); @@ -1256,6 +1267,8 @@ bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::T bool TGlslangToSpvTraverser::visitUnary(glslang::TVisit /* visit */, glslang::TIntermUnary* node) { + builder.setLine(node->getLoc().line); + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); if (node->getType().getQualifier().isSpecConstant()) spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); @@ -1499,6 +1512,7 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt return false; case glslang::EOpFunctionCall: { + builder.setLine(node->getLoc().line); if (node->isUserDefined()) result = handleUserFunctionCall(node); // assert(result); // this can happen for bad shaders because the call graph completeness checking is not yet done @@ -1605,6 +1619,7 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt case glslang::EOpConstructStruct: case glslang::EOpConstructTextureSampler: { + builder.setLine(node->getLoc().line); std::vector<spv::Id> arguments; translateArguments(*node, arguments); spv::Id constructed; @@ -1715,6 +1730,7 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt right->traverse(this); spv::Id rightId = accessChainLoad(right->getType()); + builder.setLine(node->getLoc().line); result = createBinaryOperation(binOp, precision, TranslateNoContractionDecoration(node->getType().getQualifier()), resultType(), leftId, rightId, left->getType().getBasicType(), reduceComparison); @@ -1787,10 +1803,13 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt glslangOperands[arg]->traverse(this); if (lvalue) operands.push_back(builder.accessChainGetLValue()); - else + else { + builder.setLine(node->getLoc().line); operands.push_back(accessChainLoad(glslangOperands[arg]->getAsTyped()->getType())); + } } + builder.setLine(node->getLoc().line); if (atomic) { // Handle all atomics result = createAtomicOperation(node->getOp(), precision, resultType(), operands, node->getBasicType()); @@ -1872,6 +1891,8 @@ bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang node->getFalseBlock()->traverse(this); spv::Id falseValue = accessChainLoad(node->getTrueBlock()->getAsTyped()->getType()); + builder.setLine(node->getLoc().line); + // smear condition to vector, if necessary (AST is always scalar) if (builder.isVector(trueValue)) condition = builder.smearScalar(spv::NoPrecision, condition, @@ -2014,6 +2035,7 @@ bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIn // by a block-ending branch. But we don't want to put any other body/test // instructions in it, since the body/test may have arbitrary instructions, // including merges of its own. + builder.setLine(node->getLoc().line); builder.setBuildPoint(&blocks.head); builder.createLoopMerge(&blocks.merge, &blocks.continue_target, control); if (node->testFirst() && node->getTest()) { @@ -2022,8 +2044,7 @@ bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIn builder.setBuildPoint(&test); node->getTest()->traverse(this); - spv::Id condition = - accessChainLoad(node->getTest()->getType()); + spv::Id condition = accessChainLoad(node->getTest()->getType()); builder.createConditionalBranch(condition, &blocks.body, &blocks.merge); builder.setBuildPoint(&blocks.body); @@ -2038,6 +2059,7 @@ bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIn node->getTerminal()->traverse(this); builder.createBranch(&blocks.head); } else { + builder.setLine(node->getLoc().line); builder.createBranch(&blocks.body); breakForLoop.push(true); @@ -2072,6 +2094,8 @@ bool TGlslangToSpvTraverser::visitBranch(glslang::TVisit /* visit */, glslang::T if (node->getExpression()) node->getExpression()->traverse(this); + builder.setLine(node->getLoc().line); + switch (node->getFlowOp()) { case glslang::EOpKill: builder.makeDiscard(); @@ -3049,9 +3073,11 @@ void TGlslangToSpvTraverser::translateArguments(glslang::TIntermUnary& node, std spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermOperator* node) { - if (! node->isImage() && ! node->isTexture()) { + if (! node->isImage() && ! node->isTexture()) return spv::NoResult; - } + + builder.setLine(node->getLoc().line); + auto resultType = [&node,this]{ return convertGlslangToSpvType(node->getType()); }; // Process a GLSL texturing op (will be SPV image) @@ -5561,22 +5587,27 @@ void OutputSpvHex(const std::vector<unsigned int>& spirv, const char* baseName, // // Set up the glslang traversal // -void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv) +void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv, SpvOptions* options) { spv::SpvBuildLogger logger; - GlslangToSpv(intermediate, spirv, &logger); + GlslangToSpv(intermediate, spirv, &logger, options); } -void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv, spv::SpvBuildLogger* logger) +void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& 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 aba48a383fc05846ac6db7e4c58914dfc67ae1f1..0dad4d219fc8f317add572bd06293a82e8187c84 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<unsigned int>& spirv); -void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv, spv::SpvBuildLogger* logger); +void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv, + SpvOptions* options = nullptr); +void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv, + spv::SpvBuildLogger* logger, SpvOptions* options = nullptr); void OutputSpvBin(const std::vector<unsigned int>& spirv, const char* baseName); void OutputSpvHex(const std::vector<unsigned int>& spirv, const char* baseName, const char* varName); diff --git a/SPIRV/SpvBuilder.cpp b/SPIRV/SpvBuilder.cpp index 0add1db40d962eb133c6dcfe019a24afe01d271d..ffd17af1b72bc2cc9ad099d55e18478ad538e3b0 100644 --- a/SPIRV/SpvBuilder.cpp +++ b/SPIRV/SpvBuilder.cpp @@ -59,6 +59,9 @@ namespace spv { Builder::Builder(unsigned int magicNumber, SpvBuildLogger* buildLogger) : source(SourceLanguageUnknown), sourceVersion(0), + sourceFileStringId(NoResult), + currentLine(0), + emitOpLines(false), addressModel(AddressingModelLogical), memoryModel(MemoryModelGLSL450), builderNumber(magicNumber), @@ -84,6 +87,26 @@ Id Builder::import(const char* name) return import->getResultId(); } +// Emit an OpLine if we've been asked to emit OpLines and the line number +// has changed since the last time, and is a valid line number. +void Builder::setLine(int lineNum) +{ + if (lineNum != 0 && lineNum != currentLine) { + currentLine = lineNum; + if (emitOpLines) + addLine(sourceFileStringId, currentLine, 0); + } +} + +void Builder::addLine(Id fileName, int lineNum, int column) +{ + Instruction* line = new Instruction(OpLine); + line->addIdOperand(fileName); + line->addImmediateOperand(lineNum); + line->addImmediateOperand(column); + buildPoint->addInstruction(std::unique_ptr<Instruction>(line)); +} + // For creating new groupedTypes (will return old type if the requested one was already made). Id Builder::makeVoidType() { @@ -928,17 +951,6 @@ void Builder::addMemberName(Id id, int memberNumber, const char* string) names.push_back(std::unique_ptr<Instruction>(name)); } -void Builder::addLine(Id target, Id fileName, int lineNum, int column) -{ - Instruction* line = new Instruction(OpLine); - line->addIdOperand(target); - line->addIdOperand(fileName); - line->addImmediateOperand(lineNum); - line->addImmediateOperand(column); - - lines.push_back(std::unique_ptr<Instruction>(line)); -} - void Builder::addDecoration(Id id, Decoration decoration, int num) { if (decoration == spv::DecorationMax) @@ -2411,12 +2423,8 @@ void Builder::dump(std::vector<unsigned int>& 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 +2582,48 @@ void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* els elseBlock->addPredecessor(buildPoint); } +// OpSource +// [OpSourceContinued] +// ... +void Builder::dumpSourceInstructions(std::vector<unsigned int>& 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<unsigned int>& out, const std::vector<std::unique_ptr<Instruction> >& instructions) const { for (int i = 0; i < (int)instructions.size(); ++i) { diff --git a/SPIRV/SpvBuilder.h b/SPIRV/SpvBuilder.h index d93174ed201598fc87ddc219d30c31a5eab0a088..60d97e9c81794e91c7c5d605dce10fa93072c55e 100755 --- a/SPIRV/SpvBuilder.h +++ b/SPIRV/SpvBuilder.h @@ -70,7 +70,16 @@ 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<Instruction>(fileString)); + } + void setSourceText(const std::string& text) { sourceText = text; } void addSourceExtension(const char* ext) { sourceExtensions.push_back(ext); } + void setEmitOpLines() { emitOpLines = true; } void addExtension(const char* ext) { extensions.insert(ext); } Id import(const char*); void setMemoryModel(spv::AddressingModel addr, spv::MemoryModel mem) @@ -92,6 +101,12 @@ public: return id; } + // Log the current line, and if different than the last one, + // issue a new OpLine, using the current file name. + void setLine(int line); + // Low-level OpLine. See setLine() for a layered helper. + void addLine(Id fileName, int line, int column); + // For creating new types (will return old type if the requested one was already made). Id makeVoidType(); Id makeBoolType(); @@ -213,7 +228,6 @@ public: void addExecutionMode(Function*, ExecutionMode mode, int value1 = -1, int value2 = -1, int value3 = -1); void addName(Id, const char* name); void addMemberName(Id, int member, const char* name); - void addLine(Id target, Id fileName, int line, int column); void addDecoration(Id, Decoration, int num = -1); void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1); @@ -561,10 +575,15 @@ public: void simplifyAccessChainSwizzle(); void createAndSetNoPredecessorBlock(const char*); void createSelectionMerge(Block* mergeBlock, unsigned int control); + void dumpSourceInstructions(std::vector<unsigned int>&) const; void dumpInstructions(std::vector<unsigned int>&, const std::vector<std::unique_ptr<Instruction> >&) const; SourceLanguage source; int sourceVersion; + spv::Id sourceFileStringId; + std::string sourceText; + int currentLine; + bool emitOpLines; std::set<std::string> extensions; std::vector<const char*> sourceExtensions; AddressingModel addressModel; @@ -579,6 +598,7 @@ public: AccessChain accessChain; // special blocks of instructions for output + std::vector<std::unique_ptr<Instruction> > strings; std::vector<std::unique_ptr<Instruction> > imports; std::vector<std::unique_ptr<Instruction> > entryPoints; std::vector<std::unique_ptr<Instruction> > executionModes; @@ -599,7 +619,7 @@ public: // Our loop stack. std::stack<LoopBlocks> 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 ce8b4b8a062e64f585cd577bdbe22493777977e6..087a53f2696af34e5061f907bf799292d52e9a62 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<unsigned int>& out) const @@ -151,7 +149,6 @@ protected: Id typeId; Op opCode; std::vector<Id> 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 8a6b12dc456aa3437dcfb2b46e4edd44894d53cb..ef47662cd12f99064102e8bd4c98b442c3d122d2 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<std::unique_ptr<glslang::TWorkItem>>& workItem } else Error("no <entry-point> 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<ShaderCompUnit> compUnits) std::vector<unsigned int> 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 0000000000000000000000000000000000000000..16f605bfa29c6d9b728140c1bad0d81d44d508ff --- /dev/null +++ b/Test/baseResults/spv.debugInfo.frag.out @@ -0,0 +1,272 @@ +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 126 + + Capability Shader + 2: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Fragment 5 "main" 24 52 + ExecutionMode 5 OriginUpperLeft + 1: String "spv.debugInfo.frag" + Source GLSL 450 1 "#version 450 + +struct S { + int a; +}; + +uniform ubuf { + S s; +}; + +uniform sampler2D s2d; + +layout(location = 0) in vec4 inv; +layout(location = 0) out vec4 outv; + +vec4 foo(S s) +{ + vec4 r = s.a * inv; + ++r; + if (r.x > 3.0) + --r; + else + r *= 2; + + return r; +} + +void main() +{ + outv = foo(s); + outv += texture(s2d, vec2(0.5)); + + switch (s.a) { + case 10: + ++outv; + break; + case 20: + outv = 2 * outv; + ++outv; + break; + default: + --outv; + break; + } + + for (int i = 0; i < 10; ++i) + outv *= 3.0; + + outv.x < 10.0 ? + outv = sin(outv) : + outv = cos(outv); +}" + Name 5 "main" + Name 8 "S" + MemberName 8(S) 0 "a" + Name 14 "foo(struct-S-i11;" + Name 13 "s" + Name 17 "r" + Name 24 "inv" + Name 52 "outv" + Name 53 "S" + MemberName 53(S) 0 "a" + Name 54 "ubuf" + MemberName 54(ubuf) 0 "s" + Name 56 "" + Name 57 "S" + MemberName 57(S) 0 "a" + Name 59 "param" + Name 69 "s2d" + Name 99 "i" + Decorate 24(inv) Location 0 + Decorate 52(outv) Location 0 + MemberDecorate 53(S) 0 Offset 0 + MemberDecorate 54(ubuf) 0 Offset 0 + Decorate 54(ubuf) Block + Decorate 56 DescriptorSet 0 + Decorate 69(s2d) DescriptorSet 0 + 3: TypeVoid + 4: TypeFunction 3 + 7: TypeInt 32 1 + 8(S): TypeStruct 7(int) + 9: TypePointer Function 8(S) + 10: TypeFloat 32 + 11: TypeVector 10(float) 4 + 12: TypeFunction 11(fvec4) 9(ptr) + 16: TypePointer Function 11(fvec4) + 18: 7(int) Constant 0 + 19: TypePointer Function 7(int) + 23: TypePointer Input 11(fvec4) + 24(inv): 23(ptr) Variable Input + 28: 10(float) Constant 1065353216 + 31: TypeInt 32 0 + 32: 31(int) Constant 0 + 33: TypePointer Function 10(float) + 36: 10(float) Constant 1077936128 + 37: TypeBool + 45: 10(float) Constant 1073741824 + 51: TypePointer Output 11(fvec4) + 52(outv): 51(ptr) Variable Output + 53(S): TypeStruct 7(int) + 54(ubuf): TypeStruct 53(S) + 55: TypePointer Uniform 54(ubuf) + 56: 55(ptr) Variable Uniform + 57(S): TypeStruct 7(int) + 58: TypePointer Function 57(S) + 60: TypePointer Uniform 53(S) + 66: TypeImage 10(float) 2D sampled format:Unknown + 67: TypeSampledImage 66 + 68: TypePointer UniformConstant 67 + 69(s2d): 68(ptr) Variable UniformConstant + 71: TypeVector 10(float) 2 + 72: 10(float) Constant 1056964608 + 73: 71(fvec2) ConstantComposite 72 72 + 77: TypePointer Uniform 7(int) + 106: 7(int) Constant 10 + 111: 7(int) Constant 1 + 114: TypePointer Output 10(float) + 117: 10(float) Constant 1092616192 + 5(main): 3 Function None 4 + 6: Label + 59(param): 58(ptr) Variable Function + 99(i): 19(ptr) Variable Function + 113: 16(ptr) Variable Function + Line 1 30 0 + 61: 60(ptr) AccessChain 56 18 + 62: 53(S) Load 61 + 63: 7(int) CompositeExtract 62 0 + 64: 19(ptr) AccessChain 59(param) 18 + Store 64 63 + 65: 11(fvec4) FunctionCall 14(foo(struct-S-i11;) 59(param) + Store 52(outv) 65 + Line 1 31 0 + 70: 67 Load 69(s2d) + 74: 11(fvec4) ImageSampleImplicitLod 70 73 + 75: 11(fvec4) Load 52(outv) + 76: 11(fvec4) FAdd 75 74 + Store 52(outv) 76 + Line 1 33 0 + 78: 77(ptr) AccessChain 56 18 18 + 79: 7(int) Load 78 + SelectionMerge 83 None + Switch 79 82 + case 10: 80 + case 20: 81 + 82: Label + Line 1 42 0 + 94: 11(fvec4) Load 52(outv) + 95: 11(fvec4) CompositeConstruct 28 28 28 28 + 96: 11(fvec4) FSub 94 95 + Store 52(outv) 96 + Line 1 43 0 + Branch 83 + 80: Label + Line 1 35 0 + 84: 11(fvec4) Load 52(outv) + 85: 11(fvec4) CompositeConstruct 28 28 28 28 + 86: 11(fvec4) FAdd 84 85 + Store 52(outv) 86 + Line 1 36 0 + Branch 83 + 81: Label + Line 1 38 0 + 88: 11(fvec4) Load 52(outv) + 89: 11(fvec4) VectorTimesScalar 88 45 + Store 52(outv) 89 + Line 1 39 0 + 90: 11(fvec4) Load 52(outv) + 91: 11(fvec4) CompositeConstruct 28 28 28 28 + 92: 11(fvec4) FAdd 90 91 + Store 52(outv) 92 + Line 1 40 0 + Branch 83 + 83: Label + Line 1 46 0 + Store 99(i) 18 + Branch 100 + 100: Label + LoopMerge 102 103 None + Branch 104 + 104: Label + 105: 7(int) Load 99(i) + 107: 37(bool) SLessThan 105 106 + BranchConditional 107 101 102 + 101: Label + Line 1 47 0 + 108: 11(fvec4) Load 52(outv) + 109: 11(fvec4) VectorTimesScalar 108 36 + Store 52(outv) 109 + Branch 103 + 103: Label + Line 1 46 0 + 110: 7(int) Load 99(i) + 112: 7(int) IAdd 110 111 + Store 99(i) 112 + Branch 100 + 102: Label + Line 1 49 0 + 115: 114(ptr) AccessChain 52(outv) 32 + 116: 10(float) Load 115 + 118: 37(bool) FOrdLessThan 116 117 + SelectionMerge 120 None + BranchConditional 118 119 123 + 119: Label + Line 1 50 0 + 121: 11(fvec4) Load 52(outv) + 122: 11(fvec4) ExtInst 2(GLSL.std.450) 13(Sin) 121 + Store 52(outv) 122 + Store 113 122 + Branch 120 + 123: Label + Line 1 51 0 + 124: 11(fvec4) Load 52(outv) + 125: 11(fvec4) ExtInst 2(GLSL.std.450) 14(Cos) 124 + Store 52(outv) 125 + Store 113 125 + Branch 120 + 120: Label + Return + FunctionEnd +14(foo(struct-S-i11;): 11(fvec4) Function None 12 + 13(s): 9(ptr) FunctionParameter + 15: Label + 17(r): 16(ptr) Variable Function + Line 1 18 0 + 20: 19(ptr) AccessChain 13(s) 18 + 21: 7(int) Load 20 + 22: 10(float) ConvertSToF 21 + 25: 11(fvec4) Load 24(inv) + 26: 11(fvec4) VectorTimesScalar 25 22 + Store 17(r) 26 + Line 1 19 0 + 27: 11(fvec4) Load 17(r) + 29: 11(fvec4) CompositeConstruct 28 28 28 28 + 30: 11(fvec4) FAdd 27 29 + Store 17(r) 30 + Line 1 20 0 + 34: 33(ptr) AccessChain 17(r) 32 + 35: 10(float) Load 34 + 38: 37(bool) FOrdGreaterThan 35 36 + SelectionMerge 40 None + BranchConditional 38 39 44 + 39: Label + Line 1 21 0 + 41: 11(fvec4) Load 17(r) + 42: 11(fvec4) CompositeConstruct 28 28 28 28 + 43: 11(fvec4) FSub 41 42 + Store 17(r) 43 + Branch 40 + 44: Label + Line 1 23 0 + 46: 11(fvec4) Load 17(r) + 47: 11(fvec4) VectorTimesScalar 46 45 + Store 17(r) 47 + Branch 40 + 40: Label + Line 1 25 0 + 48: 11(fvec4) Load 17(r) + ReturnValue 48 + FunctionEnd diff --git a/Test/runtests b/Test/runtests index a3c89e045b6524a9eb0097e6a621cc61096029b1..ac117fb1d1e91e367416f193942a1b5b4c460833 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 0000000000000000000000000000000000000000..3b6cd27fead7be0d65f1758e3025a19103e5ed81 --- /dev/null +++ b/Test/spv.debugInfo.frag @@ -0,0 +1,52 @@ +#version 450 + +struct S { + int a; +}; + +uniform ubuf { + S s; +}; + +uniform sampler2D s2d; + +layout(location = 0) in vec4 inv; +layout(location = 0) out vec4 outv; + +vec4 foo(S s) +{ + vec4 r = s.a * inv; + ++r; + if (r.x > 3.0) + --r; + else + r *= 2; + + return r; +} + +void main() +{ + outv = foo(s); + outv += texture(s2d, vec2(0.5)); + + switch (s.a) { + case 10: + ++outv; + break; + case 20: + outv = 2 * outv; + ++outv; + break; + default: + --outv; + break; + } + + for (int i = 0; i < 10; ++i) + outv *= 3.0; + + outv.x < 10.0 ? + outv = sin(outv) : + outv = cos(outv); +} \ No newline at end of file diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index d563bc2cdb0b5ddb5167ed1a2ab39c7a155373fe..297edfd8841bc212007b01914ceab0d70126543f 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 8f6fdffe4486ecbc7591ca693e538fd427acee75..56319dc6997aaa9bbd0f17fc2a146e5b50cab92f 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 9cd999ba29ef440db8a8de4b3249903bc31f8c66..2d366d1f6b7e8c69855cf321aee9b5ba239f72aa 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 }; //