//
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*);
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;
// 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),
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());
//
// 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);
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);
Builder::Builder(unsigned int magicNumber, SpvBuildLogger* buildLogger) :
source(SourceLanguageUnknown),
sourceVersion(0),
+ sourceFileStringId(NoResult),
addressModel(AddressingModelLogical),
memoryModel(MemoryModelGLSL450),
builderNumber(magicNumber),
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]);
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) {
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 addExtension(const char* ext) { extensions.insert(ext); }
Id import(const char*);
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;
std::set<std::string> extensions;
std::vector<const char*> sourceExtensions;
AddressingModel addressModel;
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;
// 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
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;
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
Id typeId;
Op opCode;
std::vector<Id> operands;
- std::string originalString; // could be optimized away; convenience for getting string operand
Block* block;
};
EOptionHlslOffsets = (1 << 23),
EOptionHlslIoMapping = (1 << 24),
EOptionAutoMapLocations = (1 << 25),
+ EOptionDebug = (1 << 26),
};
//
} else
Error("no <entry-point> provided for -e");
break;
+ case 'g':
+ Options |= EOptionDebug;
+ break;
case 'h':
usage();
break;
messages = (EShMessages)(messages | EShMsgKeepUncalled);
if (Options & EOptionHlslOffsets)
messages = (EShMessages)(messages | EShMsgHlslOffsets);
+ if (Options & EOptionDebug)
+ messages = (EShMessages)(messages | EShMsgDebugInfo);
}
//
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.
" (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"
--- /dev/null
+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
#
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
--- /dev/null
+#version 450\r
+\r
+struct S {\r
+ int a;\r
+};\r
+\r
+uniform ubuf {\r
+ S s;\r
+};\r
+\r
+layout(location = 0) in vec4 inv;\r
+layout(location = 0) out vec4 outv;\r
+\r
+void foo(S s)\r
+{\r
+ outv = s.a * inv;\r
+}\r
+\r
+void main()\r
+{\r
+ foo(s);\r
+}
\ No newline at end of file
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)]
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:
EShTextureSamplerTransformMode textureSamplerTransformMode;
+ // source code of shader, useful as part of debug information
+ std::string sourceFile;
+ std::string sourceText;
+
private:
void operator=(TIntermediate&); // prevent assignments
};
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
};
//