diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp index 86d18a58..a3b937ad 100644 --- a/SPIRV/GlslangToSpv.cpp +++ b/SPIRV/GlslangToSpv.cpp @@ -2543,13 +2543,18 @@ spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TType& glslangT namespace glslang { +void GetSpirvVersion(std::string& version) +{ + char buf[10]; + snprintf(buf, "0.%d", spv::Version); + version = buf; +} + // Write SPIR-V out to a binary file void OutputSpv(const std::vector& spirv, const char* baseName) { std::ofstream out; - std::string fileName(baseName); - fileName.append(".spv"); - out.open(fileName.c_str(), std::ios::binary | std::ios::out); + out.open(baseName, std::ios::binary | std::ios::out); for (int i = 0; i < (int)spirv.size(); ++i) { unsigned int word = spirv[i]; out.write((const char*)&word, 4); diff --git a/SPIRV/GlslangToSpv.h b/SPIRV/GlslangToSpv.h index 75375d81..d8a18893 100644 --- a/SPIRV/GlslangToSpv.h +++ b/SPIRV/GlslangToSpv.h @@ -36,6 +36,7 @@ namespace glslang { +void GetSpirvVersion(std::string&); void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv); void OutputSpv(const std::vector& spirv, const char* baseName); diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp index fbaf46d1..f61389c5 100644 --- a/StandAlone/StandAlone.cpp +++ b/StandAlone/StandAlone.cpp @@ -70,12 +70,13 @@ enum TOptions { EOptionDumpVersions = 0x0400, EOptionSpv = 0x0800, EOptionHumanReadableSpv = 0x1000, - EOptionDefaultDesktop = 0x2000, - EOptionOutputPreprocessed = 0x4000, + EOptionVulkanRules = 0x2000, + EOptionDefaultDesktop = 0x4000, + EOptionOutputPreprocessed = 0x8000, }; // -// Return codes from main. +// Return codes from main/exit(). // enum TFailCode { ESuccess = 0, @@ -88,18 +89,8 @@ enum TFailCode { }; // -// Just placeholders for testing purposes. The stand-alone environment -// can't actually do a full link without something specifying real -// attribute bindings. +// Forward declarations. // -ShBinding FixedAttributeBindings[] = { - { "gl_Vertex", 15 }, - { "gl_Color", 10 }, - { "gl_Normal", 7 }, -}; - -ShBindingTable FixedAttributeTable = { 3, FixedAttributeBindings }; - EShLanguage FindLanguage(const std::string& name); void CompileFile(const char* fileName, ShHandle); void usage(); @@ -112,6 +103,7 @@ bool CompileFailed = false; bool LinkFailed = false; // Use to test breaking up a single shader file into multiple strings. +// Set in ReadFileData(). int NumShaderStrings; TBuiltInResource Resources; @@ -452,7 +444,30 @@ glslang::TWorkItem** Work = 0; int NumWorkItems = 0; int Options = 0; -const char* ExecutableName; +const char* ExecutableName = nullptr; +const char* binaryFileName = nullptr; + +// +// Create the default name for saving a binary if -o is not provided. +// +const char* GetBinaryName(EShLanguage stage) +{ + const char* name; + if (binaryFileName == nullptr) { + switch (stage) { + case EShLangVertex: name = "vert.spv"; break; + case EShLangTessControl: name = "tesc.spv"; break; + case EShLangTessEvaluation: name = "tese.spv"; break; + case EShLangGeometry: name = "geom.spv"; break; + case EShLangFragment: name = "frag.spv"; break; + case EShLangCompute: name = "comp.spv"; break; + default: name = "unknown"; break; + } + } else + name = binaryFileName; + + return name; +} // // *.conf => this is a config file that can set limits/resources @@ -470,23 +485,43 @@ bool SetConfigFile(const std::string& name) return false; } -bool ProcessArguments(int argc, char* argv[]) +// +// Give error and exit with failure code. +// +void Error(const char* message) +{ + printf("%s: Error %s (use -h for usage)\n", ExecutableName, message); + exit(EFailUsage); +} + +// +// Do all command-line argument parsing. This includes building up the work-items +// to be processed later, and saving all the command-line options. +// +// Does not return (it exits) if command-line is fatally flawed. +// +void ProcessArguments(int argc, char* argv[]) { ExecutableName = argv[0]; NumWorkItems = argc; // will include some empties where the '-' options were, but it doesn't matter, they'll be 0 Work = new glslang::TWorkItem*[NumWorkItems]; - Work[0] = 0; + for (int w = 0; w < NumWorkItems; ++w) + Work[w] = 0; argc--; argv++; for (; argc >= 1; argc--, argv++) { - Work[argc] = 0; if (argv[0][0] == '-') { switch (argv[0][1]) { case 'H': Options |= EOptionHumanReadableSpv; // fall through to -V case 'V': + Options |= EOptionSpv; + Options |= EOptionVulkanRules; + Options |= EOptionLinkProgram; + break; + case 'G': Options |= EOptionSpv; Options |= EOptionLinkProgram; break; @@ -499,6 +534,9 @@ bool ProcessArguments(int argc, char* argv[]) case 'd': Options |= EOptionDefaultDesktop; break; + case 'h': + usage(); + break; case 'i': Options |= EOptionIntermediate; break; @@ -508,6 +546,14 @@ bool ProcessArguments(int argc, char* argv[]) case 'm': Options |= EOptionMemoryLeakMode; break; + case 'o': + binaryFileName = argv[1]; + if (argc > 0) { + argc--; + argv++; + } else + Error("no provided for -o"); + break; case 'q': Options |= EOptionDumpReflection; break; @@ -529,7 +575,8 @@ bool ProcessArguments(int argc, char* argv[]) Options |= EOptionSuppressWarnings; break; default: - return false; + usage(); + break; } } else { std::string name(argv[0]); @@ -540,16 +587,18 @@ bool ProcessArguments(int argc, char* argv[]) } } - // Make sure that -E is not specified alongside -V -H or -l. - if (Options & EOptionOutputPreprocessed && - ((Options & - (EOptionSpv | EOptionHumanReadableSpv | EOptionLinkProgram)))) { - return false; - } + // Make sure that -E is not specified alongside linking (which includes SPV generation) + if ((Options & EOptionOutputPreprocessed) && (Options & EOptionLinkProgram)) + Error("can't use -E when linking is selected"); - return true; + // -o makes no sense if there is no target binary + if (binaryFileName && (Options & EOptionSpv) == 0) + Error("no binary generation requested (e.g., -V)"); } +// +// Translate the meaningful subset of command-line options to parser-behavior options. +// void SetMessageOptions(EShMessages& messages) { if (Options & EOptionRelaxedErrors) @@ -558,8 +607,13 @@ void SetMessageOptions(EShMessages& messages) messages = (EShMessages)(messages | EShMsgAST); if (Options & EOptionSuppressWarnings) messages = (EShMessages)(messages | EShMsgSuppressWarnings); + if (Options & EOptionSpv) + messages = (EShMessages)(messages | EShMsgSpvRules); + if (Options & EOptionVulkanRules) + messages = (EShMessages)(messages | EShMsgVulkanRules); } +// // Thread entry point, for non-linking asynchronous mode. // // Return 0 for failure, 1 for success. @@ -658,7 +712,7 @@ void CompileAndLinkShaders() // Program-level processing... // - if (!(Options & EOptionOutputPreprocessed) && ! program.link(messages)) + if (! (Options & EOptionOutputPreprocessed) && ! program.link(messages)) LinkFailed = true; if (! (Options & EOptionSuppressInfolog)) { @@ -673,23 +727,13 @@ void CompileAndLinkShaders() if (Options & EOptionSpv) { if (CompileFailed || LinkFailed) - printf("SPIRV is not generated for failed compile or link\n"); + printf("SPIR-V is not generated for failed compile or link\n"); else { for (int stage = 0; stage < EShLangCount; ++stage) { if (program.getIntermediate((EShLanguage)stage)) { std::vector spirv; glslang::GlslangToSpv(*program.getIntermediate((EShLanguage)stage), spirv); - const char* name; - switch (stage) { - case EShLangVertex: name = "vert"; break; - case EShLangTessControl: name = "tesc"; break; - case EShLangTessEvaluation: name = "tese"; break; - case EShLangGeometry: name = "geom"; break; - case EShLangFragment: name = "frag"; break; - case EShLangCompute: name = "comp"; break; - default: name = "unknown"; break; - } - glslang::OutputSpv(spirv, name); + glslang::OutputSpv(spirv, GetBinaryName((EShLanguage)stage)); if (Options & EOptionHumanReadableSpv) { spv::Parameterize(); GLSL_STD_450::GetDebugNames(GlslStd450DebugNames); @@ -713,10 +757,7 @@ void CompileAndLinkShaders() int C_DECL main(int argc, char* argv[]) { - if (! ProcessArguments(argc, argv)) { - usage(); - return EFailUsage; - } + ProcessArguments(argc, argv); if (Options & EOptionDumpConfig) { printf("%s", DefaultConfig); @@ -727,13 +768,15 @@ int C_DECL main(int argc, char* argv[]) if (Options & EOptionDumpVersions) { printf("ESSL Version: %s\n", glslang::GetEsslVersionString()); printf("GLSL Version: %s\n", glslang::GetGlslVersionString()); + std::string spirvVersion; + glslang::GetSpirvVersion(spirvVersion); + printf("SPIR-V Version %s\n", spirvVersion.c_str()); // TODO: move to consume source-generated data if (Worklist.empty()) return ESuccess; } if (Worklist.empty()) { usage(); - return EFailUsage; } ProcessConfigFile(); @@ -835,8 +878,6 @@ void CompileFile(const char* fileName, ShHandle compiler) char** shaderStrings = ReadFileData(fileName); if (! shaderStrings) { usage(); - CompileFailed = true; - return; } int* lengths = new int[NumShaderStrings]; @@ -883,34 +924,44 @@ void usage() printf("Usage: glslangValidator [option]... [file]...\n" "\n" "Where: each 'file' ends in ., where is one of\n" - " .conf to provide an optional config file that replaces the default configuration\n" - " (see -c option below for generating a template)\n" - " .vert for a vertex shader\n" - " .tesc for a tessellation control shader\n" - " .tese for a tessellation evaluation shader\n" - " .geom for a geometry shader\n" - " .frag for a fragment shader\n" - " .comp for a compute shader\n" + " .conf to provide an optional config file that replaces the default configuration\n" + " (see -c option below for generating a template)\n" + " .vert for a vertex shader\n" + " .tesc for a tessellation control shader\n" + " .tese for a tessellation evaluation shader\n" + " .geom for a geometry shader\n" + " .frag for a fragment shader\n" + " .comp for a compute shader\n" "\n" "Compilation warnings and errors will be printed to stdout.\n" "\n" "To get other information, use one of the following options:\n" - "(Each option must be specified separately, but can go anywhere in the command line.)\n" - " -V create SPIR-V in file .spv\n" - " -H print human readable form of SPIR-V; turns on -V\n" - " -E print pre-processed GLSL; cannot be used with -V, -H, or -l.\n" - " -c configuration dump; use to create default configuration file (redirect to a .conf file)\n" - " -d default to desktop (#version 110) when there is no version in the shader (default is ES version 100)\n" - " -i intermediate tree (glslang AST) is printed out\n" - " -l link validation of all input files\n" - " -m memory leak mode\n" - " -q dump reflection query database\n" - " -r relaxed semantic error-checking mode\n" - " -s silent mode\n" - " -t multi-threaded mode\n" - " -v print version strings\n" - " -w suppress warnings (except as required by #extension : warn)\n" + "Each option must be specified separately.\n" + " -V create SPIR-V binary, under Vulkan semantics; turns on -l;\n" + " default file name is .spv (-o overrides this)\n" + " (unless -o is specified, which overrides the default file name)\n" + " -G create SPIR-V binary, under OpenGL semantics; turns on -l;\n" + " default file name is .spv (-o overrides this)\n" + " -H print human readable form of SPIR-V; turns on -V\n" + " -E print pre-processed GLSL; cannot be used with -l.\n" + " -c configuration dump;\n" + " creates the default configuration file (redirect to a .conf file)\n" + " -d default to desktop (#version 110) when there is no shader #version\n" + " (default is ES version 100)\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" + " -m memory leak mode\n" + " -o save binary into , requires a binary option (e.g., -V)\n" + " -q dump reflection query database\n" + " -r relaxed semantic error-checking mode\n" + " -s silent mode\n" + " -t multi-threaded mode\n" + " -v print version strings\n" + " -w suppress warnings (except as required by #extension : warn)\n" ); + + exit(EFailUsage); } #if !defined _MSC_VER && !defined MINGW_HAS_SECURE_API @@ -954,10 +1005,8 @@ char** ReadFileData(const char* fileName) const int maxSourceStrings = 5; // for testing splitting shader/tokens across multiple strings char** return_data = (char**)malloc(sizeof(char *) * (maxSourceStrings+1)); // freed in FreeFileData() - if (errorCode || in == nullptr) { - printf("Error: unable to open input file: %s\n", fileName); - return nullptr; - } + if (errorCode || in == nullptr) + Error("unable to open input file"); while (fgetc(in) != EOF) count++; @@ -965,15 +1014,14 @@ char** ReadFileData(const char* fileName) fseek(in, 0, SEEK_SET); char *fdata = (char*)malloc(count+2); // freed before return of this function - if (! fdata) { - printf("Error allocating memory\n"); - return nullptr; - } + if (! fdata) + Error("can't allocate memory"); + if ((int)fread(fdata, 1, count, in) != count) { - printf("Error reading input file: %s\n", fileName); free(fdata); - return nullptr; + Error("can't read input file"); } + fdata[count] = '\0'; fclose(in); diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index e71564c3..34d3fbfc 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -50,10 +50,10 @@ namespace glslang { TParseContext::TParseContext(TSymbolTable& symt, TIntermediate& interm, bool pb, int v, EProfile p, EShLanguage L, TInfoSink& is, bool fc, EShMessages m) : intermediate(interm), symbolTable(symt), infoSink(is), language(L), - version(v), profile(p), forwardCompatible(fc), messages(m), + version(v), profile(p), forwardCompatible(fc), contextPragma(true, false), loopNestingLevel(0), structNestingLevel(0), controlFlowNestingLevel(0), statementNestingLevel(0), postMainReturn(false), - tokensBeforeEOF(false), limits(resources.limits), currentScanner(0), + tokensBeforeEOF(false), limits(resources.limits), messages(m), currentScanner(0), numErrors(0), parsingBuiltins(pb), afterEOF(false), atomicUintOffsets(0), anyIndexLimits(false) { @@ -364,7 +364,7 @@ void C_DECL TParseContext::error(TSourceLoc loc, const char* szReason, const cha void C_DECL TParseContext::warn(TSourceLoc loc, const char* szReason, const char* szToken, const char* szExtraInfoFormat, ...) { - if (messages & EShMsgSuppressWarnings) + if (suppressWarnings()) return; const int maxSize = GlslangMaxTokenLength + 200; @@ -1852,7 +1852,7 @@ bool TParseContext::lineContinuationCheck(TSourceLoc loc, bool endOfComment) return lineContinuationAllowed; } - if (messages & EShMsgRelaxedErrors) { + if (relaxedErrors()) { if (! lineContinuationAllowed) warn(loc, "not allowed in this version", message, ""); return true; @@ -2347,7 +2347,7 @@ void TParseContext::precisionQualifierCheck(TSourceLoc loc, TBasicType baseType, if (baseType == EbtFloat || baseType == EbtUint || baseType == EbtInt || baseType == EbtSampler || baseType == EbtAtomicUint) { if (qualifier.precision == EpqNone) { - if (messages & EShMsgRelaxedErrors) + if (relaxedErrors()) warn(loc, "type requires declaration of default precision qualifier", TType::getBasicString(baseType), "substituting 'mediump'"); else error(loc, "type requires declaration of default precision qualifier", TType::getBasicString(baseType), ""); @@ -4265,7 +4265,7 @@ TIntermNode* TParseContext::executeInitializer(TSourceLoc loc, TIntermTyped* ini // qualifier any initializer must be a constant expression." if (symbolTable.atGlobalLevel() && initializer->getType().getQualifier().storage != EvqConst) { const char* initFeature = "non-constant global initializer"; - if (messages & EShMsgRelaxedErrors) + if (relaxedErrors()) warn(loc, "not allowed in this version", initFeature, ""); else requireProfile(loc, ~EEsProfile, initFeature); @@ -5211,7 +5211,7 @@ TIntermNode* TParseContext::addSwitch(TSourceLoc loc, TIntermTyped* expression, // "it is an error to have no statement between a label and the end of the switch statement." // The specifications were updated to remove this (being ill-defined what a "statement" was), // so, this became a warning. However, 3.0 tests still check for the error. - if (profile == EEsProfile && version <= 300 && (messages & EShMsgRelaxedErrors) == 0) + if (profile == EEsProfile && version <= 300 && ! relaxedErrors()) error(loc, "last case/default label not followed by statements", "switch", ""); else warn(loc, "last case/default label not followed by statements", "switch", ""); diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h index cac1da8c..3d933d9f 100644 --- a/glslang/MachineIndependent/ParseHelper.h +++ b/glslang/MachineIndependent/ParseHelper.h @@ -78,6 +78,12 @@ public: const char* szExtraInfoFormat, ...); void C_DECL warn(TSourceLoc, const char* szReason, const char* szToken, const char* szExtraInfoFormat, ...); + + bool relaxedErrors() const { return (messages & EShMsgRelaxedErrors) != 0; } + bool suppressWarnings() const { return (messages & EShMsgSuppressWarnings) != 0; } + bool vulkanRules() const { return (messages & EShMsgVulkanRules) != 0; } + bool spirvRules() const { return (messages & EShMsgSpvRules) != 0; } + void reservedErrorCheck(TSourceLoc, const TString&); void reservedPpErrorCheck(TSourceLoc, const char* name, const char* op); bool lineContinuationCheck(TSourceLoc, bool endOfComment); @@ -258,7 +264,6 @@ public: int version; // version, updated by #version in the shader EProfile profile; // the declared profile in the shader (core by default) bool forwardCompatible; // true if errors are to be given for use of deprecated features - EShMessages messages; // errors/warnings // Current state of parsing struct TPragma contextPragma; @@ -284,6 +289,7 @@ protected: TParseContext(TParseContext&); TParseContext& operator=(TParseContext&); + EShMessages messages; // errors/warnings/rule-sets TScanContext* scanContext; TPpContext* ppContext; TInputScanner* currentScanner; diff --git a/glslang/MachineIndependent/Scan.cpp b/glslang/MachineIndependent/Scan.cpp index 32afd713..897bec22 100644 --- a/glslang/MachineIndependent/Scan.cpp +++ b/glslang/MachineIndependent/Scan.cpp @@ -897,7 +897,7 @@ int TScanContext::tokenizeIdentifier() if (parseContext.profile == EEsProfile) reservedWord(); else if (parseContext.version < 140 && ! parseContext.symbolTable.atBuiltInLevel() && ! parseContext.extensionsTurnedOn(1, &E_GL_ARB_texture_rectangle)) { - if (parseContext.messages & EShMsgRelaxedErrors) + if (parseContext.relaxedErrors()) parseContext.requireExtensions(loc, 1, &E_GL_ARB_texture_rectangle, "texture-rectangle sampler keyword"); else reservedWord(); diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index 543e568b..a1516a01 100644 --- a/glslang/MachineIndependent/ShaderLang.cpp +++ b/glslang/MachineIndependent/ShaderLang.cpp @@ -505,7 +505,7 @@ bool ProcessDeferred( bool versionNotFirst = userInput.scanVersion(version, profile, versionNotFirstToken); bool versionNotFound = version == 0; if (forceDefaultVersionAndProfile) { - if (!(messages & EShMsgSuppressWarnings) && !versionNotFound && + if (! (messages & EShMsgSuppressWarnings) && ! versionNotFound && (version != defaultVersion || profile != defaultProfile)) { compiler->infoSink.info << "Warning, (version, profile) forced to be (" << defaultVersion << ", " << ProfileName(defaultProfile) diff --git a/glslang/MachineIndependent/Versions.cpp b/glslang/MachineIndependent/Versions.cpp index 24184f60..073b0d24 100644 --- a/glslang/MachineIndependent/Versions.cpp +++ b/glslang/MachineIndependent/Versions.cpp @@ -377,7 +377,7 @@ void TParseContext::checkDeprecated(TSourceLoc loc, int profileMask, int depVers if (version >= depVersion) { if (forwardCompatible) error(loc, "deprecated, may be removed in future release", featureDesc, ""); - else if (! (messages & EShMsgSuppressWarnings)) + else if (! suppressWarnings()) infoSink.info.message(EPrefixWarning, (TString(featureDesc) + " deprecated in version " + String(depVersion) + "; may be removed in future release").c_str(), loc); } @@ -417,7 +417,7 @@ void TParseContext::requireExtensions(TSourceLoc loc, int numExtensions, const c bool warned = false; for (int i = 0; i < numExtensions; ++i) { TExtensionBehavior behavior = getExtensionBehavior(extensions[i]); - if (behavior == EBhDisable && (messages & EShMsgRelaxedErrors)) { + if (behavior == EBhDisable && relaxedErrors()) { infoSink.info.message(EPrefixWarning, "The following extension must be enabled to use this feature:", loc); behavior = EBhWarn; } diff --git a/glslang/MachineIndependent/preprocessor/Pp.cpp b/glslang/MachineIndependent/preprocessor/Pp.cpp index 309228a3..6b8cdc0a 100644 --- a/glslang/MachineIndependent/preprocessor/Pp.cpp +++ b/glslang/MachineIndependent/preprocessor/Pp.cpp @@ -357,7 +357,7 @@ int TPpContext::extraTokenCheck(int atom, TPpToken* ppToken, int token) else label = ""; - if (parseContext.messages & EShMsgRelaxedErrors) + if (parseContext.relaxedErrors()) parseContext.warn(ppToken->loc, message, label, ""); else parseContext.error(ppToken->loc, message, label, ""); @@ -553,7 +553,7 @@ int TPpContext::evalToToken(int token, bool shortCircuit, int& res, bool& err, T if (! shortCircuit && parseContext.profile == EEsProfile) { const char* message = "undefined macro in expression not allowed in es profile"; const char* name = GetAtomString(ppToken->atom); - if (parseContext.messages & EShMsgRelaxedErrors) + if (parseContext.relaxedErrors()) parseContext.warn(ppToken->loc, message, "preprocessor evaluation", name); else parseContext.error(ppToken->loc, message, "preprocessor evaluation", name); diff --git a/glslang/MachineIndependent/preprocessor/PpScanner.cpp b/glslang/MachineIndependent/preprocessor/PpScanner.cpp index 48b0fe09..445eb305 100644 --- a/glslang/MachineIndependent/preprocessor/PpScanner.cpp +++ b/glslang/MachineIndependent/preprocessor/PpScanner.cpp @@ -204,7 +204,7 @@ int TPpContext::lFloatConst(int len, int ch, TPpToken* ppToken) } } else if (ch == 'f' || ch == 'F') { parseContext.profileRequires(ppToken->loc, EEsProfile, 300, nullptr, "floating-point suffix"); - if ((parseContext.messages & EShMsgRelaxedErrors) == 0) + if (! parseContext.relaxedErrors()) parseContext.profileRequires(ppToken->loc, ~EEsProfile, 120, nullptr, "floating-point suffix"); if (! HasDecimalOrExponent) parseContext.error(ppToken->loc, "float literal needs a decimal point or exponent", "", ""); diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h index a671221d..01a9043c 100644 --- a/glslang/Public/ShaderLang.h +++ b/glslang/Public/ShaderLang.h @@ -129,6 +129,8 @@ enum EShMessages { EShMsgRelaxedErrors = (1 << 0), // be liberal in accepting input EShMsgSuppressWarnings = (1 << 1), // suppress all warnings, except those required by the specification EShMsgAST = (1 << 2), // print the AST intermediate representation + EShMsgSpvRules = (1 << 3), // issue messages for SPIR-V generation + EShMsgVulkanRules = (1 << 4), // issue messages for Vulkan-requirements of GLSL for SPIR-V }; //