| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146 |
- //
- // Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
- // Copyright (C) 2013-2016 LunarG, Inc.
- // Copyright (C) 2015-2020 Google, 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 <cstring>
- #include <iostream>
- #include <sstream>
- #include <memory>
- #include "SymbolTable.h"
- #include "ParseHelper.h"
- #include "Scan.h"
- #include "ScanContext.h"
- #ifdef ENABLE_HLSL
- #include "../HLSL/hlslParseHelper.h"
- #include "../HLSL/hlslParseables.h"
- #include "../HLSL/hlslScanContext.h"
- #endif
- #include "../Include/ShHandle.h"
- #include "../../OGLCompilersDLL/InitializeDll.h"
- #include "preprocessor/PpContext.h"
- #define SH_EXPORTING
- #include "../Public/ShaderLang.h"
- #include "reflection.h"
- #include "iomapper.h"
- #include "Initialize.h"
- // TODO: this really shouldn't be here, it is only because of the trial addition
- // of printing pre-processed tokens, which requires knowing the string literal
- // token to print ", but none of that seems appropriate for this file.
- #include "preprocessor/PpTokens.h"
- // Build-time generated includes
- #include "glslang/build_info.h"
- namespace { // anonymous namespace for file-local functions and symbols
- // Total number of successful initializers of glslang: a refcount
- // Shared global; access should be protected by a global mutex/critical section.
- int NumberOfClients = 0;
- using namespace glslang;
- // Create a language specific version of parseables.
- TBuiltInParseables* CreateBuiltInParseables(TInfoSink& infoSink, EShSource source)
- {
- switch (source) {
- case EShSourceGlsl: return new TBuiltIns(); // GLSL builtIns
- #ifdef ENABLE_HLSL
- case EShSourceHlsl: return new TBuiltInParseablesHlsl(); // HLSL intrinsics
- #endif
- default:
- infoSink.info.message(EPrefixInternalError, "Unable to determine source language");
- return nullptr;
- }
- }
- // Create a language specific version of a parse context.
- TParseContextBase* CreateParseContext(TSymbolTable& symbolTable, TIntermediate& intermediate,
- int version, EProfile profile, EShSource source,
- EShLanguage language, TInfoSink& infoSink,
- SpvVersion spvVersion, bool forwardCompatible, EShMessages messages,
- bool parsingBuiltIns, std::string sourceEntryPointName = "")
- {
- switch (source) {
- case EShSourceGlsl: {
- if (sourceEntryPointName.size() == 0)
- intermediate.setEntryPointName("main");
- TString entryPoint = sourceEntryPointName.c_str();
- return new TParseContext(symbolTable, intermediate, parsingBuiltIns, version, profile, spvVersion,
- language, infoSink, forwardCompatible, messages, &entryPoint);
- }
- #ifdef ENABLE_HLSL
- case EShSourceHlsl:
- return new HlslParseContext(symbolTable, intermediate, parsingBuiltIns, version, profile, spvVersion,
- language, infoSink, sourceEntryPointName.c_str(), forwardCompatible, messages);
- #endif
- default:
- infoSink.info.message(EPrefixInternalError, "Unable to determine source language");
- return nullptr;
- }
- }
- // Local mapping functions for making arrays of symbol tables....
- const int VersionCount = 17; // index range in MapVersionToIndex
- int MapVersionToIndex(int version)
- {
- int index = 0;
- switch (version) {
- case 100: index = 0; break;
- case 110: index = 1; break;
- case 120: index = 2; break;
- case 130: index = 3; break;
- case 140: index = 4; break;
- case 150: index = 5; break;
- case 300: index = 6; break;
- case 330: index = 7; break;
- case 400: index = 8; break;
- case 410: index = 9; break;
- case 420: index = 10; break;
- case 430: index = 11; break;
- case 440: index = 12; break;
- case 310: index = 13; break;
- case 450: index = 14; break;
- case 500: index = 0; break; // HLSL
- case 320: index = 15; break;
- case 460: index = 16; break;
- default: assert(0); break;
- }
- assert(index < VersionCount);
- return index;
- }
- const int SpvVersionCount = 3; // index range in MapSpvVersionToIndex
- int MapSpvVersionToIndex(const SpvVersion& spvVersion)
- {
- int index = 0;
- if (spvVersion.openGl > 0)
- index = 1;
- else if (spvVersion.vulkan > 0)
- index = 2;
- assert(index < SpvVersionCount);
- return index;
- }
- const int ProfileCount = 4; // index range in MapProfileToIndex
- int MapProfileToIndex(EProfile profile)
- {
- int index = 0;
- switch (profile) {
- case ENoProfile: index = 0; break;
- case ECoreProfile: index = 1; break;
- case ECompatibilityProfile: index = 2; break;
- case EEsProfile: index = 3; break;
- default: break;
- }
- assert(index < ProfileCount);
- return index;
- }
- const int SourceCount = 2;
- int MapSourceToIndex(EShSource source)
- {
- int index = 0;
- switch (source) {
- case EShSourceGlsl: index = 0; break;
- case EShSourceHlsl: index = 1; break;
- default: break;
- }
- assert(index < SourceCount);
- return index;
- }
- // 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 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][SpvVersionCount][ProfileCount][SourceCount][EPcCount] = {};
- TSymbolTable* SharedSymbolTables[VersionCount][SpvVersionCount][ProfileCount][SourceCount][EShLangCount] = {};
- TPoolAllocator* PerProcessGPA = nullptr;
- //
- // Parse and add to the given symbol table the content of the given shader string.
- //
- bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language,
- EShSource source, TInfoSink& infoSink, TSymbolTable& symbolTable)
- {
- TIntermediate intermediate(language, version, profile);
- intermediate.setSource(source);
- std::unique_ptr<TParseContextBase> parseContext(CreateParseContext(symbolTable, intermediate, version, profile, source,
- language, infoSink, spvVersion, true, EShMsgDefault,
- true));
- TShader::ForbidIncluder includer;
- TPpContext ppContext(*parseContext, "", includer);
- 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();
- if (builtInLengths[0] == 0)
- return true;
- 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());
- printf("%s\n", builtInShaders[0]);
- 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(TBuiltInParseables& builtInParseables, int version, EProfile profile, const SpvVersion& spvVersion,
- EShLanguage language, EShSource source, TInfoSink& infoSink, TSymbolTable** commonTable,
- TSymbolTable** symbolTables)
- {
- #ifdef GLSLANG_WEB
- profile = EEsProfile;
- version = 310;
- #elif defined(GLSLANG_ANGLE)
- profile = ECoreProfile;
- version = 450;
- #endif
- (*symbolTables[language]).adoptLevels(*commonTable[CommonIndex(profile, language)]);
- InitializeSymbolTable(builtInParseables.getStageString(language), version, profile, spvVersion, language, source,
- infoSink, *symbolTables[language]);
- builtInParseables.identifyBuiltIns(version, profile, spvVersion, 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, const SpvVersion& spvVersion, EShSource source)
- {
- #ifdef GLSLANG_WEB
- profile = EEsProfile;
- version = 310;
- #elif defined(GLSLANG_ANGLE)
- profile = ECoreProfile;
- version = 450;
- #endif
- std::unique_ptr<TBuiltInParseables> builtInParseables(CreateBuiltInParseables(infoSink, source));
- if (builtInParseables == nullptr)
- return false;
- builtInParseables->initialize(version, profile, spvVersion);
- // do the common tables
- InitializeSymbolTable(builtInParseables->getCommonString(), version, profile, spvVersion, EShLangVertex, source,
- infoSink, *commonTable[EPcGeneral]);
- if (profile == EEsProfile)
- InitializeSymbolTable(builtInParseables->getCommonString(), version, profile, spvVersion, EShLangFragment, source,
- infoSink, *commonTable[EPcFragment]);
- // do the per-stage tables
- // always have vertex and fragment
- InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangVertex, source,
- infoSink, commonTable, symbolTables);
- InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangFragment, source,
- infoSink, commonTable, symbolTables);
- #ifndef GLSLANG_WEB
- // check for tessellation
- if ((profile != EEsProfile && version >= 150) ||
- (profile == EEsProfile && version >= 310)) {
- InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangTessControl, source,
- infoSink, commonTable, symbolTables);
- InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangTessEvaluation, source,
- infoSink, commonTable, symbolTables);
- }
- // check for geometry
- if ((profile != EEsProfile && version >= 150) ||
- (profile == EEsProfile && version >= 310))
- InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangGeometry, source,
- infoSink, commonTable, symbolTables);
- // check for compute
- if ((profile != EEsProfile && version >= 420) ||
- (profile == EEsProfile && version >= 310))
- InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangCompute, source,
- infoSink, commonTable, symbolTables);
- #ifndef GLSLANG_ANGLE
- // check for ray tracing stages
- if (profile != EEsProfile && version >= 450) {
- InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangRayGen, source,
- infoSink, commonTable, symbolTables);
- InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangIntersect, source,
- infoSink, commonTable, symbolTables);
- InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangAnyHit, source,
- infoSink, commonTable, symbolTables);
- InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangClosestHit, source,
- infoSink, commonTable, symbolTables);
- InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangMiss, source,
- infoSink, commonTable, symbolTables);
- InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangCallable, source,
- infoSink, commonTable, symbolTables);
- }
- // check for mesh
- if ((profile != EEsProfile && version >= 450) ||
- (profile == EEsProfile && version >= 320))
- InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangMeshNV, source,
- infoSink, commonTable, symbolTables);
- // check for task
- if ((profile != EEsProfile && version >= 450) ||
- (profile == EEsProfile && version >= 320))
- InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangTaskNV, source,
- infoSink, commonTable, symbolTables);
- #endif // !GLSLANG_ANGLE
- #endif // !GLSLANG_WEB
- return true;
- }
- bool AddContextSpecificSymbols(const TBuiltInResource* resources, TInfoSink& infoSink, TSymbolTable& symbolTable, int version,
- EProfile profile, const SpvVersion& spvVersion, EShLanguage language, EShSource source)
- {
- std::unique_ptr<TBuiltInParseables> builtInParseables(CreateBuiltInParseables(infoSink, source));
- if (builtInParseables == nullptr)
- return false;
- builtInParseables->initialize(*resources, version, profile, spvVersion, language);
- InitializeSymbolTable(builtInParseables->getCommonString(), version, profile, spvVersion, language, source, infoSink, symbolTable);
- builtInParseables->identifyBuiltIns(version, profile, spvVersion, 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 of 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, const SpvVersion& spvVersion, EShSource source)
- {
- 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 spvVersionIndex = MapSpvVersionToIndex(spvVersion);
- int profileIndex = MapProfileToIndex(profile);
- int sourceIndex = MapSourceToIndex(source);
- if (CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][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, spvVersion, source);
- // 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][spvVersionIndex][profileIndex][sourceIndex][precClass] = new TSymbolTable;
- CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][precClass]->copyTable(*commonTable[precClass]);
- CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][precClass]->readOnly();
- }
- }
- for (int stage = 0; stage < EShLangCount; ++stage) {
- if (! stageTables[stage]->isEmpty()) {
- SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage] = new TSymbolTable;
- SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage]->adoptLevels(*CommonSymbolTable
- [versionIndex][spvVersionIndex][profileIndex][sourceIndex][CommonIndex(profile, (EShLanguage)stage)]);
- SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage]->copyTable(*stageTables[stage]);
- SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][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();
- }
- // Function to Print all builtins
- void DumpBuiltinSymbolTable(TInfoSink& infoSink, const TSymbolTable& symbolTable)
- {
- #if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
- infoSink.debug << "BuiltinSymbolTable {\n";
- symbolTable.dump(infoSink, true);
- infoSink.debug << "}\n";
- #endif
- }
- // Return true if the shader was correctly specified for version/profile/stage.
- bool DeduceVersionProfile(TInfoSink& infoSink, EShLanguage stage, bool versionNotFirst, int defaultVersion,
- EShSource source, int& version, EProfile& profile, const SpvVersion& spvVersion)
- {
- const int FirstProfileVersion = 150;
- bool correct = true;
- if (source == EShSourceHlsl) {
- version = 500; // shader model; currently a characteristic of glslang, not the input
- profile = ECoreProfile; // allow doubles in prototype parsing
- return correct;
- }
- // Get a 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 || version == 320) {
- correct = false;
- infoSink.info.message(EPrefixError, "#version: versions 300, 310, and 320 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 || version == 320) {
- if (profile != EEsProfile) {
- correct = false;
- infoSink.info.message(EPrefixError, "#version: versions 300, 310, and 320 support only the es profile");
- }
- profile = EEsProfile;
- } else {
- if (profile == EEsProfile) {
- correct = false;
- infoSink.info.message(EPrefixError, "#version: only version 300, 310, and 320 support the es profile");
- if (version >= FirstProfileVersion)
- profile = ECoreProfile;
- else
- profile = ENoProfile;
- }
- // else: typical desktop case... e.g., "#version 410 core"
- }
- }
- // Fix version...
- switch (version) {
- // ES versions
- case 100: break;
- case 300: break;
- case 310: break;
- case 320: break;
- // desktop versions
- case 110: break;
- case 120: break;
- case 130: break;
- case 140: break;
- case 150: break;
- case 330: break;
- case 400: break;
- case 410: break;
- case 420: break;
- case 430: break;
- case 440: break;
- case 450: break;
- case 460: break;
- // unknown version
- default:
- correct = false;
- infoSink.info.message(EPrefixError, "version not supported");
- if (profile == EEsProfile)
- version = 310;
- else {
- version = 450;
- profile = ECoreProfile;
- }
- break;
- }
- #if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
- // 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 : 420;
- }
- break;
- case EShLangRayGen:
- case EShLangIntersect:
- case EShLangAnyHit:
- case EShLangClosestHit:
- case EShLangMiss:
- case EShLangCallable:
- if (profile == EEsProfile || version < 460) {
- correct = false;
- infoSink.info.message(EPrefixError, "#version: ray tracing shaders require non-es profile with version 460 or above");
- version = 460;
- }
- break;
- case EShLangMeshNV:
- case EShLangTaskNV:
- if ((profile == EEsProfile && version < 320) ||
- (profile != EEsProfile && version < 450)) {
- correct = false;
- infoSink.info.message(EPrefixError, "#version: mesh/task shaders require es profile with version 320 or above, or non-es profile with version 450 or above");
- version = profile == EEsProfile ? 320 : 450;
- }
- 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");
- }
- // Check for SPIR-V compatibility
- if (spvVersion.spv != 0) {
- switch (profile) {
- case EEsProfile:
- if (version < 310) {
- correct = false;
- infoSink.info.message(EPrefixError, "#version: ES shaders for SPIR-V require version 310 or higher");
- version = 310;
- }
- break;
- case ECompatibilityProfile:
- infoSink.info.message(EPrefixError, "#version: compilation for SPIR-V does not support the compatibility profile");
- break;
- default:
- if (spvVersion.vulkan > 0 && version < 140) {
- correct = false;
- infoSink.info.message(EPrefixError, "#version: Desktop shaders for Vulkan SPIR-V require version 140 or higher");
- version = 140;
- }
- if (spvVersion.openGl >= 100 && version < 330) {
- correct = false;
- infoSink.info.message(EPrefixError, "#version: Desktop shaders for OpenGL SPIR-V require version 330 or higher");
- version = 330;
- }
- break;
- }
- }
- #endif
- 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 = EShTargetSpv_1_0;
- if (messages & EShMsgVulkanRules) {
- spvVersion.vulkan = EShTargetVulkan_1_0;
- 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;
- case EShClientCount:
- assert(0);
- 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;
- case EShSourceCount:
- assert(0);
- 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;
- }
- }
- }
- // Most processes are recorded when set in the intermediate representation,
- // These are the few that are not.
- void RecordProcesses(TIntermediate& intermediate, EShMessages messages, const std::string& sourceEntryPointName)
- {
- if ((messages & EShMsgRelaxedErrors) != 0)
- intermediate.addProcess("relaxed-errors");
- if ((messages & EShMsgSuppressWarnings) != 0)
- intermediate.addProcess("suppress-warnings");
- if ((messages & EShMsgKeepUncalled) != 0)
- intermediate.addProcess("keep-uncalled");
- if (sourceEntryPointName.size() > 0) {
- intermediate.addProcess("source-entrypoint");
- intermediate.addProcessArgument(sourceEntryPointName);
- }
- }
- // This is the common setup and cleanup code for PreprocessDeferred and
- // CompileDeferred.
- // It takes any callable with a signature of
- // bool (TParseContextBase& 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* const stringNames[],
- 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
- 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,
- TShader::Includer& includer,
- const std::string sourceEntryPointName = "",
- const TEnvironment* environment = nullptr) // optional way of fully setting all versions, overriding the above
- {
- // 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;
- const int numTotal = numPre + numStrings + numPost;
- std::unique_ptr<size_t[]> lengths(new size_t[numTotal]);
- std::unique_ptr<const char*[]> strings(new const char*[numTotal]);
- std::unique_ptr<const char*[]> names(new const char*[numTotal]);
- for (int s = 0; s < numStrings; ++s) {
- strings[s + numPre] = shaderStrings[s];
- if (inputLengths == nullptr || inputLengths[s] < 0)
- lengths[s + numPre] = strlen(shaderStrings[s]);
- else
- lengths[s + numPre] = inputLengths[s];
- }
- if (stringNames != nullptr) {
- for (int s = 0; s < numStrings; ++s)
- names[s + numPre] = stringNames[s];
- } else {
- for (int s = 0; s < numStrings; ++s)
- names[s + numPre] = nullptr;
- }
- // Get all the stages, languages, clients, and other environment
- // stuff sorted out.
- EShSource sourceGuess = (messages & EShMsgReadHlsl) != 0 ? EShSourceHlsl : EShSourceGlsl;
- SpvVersion spvVersion;
- EShLanguage stage = compiler->getLanguage();
- TranslateEnvironment(environment, messages, sourceGuess, stage, spvVersion);
- #ifdef ENABLE_HLSL
- EShSource source = sourceGuess;
- if (environment != nullptr && environment->target.hlslFunctionality1)
- intermediate.setHlslFunctionality1();
- #else
- const EShSource source = EShSourceGlsl;
- #endif
- // First, without using the preprocessor or parser, find the #version, so we know what
- // symbol tables, processing rules, etc. to set up. This does not need the extra strings
- // outlined above, just the user shader, after the system and user preambles.
- glslang::TInputScanner userInput(numStrings, &strings[numPre], &lengths[numPre]);
- int version = 0;
- EProfile profile = ENoProfile;
- bool versionNotFirstToken = false;
- bool versionNotFirst = (source == EShSourceHlsl)
- ? true
- : userInput.scanVersion(version, profile, versionNotFirstToken);
- bool versionNotFound = version == 0;
- if (forceDefaultVersionAndProfile && source == EShSourceGlsl) {
- #if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
- 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";
- }
- #endif
- if (versionNotFound) {
- versionNotFirstToken = false;
- versionNotFirst = false;
- versionNotFound = false;
- }
- version = defaultVersion;
- profile = defaultProfile;
- }
- bool goodVersion = DeduceVersionProfile(compiler->infoSink, stage,
- versionNotFirst, defaultVersion, source, version, profile, spvVersion);
- #ifdef GLSLANG_WEB
- profile = EEsProfile;
- version = 310;
- #elif defined(GLSLANG_ANGLE)
- profile = ECoreProfile;
- version = 450;
- #endif
- bool versionWillBeError = (versionNotFound || (profile == EEsProfile && version >= 300 && versionNotFirst));
- #if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
- bool warnVersionNotFirst = false;
- if (! versionWillBeError && versionNotFirstToken) {
- if (messages & EShMsgRelaxedErrors)
- warnVersionNotFirst = true;
- else
- versionWillBeError = true;
- }
- #endif
- intermediate.setSource(source);
- intermediate.setVersion(version);
- intermediate.setProfile(profile);
- intermediate.setSpv(spvVersion);
- RecordProcesses(intermediate, messages, sourceEntryPointName);
- if (spvVersion.vulkan > 0)
- intermediate.setOriginUpperLeft();
- #ifdef ENABLE_HLSL
- if ((messages & EShMsgHlslOffsets) || source == EShSourceHlsl)
- intermediate.setHlslOffsets();
- #endif
- if (messages & EShMsgDebugInfo) {
- intermediate.setSourceFile(names[numPre]);
- for (int s = 0; s < numStrings; ++s) {
- // The string may not be null-terminated, so make sure we provide
- // the length along with the string.
- intermediate.addSourceText(strings[numPre + s], lengths[numPre + s]);
- }
- }
- SetupBuiltinSymbolTable(version, profile, spvVersion, source);
- TSymbolTable* cachedTable = SharedSymbolTables[MapVersionToIndex(version)]
- [MapSpvVersionToIndex(spvVersion)]
- [MapProfileToIndex(profile)]
- [MapSourceToIndex(source)]
- [stage];
- // Dynamically allocate the symbol table so we can control when it is deallocated WRT the pool.
- std::unique_ptr<TSymbolTable> symbolTable(new TSymbolTable);
- if (cachedTable)
- symbolTable->adoptLevels(*cachedTable);
- // Add built-in symbols that are potentially context dependent;
- // they get popped again further down.
- if (! AddContextSpecificSymbols(resources, compiler->infoSink, *symbolTable, version, profile, spvVersion,
- stage, source)) {
- return false;
- }
- if (messages & EShMsgBuiltinSymbolTable)
- DumpBuiltinSymbolTable(compiler->infoSink, *symbolTable);
- //
- // Now we can process the full shader under proper symbols and rules.
- //
- std::unique_ptr<TParseContextBase> parseContext(CreateParseContext(*symbolTable, intermediate, version, profile, source,
- stage, compiler->infoSink,
- spvVersion, forwardCompatible, messages, false, sourceEntryPointName));
- TPpContext ppContext(*parseContext, names[numPre] ? names[numPre] : "", includer);
- // only GLSL (bison triggered, really) needs an externally set scan context
- glslang::TScanContext scanContext(*parseContext);
- if (source == EShSourceGlsl)
- parseContext->setScanContext(&scanContext);
- parseContext->setPpContext(&ppContext);
- parseContext->setLimits(*resources);
- if (! goodVersion)
- parseContext->addError();
- #if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
- if (warnVersionNotFirst) {
- TSourceLoc loc;
- loc.init();
- parseContext->warn(loc, "Illegal to have non-comment, non-whitespace tokens before #version", "#version", "");
- }
- #endif
- parseContext->initializeExtensionBehavior();
- // Fill in the strings as outlined above.
- std::string preamble;
- parseContext->getPreamble(preamble);
- strings[0] = preamble.c_str();
- lengths[0] = strlen(strings[0]);
- names[0] = nullptr;
- strings[1] = customPreamble;
- lengths[1] = strlen(strings[1]);
- names[1] = nullptr;
- assert(2 == numPre);
- if (requireNonempty) {
- const int postIndex = numStrings + numPre;
- strings[postIndex] = "\n int;";
- lengths[postIndex] = strlen(strings[numStrings + numPre]);
- names[postIndex] = nullptr;
- }
- TInputScanner fullInput(numStrings + numPre + numPost, strings.get(), lengths.get(), names.get(), 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);
- return success;
- }
- #if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
- // Responsible for keeping track of the most recent source string and line in
- // the preprocessor and outputting newlines appropriately if the source string
- // or line changes.
- class SourceLineSynchronizer {
- public:
- SourceLineSynchronizer(const std::function<int()>& lastSourceIndex,
- std::string* output)
- : getLastSourceIndex(lastSourceIndex), output(output), lastSource(-1), lastLine(0) {}
- // SourceLineSynchronizer(const SourceLineSynchronizer&) = delete;
- // SourceLineSynchronizer& operator=(const SourceLineSynchronizer&) = delete;
- // Sets the internally tracked source string index to that of the most
- // recently read token. If we switched to a new source string, returns
- // true and inserts a newline. Otherwise, returns false and outputs nothing.
- bool syncToMostRecentString() {
- if (getLastSourceIndex() != lastSource) {
- // After switching to a new source string, we need to reset lastLine
- // because line number resets every time a new source string is
- // used. We also need to output a newline to separate the output
- // from the previous source string (if there is one).
- if (lastSource != -1 || lastLine != 0)
- *output += '\n';
- lastSource = getLastSourceIndex();
- lastLine = -1;
- return true;
- }
- return false;
- }
- // Calls syncToMostRecentString() and then sets the internally tracked line
- // number to tokenLine. If we switched to a new line, returns true and inserts
- // newlines appropriately. Otherwise, returns false and outputs nothing.
- bool syncToLine(int tokenLine) {
- syncToMostRecentString();
- const bool newLineStarted = lastLine < tokenLine;
- for (; lastLine < tokenLine; ++lastLine) {
- if (lastLine > 0) *output += '\n';
- }
- return newLineStarted;
- }
- // Sets the internally tracked line number to newLineNum.
- void setLineNum(int newLineNum) { lastLine = newLineNum; }
- private:
- SourceLineSynchronizer& operator=(const SourceLineSynchronizer&);
- // A function for getting the index of the last valid source string we've
- // read tokens from.
- const std::function<int()> getLastSourceIndex;
- // output string for newlines.
- std::string* output;
- // lastSource is the source string index (starting from 0) of the last token
- // processed. It is tracked in order for newlines to be inserted when a new
- // source string starts. -1 means we haven't started processing any source
- // string.
- int lastSource;
- // lastLine is the line number (starting from 1) of the last token processed.
- // It is tracked in order for newlines to be inserted when a token appears
- // on a new line. 0 means we haven't started processing any line in the
- // current source string.
- int lastLine;
- };
- // 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.
- //
- // This is not an officially supported or fully working path.
- struct DoPreprocessing {
- explicit DoPreprocessing(std::string* string): outputString(string) {}
- bool operator()(TParseContextBase& 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 ppToken;
- parseContext.setScanner(&input);
- ppContext.setInput(input, versionWillBeError);
- std::string outputBuffer;
- SourceLineSynchronizer lineSync(
- std::bind(&TInputScanner::getLastValidSourceIndex, &input), &outputBuffer);
- parseContext.setExtensionCallback([&lineSync, &outputBuffer](
- int line, const char* extension, const char* behavior) {
- lineSync.syncToLine(line);
- outputBuffer += "#extension ";
- outputBuffer += extension;
- outputBuffer += " : ";
- outputBuffer += behavior;
- });
- parseContext.setLineCallback([&lineSync, &outputBuffer, &parseContext](
- int curLineNum, int newLineNum, bool hasSource, int sourceNum, const char* sourceName) {
- // SourceNum is the number of the source-string that is being parsed.
- lineSync.syncToLine(curLineNum);
- outputBuffer += "#line ";
- outputBuffer += std::to_string(newLineNum);
- if (hasSource) {
- outputBuffer += ' ';
- if (sourceName != nullptr) {
- outputBuffer += '\"';
- outputBuffer += sourceName;
- outputBuffer += '\"';
- } else {
- outputBuffer += std::to_string(sourceNum);
- }
- }
- if (parseContext.lineDirectiveShouldSetNextLine()) {
- // newLineNum is the new line number for the line following the #line
- // directive. So the new line number for the current line is
- newLineNum -= 1;
- }
- outputBuffer += '\n';
- // And we are at the next line of the #line directive now.
- lineSync.setLineNum(newLineNum + 1);
- });
- parseContext.setVersionCallback(
- [&lineSync, &outputBuffer](int line, int version, const char* str) {
- lineSync.syncToLine(line);
- outputBuffer += "#version ";
- outputBuffer += std::to_string(version);
- if (str) {
- outputBuffer += ' ';
- outputBuffer += str;
- }
- });
- parseContext.setPragmaCallback([&lineSync, &outputBuffer](
- int line, const glslang::TVector<glslang::TString>& ops) {
- lineSync.syncToLine(line);
- outputBuffer += "#pragma ";
- for(size_t i = 0; i < ops.size(); ++i) {
- outputBuffer += ops[i].c_str();
- }
- });
- parseContext.setErrorCallback([&lineSync, &outputBuffer](
- int line, const char* errorMessage) {
- lineSync.syncToLine(line);
- outputBuffer += "#error ";
- outputBuffer += errorMessage;
- });
- int lastToken = EndOfInput; // lastToken records the last token processed.
- do {
- int token = ppContext.tokenize(ppToken);
- if (token == EndOfInput)
- break;
- bool isNewString = lineSync.syncToMostRecentString();
- bool isNewLine = lineSync.syncToLine(ppToken.loc.line);
- if (isNewLine) {
- // Don't emit whitespace onto empty lines.
- // Copy any whitespace characters at the start of a line
- // from the input to the output.
- outputBuffer += std::string(ppToken.loc.column - 1, ' ');
- }
- // 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 (!isNewString && !isNewLine && lastToken != EndOfInput &&
- (unNeededSpaceTokens.find((char)token) == std::string::npos) &&
- (unNeededSpaceTokens.find((char)lastToken) == std::string::npos) &&
- (noSpaceBeforeTokens.find((char)token) == std::string::npos)) {
- outputBuffer += ' ';
- }
- lastToken = token;
- if (token == PpAtomConstString)
- outputBuffer += "\"";
- outputBuffer += ppToken.name;
- if (token == PpAtomConstString)
- outputBuffer += "\"";
- } while (true);
- outputBuffer += '\n';
- *outputString = std::move(outputBuffer);
- bool success = true;
- if (parseContext.getNumErrors() > 0) {
- success = false;
- parseContext.infoSink.info.prefix(EPrefixError);
- parseContext.infoSink.info << parseContext.getNumErrors() << " compilation errors. No code generated.\n\n";
- }
- return success;
- }
- std::string* outputString;
- };
- #endif
- // DoFullParse is a valid ProcessingConext template argument for fully
- // parsing the shader. It populates the "intermediate" with the AST.
- struct DoFullParse{
- bool operator()(TParseContextBase& parseContext, TPpContext& ppContext,
- TInputScanner& fullInput, bool versionWillBeError,
- TSymbolTable&, TIntermediate& intermediate,
- EShOptimizationLevel optLevel, EShMessages messages)
- {
- bool success = true;
- // Parse the full shader.
- if (! parseContext.parseShaderStrings(ppContext, fullInput, versionWillBeError))
- success = false;
- 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.getLanguage());
- } else if (! success) {
- parseContext.infoSink.info.prefix(EPrefixError);
- parseContext.infoSink.info << parseContext.getNumErrors() << " compilation errors. No code generated.\n\n";
- }
- #ifndef GLSLANG_ANGLE
- if (messages & EShMsgAST)
- intermediate.output(parseContext.infoSink, true);
- #endif
- return success;
- }
- };
- #if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
- // 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.
- //
- // NOTE: Doing just preprocessing to obtain a correct preprocessed shader string
- // is not an officially supported or fully working path.
- bool PreprocessDeferred(
- TCompiler* compiler,
- const char* const shaderStrings[],
- const int numStrings,
- const int* inputLengths,
- const char* const stringNames[],
- 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
- TShader::Includer& includer,
- TIntermediate& intermediate, // returned tree, etc.
- std::string* outputString)
- {
- DoPreprocessing parser(outputString);
- return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames,
- preamble, optLevel, resources, defaultVersion,
- defaultProfile, forceDefaultVersionAndProfile,
- forwardCompatible, messages, intermediate, parser,
- false, includer);
- }
- #endif
- //
- // 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* const stringNames[],
- 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.
- TShader::Includer& includer,
- const std::string sourceEntryPointName = "",
- TEnvironment* environment = nullptr)
- {
- DoFullParse parser;
- return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames,
- preamble, optLevel, resources, defaultVersion,
- defaultProfile, forceDefaultVersionAndProfile,
- forwardCompatible, messages, intermediate, parser,
- true, includer, sourceEntryPointName, environment);
- }
- } // 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;
- glslang::GetGlobalLock();
- ++NumberOfClients;
- glslang::ReleaseGlobalLock();
- if (PerProcessGPA == nullptr)
- PerProcessGPA = new TPoolAllocator();
- glslang::TScanContext::fillInKeywordMap();
- #ifdef ENABLE_HLSL
- glslang::HlslScanContext::fillInKeywordMap();
- #endif
- 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 ShFinalize()
- {
- glslang::GetGlobalLock();
- --NumberOfClients;
- assert(NumberOfClients >= 0);
- bool finalize = NumberOfClients == 0;
- glslang::ReleaseGlobalLock();
- if (! finalize)
- return 1;
- for (int version = 0; version < VersionCount; ++version) {
- for (int spvVersion = 0; spvVersion < SpvVersionCount; ++spvVersion) {
- for (int p = 0; p < ProfileCount; ++p) {
- for (int source = 0; source < SourceCount; ++source) {
- for (int stage = 0; stage < EShLangCount; ++stage) {
- delete SharedSymbolTables[version][spvVersion][p][source][stage];
- SharedSymbolTables[version][spvVersion][p][source][stage] = 0;
- }
- }
- }
- }
- }
- for (int version = 0; version < VersionCount; ++version) {
- for (int spvVersion = 0; spvVersion < SpvVersionCount; ++spvVersion) {
- for (int p = 0; p < ProfileCount; ++p) {
- for (int source = 0; source < SourceCount; ++source) {
- for (int pc = 0; pc < EPcCount; ++pc) {
- delete CommonSymbolTable[version][spvVersion][p][source][pc];
- CommonSymbolTable[version][spvVersion][p][source][pc] = 0;
- }
- }
- }
- }
- }
- if (PerProcessGPA != nullptr) {
- delete PerProcessGPA;
- PerProcessGPA = nullptr;
- }
- glslang::TScanContext::deleteKeywordMap();
- #ifdef ENABLE_HLSL
- glslang::HlslScanContext::deleteKeywordMap();
- #endif
- 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;
- SetThreadPoolAllocator(compiler->getPool());
- compiler->infoSink.info.erase();
- compiler->infoSink.debug.erase();
- TIntermediate intermediate(compiler->getLanguage());
- TShader::ForbidIncluder includer;
- bool success = CompileDeferred(compiler, shaderStrings, numStrings, inputLengths, nullptr,
- "", optLevel, resources, defaultVersion, ENoProfile, false,
- forwardCompatible, messages, intermediate, includer);
- //
- // 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());
- SetThreadPoolAllocator(linker->getPool());
- 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 (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 (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 (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 (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 (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 (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 {
- Version GetVersion()
- {
- Version version;
- version.major = GLSLANG_VERSION_MAJOR;
- version.minor = GLSLANG_VERSION_MINOR;
- version.patch = GLSLANG_VERSION_PATCH;
- version.flavor = GLSLANG_VERSION_FLAVOR;
- return version;
- }
- #define QUOTE(s) #s
- #define STR(n) QUOTE(n)
- const char* GetEsslVersionString()
- {
- return "OpenGL ES GLSL 3.20 glslang Khronos. " STR(GLSLANG_VERSION_MAJOR) "." STR(GLSLANG_VERSION_MINOR) "." STR(
- GLSLANG_VERSION_PATCH) GLSLANG_VERSION_FLAVOR;
- }
- const char* GetGlslVersionString()
- {
- return "4.60 glslang Khronos. " STR(GLSLANG_VERSION_MAJOR) "." STR(GLSLANG_VERSION_MINOR) "." STR(
- GLSLANG_VERSION_PATCH) GLSLANG_VERSION_FLAVOR;
- }
- int GetKhronosToolId()
- {
- return 8;
- }
- 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)
- : stage(s), lengths(nullptr), stringNames(nullptr), preamble("")
- {
- pool = new TPoolAllocator;
- infoSink = new TInfoSink;
- compiler = new TDeferredCompiler(stage, *infoSink);
- intermediate = new TIntermediate(s);
- // clear environment (avoid constructors in them for use in a C interface)
- environment.input.languageFamily = EShSourceNone;
- environment.input.dialect = EShClientNone;
- environment.client.client = EShClientNone;
- environment.target.language = EShTargetNone;
- environment.target.hlslFunctionality1 = false;
- }
- TShader::~TShader()
- {
- delete infoSink;
- delete compiler;
- delete intermediate;
- delete pool;
- }
- void TShader::setStrings(const char* const* s, int n)
- {
- strings = s;
- numStrings = n;
- lengths = nullptr;
- }
- void TShader::setStringsWithLengths(const char* const* s, const int* l, int n)
- {
- strings = s;
- numStrings = n;
- lengths = l;
- }
- void TShader::setStringsWithLengthsAndNames(
- const char* const* s, const int* l, const char* const* names, int n)
- {
- strings = s;
- numStrings = n;
- lengths = l;
- stringNames = names;
- }
- void TShader::setEntryPoint(const char* entryPoint)
- {
- intermediate->setEntryPointName(entryPoint);
- }
- void TShader::setSourceEntryPoint(const char* name)
- {
- sourceEntryPointName = name;
- }
- // Log initial settings and transforms.
- // See comment for class TProcesses.
- void TShader::addProcesses(const std::vector<std::string>& p)
- {
- intermediate->addProcesses(p);
- }
- void TShader::setInvertY(bool invert) { intermediate->setInvertY(invert); }
- void TShader::setNanMinMaxClamp(bool useNonNan) { intermediate->setNanMinMaxClamp(useNonNan); }
- #ifndef GLSLANG_WEB
- // Set binding base for given resource type
- void TShader::setShiftBinding(TResourceType res, unsigned int base) {
- intermediate->setShiftBinding(res, base);
- }
- // Set binding base for given resource type for a given binding set.
- void TShader::setShiftBindingForSet(TResourceType res, unsigned int base, unsigned int set) {
- intermediate->setShiftBindingForSet(res, base, set);
- }
- // Set binding base for sampler types
- void TShader::setShiftSamplerBinding(unsigned int base) { setShiftBinding(EResSampler, base); }
- // Set binding base for texture types (SRV)
- void TShader::setShiftTextureBinding(unsigned int base) { setShiftBinding(EResTexture, base); }
- // Set binding base for image types
- void TShader::setShiftImageBinding(unsigned int base) { setShiftBinding(EResImage, base); }
- // Set binding base for uniform buffer objects (CBV)
- void TShader::setShiftUboBinding(unsigned int base) { setShiftBinding(EResUbo, base); }
- // Synonym for setShiftUboBinding, to match HLSL language.
- void TShader::setShiftCbufferBinding(unsigned int base) { setShiftBinding(EResUbo, base); }
- // Set binding base for UAV (unordered access view)
- void TShader::setShiftUavBinding(unsigned int base) { setShiftBinding(EResUav, base); }
- // Set binding base for SSBOs
- void TShader::setShiftSsboBinding(unsigned int base) { setShiftBinding(EResSsbo, base); }
- // Enables binding automapping using TIoMapper
- void TShader::setAutoMapBindings(bool map) { intermediate->setAutoMapBindings(map); }
- // Enables position.Y output negation in vertex shader
- // Fragile: currently within one stage: simple auto-assignment of location
- void TShader::setAutoMapLocations(bool map) { intermediate->setAutoMapLocations(map); }
- void TShader::addUniformLocationOverride(const char* name, int loc)
- {
- intermediate->addUniformLocationOverride(name, loc);
- }
- void TShader::setUniformLocationBase(int base)
- {
- intermediate->setUniformLocationBase(base);
- }
- void TShader::setNoStorageFormat(bool useUnknownFormat) { intermediate->setNoStorageFormat(useUnknownFormat); }
- void TShader::setResourceSetBinding(const std::vector<std::string>& base) { intermediate->setResourceSetBinding(base); }
- void TShader::setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode) { intermediate->setTextureSamplerTransformMode(mode); }
- #endif
- #ifdef ENABLE_HLSL
- // See comment above TDefaultHlslIoMapper in iomapper.cpp:
- void TShader::setHlslIoMapping(bool hlslIoMap) { intermediate->setHlslIoMapping(hlslIoMap); }
- void TShader::setFlattenUniformArrays(bool flatten) { intermediate->setFlattenUniformArrays(flatten); }
- #endif
- //
- // 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, Includer& includer)
- {
- if (! InitThread())
- return false;
- SetThreadPoolAllocator(pool);
- if (! preamble)
- preamble = "";
- return CompileDeferred(compiler, strings, numStrings, lengths, stringNames,
- preamble, EShOptNone, builtInResources, defaultVersion,
- defaultProfile, forceDefaultVersionAndProfile,
- forwardCompatible, messages, *intermediate, includer, sourceEntryPointName,
- &environment);
- }
- #if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
- // Fill in a string with the result of preprocessing ShaderStrings
- // Returns true if all extensions, pragmas and version strings were valid.
- //
- // NOTE: Doing just preprocessing to obtain a correct preprocessed shader string
- // is not an officially supported or fully working path.
- bool TShader::preprocess(const TBuiltInResource* builtInResources,
- int defaultVersion, EProfile defaultProfile,
- bool forceDefaultVersionAndProfile,
- bool forwardCompatible, EShMessages message,
- std::string* output_string,
- Includer& includer)
- {
- if (! InitThread())
- return false;
- SetThreadPoolAllocator(pool);
- if (! preamble)
- preamble = "";
- return PreprocessDeferred(compiler, strings, numStrings, lengths, stringNames, preamble,
- EShOptNone, builtInResources, defaultVersion,
- defaultProfile, forceDefaultVersionAndProfile,
- forwardCompatible, message, includer, *intermediate, output_string);
- }
- #endif
- const char* TShader::getInfoLog()
- {
- return infoSink->info.c_str();
- }
- const char* TShader::getInfoDebugLog()
- {
- return infoSink->debug.c_str();
- }
- TProgram::TProgram() :
- #if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
- reflection(0),
- #endif
- linked(false)
- {
- pool = new TPoolAllocator;
- infoSink = new TInfoSink;
- for (int s = 0; s < EShLangCount; ++s) {
- intermediate[s] = 0;
- newedIntermediate[s] = false;
- }
- }
- TProgram::~TProgram()
- {
- delete infoSink;
- #if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
- delete reflection;
- #endif
- 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;
- 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;
- #if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
- int numEsShaders = 0, numNonEsShaders = 0;
- for (auto it = stages[stage].begin(); it != stages[stage].end(); ++it) {
- if ((*it)->intermediate->getProfile() == EEsProfile) {
- numEsShaders++;
- } else {
- numNonEsShaders++;
- }
- }
- if (numEsShaders > 0 && numNonEsShaders > 0) {
- infoSink->info.message(EPrefixError, "Cannot mix ES profile with non-ES profile shaders");
- return false;
- } else if (numEsShaders > 1) {
- infoSink->info.message(EPrefixError, "Cannot attach multiple ES shaders of the same type to a single program");
- return false;
- }
- //
- // Be efficient for the common single compilation unit per stage case,
- // reusing it's TIntermediate instead of merging into a new one.
- //
- TIntermediate *firstIntermediate = stages[stage].front()->intermediate;
- if (stages[stage].size() == 1)
- intermediate[stage] = firstIntermediate;
- else {
- intermediate[stage] = new TIntermediate(stage,
- firstIntermediate->getVersion(),
- firstIntermediate->getProfile());
- intermediate[stage]->setLimits(firstIntermediate->getLimits());
- // The new TIntermediate must use the same origin as the original TIntermediates.
- // Otherwise linking will fail due to different coordinate systems.
- if (firstIntermediate->getOriginUpperLeft()) {
- intermediate[stage]->setOriginUpperLeft();
- }
- intermediate[stage]->setSpv(firstIntermediate->getSpv());
- newedIntermediate[stage] = true;
- }
- if (messages & EShMsgAST)
- 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);
- }
- #else
- intermediate[stage] = stages[stage].front()->intermediate;
- #endif
- intermediate[stage]->finalCheck(*infoSink, (messages & EShMsgKeepUncalled) != 0);
- #ifndef GLSLANG_ANGLE
- if (messages & EShMsgAST)
- intermediate[stage]->output(*infoSink, true);
- #endif
- return intermediate[stage]->getNumErrors() == 0;
- }
- const char* TProgram::getInfoLog()
- {
- return infoSink->info.c_str();
- }
- const char* TProgram::getInfoDebugLog()
- {
- return infoSink->debug.c_str();
- }
- #if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
- //
- // Reflection implementation.
- //
- bool TProgram::buildReflection(int opts)
- {
- if (! linked || reflection != nullptr)
- return false;
- int firstStage = EShLangVertex, lastStage = EShLangFragment;
- if (opts & EShReflectionIntermediateIO) {
- // if we're reflecting intermediate I/O, determine the first and last stage linked and use those as the
- // boundaries for which stages generate pipeline inputs/outputs
- firstStage = EShLangCount;
- lastStage = 0;
- for (int s = 0; s < EShLangCount; ++s) {
- if (intermediate[s]) {
- firstStage = std::min(firstStage, s);
- lastStage = std::max(lastStage, s);
- }
- }
- }
- reflection = new TReflection((EShReflectionOptions)opts, (EShLanguage)firstStage, (EShLanguage)lastStage);
- for (int s = 0; s < EShLangCount; ++s) {
- if (intermediate[s]) {
- if (! reflection->addStage((EShLanguage)s, *intermediate[s]))
- return false;
- }
- }
- return true;
- }
- unsigned TProgram::getLocalSize(int dim) const { return reflection->getLocalSize(dim); }
- int TProgram::getReflectionIndex(const char* name) const { return reflection->getIndex(name); }
- int TProgram::getReflectionPipeIOIndex(const char* name, const bool inOrOut) const
- { return reflection->getPipeIOIndex(name, inOrOut); }
- int TProgram::getNumUniformVariables() const { return reflection->getNumUniforms(); }
- const TObjectReflection& TProgram::getUniform(int index) const { return reflection->getUniform(index); }
- int TProgram::getNumUniformBlocks() const { return reflection->getNumUniformBlocks(); }
- const TObjectReflection& TProgram::getUniformBlock(int index) const { return reflection->getUniformBlock(index); }
- int TProgram::getNumPipeInputs() const { return reflection->getNumPipeInputs(); }
- const TObjectReflection& TProgram::getPipeInput(int index) const { return reflection->getPipeInput(index); }
- int TProgram::getNumPipeOutputs() const { return reflection->getNumPipeOutputs(); }
- const TObjectReflection& TProgram::getPipeOutput(int index) const { return reflection->getPipeOutput(index); }
- int TProgram::getNumBufferVariables() const { return reflection->getNumBufferVariables(); }
- const TObjectReflection& TProgram::getBufferVariable(int index) const { return reflection->getBufferVariable(index); }
- int TProgram::getNumBufferBlocks() const { return reflection->getNumStorageBuffers(); }
- const TObjectReflection& TProgram::getBufferBlock(int index) const { return reflection->getStorageBufferBlock(index); }
- int TProgram::getNumAtomicCounters() const { return reflection->getNumAtomicCounters(); }
- const TObjectReflection& TProgram::getAtomicCounter(int index) const { return reflection->getAtomicCounter(index); }
- void TProgram::dumpReflection() { if (reflection != nullptr) reflection->dump(); }
- //
- // I/O mapping implementation.
- //
- bool TProgram::mapIO(TIoMapResolver* pResolver, TIoMapper* pIoMapper)
- {
- if (! linked)
- return false;
- TIoMapper* ioMapper = nullptr;
- TIoMapper defaultIOMapper;
- if (pIoMapper == nullptr)
- ioMapper = &defaultIOMapper;
- else
- ioMapper = pIoMapper;
- for (int s = 0; s < EShLangCount; ++s) {
- if (intermediate[s]) {
- if (! ioMapper->addStage((EShLanguage)s, *intermediate[s], *infoSink, pResolver))
- return false;
- }
- }
- return ioMapper->doMap(pResolver, *infoSink);
- }
- #endif // !GLSLANG_WEB && !GLSLANG_ANGLE
- } // end namespace glslang
|