Infrastructure: Non-functional: Move to rich description of environment.
This is for input languages, client APIs, code to generate, etc.
diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp
index 6303735..2f5b953 100644
--- a/StandAlone/StandAlone.cpp
+++ b/StandAlone/StandAlone.cpp
@@ -715,9 +715,27 @@
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, 100);
+ shader->setEnvClient(glslang::EShClientVulkan, 100);
+ shader->setEnvTarget(glslang::EshTargetSpv, 0x00001000);
+ } else {
+ shader->setEnvInput((Options & EOptionReadHlsl) ? glslang::EShSourceHlsl
+ : glslang::EShSourceGlsl,
+ compUnit.stage, glslang::EShClientOpenGL, 100);
+ shader->setEnvClient(glslang::EShClientOpenGL, 450);
+ shader->setEnvTarget(glslang::EshTargetSpv, 0x00001000);
+ }
+ }
+
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) {
diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp
index f200234..517ac1d 100644
--- a/glslang/MachineIndependent/ParseHelper.cpp
+++ b/glslang/MachineIndependent/ParseHelper.cpp
@@ -50,7 +50,8 @@
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 d75119b..26f81bd 100644
--- a/glslang/MachineIndependent/ShaderLang.cpp
+++ b/glslang/MachineIndependent/ShaderLang.cpp
@@ -569,6 +569,73 @@
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<EShMessages>(messages & ~EShMsgReadHlsl);
+ break;
+ case EShSourceHlsl:
+ source = EShSourceHlsl;
+ messages = static_cast<EShMessages>(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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
[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 @@
// 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 @@
//
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 @@
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 @@
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 @@
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 800956c..a800ea6 100644
--- a/glslang/MachineIndependent/Versions.cpp
+++ b/glslang/MachineIndependent/Versions.cpp
@@ -355,9 +355,9 @@
// #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 702a5ae..63bb517 100644
--- a/glslang/MachineIndependent/Versions.h
+++ b/glslang/MachineIndependent/Versions.h
@@ -72,14 +72,19 @@
}
//
-// 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 b2a4dea..10c1b1c 100644
--- a/glslang/Public/ShaderLang.h
+++ b/glslang/Public/ShaderLang.h
@@ -110,7 +110,44 @@
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 @@
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 @@
// 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: