diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp index 63037355..1e2c8f73 100644 --- a/StandAlone/StandAlone.cpp +++ b/StandAlone/StandAlone.cpp @@ -148,6 +148,10 @@ const char* sourceEntryPointName = nullptr; const char* shaderStageName = nullptr; const char* variableName = nullptr; std::vector IncludeDirectoryList; +int ClientInputSemanticsVersion = 100; // maps to, say, #define VULKAN 100 +int VulkanClientVersion = 100; // would map to, say, Vulkan 1.0 +int OpenGLClientVersion = 450; // doesn't influence anything yet, but maps to OpenGL 4.50 +unsigned int TargetVersion = 0x00001000; // maps to, say, SPIR-V 1.0 std::array baseSamplerBinding; std::array baseTextureBinding; @@ -157,7 +161,6 @@ std::array baseSsboBinding; std::array baseUavBinding; std::array, EShLangCount> baseResourceSetBinding; - // Add things like "#define ..." to a preamble to use in the beginning of the shader. class TPreamble { public: @@ -295,15 +298,15 @@ void ProcessResourceSetBindingBase(int& argc, char**& argv, std::array>& workItem ExecutableName = argv[0]; workItems.reserve(argc); + const auto bumpArg = [&]() { + if (argc > 0) { + argc--; + argv++; + } + }; + + // read a string directly attached to a single-letter option const auto getStringOperand = [&](const char* desc) { if (argv[0][2] == 0) { printf("%s must immediately follow option (no spaces)\n", desc); @@ -345,9 +356,32 @@ void ProcessArguments(std::vector>& workItem return argv[0] + 2; }; - argc--; - argv++; - for (; argc >= 1; argc--, argv++) { + // read a number attached to a single-letter option + const auto getAttachedNumber = [&](const char* desc) { + int num = atoi(argv[0] + 2); + if (num == 0) { + printf("%s: expected attached non-0 number\n", desc); + exit(EFailUsage); + } + return num; + }; + + // minimum needed (without overriding something else) to target Vulkan SPIR-V + const auto setVulkanSpv = []() { + Options |= EOptionSpv; + Options |= EOptionVulkanRules; + Options |= EOptionLinkProgram; + }; + + // minimum needed (without overriding something else) to target OpenGL SPIR-V + const auto setOpenGlSpv = []() { + Options |= EOptionSpv; + Options |= EOptionLinkProgram; + // undo a -H default to Vulkan + Options &= ~EOptionVulkanRules; + }; + + for (bumpArg(); argc >= 1; bumpArg()) { if (argv[0][0] == '-') { switch (argv[0][1]) { case '-': @@ -357,12 +391,22 @@ void ProcessArguments(std::vector>& workItem // handle --word style options if (lowerword == "auto-map-bindings" || // synonyms - lowerword == "auto-map-binding" || - lowerword == "amb") { + lowerword == "auto-map-binding" || + lowerword == "amb") { Options |= EOptionAutoMapBindings; } else if (lowerword == "auto-map-locations" || // synonyms lowerword == "aml") { Options |= EOptionAutoMapLocations; + } else if (lowerword == "client") { + if (argc > 1) { + if (strcmp(argv[1], "vulkan100") == 0) + setVulkanSpv(); + else if (strcmp(argv[1], "opengl100") == 0) + setOpenGlSpv(); + else + Error("--client expects vulkan100 or opengl100"); + } + bumpArg(); } else if (lowerword == "flatten-uniform-arrays" || // synonyms lowerword == "flatten-uniform-array" || lowerword == "fua") { @@ -412,22 +456,30 @@ void ProcessArguments(std::vector>& workItem ProcessBindingBase(argc, argv, baseSsboBinding); } else if (lowerword == "source-entrypoint" || // synonyms lowerword == "sep") { - sourceEntryPointName = argv[1]; - if (argc > 0) { - argc--; - argv++; - } else + if (argc <= 1) Error("no provided for --source-entrypoint"); + sourceEntryPointName = argv[1]; + bumpArg(); break; + } else if (lowerword == "target-env") { + if (argc > 1) { + if (strcmp(argv[1], "vulkan1.0") == 0) { + setVulkanSpv(); + VulkanClientVersion = 100; + } else if (strcmp(argv[1], "opengl") == 0) { + setOpenGlSpv(); + OpenGLClientVersion = 450; + } else + Error("--target-env expected vulkan1.0 or opengl"); + } + bumpArg(); } else if (lowerword == "variable-name" || // synonyms lowerword == "vn") { Options |= EOptionOutputHexadecimal; - variableName = argv[1]; - if (argc > 0) { - argc--; - argv++; - } else + if (argc <= 1) Error("no provided for --variable-name"); + variableName = argv[1]; + bumpArg(); break; } else { usage(); @@ -447,38 +499,34 @@ void ProcessArguments(std::vector>& workItem Options |= EOptionOutputPreprocessed; break; case 'G': - Options |= EOptionSpv; - Options |= EOptionLinkProgram; - // undo a -H default to Vulkan - Options &= ~EOptionVulkanRules; + // OpenGL Client + setOpenGlSpv(); + if (argv[0][2] != 0) + ClientInputSemanticsVersion = getAttachedNumber("-G client input semantics"); break; case 'H': Options |= EOptionHumanReadableSpv; if ((Options & EOptionSpv) == 0) { // default to Vulkan - Options |= EOptionSpv; - Options |= EOptionVulkanRules; - Options |= EOptionLinkProgram; + setVulkanSpv(); } break; case 'I': IncludeDirectoryList.push_back(getStringOperand("-I include path")); break; case 'S': - shaderStageName = argv[1]; - if (argc > 0) { - argc--; - argv++; - } else + if (argc <= 1) Error("no specified for -S"); + shaderStageName = argv[1]; + bumpArg(); break; case 'U': UserPreamble.addUndef(getStringOperand("-U: macro name")); break; case 'V': - Options |= EOptionSpv; - Options |= EOptionVulkanRules; - Options |= EOptionLinkProgram; + setVulkanSpv(); + if (argv[0][2] != 0) + ClientInputSemanticsVersion = getAttachedNumber("-G client input semantics"); break; case 'c': Options |= EOptionDumpConfig; @@ -490,11 +538,9 @@ void ProcessArguments(std::vector>& workItem // HLSL todo: entry point handle needs much more sophistication. // This is okay for one compilation unit with one entry point. entryPointName = argv[1]; - if (argc > 0) { - argc--; - argv++; - } else + if (argc <= 1) Error("no provided for -e"); + bumpArg(); break; case 'g': Options |= EOptionDebug; @@ -512,12 +558,10 @@ void ProcessArguments(std::vector>& workItem Options |= EOptionMemoryLeakMode; break; case 'o': - binaryFileName = argv[1]; - if (argc > 0) { - argc--; - argv++; - } else + if (argc <= 1) Error("no provided for -o"); + binaryFileName = argv[1]; + bumpArg(); break; case 'q': Options |= EOptionDumpReflection; @@ -715,9 +759,27 @@ void CompileAndLinkShaderUnits(std::vector compUnits) if (Options & EOptionAutoMapLocations) shader->setAutoMapLocations(true); + // Set up the environment, some subsettings take precedence over earlier + // ways of setting things. + if (Options & EOptionSpv) { + if (Options & EOptionVulkanRules) { + shader->setEnvInput((Options & EOptionReadHlsl) ? glslang::EShSourceHlsl + : glslang::EShSourceGlsl, + compUnit.stage, glslang::EShClientVulkan, ClientInputSemanticsVersion); + shader->setEnvClient(glslang::EShClientVulkan, VulkanClientVersion); + shader->setEnvTarget(glslang::EshTargetSpv, TargetVersion); + } else { + shader->setEnvInput((Options & EOptionReadHlsl) ? glslang::EShSourceHlsl + : glslang::EShSourceGlsl, + compUnit.stage, glslang::EShClientOpenGL, ClientInputSemanticsVersion); + shader->setEnvClient(glslang::EShClientOpenGL, OpenGLClientVersion); + shader->setEnvTarget(glslang::EshTargetSpv, TargetVersion); + } + } + shaders.push_back(shader); - const int defaultVersion = Options & EOptionDefaultDesktop? 110: 100; + const int defaultVersion = Options & EOptionDefaultDesktop ? 110 : 100; DirStackFileIncluder includer; std::for_each(IncludeDirectoryList.rbegin(), IncludeDirectoryList.rend(), [&includer](const std::string& dir) { @@ -1072,16 +1134,24 @@ void usage() " -D define a pre-processor macro\n" " -E print pre-processed GLSL; cannot be used with -l;\n" " errors will appear on stderr.\n" - " -G create SPIR-V binary, under OpenGL semantics; turns on -l;\n" + " -G[ver] create SPIR-V binary, under OpenGL semantics; turns on -l;\n" " default file name is .spv (-o overrides this)\n" + " 'ver', when present, is the version of the input semantics,\n" + " which will appear in #define GL_SPIRV ver\n" + " '--client opengl100' is the same as -G100\n" + " a '--target-env' for OpenGL will also imply '-G'\n" " -H print human readable form of SPIR-V; turns on -V\n" " -I add dir to the include search path; includer's directory\n" " is searched first, followed by left-to-right order of -I\n" " -S uses specified stage rather than parsing the file extension\n" " choices for are vert, tesc, tese, geom, frag, or comp\n" - " -U undefine a pre-precossor macro\n" - " -V create SPIR-V binary, under Vulkan semantics; turns on -l;\n" + " -U undefine a pre-processor macro\n" + " -V[ver] create SPIR-V binary, under Vulkan semantics; turns on -l;\n" " default file name is .spv (-o overrides this)\n" + " 'ver', when present, is the version of the input semantics,\n" + " which will appear in #define VULKAN ver\n" + " '--client vulkan100' is the same as -V100\n" + " a '--target-env' for Vulkan will also imply '-V'\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" @@ -1104,12 +1174,12 @@ void usage() " without explicit bindings.\n" " --amb synonym for --auto-map-bindings\n" " --auto-map-locations automatically locate input/output lacking\n" - " 'location'\n (fragile, not cross stage)\n" + " 'location' (fragile, not cross stage)\n" " --aml synonym for --auto-map-locations\n" + " --client {vulkan|opengl} see -V and -G\n" " --flatten-uniform-arrays flatten uniform texture/sampler arrays to\n" " scalars\n" " --fua synonym for --flatten-uniform-arrays\n" - "\n" " --hlsl-offsets Allow block offsets to follow HLSL rules\n" " Works independently of source language\n" " --hlsl-iomap Perform IO mapping in HLSL register space\n" @@ -1135,6 +1205,11 @@ void usage() " --source-entrypoint name the given shader source function is\n" " renamed to be the entry point given in -e\n" " --sep synonym for --source-entrypoint\n" + " --target-env {vulkan1.0|opengl} set the execution environment the generated\n" + " code will execute in (as opposed to language\n" + " semantics selected by --client)\n" + " default is 'vulkan1.0' under '--client vulkan'\n" + " default is 'opengl' under '--client opengl'\n" " --variable-name Creates a C header file that contains a\n" " uint32_t array named \n" " initialized with the shader binary code.\n" diff --git a/Test/runtests b/Test/runtests index e42aaadd..2caa6506 100755 --- a/Test/runtests +++ b/Test/runtests @@ -127,6 +127,16 @@ diff -b $BASEDIR/glsl.-D-U.frag.out $TARGETDIR/glsl.-D-U.frag.out || HASERROR=1 $EXE -D -e main -V -i -DUNDEFED -UIN_SHADER -DFOO=200 -UUNDEFED hlsl.-D-U.frag > $TARGETDIR/hlsl.-D-U.frag.out diff -b $BASEDIR/hlsl.-D-U.frag.out $TARGETDIR/hlsl.-D-U.frag.out || HASERROR=1 +# +# Test --client and --target-env +# +$EXE --client vulkan100 spv.targetVulkan.vert || HASERROR=1 +$EXE --client opengl100 spv.targetOpenGL.vert || HASERROR=1 +$EXE --target-env vulkan1.0 spv.targetVulkan.vert || HASERROR=1 +$EXE --target-env opengl spv.targetOpenGL.vert || HASERROR=1 +$EXE -V100 spv.targetVulkan.vert || HASERROR=1 +$EXE -G100 spv.targetOpenGL.vert || HASERROR=1 + # # Final checking # diff --git a/Test/spv.targetOpenGL.vert b/Test/spv.targetOpenGL.vert new file mode 100755 index 00000000..0920334f --- /dev/null +++ b/Test/spv.targetOpenGL.vert @@ -0,0 +1,9 @@ +#version 450 + +layout(constant_id = 3) const int a = 2; + +uniform float f; + +void main() +{ +} diff --git a/Test/spv.targetVulkan.vert b/Test/spv.targetVulkan.vert new file mode 100755 index 00000000..d879122b --- /dev/null +++ b/Test/spv.targetVulkan.vert @@ -0,0 +1,9 @@ +#version 450 + +layout(constant_id = 3) const int a = 2; + +layout(push_constant) uniform pc { float f; }; + +void main() +{ +} diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index f2002341..517ac1dd 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -50,7 +50,8 @@ namespace glslang { TParseContext::TParseContext(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins, int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TInfoSink& infoSink, bool forwardCompatible, EShMessages messages) : - TParseContextBase(symbolTable, interm, parsingBuiltins, version, profile, spvVersion, language, infoSink, forwardCompatible, messages), + TParseContextBase(symbolTable, interm, parsingBuiltins, version, profile, spvVersion, language, + infoSink, forwardCompatible, messages), inMain(false), blockName(nullptr), limits(resources.limits), diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index d75119b0..26f81bd9 100644 --- a/glslang/MachineIndependent/ShaderLang.cpp +++ b/glslang/MachineIndependent/ShaderLang.cpp @@ -569,6 +569,73 @@ bool DeduceVersionProfile(TInfoSink& infoSink, EShLanguage stage, bool versionNo return correct; } +// There are multiple paths in for setting environment stuff. +// TEnvironment takes precedence, for what it sets, so sort all this out. +// Ideally, the internal code could be made to use TEnvironment, but for +// now, translate it to the historically used parameters. +void TranslateEnvironment(const TEnvironment* environment, EShMessages& messages, EShSource& source, + EShLanguage& stage, SpvVersion& spvVersion) +{ + // Set up environmental defaults, first ignoring 'environment'. + if (messages & EShMsgSpvRules) + spvVersion.spv = 0x00010000; + if (messages & EShMsgVulkanRules) { + spvVersion.vulkan = 100; + spvVersion.vulkanGlsl = 100; + } else if (spvVersion.spv != 0) + spvVersion.openGl = 100; + + // Now, override, based on any content set in 'environment'. + // 'environment' must be cleared to ESh*None settings when items + // are not being set. + if (environment != nullptr) { + // input language + if (environment->input.languageFamily != EShSourceNone) { + stage = environment->input.stage; + switch (environment->input.dialect) { + case EShClientNone: + break; + case EShClientVulkan: + spvVersion.vulkanGlsl = environment->input.dialectVersion; + break; + case EShClientOpenGL: + spvVersion.openGl = environment->input.dialectVersion; + break; + } + switch (environment->input.languageFamily) { + case EShSourceNone: + break; + case EShSourceGlsl: + source = EShSourceGlsl; + messages = static_cast(messages & ~EShMsgReadHlsl); + break; + case EShSourceHlsl: + source = EShSourceHlsl; + messages = static_cast(messages | EShMsgReadHlsl); + break; + } + } + + // client + switch (environment->client.client) { + case EShClientVulkan: + spvVersion.vulkan = environment->client.version; + break; + default: + break; + } + + // generated code + switch (environment->target.language) { + case EshTargetSpv: + spvVersion.spv = environment->target.version; + break; + default: + break; + } + } +} + // This is the common setup and cleanup code for PreprocessDeferred and // CompileDeferred. // It takes any callable with a signature of @@ -599,8 +666,8 @@ bool ProcessDeferred( ProcessingContext& processingContext, bool requireNonempty, TShader::Includer& includer, - const std::string sourceEntryPointName = "" - ) + const std::string sourceEntryPointName = "", + const TEnvironment* environment = nullptr) // optional way of fully setting all versions, overriding the above { if (! InitThread()) return false; @@ -641,6 +708,13 @@ bool ProcessDeferred( names[s + numPre] = nullptr; } + // Get all the stages, languages, clients, and other environment + // stuff sorted out. + EShSource source = (messages & EShMsgReadHlsl) != 0 ? EShSourceHlsl : EShSourceGlsl; + SpvVersion spvVersion; + EShLanguage stage = compiler->getLanguage(); + TranslateEnvironment(environment, messages, source, stage, spvVersion); + // First, without using the preprocessor or parser, find the #version, so we know what // symbol tables, processing rules, etc. to set up. This does not need the extra strings // outlined above, just the user shader, after the system and user preambles. @@ -648,11 +722,11 @@ bool ProcessDeferred( int version = 0; EProfile profile = ENoProfile; bool versionNotFirstToken = false; - bool versionNotFirst = (messages & EShMsgReadHlsl) ? - true : - userInput.scanVersion(version, profile, versionNotFirstToken); + bool versionNotFirst = (source == EShSourceHlsl) + ? true + : userInput.scanVersion(version, profile, versionNotFirstToken); bool versionNotFound = version == 0; - if (forceDefaultVersionAndProfile && (messages & EShMsgReadHlsl) == 0) { + if (forceDefaultVersionAndProfile && source == EShSourceGlsl) { if (! (messages & EShMsgSuppressWarnings) && ! versionNotFound && (version != defaultVersion || profile != defaultProfile)) { compiler->infoSink.info << "Warning, (version, profile) forced to be (" @@ -669,15 +743,8 @@ bool ProcessDeferred( version = defaultVersion; profile = defaultProfile; } - SpvVersion spvVersion; - if (messages & EShMsgSpvRules) - spvVersion.spv = 0x00010000; // TODO: eventually have this come from the outside - EShSource source = (messages & EShMsgReadHlsl) ? EShSourceHlsl : EShSourceGlsl; - if (messages & EShMsgVulkanRules) - spvVersion.vulkan = 100; // TODO: eventually have this come from the outside - else if (spvVersion.spv != 0) - spvVersion.openGl = 100; // TODO: eventually have this come from the outside - bool goodVersion = DeduceVersionProfile(compiler->infoSink, compiler->getLanguage(), + + bool goodVersion = DeduceVersionProfile(compiler->infoSink, stage, versionNotFirst, defaultVersion, source, version, profile, spvVersion); bool versionWillBeError = (versionNotFound || (profile == EEsProfile && version >= 300 && versionNotFirst)); bool warnVersionNotFirst = false; @@ -694,7 +761,7 @@ bool ProcessDeferred( intermediate.setSpv(spvVersion); if (spvVersion.vulkan >= 100) intermediate.setOriginUpperLeft(); - if ((messages & EShMsgHlslOffsets) || (messages & EShMsgReadHlsl)) + if ((messages & EShMsgHlslOffsets) || source == EShSourceHlsl) intermediate.setHlslOffsets(); if (messages & EShMsgDebugInfo) { intermediate.setSourceFile(names[numPre]); @@ -707,7 +774,7 @@ bool ProcessDeferred( [MapSpvVersionToIndex(spvVersion)] [MapProfileToIndex(profile)] [MapSourceToIndex(source)] - [compiler->getLanguage()]; + [stage]; // Dynamically allocate the symbol table so we can control when it is deallocated WRT the pool. TSymbolTable* symbolTableMemory = new TSymbolTable; @@ -718,7 +785,7 @@ bool ProcessDeferred( // Add built-in symbols that are potentially context dependent; // they get popped again further down. if (! AddContextSpecificSymbols(resources, compiler->infoSink, symbolTable, version, profile, spvVersion, - compiler->getLanguage(), source)) + stage, source)) return false; // @@ -726,14 +793,14 @@ bool ProcessDeferred( // TParseContextBase* parseContext = CreateParseContext(symbolTable, intermediate, version, profile, source, - compiler->getLanguage(), compiler->infoSink, + stage, compiler->infoSink, spvVersion, forwardCompatible, messages, false, sourceEntryPointName); TPpContext ppContext(*parseContext, names[numPre] ? names[numPre] : "", includer); // only GLSL (bison triggered, really) needs an externally set scan context glslang::TScanContext scanContext(*parseContext); - if ((messages & EShMsgReadHlsl) == 0) + if (source == EShSourceGlsl) parseContext->setScanContext(&scanContext); parseContext->setPpContext(&ppContext); @@ -1052,14 +1119,15 @@ bool CompileDeferred( EShMessages messages, // warnings/errors/AST; things to print out TIntermediate& intermediate,// returned tree, etc. TShader::Includer& includer, - const std::string sourceEntryPointName = "") + const std::string sourceEntryPointName = "", + TEnvironment* environment = nullptr) { DoFullParse parser; return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames, preamble, optLevel, resources, defaultVersion, defaultProfile, forceDefaultVersionAndProfile, forwardCompatible, messages, intermediate, parser, - true, includer, sourceEntryPointName); + true, includer, sourceEntryPointName, environment); } } // end anonymous namespace for local functions @@ -1485,6 +1553,12 @@ TShader::TShader(EShLanguage s) infoSink = new TInfoSink; compiler = new TDeferredCompiler(stage, *infoSink); intermediate = new TIntermediate(s); + + // clear environment (avoid constructors in them for use in a C interface) + environment.input.languageFamily = EShSourceNone; + environment.input.dialect = EShClientNone; + environment.client.client = EShClientNone; + environment.target.language = EShTargetNone; } TShader::~TShader() @@ -1572,7 +1646,8 @@ bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion return CompileDeferred(compiler, strings, numStrings, lengths, stringNames, preamble, EShOptNone, builtInResources, defaultVersion, defaultProfile, forceDefaultVersionAndProfile, - forwardCompatible, messages, *intermediate, includer, sourceEntryPointName); + forwardCompatible, messages, *intermediate, includer, sourceEntryPointName, + &environment); } // Fill in a string with the result of preprocessing ShaderStrings diff --git a/glslang/MachineIndependent/Versions.cpp b/glslang/MachineIndependent/Versions.cpp index 800956c9..a800ea6b 100644 --- a/glslang/MachineIndependent/Versions.cpp +++ b/glslang/MachineIndependent/Versions.cpp @@ -355,9 +355,9 @@ void TParseVersions::getPreamble(std::string& preamble) // #define VULKAN XXXX const int numberBufSize = 12; char numberBuf[numberBufSize]; - if (spvVersion.vulkan > 0) { + if (spvVersion.vulkanGlsl > 0) { preamble += "#define VULKAN "; - snprintf(numberBuf, numberBufSize, "%d", spvVersion.vulkan); + snprintf(numberBuf, numberBufSize, "%d", spvVersion.vulkanGlsl); preamble += numberBuf; preamble += "\n"; } diff --git a/glslang/MachineIndependent/Versions.h b/glslang/MachineIndependent/Versions.h index 702a5ae5..63bb5178 100644 --- a/glslang/MachineIndependent/Versions.h +++ b/glslang/MachineIndependent/Versions.h @@ -72,14 +72,19 @@ inline const char* ProfileName(EProfile profile) } // -// SPIR-V has versions for multiple things; tie them together. -// 0 means a target or rule set is not enabled. +// What source rules, validation rules, target language, etc. are needed +// desired for SPIR-V? +// +// 0 means a target or rule set is not enabled (ignore rules from that entity). +// Non-0 means to apply semantic rules arising from that version of its rule set. +// The union of all requested rule sets will be applied. // struct SpvVersion { - SpvVersion() : spv(0), vulkan(0), openGl(0) {} - unsigned int spv; // the version of the targeted SPIR-V, as defined by SPIR-V in word 1 of the SPIR-V binary header - int vulkan; // the version of semantics for Vulkan; e.g., for GLSL from KHR_vulkan_glsl "#define VULKAN" - int openGl; // the version of semantics for OpenGL; e.g., for GLSL from KHR_vulkan_glsl "#define GL_SPIRV" + SpvVersion() : spv(0), vulkanGlsl(0), vulkan(0), openGl(0) {} + unsigned int spv; // the version of SPIR-V to target, as defined by "word 1" of the SPIR-V binary header + int vulkanGlsl; // the version of GLSL semantics for Vulkan, from GL_KHR_vulkan_glsl, for "#define VULKAN XXX" + int vulkan; // the version of Vulkan, for which SPIR-V execution environment rules to use (100 means 1.0) + int openGl; // the version of GLSL semantics for OpenGL, from GL_ARB_gl_spirv, for "#define GL_SPIRV XXX" }; // diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h index b2a4deac..10c1b1c5 100644 --- a/glslang/Public/ShaderLang.h +++ b/glslang/Public/ShaderLang.h @@ -110,7 +110,44 @@ typedef enum { EShSourceNone, EShSourceGlsl, EShSourceHlsl, -} EShSource; // if EShLanguage were EShStage, this could be EShLanguage instead +} EShSource; // if EShLanguage were EShStage, this could be EShLanguage instead + +typedef enum { + EShClientNone, + EShClientVulkan, + EShClientOpenGL, +} EShClient; + +typedef enum { + EShTargetNone, + EshTargetSpv, +} EShTargetLanguage; + +struct TInputLanguage { + EShSource languageFamily; // redundant information with other input, this one overrides when not EShSourceNone + EShLanguage stage; // redundant information with other input, this one overrides when not EShSourceNone + EShClient dialect; + int dialectVersion; // version of client's language definition, not the client (when not EShClientNone) +}; + +struct TClient { + EShClient client; + int version; // version of client itself (not the client's input dialect) +}; + +struct TTarget { + EShTargetLanguage language; + unsigned int version; // the version to target, if SPIR-V, defined by "word 1" of the SPIR-V binary header +}; + +// All source/client/target versions and settings. +// Can override previous methods of setting, when items are set here. +// Expected to grow, as more are added, rather than growing parameter lists. +struct TEnvironment { + TInputLanguage input; // definition of the input language + TClient client; // what client is the overall compilation being done for? + TTarget target; // what to generate +}; const char* StageName(EShLanguage); @@ -324,6 +361,25 @@ public: void setNoStorageFormat(bool useUnknownFormat); void setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode); + // For setting up the environment (initialized in the constructor): + void setEnvInput(EShSource lang, EShLanguage stage, EShClient client, int version) + { + environment.input.languageFamily = lang; + environment.input.stage = stage; + environment.input.dialect = client; + environment.input.dialectVersion = version; + } + void setEnvClient(EShClient client, int version) + { + environment.client.client = client; + environment.client.version = version; + } + void setEnvTarget(EShTargetLanguage lang, unsigned int version) + { + environment.target.language = lang; + environment.target.version = version; + } + // Interface to #include handlers. // // To support #include, a client of Glslang does the following: @@ -463,6 +519,8 @@ protected: // a function in the source string can be renamed FROM this TO the name given in setEntryPoint. std::string sourceEntryPointName; + TEnvironment environment; + friend class TProgram; private: