| // |
| //Copyright (C) 2002-2005 3Dlabs Inc. Ltd. |
| //Copyright (C) 2013 LunarG, Inc. |
| // |
| //All rights reserved. |
| // |
| //Redistribution and use in source and binary forms, with or without |
| //modification, are permitted provided that the following conditions |
| //are met: |
| // |
| // Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // |
| // Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following |
| // disclaimer in the documentation and/or other materials provided |
| // with the distribution. |
| // |
| // Neither the name of 3Dlabs Inc. Ltd. nor the names of its |
| // contributors may be used to endorse or promote products derived |
| // from this software without specific prior written permission. |
| // |
| //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| //"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| //LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| //FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| //COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| //INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| //BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| //LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| //CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| //LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| //ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| //POSSIBILITY OF SUCH DAMAGE. |
| // |
| |
| // |
| // Implement the top-level of interface to the compiler/linker, |
| // as defined in ShaderLang.h |
| // This is the platform independent interface between an OGL driver |
| // and the shading language compiler/linker. |
| // |
| #include <string.h> |
| #include <iostream> |
| #include <sstream> |
| #include "SymbolTable.h" |
| #include "ParseHelper.h" |
| #include "Scan.h" |
| #include "ScanContext.h" |
| |
| #include "../Include/ShHandle.h" |
| #include "../../OGLCompilersDLL/InitializeDll.h" |
| |
| #include "preprocessor/PpContext.h" |
| |
| #define SH_EXPORTING |
| #include "../Public/ShaderLang.h" |
| #include "reflection.h" |
| #include "Initialize.h" |
| |
| namespace { // anonymous namespace for file-local functions and symbols |
| |
| using namespace glslang; |
| |
| // Local mapping functions for making arrays of symbol tables.... |
| |
| int MapVersionToIndex(int version) |
| { |
| switch (version) { |
| case 100: return 0; |
| case 110: return 1; |
| case 120: return 2; |
| case 130: return 3; |
| case 140: return 4; |
| case 150: return 5; |
| case 300: return 6; |
| case 330: return 7; |
| case 400: return 8; |
| case 410: return 9; |
| case 420: return 10; |
| case 430: return 11; |
| case 440: return 12; |
| case 310: return 13; |
| case 450: return 14; |
| default: // | |
| return 0; // | |
| } // | |
| } // V |
| const int VersionCount = 15; // number of case statements above |
| |
| int MapProfileToIndex(EProfile profile) |
| { |
| switch (profile) { |
| case ENoProfile: return 0; |
| case ECoreProfile: return 1; |
| case ECompatibilityProfile: return 2; |
| case EEsProfile: return 3; |
| default: // | |
| return 0; // | |
| } // | |
| } // V |
| const int ProfileCount = 4; // number of case statements above |
| |
| // only one of these needed for non-ES; ES needs 2 for different precision defaults of built-ins |
| enum EPrecisionClass { |
| EPcGeneral, |
| EPcFragment, |
| EPcCount |
| }; |
| |
| // A process-global symbol table per version per profile for built-ins common |
| // to multiple stages (languages), and a process-global symbol table per version |
| // per profile per stage for built-ins unique to each stage. They will be sparsely |
| // populated, so they will only only be generated as needed. |
| // |
| // Each has a different set of built-ins, and we want to preserve that from |
| // compile to compile. |
| // |
| TSymbolTable* CommonSymbolTable[VersionCount][ProfileCount][EPcCount] = {}; |
| TSymbolTable* SharedSymbolTables[VersionCount][ProfileCount][EShLangCount] = {}; |
| |
| TPoolAllocator* PerProcessGPA = 0; |
| |
| // |
| // Parse and add to the given symbol table the content of the given shader string. |
| // |
| bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profile, EShLanguage language, TInfoSink& infoSink, |
| TSymbolTable& symbolTable) |
| { |
| TIntermediate intermediate(language, version, profile); |
| |
| TParseContext parseContext(symbolTable, intermediate, true, version, profile, language, infoSink); |
| TPpContext ppContext(parseContext); |
| TScanContext scanContext(parseContext); |
| parseContext.setScanContext(&scanContext); |
| parseContext.setPpContext(&ppContext); |
| |
| // |
| // Push the symbol table to give it an initial scope. This |
| // push should not have a corresponding pop, so that built-ins |
| // are preserved, and the test for an empty table fails. |
| // |
| |
| symbolTable.push(); |
| |
| const char* builtInShaders[2]; |
| size_t builtInLengths[2]; |
| builtInShaders[0] = builtIns.c_str(); |
| builtInLengths[0] = builtIns.size(); |
| |
| TInputScanner input(1, builtInShaders, builtInLengths); |
| if (! parseContext.parseShaderStrings(ppContext, input) != 0) { |
| infoSink.info.message(EPrefixInternalError, "Unable to parse built-ins"); |
| printf("Unable to parse built-ins\n%s\n", infoSink.info.c_str()); |
| |
| return false; |
| } |
| |
| return true; |
| } |
| |
| int CommonIndex(EProfile profile, EShLanguage language) |
| { |
| return (profile == EEsProfile && language == EShLangFragment) ? EPcFragment : EPcGeneral; |
| } |
| |
| // |
| // To initialize per-stage shared tables, with the common table already complete. |
| // |
| void InitializeStageSymbolTable(TBuiltIns& builtIns, int version, EProfile profile, EShLanguage language, TInfoSink& infoSink, TSymbolTable** commonTable, TSymbolTable** symbolTables) |
| { |
| (*symbolTables[language]).adoptLevels(*commonTable[CommonIndex(profile, language)]); |
| InitializeSymbolTable(builtIns.getStageString(language), version, profile, language, infoSink, *symbolTables[language]); |
| IdentifyBuiltIns(version, profile, language, *symbolTables[language]); |
| if (profile == EEsProfile && version >= 300) |
| (*symbolTables[language]).setNoBuiltInRedeclarations(); |
| if (version == 110) |
| (*symbolTables[language]).setSeparateNameSpaces(); |
| } |
| |
| // |
| // Initialize the full set of shareable symbol tables; |
| // The common (cross-stage) and those shareable per-stage. |
| // |
| bool InitializeSymbolTables(TInfoSink& infoSink, TSymbolTable** commonTable, TSymbolTable** symbolTables, int version, EProfile profile) |
| { |
| TBuiltIns builtIns; |
| builtIns.initialize(version, profile); |
| |
| // do the common tables |
| InitializeSymbolTable(builtIns.getCommonString(), version, profile, EShLangVertex, infoSink, *commonTable[EPcGeneral]); |
| if (profile == EEsProfile) |
| InitializeSymbolTable(builtIns.getCommonString(), version, profile, EShLangFragment, infoSink, *commonTable[EPcFragment]); |
| |
| // do the per-stage tables |
| |
| // always have vertex and fragment |
| InitializeStageSymbolTable(builtIns, version, profile, EShLangVertex, infoSink, commonTable, symbolTables); |
| InitializeStageSymbolTable(builtIns, version, profile, EShLangFragment, infoSink, commonTable, symbolTables); |
| |
| // check for tessellation |
| if ((profile != EEsProfile && version >= 150) || |
| (profile == EEsProfile && version >= 310)) { |
| InitializeStageSymbolTable(builtIns, version, profile, EShLangTessControl, infoSink, commonTable, symbolTables); |
| InitializeStageSymbolTable(builtIns, version, profile, EShLangTessEvaluation, infoSink, commonTable, symbolTables); |
| } |
| |
| // check for geometry |
| if ((profile != EEsProfile && version >= 150) || |
| (profile == EEsProfile && version >= 310)) |
| InitializeStageSymbolTable(builtIns, version, profile, EShLangGeometry, infoSink, commonTable, symbolTables); |
| |
| // check for compute |
| if ((profile != EEsProfile && version >= 430) || |
| (profile == EEsProfile && version >= 310)) |
| InitializeStageSymbolTable(builtIns, version, profile, EShLangCompute, infoSink, commonTable, symbolTables); |
| |
| return true; |
| } |
| |
| bool AddContextSpecificSymbols(const TBuiltInResource* resources, TInfoSink& infoSink, TSymbolTable& symbolTable, int version, EProfile profile, EShLanguage language) |
| { |
| TBuiltIns builtIns; |
| |
| builtIns.initialize(*resources, version, profile, language); |
| InitializeSymbolTable(builtIns.getCommonString(), version, profile, language, infoSink, symbolTable); |
| IdentifyBuiltIns(version, profile, language, symbolTable, *resources); |
| |
| return true; |
| } |
| |
| // |
| // To do this on the fly, we want to leave the current state of our thread's |
| // pool allocator intact, so: |
| // - Switch to a new pool for parsing the built-ins |
| // - Do the parsing, which builds the symbol table, using the new pool |
| // - Switch to the process-global pool to save a copy the resulting symbol table |
| // - Free up the new pool used to parse the built-ins |
| // - Switch back to the original thread's pool |
| // |
| // This only gets done the first time any thread needs a particular symbol table |
| // (lazy evaluation). |
| // |
| void SetupBuiltinSymbolTable(int version, EProfile profile) |
| { |
| TInfoSink infoSink; |
| |
| // Make sure only one thread tries to do this at a time |
| glslang::GetGlobalLock(); |
| |
| // See if it's already been done for this version/profile combination |
| int versionIndex = MapVersionToIndex(version); |
| int profileIndex = MapProfileToIndex(profile); |
| if (CommonSymbolTable[versionIndex][profileIndex][EPcGeneral]) { |
| glslang::ReleaseGlobalLock(); |
| |
| return; |
| } |
| |
| // Switch to a new pool |
| TPoolAllocator& previousAllocator = GetThreadPoolAllocator(); |
| TPoolAllocator* builtInPoolAllocator = new TPoolAllocator(); |
| SetThreadPoolAllocator(*builtInPoolAllocator); |
| |
| // Dynamically allocate the local symbol tables so we can control when they are deallocated WRT when the pool is popped. |
| TSymbolTable* commonTable[EPcCount]; |
| TSymbolTable* stageTables[EShLangCount]; |
| for (int precClass = 0; precClass < EPcCount; ++precClass) |
| commonTable[precClass] = new TSymbolTable; |
| for (int stage = 0; stage < EShLangCount; ++stage) |
| stageTables[stage] = new TSymbolTable; |
| |
| // Generate the local symbol tables using the new pool |
| InitializeSymbolTables(infoSink, commonTable, stageTables, version, profile); |
| |
| // Switch to the process-global pool |
| SetThreadPoolAllocator(*PerProcessGPA); |
| |
| // Copy the local symbol tables from the new pool to the global tables using the process-global pool |
| for (int precClass = 0; precClass < EPcCount; ++precClass) { |
| if (! commonTable[precClass]->isEmpty()) { |
| CommonSymbolTable[versionIndex][profileIndex][precClass] = new TSymbolTable; |
| CommonSymbolTable[versionIndex][profileIndex][precClass]->copyTable(*commonTable[precClass]); |
| CommonSymbolTable[versionIndex][profileIndex][precClass]->readOnly(); |
| } |
| } |
| for (int stage = 0; stage < EShLangCount; ++stage) { |
| if (! stageTables[stage]->isEmpty()) { |
| SharedSymbolTables[versionIndex][profileIndex][stage] = new TSymbolTable; |
| SharedSymbolTables[versionIndex][profileIndex][stage]->adoptLevels(*CommonSymbolTable[versionIndex][profileIndex][CommonIndex(profile, (EShLanguage)stage)]); |
| SharedSymbolTables[versionIndex][profileIndex][stage]->copyTable(*stageTables[stage]); |
| SharedSymbolTables[versionIndex][profileIndex][stage]->readOnly(); |
| } |
| } |
| |
| // Clean up the local tables before deleting the pool they used. |
| for (int precClass = 0; precClass < EPcCount; ++precClass) |
| delete commonTable[precClass]; |
| for (int stage = 0; stage < EShLangCount; ++stage) |
| delete stageTables[stage]; |
| |
| delete builtInPoolAllocator; |
| SetThreadPoolAllocator(previousAllocator); |
| |
| glslang::ReleaseGlobalLock(); |
| } |
| |
| bool DeduceVersionProfile(TInfoSink& infoSink, EShLanguage stage, bool versionNotFirst, int defaultVersion, int& version, EProfile& profile) |
| { |
| const int FirstProfileVersion = 150; |
| bool correct = true; |
| |
| // Get a good version... |
| if (version == 0) { |
| version = defaultVersion; |
| // infoSink.info.message(EPrefixWarning, "#version: statement missing; use #version on first line of shader"); |
| } |
| |
| // Get a good profile... |
| if (profile == ENoProfile) { |
| if (version == 300 || version == 310) { |
| correct = false; |
| infoSink.info.message(EPrefixError, "#version: versions 300 and 310 require specifying the 'es' profile"); |
| profile = EEsProfile; |
| } else if (version == 100) |
| profile = EEsProfile; |
| else if (version >= FirstProfileVersion) |
| profile = ECoreProfile; |
| else |
| profile = ENoProfile; |
| } else { |
| // a profile was provided... |
| if (version < 150) { |
| correct = false; |
| infoSink.info.message(EPrefixError, "#version: versions before 150 do not allow a profile token"); |
| if (version == 100) |
| profile = EEsProfile; |
| else |
| profile = ENoProfile; |
| } else if (version == 300 || version == 310) { |
| if (profile != EEsProfile) { |
| correct = false; |
| infoSink.info.message(EPrefixError, "#version: versions 300 and 310 support only the es profile"); |
| } |
| profile = EEsProfile; |
| } else { |
| if (profile == EEsProfile) { |
| correct = false; |
| infoSink.info.message(EPrefixError, "#version: only version 300 and 310 support the es profile"); |
| if (version >= FirstProfileVersion) |
| profile = ECoreProfile; |
| else |
| profile = ENoProfile; |
| } |
| // else: typical desktop case... e.g., "#version 410 core" |
| } |
| } |
| |
| // Correct for stage type... |
| switch (stage) { |
| case EShLangGeometry: |
| if ((profile == EEsProfile && version < 310) || |
| (profile != EEsProfile && version < 150)) { |
| correct = false; |
| infoSink.info.message(EPrefixError, "#version: geometry shaders require es profile with version 310 or non-es profile with version 150 or above"); |
| version = (profile == EEsProfile) ? 310 : 150; |
| if (profile == EEsProfile || profile == ENoProfile) |
| profile = ECoreProfile; |
| } |
| break; |
| case EShLangTessControl: |
| case EShLangTessEvaluation: |
| if ((profile == EEsProfile && version < 310) || |
| (profile != EEsProfile && version < 150)) { |
| correct = false; |
| infoSink.info.message(EPrefixError, "#version: tessellation shaders require es profile with version 310 or non-es profile with version 150 or above"); |
| version = (profile == EEsProfile) ? 310 : 400; // 150 supports the extension, correction is to 400 which does not |
| if (profile == EEsProfile || profile == ENoProfile) |
| profile = ECoreProfile; |
| } |
| break; |
| case EShLangCompute: |
| if ((profile == EEsProfile && version < 310) || |
| (profile != EEsProfile && version < 420)) { |
| correct = false; |
| infoSink.info.message(EPrefixError, "#version: compute shaders require es profile with version 310 or above, or non-es profile with version 420 or above"); |
| version = profile == EEsProfile ? 310 : 430; // 420 supports the extension, correction is to 430 which does not |
| profile = ECoreProfile; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| if (profile == EEsProfile && version >= 300 && versionNotFirst) { |
| correct = false; |
| infoSink.info.message(EPrefixError, "#version: statement must appear first in es-profile shader; before comments or newlines"); |
| } |
| |
| // A metacheck on the condition of the compiler itself... |
| switch (version) { |
| |
| // ES versions |
| case 100: |
| case 300: |
| // versions are complete |
| break; |
| |
| // Desktop versions |
| case 110: |
| case 120: |
| case 130: |
| case 140: |
| case 150: |
| case 330: |
| // versions are complete |
| break; |
| |
| case 310: |
| case 400: |
| case 410: |
| case 420: |
| case 430: |
| case 440: |
| case 450: |
| infoSink.info << "Warning, version " << version << " is not yet complete; most version-specific features are present, but some are missing.\n"; |
| break; |
| |
| default: |
| infoSink.info << "Warning, version " << version << " is unknown.\n"; |
| break; |
| |
| } |
| |
| return correct; |
| } |
| |
| // This is the common setup and cleanup code for PreprocessDeferred and |
| // CompileDeferred. |
| // It takes any callable with a signature of |
| // bool (TParseContext& parseContext, TPpContext& ppContext, |
| // TInputScanner& input, bool versionWillBeError, |
| // TSymbolTable& , TIntermediate& , |
| // EShOptimizationLevel , EShMessages ); |
| // Which returns false if a failure was detected and true otherwise. |
| // |
| template<typename ProcessingContext> |
| bool ProcessDeferred( |
| TCompiler* compiler, |
| const char* const shaderStrings[], |
| const int numStrings, |
| const int* inputLengths, |
| const char* customPreamble, |
| const EShOptimizationLevel optLevel, |
| const TBuiltInResource* resources, |
| int defaultVersion, // use 100 for ES environment, 110 for desktop |
| EProfile defaultProfile, |
| // set version/profile to defaultVersion/defaultProfile regardless of the #version |
| // directive in the source code |
| bool forceDefaultVersionAndProfile, |
| bool forwardCompatible, // give errors for use of deprecated features |
| EShMessages messages, // warnings/errors/AST; things to print out |
| TIntermediate& intermediate, // returned tree, etc. |
| ProcessingContext& processingContext, |
| bool requireNonempty |
| ) |
| { |
| if (! InitThread()) |
| return false; |
| |
| // This must be undone (.pop()) by the caller, after it finishes consuming the created tree. |
| GetThreadPoolAllocator().push(); |
| |
| if (numStrings == 0) |
| return true; |
| |
| // Move to length-based strings, rather than null-terminated strings. |
| // Also, add strings to include the preamble and to ensure the shader is not null, |
| // which lets the grammar accept what was a null (post preprocessing) shader. |
| // |
| // Shader will look like |
| // string 0: system preamble |
| // string 1: custom preamble |
| // string 2...numStrings+1: user's shader |
| // string numStrings+2: "int;" |
| const int numPre = 2; |
| const int numPost = requireNonempty? 1 : 0; |
| size_t* lengths = new size_t[numStrings + numPre + numPost]; |
| const char** strings = new const char*[numStrings + numPre + numPost]; |
| for (int s = 0; s < numStrings; ++s) { |
| strings[s + numPre] = shaderStrings[s]; |
| if (inputLengths == 0 || inputLengths[s] < 0) |
| lengths[s + numPre] = strlen(shaderStrings[s]); |
| else |
| lengths[s + numPre] = inputLengths[s]; |
| } |
| |
| // 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. |
| int version; |
| EProfile profile; |
| glslang::TInputScanner userInput(numStrings, &strings[numPre], &lengths[numPre]); // no preamble |
| bool versionNotFirstToken; |
| bool versionNotFirst = userInput.scanVersion(version, profile, versionNotFirstToken); |
| bool versionNotFound = version == 0; |
| if (forceDefaultVersionAndProfile) { |
| if (! (messages & EShMsgSuppressWarnings) && ! versionNotFound && |
| (version != defaultVersion || profile != defaultProfile)) { |
| compiler->infoSink.info << "Warning, (version, profile) forced to be (" |
| << defaultVersion << ", " << ProfileName(defaultProfile) |
| << "), while in source code it is (" |
| << version << ", " << ProfileName(profile) << ")\n"; |
| } |
| |
| if (versionNotFound) { |
| versionNotFirstToken = false; |
| versionNotFirst = false; |
| versionNotFound = false; |
| } |
| version = defaultVersion; |
| profile = defaultProfile; |
| } |
| bool goodVersion = DeduceVersionProfile(compiler->infoSink, compiler->getLanguage(), versionNotFirst, defaultVersion, version, profile); |
| bool versionWillBeError = (versionNotFound || (profile == EEsProfile && version >= 300 && versionNotFirst)); |
| bool warnVersionNotFirst = false; |
| if (! versionWillBeError && versionNotFirstToken) { |
| if (messages & EShMsgRelaxedErrors) |
| warnVersionNotFirst = true; |
| else |
| versionWillBeError = true; |
| } |
| |
| intermediate.setVersion(version); |
| intermediate.setProfile(profile); |
| SetupBuiltinSymbolTable(version, profile); |
| |
| TSymbolTable* cachedTable = SharedSymbolTables[MapVersionToIndex(version)] |
| [MapProfileToIndex(profile)] |
| [compiler->getLanguage()]; |
| |
| // Dynamically allocate the symbol table so we can control when it is deallocated WRT the pool. |
| TSymbolTable* symbolTableMemory = new TSymbolTable; |
| TSymbolTable& symbolTable = *symbolTableMemory; |
| if (cachedTable) |
| symbolTable.adoptLevels(*cachedTable); |
| |
| // Add built-in symbols that are potentially context dependent; |
| // they get popped again further down. |
| AddContextSpecificSymbols(resources, compiler->infoSink, symbolTable, version, profile, compiler->getLanguage()); |
| |
| // |
| // Now we can process the full shader under proper symbols and rules. |
| // |
| |
| TParseContext parseContext(symbolTable, intermediate, false, version, profile, compiler->getLanguage(), compiler->infoSink, forwardCompatible, messages); |
| glslang::TScanContext scanContext(parseContext); |
| TPpContext ppContext(parseContext); |
| parseContext.setScanContext(&scanContext); |
| parseContext.setPpContext(&ppContext); |
| parseContext.setLimits(*resources); |
| if (! goodVersion) |
| parseContext.addError(); |
| if (warnVersionNotFirst) { |
| TSourceLoc loc; |
| loc.init(); |
| parseContext.warn(loc, "Illegal to have non-comment, non-whitespace tokens before #version", "#version", ""); |
| } |
| |
| parseContext.initializeExtensionBehavior(); |
| |
| |
| // Fill in the strings as outlined above. |
| strings[0] = parseContext.getPreamble(); |
| lengths[0] = strlen(strings[0]); |
| strings[1] = customPreamble; |
| lengths[1] = strlen(strings[1]); |
| assert(2 == numPre); |
| if (requireNonempty) { |
| strings[numStrings + numPre] = "\n int;"; |
| lengths[numStrings + numPre] = strlen(strings[numStrings + numPre]); |
| } |
| TInputScanner fullInput(numStrings + numPre + numPost, strings, lengths, numPre, numPost); |
| |
| // Push a new symbol allocation scope that will get used for the shader's globals. |
| symbolTable.push(); |
| |
| bool success = processingContext(parseContext, ppContext, fullInput, |
| versionWillBeError, symbolTable, |
| intermediate, optLevel, messages); |
| |
| // Clean up the symbol table. The AST is self-sufficient now. |
| delete symbolTableMemory; |
| |
| |
| delete [] lengths; |
| delete [] strings; |
| |
| return success; |
| } |
| |
| // DoPreprocessing is a valid ProcessingContext template argument, |
| // which only performs the preprocessing step of compilation. |
| // It places the result in the "string" argument to its constructor. |
| struct DoPreprocessing { |
| explicit DoPreprocessing(std::string* string): outputString(string) {} |
| bool operator()(TParseContext& parseContext, TPpContext& ppContext, |
| TInputScanner& input, bool versionWillBeError, |
| TSymbolTable& , TIntermediate& , |
| EShOptimizationLevel , EShMessages ) |
| { |
| // This is a list of tokens that do not require a space before or after. |
| static const std::string unNeededSpaceTokens = ";()[]"; |
| static const std::string noSpaceBeforeTokens = ","; |
| glslang::TPpToken token; |
| |
| std::stringstream outputStream; |
| int lastLine = -1; // lastLine is the line number of the last token |
| // processed. It is tracked in order for new-lines to be inserted when |
| // a token appears on a new line. |
| int lastToken = -1; |
| parseContext.setScanner(&input); |
| ppContext.setInput(input, versionWillBeError); |
| |
| // Inserts newlines and incremnets lastLine until |
| // lastLine >= line. |
| auto adjustLine = [&lastLine, &outputStream](int line) { |
| int tokenLine = line - 1; |
| while(lastLine < tokenLine) { |
| if (lastLine >= 0) { |
| outputStream << std::endl; |
| } |
| ++lastLine; |
| } |
| }; |
| |
| parseContext.setExtensionCallback([&adjustLine, &outputStream]( |
| int line, const char* extension, const char* behavior) { |
| adjustLine(line); |
| outputStream << "#extension " << extension << " : " << behavior; |
| }); |
| parseContext.setLineCallback([&lastLine, &outputStream]( |
| int line, bool hasSource, int sourceNum) { |
| // SourceNum is the number of the source-string that is being parsed. |
| if (lastLine != -1) { |
| outputStream << std::endl; |
| } |
| outputStream << "#line " << line; |
| if (hasSource) { |
| outputStream << " " << sourceNum; |
| } |
| outputStream << std::endl; |
| lastLine = std::max(line - 1, 1); |
| }); |
| |
| |
| parseContext.setVersionCallback( |
| [&adjustLine, &lastLine, &outputStream](int line, int version, const char* str) { |
| adjustLine(line); |
| outputStream << "#version " << version; |
| if (str) { |
| outputStream << " " << str; |
| } |
| outputStream << std::endl; |
| ++lastLine; |
| }); |
| |
| parseContext.setPragmaCallback([&adjustLine, &outputStream]( |
| int line, const glslang::TVector<glslang::TString>& ops) { |
| adjustLine(line); |
| outputStream << "#pragma "; |
| for(size_t i = 0; i < ops.size(); ++i) { |
| outputStream << ops[i]; |
| } |
| }); |
| |
| parseContext.setErrorCallback([&adjustLine, &outputStream]( |
| int line, const char* errorMessage) { |
| adjustLine(line); |
| outputStream << "#error " << errorMessage; |
| }); |
| while (const char* tok = ppContext.tokenize(&token)) { |
| int tokenLine = token.loc.line - 1; // start at 0; |
| bool newLine = false; |
| while (lastLine < tokenLine) { |
| if (lastLine > -1) { |
| outputStream << std::endl; |
| newLine = true; |
| } |
| ++lastLine; |
| if (lastLine == tokenLine) { |
| // Don't emit whitespace onto empty lines. |
| // Copy any whitespace characters at the start of a line |
| // from the input to the output. |
| for(int i = 0; i < token.loc.column - 1; ++i) { |
| outputStream << " "; |
| } |
| } |
| } |
| |
| // Output a space in between tokens, but not at the start of a line, |
| // and also not around special tokens. This helps with readability |
| // and consistency. |
| if (!newLine && |
| lastToken != -1 && |
| (unNeededSpaceTokens.find((char)token.token) == std::string::npos) && |
| (unNeededSpaceTokens.find((char)lastToken) == std::string::npos) && |
| (noSpaceBeforeTokens.find((char)token.token) == std::string::npos)) { |
| outputStream << " "; |
| } |
| lastToken = token.token; |
| outputStream << tok; |
| } |
| outputStream << std::endl; |
| *outputString = outputStream.str(); |
| |
| return true; |
| } |
| std::string* outputString; |
| }; |
| |
| // DoFullParse is a valid ProcessingConext template argument for fully |
| // parsing the shader. It populates the "intermediate" with the AST. |
| struct DoFullParse{ |
| bool operator()(TParseContext& parseContext, TPpContext& ppContext, |
| TInputScanner& fullInput, bool versionWillBeError, |
| TSymbolTable& symbolTable, TIntermediate& intermediate, |
| EShOptimizationLevel optLevel, EShMessages messages) |
| { |
| bool success = true; |
| // Parse the full shader. |
| if (! parseContext.parseShaderStrings(ppContext, fullInput, versionWillBeError)) |
| success = false; |
| intermediate.addSymbolLinkageNodes(parseContext.linkage, parseContext.language, symbolTable); |
| |
| if (success && intermediate.getTreeRoot()) { |
| if (optLevel == EShOptNoGeneration) |
| parseContext.infoSink.info.message(EPrefixNone, "No errors. No code generation or linking was requested."); |
| else |
| success = intermediate.postProcess(intermediate.getTreeRoot(), parseContext.language); |
| } else if (! success) { |
| parseContext.infoSink.info.prefix(EPrefixError); |
| parseContext.infoSink.info << parseContext.getNumErrors() << " compilation errors. No code generated.\n\n"; |
| } |
| |
| if (messages & EShMsgAST) |
| intermediate.output(parseContext.infoSink, true); |
| |
| return success; |
| } |
| }; |
| |
| // Take a single compilation unit, and run the preprocessor on it. |
| // Return: True if there were no issues found in preprocessing, |
| // False if during preprocessing any unknown version, pragmas or |
| // extensions were found. |
| bool PreprocessDeferred( |
| TCompiler* compiler, |
| const char* const shaderStrings[], |
| const int numStrings, |
| const int* inputLengths, |
| const char* preamble, |
| const EShOptimizationLevel optLevel, |
| const TBuiltInResource* resources, |
| int defaultVersion, // use 100 for ES environment, 110 for desktop |
| EProfile defaultProfile, |
| bool forceDefaultVersionAndProfile, |
| bool forwardCompatible, // give errors for use of deprecated features |
| EShMessages messages, // warnings/errors/AST; things to print out |
| TIntermediate& intermediate, // returned tree, etc. |
| std::string* outputString) |
| { |
| DoPreprocessing parser(outputString); |
| return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, |
| preamble, optLevel, resources, defaultVersion, defaultProfile, forceDefaultVersionAndProfile, |
| forwardCompatible, messages, intermediate, parser, false); |
| } |
| |
| |
| // |
| // do a partial compile on the given strings for a single compilation unit |
| // for a potential deferred link into a single stage (and deferred full compile of that |
| // stage through machine-dependent compilation). |
| // |
| // all preprocessing, parsing, semantic checks, etc. for a single compilation unit |
| // are done here. |
| // |
| // return: the tree and other information is filled into the intermediate argument, |
| // and true is returned by the function for success. |
| // |
| bool CompileDeferred( |
| TCompiler* compiler, |
| const char* const shaderStrings[], |
| const int numStrings, |
| const int* inputLengths, |
| const char* preamble, |
| const EShOptimizationLevel optLevel, |
| const TBuiltInResource* resources, |
| int defaultVersion, // use 100 for ES environment, 110 for desktop |
| EProfile defaultProfile, |
| bool forceDefaultVersionAndProfile, |
| bool forwardCompatible, // give errors for use of deprecated features |
| EShMessages messages, // warnings/errors/AST; things to print out |
| TIntermediate& intermediate) // returned tree, etc. |
| { |
| DoFullParse parser; |
| return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, |
| preamble, optLevel, resources, defaultVersion, defaultProfile, forceDefaultVersionAndProfile, |
| forwardCompatible, messages, intermediate, parser, true); |
| } |
| |
| } // end anonymous namespace for local functions |
| |
| |
| // |
| // ShInitialize() should be called exactly once per process, not per thread. |
| // |
| int ShInitialize() |
| { |
| glslang::InitGlobalLock(); |
| |
| if (! InitProcess()) |
| return 0; |
| |
| if (! PerProcessGPA) |
| PerProcessGPA = new TPoolAllocator(); |
| |
| glslang::TScanContext::fillInKeywordMap(); |
| |
| return 1; |
| } |
| |
| // |
| // Driver calls these to create and destroy compiler/linker |
| // objects. |
| // |
| |
| ShHandle ShConstructCompiler(const EShLanguage language, int debugOptions) |
| { |
| if (!InitThread()) |
| return 0; |
| |
| TShHandleBase* base = static_cast<TShHandleBase*>(ConstructCompiler(language, debugOptions)); |
| |
| return reinterpret_cast<void*>(base); |
| } |
| |
| ShHandle ShConstructLinker(const EShExecutable executable, int debugOptions) |
| { |
| if (!InitThread()) |
| return 0; |
| |
| TShHandleBase* base = static_cast<TShHandleBase*>(ConstructLinker(executable, debugOptions)); |
| |
| return reinterpret_cast<void*>(base); |
| } |
| |
| ShHandle ShConstructUniformMap() |
| { |
| if (!InitThread()) |
| return 0; |
| |
| TShHandleBase* base = static_cast<TShHandleBase*>(ConstructUniformMap()); |
| |
| return reinterpret_cast<void*>(base); |
| } |
| |
| void ShDestruct(ShHandle handle) |
| { |
| if (handle == 0) |
| return; |
| |
| TShHandleBase* base = static_cast<TShHandleBase*>(handle); |
| |
| if (base->getAsCompiler()) |
| DeleteCompiler(base->getAsCompiler()); |
| else if (base->getAsLinker()) |
| DeleteLinker(base->getAsLinker()); |
| else if (base->getAsUniformMap()) |
| DeleteUniformMap(base->getAsUniformMap()); |
| } |
| |
| // |
| // Cleanup symbol tables |
| // |
| int __fastcall ShFinalize() |
| { |
| for (int version = 0; version < VersionCount; ++version) { |
| for (int p = 0; p < ProfileCount; ++p) { |
| for (int lang = 0; lang < EShLangCount; ++lang) { |
| delete SharedSymbolTables[version][p][lang]; |
| SharedSymbolTables[version][p][lang] = 0; |
| } |
| } |
| } |
| |
| for (int version = 0; version < VersionCount; ++version) { |
| for (int p = 0; p < ProfileCount; ++p) { |
| for (int pc = 0; pc < EPcCount; ++pc) { |
| delete CommonSymbolTable[version][p][pc]; |
| CommonSymbolTable[version][p][pc] = 0; |
| } |
| } |
| } |
| |
| if (PerProcessGPA) { |
| PerProcessGPA->popAll(); |
| delete PerProcessGPA; |
| PerProcessGPA = 0; |
| } |
| |
| glslang::TScanContext::deleteKeywordMap(); |
| |
| return 1; |
| } |
| |
| // |
| // Do a full compile on the given strings for a single compilation unit |
| // forming a complete stage. The result of the machine dependent compilation |
| // is left in the provided compile object. |
| // |
| // Return: The return value is really boolean, indicating |
| // success (1) or failure (0). |
| // |
| int ShCompile( |
| const ShHandle handle, |
| const char* const shaderStrings[], |
| const int numStrings, |
| const int* inputLengths, |
| const EShOptimizationLevel optLevel, |
| const TBuiltInResource* resources, |
| int /*debugOptions*/, |
| int defaultVersion, // use 100 for ES environment, 110 for desktop |
| bool forwardCompatible, // give errors for use of deprecated features |
| EShMessages messages // warnings/errors/AST; things to print out |
| ) |
| { |
| // Map the generic handle to the C++ object |
| if (handle == 0) |
| return 0; |
| |
| TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle); |
| TCompiler* compiler = base->getAsCompiler(); |
| if (compiler == 0) |
| return 0; |
| |
| compiler->infoSink.info.erase(); |
| compiler->infoSink.debug.erase(); |
| |
| TIntermediate intermediate(compiler->getLanguage()); |
| bool success = CompileDeferred(compiler, shaderStrings, numStrings, inputLengths, "", optLevel, resources, defaultVersion, ENoProfile, false, forwardCompatible, messages, intermediate); |
| |
| // |
| // Call the machine dependent compiler |
| // |
| if (success && intermediate.getTreeRoot() && optLevel != EShOptNoGeneration) |
| success = compiler->compile(intermediate.getTreeRoot(), intermediate.getVersion(), intermediate.getProfile()); |
| |
| intermediate.removeTree(); |
| |
| // Throw away all the temporary memory used by the compilation process. |
| // The push was done in the CompileDeferred() call above. |
| GetThreadPoolAllocator().pop(); |
| |
| return success ? 1 : 0; |
| } |
| |
| // |
| // Link the given compile objects. |
| // |
| // Return: The return value of is really boolean, indicating |
| // success or failure. |
| // |
| int ShLinkExt( |
| const ShHandle linkHandle, |
| const ShHandle compHandles[], |
| const int numHandles) |
| { |
| if (linkHandle == 0 || numHandles == 0) |
| return 0; |
| |
| THandleList cObjects; |
| |
| for (int i = 0; i < numHandles; ++i) { |
| if (compHandles[i] == 0) |
| return 0; |
| TShHandleBase* base = reinterpret_cast<TShHandleBase*>(compHandles[i]); |
| if (base->getAsLinker()) { |
| cObjects.push_back(base->getAsLinker()); |
| } |
| if (base->getAsCompiler()) |
| cObjects.push_back(base->getAsCompiler()); |
| |
| |
| if (cObjects[i] == 0) |
| return 0; |
| } |
| |
| TShHandleBase* base = reinterpret_cast<TShHandleBase*>(linkHandle); |
| TLinker* linker = static_cast<TLinker*>(base->getAsLinker()); |
| |
| if (linker == 0) |
| return 0; |
| |
| linker->infoSink.info.erase(); |
| |
| for (int i = 0; i < numHandles; ++i) { |
| if (cObjects[i]->getAsCompiler()) { |
| if (! cObjects[i]->getAsCompiler()->linkable()) { |
| linker->infoSink.info.message(EPrefixError, "Not all shaders have valid object code."); |
| return 0; |
| } |
| } |
| } |
| |
| bool ret = linker->link(cObjects); |
| |
| return ret ? 1 : 0; |
| } |
| |
| // |
| // ShSetEncrpytionMethod is a place-holder for specifying |
| // how source code is encrypted. |
| // |
| void ShSetEncryptionMethod(ShHandle handle) |
| { |
| if (handle == 0) |
| return; |
| } |
| |
| // |
| // Return any compiler/linker/uniformmap log of messages for the application. |
| // |
| const char* ShGetInfoLog(const ShHandle handle) |
| { |
| if (!InitThread()) |
| return 0; |
| |
| if (handle == 0) |
| return 0; |
| |
| TShHandleBase* base = static_cast<TShHandleBase*>(handle); |
| TInfoSink* infoSink; |
| |
| if (base->getAsCompiler()) |
| infoSink = &(base->getAsCompiler()->getInfoSink()); |
| else if (base->getAsLinker()) |
| infoSink = &(base->getAsLinker()->getInfoSink()); |
| else |
| return 0; |
| |
| infoSink->info << infoSink->debug.c_str(); |
| return infoSink->info.c_str(); |
| } |
| |
| // |
| // Return the resulting binary code from the link process. Structure |
| // is machine dependent. |
| // |
| const void* ShGetExecutable(const ShHandle handle) |
| { |
| if (!InitThread()) |
| return 0; |
| |
| if (handle == 0) |
| return 0; |
| |
| TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle); |
| |
| TLinker* linker = static_cast<TLinker*>(base->getAsLinker()); |
| if (linker == 0) |
| return 0; |
| |
| return linker->getObjectCode(); |
| } |
| |
| // |
| // Let the linker know where the application said it's attributes are bound. |
| // The linker does not use these values, they are remapped by the ICD or |
| // hardware. It just needs them to know what's aliased. |
| // |
| // Return: The return value of is really boolean, indicating |
| // success or failure. |
| // |
| int ShSetVirtualAttributeBindings(const ShHandle handle, const ShBindingTable* table) |
| { |
| if (!InitThread()) |
| return 0; |
| |
| if (handle == 0) |
| return 0; |
| |
| TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle); |
| TLinker* linker = static_cast<TLinker*>(base->getAsLinker()); |
| |
| if (linker == 0) |
| return 0; |
| |
| linker->setAppAttributeBindings(table); |
| |
| return 1; |
| } |
| |
| // |
| // Let the linker know where the predefined attributes have to live. |
| // |
| int ShSetFixedAttributeBindings(const ShHandle handle, const ShBindingTable* table) |
| { |
| if (!InitThread()) |
| return 0; |
| |
| if (handle == 0) |
| return 0; |
| |
| TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle); |
| TLinker* linker = static_cast<TLinker*>(base->getAsLinker()); |
| |
| if (linker == 0) |
| return 0; |
| |
| linker->setFixedAttributeBindings(table); |
| return 1; |
| } |
| |
| // |
| // Some attribute locations are off-limits to the linker... |
| // |
| int ShExcludeAttributes(const ShHandle handle, int *attributes, int count) |
| { |
| if (!InitThread()) |
| return 0; |
| |
| if (handle == 0) |
| return 0; |
| |
| TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle); |
| TLinker* linker = static_cast<TLinker*>(base->getAsLinker()); |
| if (linker == 0) |
| return 0; |
| |
| linker->setExcludedAttributes(attributes, count); |
| |
| return 1; |
| } |
| |
| // |
| // Return the index for OpenGL to use for knowing where a uniform lives. |
| // |
| // Return: The return value of is really boolean, indicating |
| // success or failure. |
| // |
| int ShGetUniformLocation(const ShHandle handle, const char* name) |
| { |
| if (!InitThread()) |
| return 0; |
| |
| if (handle == 0) |
| return -1; |
| |
| TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle); |
| TUniformMap* uniformMap= base->getAsUniformMap(); |
| if (uniformMap == 0) |
| return -1; |
| |
| return uniformMap->getLocation(name); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // Deferred-Lowering C++ Interface |
| // ----------------------------------- |
| // |
| // Below is a new alternate C++ interface that might potentially replace the above |
| // opaque handle-based interface. |
| // |
| // See more detailed comment in ShaderLang.h |
| // |
| |
| namespace glslang { |
| |
| #include "../Include/revision.h" |
| |
| const char* GetEsslVersionString() |
| { |
| return "OpenGL ES GLSL 3.00 glslang LunarG Khronos." GLSLANG_REVISION " " GLSLANG_DATE; |
| } |
| |
| const char* GetGlslVersionString() |
| { |
| return "4.20 glslang LunarG Khronos." GLSLANG_REVISION " " GLSLANG_DATE; |
| } |
| |
| bool InitializeProcess() |
| { |
| return ShInitialize() != 0; |
| } |
| |
| void FinalizeProcess() |
| { |
| ShFinalize(); |
| } |
| |
| class TDeferredCompiler : public TCompiler { |
| public: |
| TDeferredCompiler(EShLanguage s, TInfoSink& i) : TCompiler(s, i) { } |
| virtual bool compile(TIntermNode*, int = 0, EProfile = ENoProfile) { return true; } |
| }; |
| |
| TShader::TShader(EShLanguage s) |
| : pool(0), stage(s), preamble("") |
| { |
| infoSink = new TInfoSink; |
| compiler = new TDeferredCompiler(stage, *infoSink); |
| intermediate = new TIntermediate(s); |
| } |
| |
| TShader::~TShader() |
| { |
| delete infoSink; |
| delete compiler; |
| delete intermediate; |
| delete pool; |
| } |
| |
| // |
| // Turn the shader strings into a parse tree in the TIntermediate. |
| // |
| // Returns true for success. |
| // |
| bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, |
| bool forwardCompatible, EShMessages messages) |
| { |
| if (! InitThread()) |
| return false; |
| |
| pool = new TPoolAllocator(); |
| SetThreadPoolAllocator(*pool); |
| if (! preamble) |
| preamble = ""; |
| |
| return CompileDeferred(compiler, strings, numStrings, nullptr, preamble, EShOptNone, builtInResources, defaultVersion, defaultProfile, forceDefaultVersionAndProfile, forwardCompatible, messages, *intermediate); |
| } |
| |
| bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion, bool forwardCompatible, EShMessages messages) |
| { |
| return parse(builtInResources, defaultVersion, ENoProfile, false, forwardCompatible, messages); |
| } |
| |
| // Fill in a string with the result of preprocessing ShaderStrings |
| // Returns true if all extensions, pragmas and version strings were valid. |
| bool TShader::preprocess(const TBuiltInResource* builtInResources, |
| int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, |
| bool forwardCompatible, |
| EShMessages message, std::string* output_string) |
| { |
| if (! InitThread()) |
| return false; |
| |
| pool = new TPoolAllocator(); |
| SetThreadPoolAllocator(*pool); |
| if (! preamble) |
| preamble = ""; |
| |
| return PreprocessDeferred(compiler, strings, numStrings, |
| nullptr, preamble, EShOptNone, builtInResources, |
| defaultVersion, defaultProfile, forceDefaultVersionAndProfile, forwardCompatible, message, |
| *intermediate, output_string); |
| } |
| |
| const char* TShader::getInfoLog() |
| { |
| return infoSink->info.c_str(); |
| } |
| |
| const char* TShader::getInfoDebugLog() |
| { |
| return infoSink->debug.c_str(); |
| } |
| |
| TProgram::TProgram() : pool(0), reflection(0), linked(false) |
| { |
| infoSink = new TInfoSink; |
| for (int s = 0; s < EShLangCount; ++s) { |
| intermediate[s] = 0; |
| newedIntermediate[s] = false; |
| } |
| } |
| |
| TProgram::~TProgram() |
| { |
| delete infoSink; |
| delete reflection; |
| |
| for (int s = 0; s < EShLangCount; ++s) |
| if (newedIntermediate[s]) |
| delete intermediate[s]; |
| |
| delete pool; |
| } |
| |
| // |
| // Merge the compilation units within each stage into a single TIntermediate. |
| // All starting compilation units need to be the result of calling TShader::parse(). |
| // |
| // Return true for success. |
| // |
| bool TProgram::link(EShMessages messages) |
| { |
| if (linked) |
| return false; |
| linked = true; |
| |
| bool error = false; |
| |
| pool = new TPoolAllocator(); |
| SetThreadPoolAllocator(*pool); |
| |
| for (int s = 0; s < EShLangCount; ++s) { |
| if (! linkStage((EShLanguage)s, messages)) |
| error = true; |
| } |
| |
| // TODO: Link: cross-stage error checking |
| |
| return ! error; |
| } |
| |
| // |
| // Merge the compilation units within the given stage into a single TIntermediate. |
| // |
| // Return true for success. |
| // |
| bool TProgram::linkStage(EShLanguage stage, EShMessages messages) |
| { |
| if (stages[stage].size() == 0) |
| return true; |
| |
| // |
| // Be efficient for the common single compilation unit per stage case, |
| // reusing it's TIntermediate instead of merging into a new one. |
| // |
| if (stages[stage].size() == 1) |
| intermediate[stage] = stages[stage].front()->intermediate; |
| else { |
| intermediate[stage] = new TIntermediate(stage); |
| newedIntermediate[stage] = true; |
| } |
| |
| infoSink->info << "\nLinked " << StageName(stage) << " stage:\n\n"; |
| |
| if (stages[stage].size() > 1) { |
| std::list<TShader*>::const_iterator it; |
| for (it = stages[stage].begin(); it != stages[stage].end(); ++it) |
| intermediate[stage]->merge(*infoSink, *(*it)->intermediate); |
| } |
| |
| intermediate[stage]->finalCheck(*infoSink); |
| |
| if (messages & EShMsgAST) |
| intermediate[stage]->output(*infoSink, true); |
| |
| return intermediate[stage]->getNumErrors() == 0; |
| } |
| |
| const char* TProgram::getInfoLog() |
| { |
| return infoSink->info.c_str(); |
| } |
| |
| const char* TProgram::getInfoDebugLog() |
| { |
| return infoSink->debug.c_str(); |
| } |
| |
| // |
| // Reflection implementation. |
| // |
| |
| bool TProgram::buildReflection() |
| { |
| if (! linked || reflection) |
| return false; |
| |
| reflection = new TReflection; |
| |
| for (int s = 0; s < EShLangCount; ++s) { |
| if (intermediate[s]) { |
| if (! reflection->addStage((EShLanguage)s, *intermediate[s])) |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| int TProgram::getNumLiveUniformVariables() { return reflection->getNumUniforms(); } |
| int TProgram::getNumLiveUniformBlocks() { return reflection->getNumUniformBlocks(); } |
| const char* TProgram::getUniformName(int index) { return reflection->getUniform(index).name.c_str(); } |
| const char* TProgram::getUniformBlockName(int index) { return reflection->getUniformBlock(index).name.c_str(); } |
| int TProgram::getUniformBlockSize(int index) { return reflection->getUniformBlock(index).size; } |
| int TProgram::getUniformIndex(const char* name) { return reflection->getIndex(name); } |
| int TProgram::getUniformBlockIndex(int index) { return reflection->getUniform(index).index; } |
| int TProgram::getUniformType(int index) { return reflection->getUniform(index).glDefineType; } |
| int TProgram::getUniformBufferOffset(int index) { return reflection->getUniform(index).offset; } |
| int TProgram::getUniformArraySize(int index) { return reflection->getUniform(index).size; } |
| |
| void TProgram::dumpReflection() { reflection->dump(); } |
| |
| } // end namespace glslang |