Infrastructure: Non-functional: Move to rich description of environment.

This is for input languages, client APIs, code to generate, etc.
This commit is contained in:
John Kessenich 2017-06-23 10:50:22 -06:00
Родитель 4fbb8cb45e
Коммит 4be4aebdcd
6 изменённых файлов: 191 добавлений и 34 удалений

Просмотреть файл

@ -715,9 +715,27 @@ void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits)
if (Options & EOptionAutoMapLocations) if (Options & EOptionAutoMapLocations)
shader->setAutoMapLocations(true); 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); shaders.push_back(shader);
const int defaultVersion = Options & EOptionDefaultDesktop? 110: 100; const int defaultVersion = Options & EOptionDefaultDesktop ? 110 : 100;
DirStackFileIncluder includer; DirStackFileIncluder includer;
std::for_each(IncludeDirectoryList.rbegin(), IncludeDirectoryList.rend(), [&includer](const std::string& dir) { std::for_each(IncludeDirectoryList.rbegin(), IncludeDirectoryList.rend(), [&includer](const std::string& dir) {

Просмотреть файл

@ -50,7 +50,8 @@ namespace glslang {
TParseContext::TParseContext(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins, TParseContext::TParseContext(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins,
int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language,
TInfoSink& infoSink, bool forwardCompatible, EShMessages messages) : 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), inMain(false),
blockName(nullptr), blockName(nullptr),
limits(resources.limits), limits(resources.limits),

Просмотреть файл

@ -569,6 +569,73 @@ bool DeduceVersionProfile(TInfoSink& infoSink, EShLanguage stage, bool versionNo
return correct; 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 // This is the common setup and cleanup code for PreprocessDeferred and
// CompileDeferred. // CompileDeferred.
// It takes any callable with a signature of // It takes any callable with a signature of
@ -599,8 +666,8 @@ bool ProcessDeferred(
ProcessingContext& processingContext, ProcessingContext& processingContext,
bool requireNonempty, bool requireNonempty,
TShader::Includer& includer, 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()) if (! InitThread())
return false; return false;
@ -641,6 +708,13 @@ bool ProcessDeferred(
names[s + numPre] = nullptr; 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 // 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 // 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. // outlined above, just the user shader, after the system and user preambles.
@ -648,11 +722,11 @@ bool ProcessDeferred(
int version = 0; int version = 0;
EProfile profile = ENoProfile; EProfile profile = ENoProfile;
bool versionNotFirstToken = false; bool versionNotFirstToken = false;
bool versionNotFirst = (messages & EShMsgReadHlsl) ? bool versionNotFirst = (source == EShSourceHlsl)
true : ? true
userInput.scanVersion(version, profile, versionNotFirstToken); : userInput.scanVersion(version, profile, versionNotFirstToken);
bool versionNotFound = version == 0; bool versionNotFound = version == 0;
if (forceDefaultVersionAndProfile && (messages & EShMsgReadHlsl) == 0) { if (forceDefaultVersionAndProfile && source == EShSourceGlsl) {
if (! (messages & EShMsgSuppressWarnings) && ! versionNotFound && if (! (messages & EShMsgSuppressWarnings) && ! versionNotFound &&
(version != defaultVersion || profile != defaultProfile)) { (version != defaultVersion || profile != defaultProfile)) {
compiler->infoSink.info << "Warning, (version, profile) forced to be (" compiler->infoSink.info << "Warning, (version, profile) forced to be ("
@ -669,15 +743,8 @@ bool ProcessDeferred(
version = defaultVersion; version = defaultVersion;
profile = defaultProfile; profile = defaultProfile;
} }
SpvVersion spvVersion;
if (messages & EShMsgSpvRules) bool goodVersion = DeduceVersionProfile(compiler->infoSink, stage,
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(),
versionNotFirst, defaultVersion, source, version, profile, spvVersion); versionNotFirst, defaultVersion, source, version, profile, spvVersion);
bool versionWillBeError = (versionNotFound || (profile == EEsProfile && version >= 300 && versionNotFirst)); bool versionWillBeError = (versionNotFound || (profile == EEsProfile && version >= 300 && versionNotFirst));
bool warnVersionNotFirst = false; bool warnVersionNotFirst = false;
@ -694,7 +761,7 @@ bool ProcessDeferred(
intermediate.setSpv(spvVersion); intermediate.setSpv(spvVersion);
if (spvVersion.vulkan >= 100) if (spvVersion.vulkan >= 100)
intermediate.setOriginUpperLeft(); intermediate.setOriginUpperLeft();
if ((messages & EShMsgHlslOffsets) || (messages & EShMsgReadHlsl)) if ((messages & EShMsgHlslOffsets) || source == EShSourceHlsl)
intermediate.setHlslOffsets(); intermediate.setHlslOffsets();
if (messages & EShMsgDebugInfo) { if (messages & EShMsgDebugInfo) {
intermediate.setSourceFile(names[numPre]); intermediate.setSourceFile(names[numPre]);
@ -707,7 +774,7 @@ bool ProcessDeferred(
[MapSpvVersionToIndex(spvVersion)] [MapSpvVersionToIndex(spvVersion)]
[MapProfileToIndex(profile)] [MapProfileToIndex(profile)]
[MapSourceToIndex(source)] [MapSourceToIndex(source)]
[compiler->getLanguage()]; [stage];
// Dynamically allocate the symbol table so we can control when it is deallocated WRT the pool. // Dynamically allocate the symbol table so we can control when it is deallocated WRT the pool.
TSymbolTable* symbolTableMemory = new TSymbolTable; TSymbolTable* symbolTableMemory = new TSymbolTable;
@ -718,7 +785,7 @@ bool ProcessDeferred(
// Add built-in symbols that are potentially context dependent; // Add built-in symbols that are potentially context dependent;
// they get popped again further down. // they get popped again further down.
if (! AddContextSpecificSymbols(resources, compiler->infoSink, symbolTable, version, profile, spvVersion, if (! AddContextSpecificSymbols(resources, compiler->infoSink, symbolTable, version, profile, spvVersion,
compiler->getLanguage(), source)) stage, source))
return false; return false;
// //
@ -726,14 +793,14 @@ bool ProcessDeferred(
// //
TParseContextBase* parseContext = CreateParseContext(symbolTable, intermediate, version, profile, source, TParseContextBase* parseContext = CreateParseContext(symbolTable, intermediate, version, profile, source,
compiler->getLanguage(), compiler->infoSink, stage, compiler->infoSink,
spvVersion, forwardCompatible, messages, false, sourceEntryPointName); spvVersion, forwardCompatible, messages, false, sourceEntryPointName);
TPpContext ppContext(*parseContext, names[numPre] ? names[numPre] : "", includer); TPpContext ppContext(*parseContext, names[numPre] ? names[numPre] : "", includer);
// only GLSL (bison triggered, really) needs an externally set scan context // only GLSL (bison triggered, really) needs an externally set scan context
glslang::TScanContext scanContext(*parseContext); glslang::TScanContext scanContext(*parseContext);
if ((messages & EShMsgReadHlsl) == 0) if (source == EShSourceGlsl)
parseContext->setScanContext(&scanContext); parseContext->setScanContext(&scanContext);
parseContext->setPpContext(&ppContext); parseContext->setPpContext(&ppContext);
@ -1052,14 +1119,15 @@ bool CompileDeferred(
EShMessages messages, // warnings/errors/AST; things to print out EShMessages messages, // warnings/errors/AST; things to print out
TIntermediate& intermediate,// returned tree, etc. TIntermediate& intermediate,// returned tree, etc.
TShader::Includer& includer, TShader::Includer& includer,
const std::string sourceEntryPointName = "") const std::string sourceEntryPointName = "",
TEnvironment* environment = nullptr)
{ {
DoFullParse parser; DoFullParse parser;
return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames, return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames,
preamble, optLevel, resources, defaultVersion, preamble, optLevel, resources, defaultVersion,
defaultProfile, forceDefaultVersionAndProfile, defaultProfile, forceDefaultVersionAndProfile,
forwardCompatible, messages, intermediate, parser, forwardCompatible, messages, intermediate, parser,
true, includer, sourceEntryPointName); true, includer, sourceEntryPointName, environment);
} }
} // end anonymous namespace for local functions } // end anonymous namespace for local functions
@ -1485,6 +1553,12 @@ TShader::TShader(EShLanguage s)
infoSink = new TInfoSink; infoSink = new TInfoSink;
compiler = new TDeferredCompiler(stage, *infoSink); compiler = new TDeferredCompiler(stage, *infoSink);
intermediate = new TIntermediate(s); 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() TShader::~TShader()
@ -1572,7 +1646,8 @@ bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion
return CompileDeferred(compiler, strings, numStrings, lengths, stringNames, return CompileDeferred(compiler, strings, numStrings, lengths, stringNames,
preamble, EShOptNone, builtInResources, defaultVersion, preamble, EShOptNone, builtInResources, defaultVersion,
defaultProfile, forceDefaultVersionAndProfile, defaultProfile, forceDefaultVersionAndProfile,
forwardCompatible, messages, *intermediate, includer, sourceEntryPointName); forwardCompatible, messages, *intermediate, includer, sourceEntryPointName,
&environment);
} }
// Fill in a string with the result of preprocessing ShaderStrings // Fill in a string with the result of preprocessing ShaderStrings

Просмотреть файл

@ -355,9 +355,9 @@ void TParseVersions::getPreamble(std::string& preamble)
// #define VULKAN XXXX // #define VULKAN XXXX
const int numberBufSize = 12; const int numberBufSize = 12;
char numberBuf[numberBufSize]; char numberBuf[numberBufSize];
if (spvVersion.vulkan > 0) { if (spvVersion.vulkanGlsl > 0) {
preamble += "#define VULKAN "; preamble += "#define VULKAN ";
snprintf(numberBuf, numberBufSize, "%d", spvVersion.vulkan); snprintf(numberBuf, numberBufSize, "%d", spvVersion.vulkanGlsl);
preamble += numberBuf; preamble += numberBuf;
preamble += "\n"; preamble += "\n";
} }

Просмотреть файл

@ -72,14 +72,19 @@ inline const char* ProfileName(EProfile profile)
} }
// //
// SPIR-V has versions for multiple things; tie them together. // What source rules, validation rules, target language, etc. are needed
// 0 means a target or rule set is not enabled. // 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 { struct SpvVersion {
SpvVersion() : spv(0), vulkan(0), openGl(0) {} SpvVersion() : spv(0), vulkanGlsl(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 unsigned int spv; // the version of SPIR-V to target, as defined by "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 vulkanGlsl; // the version of GLSL semantics for Vulkan, from GL_KHR_vulkan_glsl, for "#define VULKAN XXX"
int openGl; // the version of semantics for OpenGL; e.g., for GLSL from KHR_vulkan_glsl "#define GL_SPIRV" 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"
}; };
// //

Просмотреть файл

@ -110,7 +110,44 @@ typedef enum {
EShSourceNone, EShSourceNone,
EShSourceGlsl, EShSourceGlsl,
EShSourceHlsl, 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); const char* StageName(EShLanguage);
@ -324,6 +361,25 @@ public:
void setNoStorageFormat(bool useUnknownFormat); void setNoStorageFormat(bool useUnknownFormat);
void setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode); 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. // Interface to #include handlers.
// //
// To support #include, a client of Glslang does the following: // 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. // a function in the source string can be renamed FROM this TO the name given in setEntryPoint.
std::string sourceEntryPointName; std::string sourceEntryPointName;
TEnvironment environment;
friend class TProgram; friend class TProgram;
private: private: