From a931366f569c5c9acab37efe5d5c72bdbb0519cc Mon Sep 17 00:00:00 2001 From: John Kessenich Date: Thu, 15 Jun 2017 10:40:49 -0600 Subject: [PATCH] Standalone: Implement -D and -U for preprocessor macros. Works for both GLSL and HLSL. Fixes #87. --- StandAlone/StandAlone.cpp | 73 ++++++++++++++++++++++++++++--- Test/baseResults/glsl.-D-U.frag.out | 47 ++++++++++++++++++++ Test/baseResults/hlsl.-D-U.frag.out | 65 +++++++++++++++++++++++++++ Test/glsl.-D-U.frag | 32 ++++++++++++++ Test/hlsl.-D-U.frag | 31 +++++++++++++ Test/runtests | 8 ++++ glslang/MachineIndependent/ShaderLang.cpp | 19 ++++---- 7 files changed, 262 insertions(+), 13 deletions(-) create mode 100644 Test/baseResults/glsl.-D-U.frag.out create mode 100644 Test/baseResults/hlsl.-D-U.frag.out create mode 100644 Test/glsl.-D-U.frag create mode 100644 Test/hlsl.-D-U.frag diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp index 6469943..6303735 100644 --- a/StandAlone/StandAlone.cpp +++ b/StandAlone/StandAlone.cpp @@ -157,6 +157,53 @@ 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: + TPreamble() { } + + bool isSet() const { return text.size() > 0; } + const char* get() const { return text.c_str(); } + + // #define... + void addDef(std::string def) + { + text.append("#define "); + fixLine(def); + + // The first "=" needs to turn into a space + int equal = def.find_first_of("="); + if (equal != def.npos) + def[equal] = ' '; + + text.append(def); + text.append("\n"); + } + + // #undef... + void addUndef(std::string undef) + { + text.append("#undef "); + fixLine(undef); + text.append(undef); + text.append("\n"); + } + +protected: + void fixLine(std::string& line) + { + // Can't go past a newline in the line + int end = line.find_first_of("\n"); + if (end != line.npos) + line = line.substr(0, end); + } + + std::string text; // contents of preamble +}; + +TPreamble UserPreamble; + // // Create the default name for saving a binary if -o is not provided. // @@ -391,7 +438,10 @@ void ProcessArguments(std::vector>& workItem Options |= EOptionCascadingErrors; break; case 'D': - Options |= EOptionReadHlsl; + if (argv[0][2] == 0) + Options |= EOptionReadHlsl; + else + UserPreamble.addDef(getStringOperand("-D macro name")); break; case 'E': Options |= EOptionOutputPreprocessed; @@ -412,7 +462,7 @@ void ProcessArguments(std::vector>& workItem } break; case 'I': - IncludeDirectoryList.push_back(getStringOperand("-I include path")); + IncludeDirectoryList.push_back(getStringOperand("-I include path")); break; case 'S': shaderStageName = argv[1]; @@ -422,6 +472,9 @@ void ProcessArguments(std::vector>& workItem } else Error("no specified for -S"); break; + case 'U': + UserPreamble.addUndef(getStringOperand("-U: macro name")); + break; case 'V': Options |= EOptionSpv; Options |= EOptionVulkanRules; @@ -584,7 +637,7 @@ struct ShaderCompUnit { EShLanguage stage; static const int maxCount = 1; int count; // live number of strings/names - char* text[maxCount]; // memory owned/managed externally + const char* text[maxCount]; // memory owned/managed externally std::string fileName[maxCount]; // hold's the memory, but... const char* fileNameList[maxCount]; // downstream interface wants pointers @@ -601,7 +654,7 @@ struct ShaderCompUnit { } } - void addString(std::string& ifileName, char* itext) + void addString(std::string& ifileName, const char* itext) { assert(count < maxCount); fileName[count] = ifileName; @@ -640,6 +693,8 @@ void CompileAndLinkShaderUnits(std::vector compUnits) shader->setEntryPoint(entryPointName); if (sourceEntryPointName) shader->setSourceEntryPoint(sourceEntryPointName); + if (UserPreamble.isSet()) + shader->setPreamble(UserPreamble.get()); shader->setShiftSamplerBinding(baseSamplerBinding[compUnit.stage]); shader->setShiftTextureBinding(baseTextureBinding[compUnit.stage]); @@ -806,8 +861,10 @@ void CompileAndLinkShaderFiles(glslang::TWorklist& Worklist) glslang::OS_DumpMemoryCounters(); } + // free memory from ReadFileData, which got stored in a const char* + // as the first string above for (auto it = compUnits.begin(); it != compUnits.end(); ++it) - FreeFileData(it->text[0]); + FreeFileData(const_cast(it->text[0])); } int C_DECL main(int argc, char* argv[]) @@ -966,6 +1023,9 @@ void CompileFile(const char* fileName, ShHandle compiler) EShMessages messages = EShMsgDefault; SetMessageOptions(messages); + if (UserPreamble.isSet()) + Error("-D and -U options require -l (linking)\n"); + for (int i = 0; i < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++i) { for (int j = 0; j < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++j) { // ret = ShCompile(compiler, shaderStrings, NumShaderStrings, lengths, EShOptNone, &Resources, Options, (Options & EOptionDefaultDesktop) ? 110 : 100, false, messages); @@ -1008,6 +1068,8 @@ void usage() "Options:\n" " -C cascading errors; risk crash from accumulation of error recoveries\n" " -D input is HLSL\n" + " -D\n" + " -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" @@ -1017,6 +1079,7 @@ void usage() " 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" " default file name is .spv (-o overrides this)\n" " -c configuration dump;\n" diff --git a/Test/baseResults/glsl.-D-U.frag.out b/Test/baseResults/glsl.-D-U.frag.out new file mode 100644 index 0000000..0c816e2 --- /dev/null +++ b/Test/baseResults/glsl.-D-U.frag.out @@ -0,0 +1,47 @@ +glsl.-D-U.frag +Shader version: 450 +0:? Sequence +0:7 Function Definition: main( ( global void) +0:7 Function Parameters: +0:10 Sequence +0:10 move second child to first child ( temp 4-component vector of float) +0:10 'color' (layout( location=0) out 4-component vector of float) +0:10 Constant: +0:10 1.000000 +0:10 1.000000 +0:10 1.000000 +0:10 1.000000 +0:16 Post-Increment ( temp 4-component vector of float) +0:16 'color' (layout( location=0) out 4-component vector of float) +0:24 vector scale second child into first child ( temp 4-component vector of float) +0:24 'color' (layout( location=0) out 4-component vector of float) +0:24 Constant: +0:24 3.000000 +0:? Linker Objects +0:? 'color' (layout( location=0) out 4-component vector of float) + + +Linked fragment stage: + + +Shader version: 450 +0:? Sequence +0:7 Function Definition: main( ( global void) +0:7 Function Parameters: +0:10 Sequence +0:10 move second child to first child ( temp 4-component vector of float) +0:10 'color' (layout( location=0) out 4-component vector of float) +0:10 Constant: +0:10 1.000000 +0:10 1.000000 +0:10 1.000000 +0:10 1.000000 +0:16 Post-Increment ( temp 4-component vector of float) +0:16 'color' (layout( location=0) out 4-component vector of float) +0:24 vector scale second child into first child ( temp 4-component vector of float) +0:24 'color' (layout( location=0) out 4-component vector of float) +0:24 Constant: +0:24 3.000000 +0:? Linker Objects +0:? 'color' (layout( location=0) out 4-component vector of float) + diff --git a/Test/baseResults/hlsl.-D-U.frag.out b/Test/baseResults/hlsl.-D-U.frag.out new file mode 100644 index 0000000..06842e8 --- /dev/null +++ b/Test/baseResults/hlsl.-D-U.frag.out @@ -0,0 +1,65 @@ +hlsl.-D-U.frag +Shader version: 500 +gl_FragCoord origin is upper left +0:? Sequence +0:7 Function Definition: @main( ( temp void) +0:7 Function Parameters: +0:? Sequence +0:9 move second child to first child ( temp 4-component vector of float) +0:9 'color' ( global 4-component vector of float) +0:9 Constant: +0:9 1.000000 +0:9 1.000000 +0:9 1.000000 +0:9 1.000000 +0:15 subtract second child into first child ( temp 4-component vector of float) +0:15 'color' ( global 4-component vector of float) +0:15 Constant: +0:15 5.000000 +0:21 Post-Increment ( temp 4-component vector of float) +0:21 'color' ( global 4-component vector of float) +0:29 vector scale second child into first child ( temp 4-component vector of float) +0:29 'color' ( global 4-component vector of float) +0:29 Constant: +0:29 3.000000 +0:7 Function Definition: main( ( temp void) +0:7 Function Parameters: +0:? Sequence +0:7 Function Call: @main( ( temp void) +0:? Linker Objects +0:? 'color' ( global 4-component vector of float) + + +Linked fragment stage: + + +Shader version: 500 +gl_FragCoord origin is upper left +0:? Sequence +0:7 Function Definition: @main( ( temp void) +0:7 Function Parameters: +0:? Sequence +0:9 move second child to first child ( temp 4-component vector of float) +0:9 'color' ( global 4-component vector of float) +0:9 Constant: +0:9 1.000000 +0:9 1.000000 +0:9 1.000000 +0:9 1.000000 +0:15 subtract second child into first child ( temp 4-component vector of float) +0:15 'color' ( global 4-component vector of float) +0:15 Constant: +0:15 5.000000 +0:21 Post-Increment ( temp 4-component vector of float) +0:21 'color' ( global 4-component vector of float) +0:29 vector scale second child into first child ( temp 4-component vector of float) +0:29 'color' ( global 4-component vector of float) +0:29 Constant: +0:29 3.000000 +0:7 Function Definition: main( ( temp void) +0:7 Function Parameters: +0:? Sequence +0:7 Function Call: @main( ( temp void) +0:? Linker Objects +0:? 'color' ( global 4-component vector of float) + diff --git a/Test/glsl.-D-U.frag b/Test/glsl.-D-U.frag new file mode 100644 index 0000000..4d2325f --- /dev/null +++ b/Test/glsl.-D-U.frag @@ -0,0 +1,32 @@ +#version 450 + +#define IN_SHADER + +layout(location=0) out vec4 color; + +void main() +{ +#if FOO==200 + color = vec4(1.0); +#else + #error expected FOO 200 +#endif + +#ifdef IN_SHADER + color++; +#else + #error IN_SHADER was undef +#endif + +#ifdef UNDEFED + #error UNDEFED defined +#else + color *= 3.0; +#endif + +#if MUL == 400 + color *= MUL; +#else + #error bad MUL +#endif +} diff --git a/Test/hlsl.-D-U.frag b/Test/hlsl.-D-U.frag new file mode 100644 index 0000000..0a02222 --- /dev/null +++ b/Test/hlsl.-D-U.frag @@ -0,0 +1,31 @@ + +#define IN_SHADER + +static float4 color; + +void main() +{ +#if FOO==200 + color = 1.0; +#else + #error expected FOO 200 +#endif + +#ifdef FOO + color -= 5.0; +#else + #error expected FOO 200 +#endif + +#ifdef IN_SHADER + color++; +#else + #error IN_SHADER was undef +#endif + +#ifdef UNDEFED + #error UNDEFED defined +#else + color *= 3.0; +#endif +} diff --git a/Test/runtests b/Test/runtests index 945a0f3..e42aaad 100755 --- a/Test/runtests +++ b/Test/runtests @@ -120,6 +120,14 @@ $EXE -D -e main -H -Iinc1/path1 -Iinc1/path2 hlsl.dashI.vert > $TARGETDIR/hlsl.d diff -b $BASEDIR/hlsl.dashI.vert.out $TARGETDIR/hlsl.dashI.vert.out || HASERROR=1 # +# Testing -D and -U +# +$EXE -DUNDEFED -UIN_SHADER -DFOO=200 -i -l -UUNDEFED -DMUL=FOO*2 glsl.-D-U.frag > $TARGETDIR/glsl.-D-U.frag.out +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 + +# # Final checking # if [ $HASERROR -eq 0 ] diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index bb3f4f7..d75119b 100644 --- a/glslang/MachineIndependent/ShaderLang.cpp +++ b/glslang/MachineIndependent/ShaderLang.cpp @@ -588,7 +588,7 @@ bool ProcessDeferred( const char* customPreamble, const EShOptimizationLevel optLevel, const TBuiltInResource* resources, - int defaultVersion, // use 100 for ES environment, 110 for desktop; this is the GLSL version, not SPIR-V or Vulkan + int defaultVersion, // use 100 for ES environment, 110 for desktop; this is the GLSL version, not SPIR-V or Vulkan EProfile defaultProfile, // set version/profile to defaultVersion/defaultProfile regardless of the #version // directive in the source code @@ -628,7 +628,7 @@ bool ProcessDeferred( const char** names = new const char*[numTotal]; for (int s = 0; s < numStrings; ++s) { strings[s + numPre] = shaderStrings[s]; - if (inputLengths == 0 || inputLengths[s] < 0) + if (inputLengths == nullptr || inputLengths[s] < 0) lengths[s + numPre] = strlen(shaderStrings[s]); else lengths[s + numPre] = inputLengths[s]; @@ -643,12 +643,14 @@ bool ProcessDeferred( // 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. - glslang::TInputScanner userInput(numStrings, &strings[numPre], &lengths[numPre]); // no preamble + // outlined above, just the user shader, after the system and user preambles. + glslang::TInputScanner userInput(numStrings, &strings[numPre], &lengths[numPre]); int version = 0; EProfile profile = ENoProfile; bool versionNotFirstToken = false; - bool versionNotFirst = (messages & EShMsgReadHlsl) ? true : userInput.scanVersion(version, profile, versionNotFirstToken); + bool versionNotFirst = (messages & EShMsgReadHlsl) ? + true : + userInput.scanVersion(version, profile, versionNotFirstToken); bool versionNotFound = version == 0; if (forceDefaultVersionAndProfile && (messages & EShMsgReadHlsl) == 0) { if (! (messages & EShMsgSuppressWarnings) && ! versionNotFound && @@ -675,7 +677,8 @@ bool ProcessDeferred( 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); + bool goodVersion = DeduceVersionProfile(compiler->infoSink, compiler->getLanguage(), + versionNotFirst, defaultVersion, source, version, profile, spvVersion); bool versionWillBeError = (versionNotFound || (profile == EEsProfile && version >= 300 && versionNotFirst)); bool warnVersionNotFirst = false; if (! versionWillBeError && versionNotFirstToken) { @@ -696,7 +699,7 @@ bool ProcessDeferred( if (messages & EShMsgDebugInfo) { intermediate.setSourceFile(names[numPre]); for (int s = 0; s < numStrings; ++s) - intermediate.addSourceText(strings[numPre]); + intermediate.addSourceText(strings[numPre + s]); } SetupBuiltinSymbolTable(version, profile, spvVersion, source); @@ -726,7 +729,7 @@ bool ProcessDeferred( compiler->getLanguage(), compiler->infoSink, 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 glslang::TScanContext scanContext(*parseContext); -- 2.7.4