Merge pull request #948 from KhronosGroup/env-control

Compilation-environment control
This commit is contained in:
John Kessenich 2017-06-26 15:46:36 -06:00 committed by GitHub
commit a8a8320451
9 changed files with 325 additions and 83 deletions

View File

@ -148,6 +148,10 @@ const char* sourceEntryPointName = nullptr;
const char* shaderStageName = nullptr; const char* shaderStageName = nullptr;
const char* variableName = nullptr; const char* variableName = nullptr;
std::vector<std::string> IncludeDirectoryList; std::vector<std::string> 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<unsigned int, EShLangCount> baseSamplerBinding; std::array<unsigned int, EShLangCount> baseSamplerBinding;
std::array<unsigned int, EShLangCount> baseTextureBinding; std::array<unsigned int, EShLangCount> baseTextureBinding;
@ -157,7 +161,6 @@ std::array<unsigned int, EShLangCount> baseSsboBinding;
std::array<unsigned int, EShLangCount> baseUavBinding; std::array<unsigned int, EShLangCount> baseUavBinding;
std::array<std::vector<std::string>, EShLangCount> baseResourceSetBinding; std::array<std::vector<std::string>, EShLangCount> baseResourceSetBinding;
// Add things like "#define ..." to a preamble to use in the beginning of the shader. // Add things like "#define ..." to a preamble to use in the beginning of the shader.
class TPreamble { class TPreamble {
public: public:
@ -337,6 +340,14 @@ void ProcessArguments(std::vector<std::unique_ptr<glslang::TWorkItem>>& workItem
ExecutableName = argv[0]; ExecutableName = argv[0];
workItems.reserve(argc); 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) { const auto getStringOperand = [&](const char* desc) {
if (argv[0][2] == 0) { if (argv[0][2] == 0) {
printf("%s must immediately follow option (no spaces)\n", desc); printf("%s must immediately follow option (no spaces)\n", desc);
@ -345,9 +356,32 @@ void ProcessArguments(std::vector<std::unique_ptr<glslang::TWorkItem>>& workItem
return argv[0] + 2; return argv[0] + 2;
}; };
argc--; // read a number attached to a single-letter option
argv++; const auto getAttachedNumber = [&](const char* desc) {
for (; argc >= 1; argc--, argv++) { 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] == '-') { if (argv[0][0] == '-') {
switch (argv[0][1]) { switch (argv[0][1]) {
case '-': case '-':
@ -363,6 +397,16 @@ void ProcessArguments(std::vector<std::unique_ptr<glslang::TWorkItem>>& workItem
} else if (lowerword == "auto-map-locations" || // synonyms } else if (lowerword == "auto-map-locations" || // synonyms
lowerword == "aml") { lowerword == "aml") {
Options |= EOptionAutoMapLocations; 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 } else if (lowerword == "flatten-uniform-arrays" || // synonyms
lowerword == "flatten-uniform-array" || lowerword == "flatten-uniform-array" ||
lowerword == "fua") { lowerword == "fua") {
@ -412,22 +456,30 @@ void ProcessArguments(std::vector<std::unique_ptr<glslang::TWorkItem>>& workItem
ProcessBindingBase(argc, argv, baseSsboBinding); ProcessBindingBase(argc, argv, baseSsboBinding);
} else if (lowerword == "source-entrypoint" || // synonyms } else if (lowerword == "source-entrypoint" || // synonyms
lowerword == "sep") { lowerword == "sep") {
sourceEntryPointName = argv[1]; if (argc <= 1)
if (argc > 0) {
argc--;
argv++;
} else
Error("no <entry-point> provided for --source-entrypoint"); Error("no <entry-point> provided for --source-entrypoint");
sourceEntryPointName = argv[1];
bumpArg();
break; 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 } else if (lowerword == "variable-name" || // synonyms
lowerword == "vn") { lowerword == "vn") {
Options |= EOptionOutputHexadecimal; Options |= EOptionOutputHexadecimal;
variableName = argv[1]; if (argc <= 1)
if (argc > 0) {
argc--;
argv++;
} else
Error("no <C-variable-name> provided for --variable-name"); Error("no <C-variable-name> provided for --variable-name");
variableName = argv[1];
bumpArg();
break; break;
} else { } else {
usage(); usage();
@ -447,38 +499,34 @@ void ProcessArguments(std::vector<std::unique_ptr<glslang::TWorkItem>>& workItem
Options |= EOptionOutputPreprocessed; Options |= EOptionOutputPreprocessed;
break; break;
case 'G': case 'G':
Options |= EOptionSpv; // OpenGL Client
Options |= EOptionLinkProgram; setOpenGlSpv();
// undo a -H default to Vulkan if (argv[0][2] != 0)
Options &= ~EOptionVulkanRules; ClientInputSemanticsVersion = getAttachedNumber("-G<num> client input semantics");
break; break;
case 'H': case 'H':
Options |= EOptionHumanReadableSpv; Options |= EOptionHumanReadableSpv;
if ((Options & EOptionSpv) == 0) { if ((Options & EOptionSpv) == 0) {
// default to Vulkan // default to Vulkan
Options |= EOptionSpv; setVulkanSpv();
Options |= EOptionVulkanRules;
Options |= EOptionLinkProgram;
} }
break; break;
case 'I': case 'I':
IncludeDirectoryList.push_back(getStringOperand("-I<dir> include path")); IncludeDirectoryList.push_back(getStringOperand("-I<dir> include path"));
break; break;
case 'S': case 'S':
shaderStageName = argv[1]; if (argc <= 1)
if (argc > 0) {
argc--;
argv++;
} else
Error("no <stage> specified for -S"); Error("no <stage> specified for -S");
shaderStageName = argv[1];
bumpArg();
break; break;
case 'U': case 'U':
UserPreamble.addUndef(getStringOperand("-U<macro>: macro name")); UserPreamble.addUndef(getStringOperand("-U<macro>: macro name"));
break; break;
case 'V': case 'V':
Options |= EOptionSpv; setVulkanSpv();
Options |= EOptionVulkanRules; if (argv[0][2] != 0)
Options |= EOptionLinkProgram; ClientInputSemanticsVersion = getAttachedNumber("-G<num> client input semantics");
break; break;
case 'c': case 'c':
Options |= EOptionDumpConfig; Options |= EOptionDumpConfig;
@ -490,11 +538,9 @@ void ProcessArguments(std::vector<std::unique_ptr<glslang::TWorkItem>>& workItem
// HLSL todo: entry point handle needs much more sophistication. // HLSL todo: entry point handle needs much more sophistication.
// This is okay for one compilation unit with one entry point. // This is okay for one compilation unit with one entry point.
entryPointName = argv[1]; entryPointName = argv[1];
if (argc > 0) { if (argc <= 1)
argc--;
argv++;
} else
Error("no <entry-point> provided for -e"); Error("no <entry-point> provided for -e");
bumpArg();
break; break;
case 'g': case 'g':
Options |= EOptionDebug; Options |= EOptionDebug;
@ -512,12 +558,10 @@ void ProcessArguments(std::vector<std::unique_ptr<glslang::TWorkItem>>& workItem
Options |= EOptionMemoryLeakMode; Options |= EOptionMemoryLeakMode;
break; break;
case 'o': case 'o':
binaryFileName = argv[1]; if (argc <= 1)
if (argc > 0) {
argc--;
argv++;
} else
Error("no <file> provided for -o"); Error("no <file> provided for -o");
binaryFileName = argv[1];
bumpArg();
break; break;
case 'q': case 'q':
Options |= EOptionDumpReflection; Options |= EOptionDumpReflection;
@ -715,6 +759,24 @@ 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, 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); shaders.push_back(shader);
const int defaultVersion = Options & EOptionDefaultDesktop ? 110 : 100; const int defaultVersion = Options & EOptionDefaultDesktop ? 110 : 100;
@ -1072,16 +1134,24 @@ void usage()
" -D<macro> define a pre-processor macro\n" " -D<macro> define a pre-processor macro\n"
" -E print pre-processed GLSL; cannot be used with -l;\n" " -E print pre-processed GLSL; cannot be used with -l;\n"
" errors will appear on stderr.\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 <stage>.spv (-o overrides this)\n" " default file name is <stage>.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" " -H print human readable form of SPIR-V; turns on -V\n"
" -I<dir> add dir to the include search path; includer's directory\n" " -I<dir> add dir to the include search path; includer's directory\n"
" is searched first, followed by left-to-right order of -I\n" " is searched first, followed by left-to-right order of -I\n"
" -S <stage> uses specified stage rather than parsing the file extension\n" " -S <stage> uses specified stage rather than parsing the file extension\n"
" choices for <stage> are vert, tesc, tese, geom, frag, or comp\n" " choices for <stage> are vert, tesc, tese, geom, frag, or comp\n"
" -U<macro> undefine a pre-precossor macro\n" " -U<macro> undefine a pre-processor macro\n"
" -V create SPIR-V binary, under Vulkan semantics; turns on -l;\n" " -V[ver] create SPIR-V binary, under Vulkan semantics; turns on -l;\n"
" default file name is <stage>.spv (-o overrides this)\n" " default file name is <stage>.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" " -c configuration dump;\n"
" creates the default configuration file (redirect to a .conf file)\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" " -d default to desktop (#version 110) when there is no shader #version\n"
@ -1104,12 +1174,12 @@ void usage()
" without explicit bindings.\n" " without explicit bindings.\n"
" --amb synonym for --auto-map-bindings\n" " --amb synonym for --auto-map-bindings\n"
" --auto-map-locations automatically locate input/output lacking\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" " --aml synonym for --auto-map-locations\n"
" --client {vulkan<ver>|opengl<ver>} see -V and -G\n"
" --flatten-uniform-arrays flatten uniform texture/sampler arrays to\n" " --flatten-uniform-arrays flatten uniform texture/sampler arrays to\n"
" scalars\n" " scalars\n"
" --fua synonym for --flatten-uniform-arrays\n" " --fua synonym for --flatten-uniform-arrays\n"
"\n"
" --hlsl-offsets Allow block offsets to follow HLSL rules\n" " --hlsl-offsets Allow block offsets to follow HLSL rules\n"
" Works independently of source language\n" " Works independently of source language\n"
" --hlsl-iomap Perform IO mapping in HLSL register space\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" " --source-entrypoint name the given shader source function is\n"
" renamed to be the entry point given in -e\n" " renamed to be the entry point given in -e\n"
" --sep synonym for --source-entrypoint\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 <name> Creates a C header file that contains a\n" " --variable-name <name> Creates a C header file that contains a\n"
" uint32_t array named <name>\n" " uint32_t array named <name>\n"
" initialized with the shader binary code.\n" " initialized with the shader binary code.\n"

View File

@ -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 $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 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 # Final checking
# #

9
Test/spv.targetOpenGL.vert Executable file
View File

@ -0,0 +1,9 @@
#version 450
layout(constant_id = 3) const int a = 2;
uniform float f;
void main()
{
}

9
Test/spv.targetVulkan.vert Executable file
View File

@ -0,0 +1,9 @@
#version 450
layout(constant_id = 3) const int a = 2;
layout(push_constant) uniform pc { float f; };
void main()
{
}

View File

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

View File

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

View File

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

View File

@ -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"
}; };
// //

View File

@ -112,6 +112,43 @@ typedef enum {
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);
} // end namespace glslang } // end namespace glslang
@ -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: