SPV: Add OpSource shader source code and file name.

This commit is contained in:
John Kessenich 2017-05-31 17:11:16 -06:00
parent 136b1e2d5d
commit 121853f4df
12 changed files with 243 additions and 22 deletions

View File

@ -101,7 +101,7 @@ private:
// //
class TGlslangToSpvTraverser : public glslang::TIntermTraverser { class TGlslangToSpvTraverser : public glslang::TIntermTraverser {
public: public:
TGlslangToSpvTraverser(const glslang::TIntermediate*, spv::SpvBuildLogger* logger); TGlslangToSpvTraverser(const glslang::TIntermediate*, spv::SpvBuildLogger* logger, glslang::SpvOptions& options);
virtual ~TGlslangToSpvTraverser() { } virtual ~TGlslangToSpvTraverser() { }
bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate*); bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate*);
@ -179,6 +179,7 @@ protected:
spv::Id createShortCircuit(glslang::TOperator, glslang::TIntermTyped& left, glslang::TIntermTyped& right); spv::Id createShortCircuit(glslang::TOperator, glslang::TIntermTyped& left, glslang::TIntermTyped& right);
spv::Id getExtBuiltins(const char* name); spv::Id getExtBuiltins(const char* name);
glslang::SpvOptions& options;
spv::Function* shaderEntry; spv::Function* shaderEntry;
spv::Function* currentFunction; spv::Function* currentFunction;
spv::Instruction* entryPoint; spv::Instruction* entryPoint;
@ -851,8 +852,11 @@ bool HasNonLayoutQualifiers(const glslang::TType& type, const glslang::TQualifie
// Implement the TGlslangToSpvTraverser class. // Implement the TGlslangToSpvTraverser class.
// //
TGlslangToSpvTraverser::TGlslangToSpvTraverser(const glslang::TIntermediate* glslangIntermediate, spv::SpvBuildLogger* buildLogger) TGlslangToSpvTraverser::TGlslangToSpvTraverser(const glslang::TIntermediate* glslangIntermediate,
: TIntermTraverser(true, false, true), shaderEntry(nullptr), currentFunction(nullptr), spv::SpvBuildLogger* buildLogger, glslang::SpvOptions& options)
: TIntermTraverser(true, false, true),
options(options),
shaderEntry(nullptr), currentFunction(nullptr),
sequenceDepth(0), logger(buildLogger), sequenceDepth(0), logger(buildLogger),
builder((glslang::GetKhronosToolId() << 16) | GeneratorVersion, logger), builder((glslang::GetKhronosToolId() << 16) | GeneratorVersion, logger),
inEntryPoint(false), entryPointTerminated(false), linkageOnly(false), inEntryPoint(false), entryPointTerminated(false), linkageOnly(false),
@ -862,6 +866,10 @@ TGlslangToSpvTraverser::TGlslangToSpvTraverser(const glslang::TIntermediate* gls
builder.clearAccessChain(); builder.clearAccessChain();
builder.setSource(TranslateSourceLanguage(glslangIntermediate->getSource(), glslangIntermediate->getProfile()), glslangIntermediate->getVersion()); 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"); stdBuiltins = builder.import("GLSL.std.450");
builder.setMemoryModel(spv::AddressingModelLogical, spv::MemoryModelGLSL450); builder.setMemoryModel(spv::AddressingModelLogical, spv::MemoryModelGLSL450);
shaderEntry = builder.makeEntryPoint(glslangIntermediate->getEntryPointName().c_str()); shaderEntry = builder.makeEntryPoint(glslangIntermediate->getEntryPointName().c_str());
@ -5561,22 +5569,27 @@ void OutputSpvHex(const std::vector<unsigned int>& spirv, const char* baseName,
// //
// Set up the glslang traversal // 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; 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(); TIntermNode* root = intermediate.getTreeRoot();
if (root == 0) if (root == 0)
return; return;
glslang::SpvOptions defaultOptions;
if (options == nullptr)
options = &defaultOptions;
glslang::GetThreadPoolAllocator().push(); glslang::GetThreadPoolAllocator().push();
TGlslangToSpvTraverser it(&intermediate, logger); TGlslangToSpvTraverser it(&intermediate, logger, *options);
root->traverse(&it); root->traverse(&it);
it.finishSpv(); it.finishSpv();
it.dumpSpv(spirv); it.dumpSpv(spirv);

View File

@ -47,9 +47,16 @@
namespace glslang { namespace glslang {
struct SpvOptions {
SpvOptions() : generateDebugInfo(false) { }
bool generateDebugInfo;
};
void GetSpirvVersion(std::string&); 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,
void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv, spv::SpvBuildLogger* logger); 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 OutputSpvBin(const std::vector<unsigned int>& spirv, const char* baseName);
void OutputSpvHex(const std::vector<unsigned int>& spirv, const char* baseName, const char* varName); void OutputSpvHex(const std::vector<unsigned int>& spirv, const char* baseName, const char* varName);

View File

@ -59,6 +59,7 @@ namespace spv {
Builder::Builder(unsigned int magicNumber, SpvBuildLogger* buildLogger) : Builder::Builder(unsigned int magicNumber, SpvBuildLogger* buildLogger) :
source(SourceLanguageUnknown), source(SourceLanguageUnknown),
sourceVersion(0), sourceVersion(0),
sourceFileStringId(NoResult),
addressModel(AddressingModelLogical), addressModel(AddressingModelLogical),
memoryModel(MemoryModelGLSL450), memoryModel(MemoryModelGLSL450),
builderNumber(magicNumber), builderNumber(magicNumber),
@ -2411,12 +2412,8 @@ void Builder::dump(std::vector<unsigned int>& out) const
dumpInstructions(out, executionModes); dumpInstructions(out, executionModes);
// Debug instructions // Debug instructions
if (source != SourceLanguageUnknown) { dumpInstructions(out, strings);
Instruction sourceInst(0, 0, OpSource); dumpSourceInstructions(out);
sourceInst.addImmediateOperand(source);
sourceInst.addImmediateOperand(sourceVersion);
sourceInst.dump(out);
}
for (int e = 0; e < (int)sourceExtensions.size(); ++e) { for (int e = 0; e < (int)sourceExtensions.size(); ++e) {
Instruction sourceExtInst(0, 0, OpSourceExtension); Instruction sourceExtInst(0, 0, OpSourceExtension);
sourceExtInst.addStringOperand(sourceExtensions[e]); sourceExtInst.addStringOperand(sourceExtensions[e]);
@ -2574,6 +2571,48 @@ void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* els
elseBlock->addPredecessor(buildPoint); 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 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) { for (int i = 0; i < (int)instructions.size(); ++i) {

View File

@ -70,6 +70,14 @@ public:
source = lang; source = lang;
sourceVersion = version; 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 addSourceExtension(const char* ext) { sourceExtensions.push_back(ext); }
void addExtension(const char* ext) { extensions.insert(ext); } void addExtension(const char* ext) { extensions.insert(ext); }
Id import(const char*); Id import(const char*);
@ -561,10 +569,13 @@ public:
void simplifyAccessChainSwizzle(); void simplifyAccessChainSwizzle();
void createAndSetNoPredecessorBlock(const char*); void createAndSetNoPredecessorBlock(const char*);
void createSelectionMerge(Block* mergeBlock, unsigned int control); 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; void dumpInstructions(std::vector<unsigned int>&, const std::vector<std::unique_ptr<Instruction> >&) const;
SourceLanguage source; SourceLanguage source;
int sourceVersion; int sourceVersion;
spv::Id sourceFileStringId;
std::string sourceText;
std::set<std::string> extensions; std::set<std::string> extensions;
std::vector<const char*> sourceExtensions; std::vector<const char*> sourceExtensions;
AddressingModel addressModel; AddressingModel addressModel;
@ -579,6 +590,7 @@ public:
AccessChain accessChain; AccessChain accessChain;
// special blocks of instructions for output // 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> > imports;
std::vector<std::unique_ptr<Instruction> > entryPoints; std::vector<std::unique_ptr<Instruction> > entryPoints;
std::vector<std::unique_ptr<Instruction> > executionModes; std::vector<std::unique_ptr<Instruction> > executionModes;
@ -599,7 +611,7 @@ public:
// Our loop stack. // Our loop stack.
std::stack<LoopBlocks> loops; std::stack<LoopBlocks> loops;
// The stream for outputing warnings and errors. // The stream for outputting warnings and errors.
SpvBuildLogger* logger; SpvBuildLogger* logger;
}; // end Builder class }; // end Builder class

View File

@ -87,7 +87,6 @@ public:
void addImmediateOperand(unsigned int immediate) { operands.push_back(immediate); } void addImmediateOperand(unsigned int immediate) { operands.push_back(immediate); }
void addStringOperand(const char* str) void addStringOperand(const char* str)
{ {
originalString = str;
unsigned int word; unsigned int word;
char* wordString = (char*)&word; char* wordString = (char*)&word;
char* wordPtr = wordString; char* wordPtr = wordString;
@ -120,7 +119,6 @@ public:
Id getTypeId() const { return typeId; } Id getTypeId() const { return typeId; }
Id getIdOperand(int op) const { return operands[op]; } Id getIdOperand(int op) const { return operands[op]; }
unsigned int getImmediateOperand(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. // Write out the binary form.
void dump(std::vector<unsigned int>& out) const void dump(std::vector<unsigned int>& out) const
@ -151,7 +149,6 @@ protected:
Id typeId; Id typeId;
Op opCode; Op opCode;
std::vector<Id> operands; std::vector<Id> operands;
std::string originalString; // could be optimized away; convenience for getting string operand
Block* block; Block* block;
}; };

View File

@ -91,6 +91,7 @@ enum TOptions {
EOptionHlslOffsets = (1 << 23), EOptionHlslOffsets = (1 << 23),
EOptionHlslIoMapping = (1 << 24), EOptionHlslIoMapping = (1 << 24),
EOptionAutoMapLocations = (1 << 25), EOptionAutoMapLocations = (1 << 25),
EOptionDebug = (1 << 26),
}; };
// //
@ -448,6 +449,9 @@ void ProcessArguments(std::vector<std::unique_ptr<glslang::TWorkItem>>& workItem
} else } else
Error("no <entry-point> provided for -e"); Error("no <entry-point> provided for -e");
break; break;
case 'g':
Options |= EOptionDebug;
break;
case 'h': case 'h':
usage(); usage();
break; break;
@ -539,6 +543,8 @@ void SetMessageOptions(EShMessages& messages)
messages = (EShMessages)(messages | EShMsgKeepUncalled); messages = (EShMessages)(messages | EShMsgKeepUncalled);
if (Options & EOptionHlslOffsets) if (Options & EOptionHlslOffsets)
messages = (EShMessages)(messages | EShMsgHlslOffsets); 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::vector<unsigned int> spirv;
std::string warningsErrors; std::string warningsErrors;
spv::SpvBuildLogger logger; 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 // 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. // memory/perf testing, as it's not internal to programmatic use.
@ -1031,6 +1040,7 @@ void usage()
" (default is ES version 100)\n" " (default is ES version 100)\n"
" -D input is HLSL\n" " -D input is HLSL\n"
" -e specify entry-point name\n" " -e specify entry-point name\n"
" -g generate debug information\n"
" -h print this usage message\n" " -h print this usage message\n"
" -i intermediate tree (glslang AST) is printed out\n" " -i intermediate tree (glslang AST) is printed out\n"
" -l link all input files together to form a single module\n" " -l link all input files together to form a single module\n"

View File

@ -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

View File

@ -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 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 $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 # Testing location error
# #
echo Testing SPV no location echo Testing SPV no location
$EXE -V -C spv.noLocation.vert > $TARGETDIR/spv.noLocation.vert.out $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 # Final checking

22
Test/spv.debugInfo.frag Normal file
View File

@ -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);
}

View File

@ -728,6 +728,11 @@ bool ProcessDeferred(
intermediate.setOriginUpperLeft(); intermediate.setOriginUpperLeft();
if ((messages & EShMsgHlslOffsets) || (messages & EShMsgReadHlsl)) if ((messages & EShMsgHlslOffsets) || (messages & EShMsgReadHlsl))
intermediate.setHlslOffsets(); 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); SetupBuiltinSymbolTable(version, profile, spvVersion, source);
TSymbolTable* cachedTable = SharedSymbolTables[MapVersionToIndex(version)] TSymbolTable* cachedTable = SharedSymbolTables[MapVersionToIndex(version)]

View File

@ -451,6 +451,11 @@ public:
return semanticNameSet.insert(name).first->c_str(); 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"; const char* const implicitThisName = "@this";
protected: protected:
@ -541,6 +546,10 @@ protected:
EShTextureSamplerTransformMode textureSamplerTransformMode; EShTextureSamplerTransformMode textureSamplerTransformMode;
// source code of shader, useful as part of debug information
std::string sourceFile;
std::string sourceText;
private: private:
void operator=(TIntermediate&); // prevent assignments void operator=(TIntermediate&); // prevent assignments
}; };

View File

@ -157,6 +157,7 @@ enum EShMessages {
EShMsgCascadingErrors = (1 << 7), // get cascading errors; risks error-recovery issues, instead of an early exit 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 EShMsgKeepUncalled = (1 << 8), // for testing, don't eliminate uncalled functions
EShMsgHlslOffsets = (1 << 9), // allow block offsets to follow HLSL rules instead of GLSL rules EShMsgHlslOffsets = (1 << 9), // allow block offsets to follow HLSL rules instead of GLSL rules
EShMsgDebugInfo = (1 << 10), // save debug information
}; };
// //