xref: /aosp_15_r20/external/angle/third_party/glslang/src/glslang/MachineIndependent/ShaderLang.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright (C) 2002-2005  3Dlabs Inc. Ltd.
3 // Copyright (C) 2013-2016 LunarG, Inc.
4 // Copyright (C) 2015-2020 Google, Inc.
5 //
6 // All rights reserved.
7 //
8 // Redistribution and use in source and binary forms, with or without
9 // modification, are permitted provided that the following conditions
10 // are met:
11 //
12 //    Redistributions of source code must retain the above copyright
13 //    notice, this list of conditions and the following disclaimer.
14 //
15 //    Redistributions in binary form must reproduce the above
16 //    copyright notice, this list of conditions and the following
17 //    disclaimer in the documentation and/or other materials provided
18 //    with the distribution.
19 //
20 //    Neither the name of 3Dlabs Inc. Ltd. nor the names of its
21 //    contributors may be used to endorse or promote products derived
22 //    from this software without specific prior written permission.
23 //
24 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 // POSSIBILITY OF SUCH DAMAGE.
36 //
37 
38 //
39 // Implement the top-level of interface to the compiler/linker,
40 // as defined in ShaderLang.h
41 // This is the platform independent interface between an OGL driver
42 // and the shading language compiler/linker.
43 //
44 #include <cstring>
45 #include <iostream>
46 #include <sstream>
47 #include <memory>
48 #include <mutex>
49 #include "SymbolTable.h"
50 #include "ParseHelper.h"
51 #include "Scan.h"
52 #include "ScanContext.h"
53 
54 #ifdef ENABLE_HLSL
55 #include "../HLSL/hlslParseHelper.h"
56 #include "../HLSL/hlslParseables.h"
57 #include "../HLSL/hlslScanContext.h"
58 #endif
59 
60 #include "../Include/ShHandle.h"
61 
62 #include "preprocessor/PpContext.h"
63 
64 #define SH_EXPORTING
65 #include "../Public/ShaderLang.h"
66 #include "reflection.h"
67 #include "iomapper.h"
68 #include "Initialize.h"
69 
70 // TODO: this really shouldn't be here, it is only because of the trial addition
71 // of printing pre-processed tokens, which requires knowing the string literal
72 // token to print ", but none of that seems appropriate for this file.
73 #include "preprocessor/PpTokens.h"
74 
75 // Build-time generated includes
76 #include "glslang/build_info.h"
77 
78 namespace { // anonymous namespace for file-local functions and symbols
79 
80 // Total number of successful initializers of glslang: a refcount
81 // Shared global; access should be protected by a global mutex/critical section.
82 int NumberOfClients = 0;
83 
84 // global initialization lock
85 #ifndef DISABLE_THREAD_SUPPORT
86 std::mutex init_lock;
87 #endif
88 
89 
90 using namespace glslang;
91 
92 // Create a language specific version of parseables.
CreateBuiltInParseables(TInfoSink & infoSink,EShSource source)93 TBuiltInParseables* CreateBuiltInParseables(TInfoSink& infoSink, EShSource source)
94 {
95     switch (source) {
96     case EShSourceGlsl: return new TBuiltIns();              // GLSL builtIns
97 #ifdef ENABLE_HLSL
98     case EShSourceHlsl: return new TBuiltInParseablesHlsl(); // HLSL intrinsics
99 #endif
100 
101     default:
102         infoSink.info.message(EPrefixInternalError, "Unable to determine source language");
103         return nullptr;
104     }
105 }
106 
107 // Create a language specific version of a parse context.
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="")108 TParseContextBase* CreateParseContext(TSymbolTable& symbolTable, TIntermediate& intermediate,
109                                       int version, EProfile profile, EShSource source,
110                                       EShLanguage language, TInfoSink& infoSink,
111                                       SpvVersion spvVersion, bool forwardCompatible, EShMessages messages,
112                                       bool parsingBuiltIns, std::string sourceEntryPointName = "")
113 {
114     switch (source) {
115     case EShSourceGlsl: {
116         if (sourceEntryPointName.size() == 0)
117             intermediate.setEntryPointName("main");
118         TString entryPoint = sourceEntryPointName.c_str();
119         return new TParseContext(symbolTable, intermediate, parsingBuiltIns, version, profile, spvVersion,
120                                  language, infoSink, forwardCompatible, messages, &entryPoint);
121     }
122 #ifdef ENABLE_HLSL
123     case EShSourceHlsl:
124         return new HlslParseContext(symbolTable, intermediate, parsingBuiltIns, version, profile, spvVersion,
125                                     language, infoSink, sourceEntryPointName.c_str(), forwardCompatible, messages);
126 #endif
127     default:
128         infoSink.info.message(EPrefixInternalError, "Unable to determine source language");
129         return nullptr;
130     }
131 }
132 
133 // Local mapping functions for making arrays of symbol tables....
134 
135 const int VersionCount = 17;  // index range in MapVersionToIndex
136 
MapVersionToIndex(int version)137 int MapVersionToIndex(int version)
138 {
139     int index = 0;
140 
141     switch (version) {
142     case 100: index =  0; break;
143     case 110: index =  1; break;
144     case 120: index =  2; break;
145     case 130: index =  3; break;
146     case 140: index =  4; break;
147     case 150: index =  5; break;
148     case 300: index =  6; break;
149     case 330: index =  7; break;
150     case 400: index =  8; break;
151     case 410: index =  9; break;
152     case 420: index = 10; break;
153     case 430: index = 11; break;
154     case 440: index = 12; break;
155     case 310: index = 13; break;
156     case 450: index = 14; break;
157     case 500: index =  0; break; // HLSL
158     case 320: index = 15; break;
159     case 460: index = 16; break;
160     default:  assert(0);  break;
161     }
162 
163     assert(index < VersionCount);
164 
165     return index;
166 }
167 
168 const int SpvVersionCount = 4;  // index range in MapSpvVersionToIndex
169 
MapSpvVersionToIndex(const SpvVersion & spvVersion)170 int MapSpvVersionToIndex(const SpvVersion& spvVersion)
171 {
172     int index = 0;
173 
174     if (spvVersion.openGl > 0)
175         index = 1;
176     else if (spvVersion.vulkan > 0) {
177         if (!spvVersion.vulkanRelaxed)
178             index = 2;
179         else
180             index = 3;
181     }
182 
183     assert(index < SpvVersionCount);
184 
185     return index;
186 }
187 
188 const int ProfileCount = 4;   // index range in MapProfileToIndex
189 
MapProfileToIndex(EProfile profile)190 int MapProfileToIndex(EProfile profile)
191 {
192     int index = 0;
193 
194     switch (profile) {
195     case ENoProfile:            index = 0; break;
196     case ECoreProfile:          index = 1; break;
197     case ECompatibilityProfile: index = 2; break;
198     case EEsProfile:            index = 3; break;
199     default:                               break;
200     }
201 
202     assert(index < ProfileCount);
203 
204     return index;
205 }
206 
207 const int SourceCount = 2;
208 
MapSourceToIndex(EShSource source)209 int MapSourceToIndex(EShSource source)
210 {
211     int index = 0;
212 
213     switch (source) {
214     case EShSourceGlsl: index = 0; break;
215     case EShSourceHlsl: index = 1; break;
216     default:                       break;
217     }
218 
219     assert(index < SourceCount);
220 
221     return index;
222 }
223 
224 // only one of these needed for non-ES; ES needs 2 for different precision defaults of built-ins
225 enum EPrecisionClass {
226     EPcGeneral,
227     EPcFragment,
228     EPcCount
229 };
230 
231 // A process-global symbol table per version per profile for built-ins common
232 // to multiple stages (languages), and a process-global symbol table per version
233 // per profile per stage for built-ins unique to each stage.  They will be sparsely
234 // populated, so they will only be generated as needed.
235 //
236 // Each has a different set of built-ins, and we want to preserve that from
237 // compile to compile.
238 //
239 TSymbolTable* CommonSymbolTable[VersionCount][SpvVersionCount][ProfileCount][SourceCount][EPcCount] = {};
240 TSymbolTable* SharedSymbolTables[VersionCount][SpvVersionCount][ProfileCount][SourceCount][EShLangCount] = {};
241 
242 TPoolAllocator* PerProcessGPA = nullptr;
243 
244 //
245 // Parse and add to the given symbol table the content of the given shader string.
246 //
InitializeSymbolTable(const TString & builtIns,int version,EProfile profile,const SpvVersion & spvVersion,EShLanguage language,EShSource source,TInfoSink & infoSink,TSymbolTable & symbolTable)247 bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language,
248                            EShSource source, TInfoSink& infoSink, TSymbolTable& symbolTable)
249 {
250     TIntermediate intermediate(language, version, profile);
251 
252     intermediate.setSource(source);
253 
254     std::unique_ptr<TParseContextBase> parseContext(CreateParseContext(symbolTable, intermediate, version, profile, source,
255                                                                        language, infoSink, spvVersion, true, EShMsgDefault,
256                                                                        true));
257 
258     TShader::ForbidIncluder includer;
259     TPpContext ppContext(*parseContext, "", includer);
260     TScanContext scanContext(*parseContext);
261     parseContext->setScanContext(&scanContext);
262     parseContext->setPpContext(&ppContext);
263 
264     //
265     // Push the symbol table to give it an initial scope.  This
266     // push should not have a corresponding pop, so that built-ins
267     // are preserved, and the test for an empty table fails.
268     //
269 
270     symbolTable.push();
271 
272     const char* builtInShaders[2];
273     size_t builtInLengths[2];
274     builtInShaders[0] = builtIns.c_str();
275     builtInLengths[0] = builtIns.size();
276 
277     if (builtInLengths[0] == 0)
278         return true;
279 
280     TInputScanner input(1, builtInShaders, builtInLengths);
281     if (! parseContext->parseShaderStrings(ppContext, input) != 0) {
282         infoSink.info.message(EPrefixInternalError, "Unable to parse built-ins");
283         printf("Unable to parse built-ins\n%s\n", infoSink.info.c_str());
284         printf("%s\n", builtInShaders[0]);
285 
286         return false;
287     }
288 
289     return true;
290 }
291 
CommonIndex(EProfile profile,EShLanguage language)292 int CommonIndex(EProfile profile, EShLanguage language)
293 {
294     return (profile == EEsProfile && language == EShLangFragment) ? EPcFragment : EPcGeneral;
295 }
296 
297 //
298 // To initialize per-stage shared tables, with the common table already complete.
299 //
InitializeStageSymbolTable(TBuiltInParseables & builtInParseables,int version,EProfile profile,const SpvVersion & spvVersion,EShLanguage language,EShSource source,TInfoSink & infoSink,TSymbolTable ** commonTable,TSymbolTable ** symbolTables)300 bool InitializeStageSymbolTable(TBuiltInParseables& builtInParseables, int version, EProfile profile, const SpvVersion& spvVersion,
301                                 EShLanguage language, EShSource source, TInfoSink& infoSink, TSymbolTable** commonTable,
302                                 TSymbolTable** symbolTables)
303 {
304     (*symbolTables[language]).adoptLevels(*commonTable[CommonIndex(profile, language)]);
305     if (!InitializeSymbolTable(builtInParseables.getStageString(language), version, profile, spvVersion, language, source,
306                           infoSink, *symbolTables[language]))
307         return false;
308     builtInParseables.identifyBuiltIns(version, profile, spvVersion, language, *symbolTables[language]);
309     if (profile == EEsProfile && version >= 300)
310         (*symbolTables[language]).setNoBuiltInRedeclarations();
311     if (version == 110)
312         (*symbolTables[language]).setSeparateNameSpaces();
313 
314     return true;
315 }
316 
317 //
318 // Initialize the full set of shareable symbol tables;
319 // The common (cross-stage) and those shareable per-stage.
320 //
InitializeSymbolTables(TInfoSink & infoSink,TSymbolTable ** commonTable,TSymbolTable ** symbolTables,int version,EProfile profile,const SpvVersion & spvVersion,EShSource source)321 bool InitializeSymbolTables(TInfoSink& infoSink, TSymbolTable** commonTable,  TSymbolTable** symbolTables, int version, EProfile profile, const SpvVersion& spvVersion, EShSource source)
322 {
323     bool success = true;
324     std::unique_ptr<TBuiltInParseables> builtInParseables(CreateBuiltInParseables(infoSink, source));
325 
326     if (builtInParseables == nullptr)
327         return false;
328 
329     builtInParseables->initialize(version, profile, spvVersion);
330 
331     // do the common tables
332     success &= InitializeSymbolTable(builtInParseables->getCommonString(), version, profile, spvVersion, EShLangVertex, source,
333                           infoSink, *commonTable[EPcGeneral]);
334     if (profile == EEsProfile)
335         success &= InitializeSymbolTable(builtInParseables->getCommonString(), version, profile, spvVersion, EShLangFragment, source,
336                               infoSink, *commonTable[EPcFragment]);
337 
338     // do the per-stage tables
339 
340     // always have vertex and fragment
341     success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangVertex, source,
342                                infoSink, commonTable, symbolTables);
343     success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangFragment, source,
344                                infoSink, commonTable, symbolTables);
345 
346     // check for tessellation
347     if ((profile != EEsProfile && version >= 150) ||
348         (profile == EEsProfile && version >= 310)) {
349         success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangTessControl, source,
350                                    infoSink, commonTable, symbolTables);
351         success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangTessEvaluation, source,
352                                    infoSink, commonTable, symbolTables);
353     }
354 
355     // check for geometry
356     if ((profile != EEsProfile && version >= 150) ||
357         (profile == EEsProfile && version >= 310))
358         success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangGeometry, source,
359                                    infoSink, commonTable, symbolTables);
360 
361     // check for compute
362     if ((profile != EEsProfile && version >= 420) ||
363         (profile == EEsProfile && version >= 310))
364         success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangCompute, source,
365                                    infoSink, commonTable, symbolTables);
366 
367     // check for ray tracing stages
368     if (profile != EEsProfile && version >= 450) {
369         success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangRayGen, source,
370             infoSink, commonTable, symbolTables);
371         success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangIntersect, source,
372             infoSink, commonTable, symbolTables);
373         success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangAnyHit, source,
374             infoSink, commonTable, symbolTables);
375         success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangClosestHit, source,
376             infoSink, commonTable, symbolTables);
377         success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangMiss, source,
378             infoSink, commonTable, symbolTables);
379         success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangCallable, source,
380             infoSink, commonTable, symbolTables);
381     }
382 
383     // check for mesh
384     if ((profile != EEsProfile && version >= 450) ||
385         (profile == EEsProfile && version >= 320))
386         success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangMesh, source,
387                                    infoSink, commonTable, symbolTables);
388 
389     // check for task
390     if ((profile != EEsProfile && version >= 450) ||
391         (profile == EEsProfile && version >= 320))
392         success &= InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangTask, source,
393                                    infoSink, commonTable, symbolTables);
394 
395     return success;
396 }
397 
AddContextSpecificSymbols(const TBuiltInResource * resources,TInfoSink & infoSink,TSymbolTable & symbolTable,int version,EProfile profile,const SpvVersion & spvVersion,EShLanguage language,EShSource source)398 bool AddContextSpecificSymbols(const TBuiltInResource* resources, TInfoSink& infoSink, TSymbolTable& symbolTable, int version,
399                                EProfile profile, const SpvVersion& spvVersion, EShLanguage language, EShSource source)
400 {
401     std::unique_ptr<TBuiltInParseables> builtInParseables(CreateBuiltInParseables(infoSink, source));
402 
403     if (builtInParseables == nullptr)
404         return false;
405 
406     builtInParseables->initialize(*resources, version, profile, spvVersion, language);
407     if (!InitializeSymbolTable(builtInParseables->getCommonString(), version, profile, spvVersion, language, source, infoSink, symbolTable))
408         return false;
409     builtInParseables->identifyBuiltIns(version, profile, spvVersion, language, symbolTable, *resources);
410 
411     return true;
412 }
413 
414 //
415 // To do this on the fly, we want to leave the current state of our thread's
416 // pool allocator intact, so:
417 //  - Switch to a new pool for parsing the built-ins
418 //  - Do the parsing, which builds the symbol table, using the new pool
419 //  - Switch to the process-global pool to save a copy of the resulting symbol table
420 //  - Free up the new pool used to parse the built-ins
421 //  - Switch back to the original thread's pool
422 //
423 // This only gets done the first time any thread needs a particular symbol table
424 // (lazy evaluation).
425 //
SetupBuiltinSymbolTable(int version,EProfile profile,const SpvVersion & spvVersion,EShSource source)426 bool SetupBuiltinSymbolTable(int version, EProfile profile, const SpvVersion& spvVersion, EShSource source)
427 {
428     TInfoSink infoSink;
429     bool success;
430 
431     // Make sure only one thread tries to do this at a time
432 #ifndef DISABLE_THREAD_SUPPORT
433     const std::lock_guard<std::mutex> lock(init_lock);
434 #endif
435 
436     // See if it's already been done for this version/profile combination
437     int versionIndex = MapVersionToIndex(version);
438     int spvVersionIndex = MapSpvVersionToIndex(spvVersion);
439     int profileIndex = MapProfileToIndex(profile);
440     int sourceIndex = MapSourceToIndex(source);
441     if (CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][EPcGeneral]) {
442         return true;
443     }
444 
445     // Switch to a new pool
446     TPoolAllocator& previousAllocator = GetThreadPoolAllocator();
447     TPoolAllocator* builtInPoolAllocator = new TPoolAllocator;
448     SetThreadPoolAllocator(builtInPoolAllocator);
449 
450     // Dynamically allocate the local symbol tables so we can control when they are deallocated WRT when the pool is popped.
451     TSymbolTable* commonTable[EPcCount];
452     TSymbolTable* stageTables[EShLangCount];
453     for (int precClass = 0; precClass < EPcCount; ++precClass)
454         commonTable[precClass] = new TSymbolTable;
455     for (int stage = 0; stage < EShLangCount; ++stage)
456         stageTables[stage] = new TSymbolTable;
457 
458     // Generate the local symbol tables using the new pool
459     if (!InitializeSymbolTables(infoSink, commonTable, stageTables, version, profile, spvVersion, source)) {
460         success = false;
461         goto cleanup;
462     }
463 
464     // Switch to the process-global pool
465     SetThreadPoolAllocator(PerProcessGPA);
466 
467     // Copy the local symbol tables from the new pool to the global tables using the process-global pool
468     for (int precClass = 0; precClass < EPcCount; ++precClass) {
469         if (! commonTable[precClass]->isEmpty()) {
470             CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][precClass] = new TSymbolTable;
471             CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][precClass]->copyTable(*commonTable[precClass]);
472             CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][precClass]->readOnly();
473         }
474     }
475     for (int stage = 0; stage < EShLangCount; ++stage) {
476         if (! stageTables[stage]->isEmpty()) {
477             SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage] = new TSymbolTable;
478             SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage]->adoptLevels(*CommonSymbolTable
479                               [versionIndex][spvVersionIndex][profileIndex][sourceIndex][CommonIndex(profile, (EShLanguage)stage)]);
480             SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage]->copyTable(*stageTables[stage]);
481             SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage]->readOnly();
482         }
483     }
484     success = true;
485 
486 cleanup:
487     // Clean up the local tables before deleting the pool they used.
488     for (int precClass = 0; precClass < EPcCount; ++precClass)
489         delete commonTable[precClass];
490     for (int stage = 0; stage < EShLangCount; ++stage)
491         delete stageTables[stage];
492 
493     delete builtInPoolAllocator;
494     SetThreadPoolAllocator(&previousAllocator);
495 
496     return success;
497 }
498 
499 // Function to Print all builtins
DumpBuiltinSymbolTable(TInfoSink & infoSink,const TSymbolTable & symbolTable)500 void DumpBuiltinSymbolTable(TInfoSink& infoSink, const TSymbolTable& symbolTable)
501 {
502     infoSink.debug << "BuiltinSymbolTable {\n";
503 
504     symbolTable.dump(infoSink, true);
505 
506     infoSink.debug << "}\n";
507 }
508 
509 // Return true if the shader was correctly specified for version/profile/stage.
DeduceVersionProfile(TInfoSink & infoSink,EShLanguage stage,bool versionNotFirst,int defaultVersion,EShSource source,int & version,EProfile & profile,const SpvVersion & spvVersion)510 bool DeduceVersionProfile(TInfoSink& infoSink, EShLanguage stage, bool versionNotFirst, int defaultVersion,
511                           EShSource source, int& version, EProfile& profile, const SpvVersion& spvVersion)
512 {
513     const int FirstProfileVersion = 150;
514     bool correct = true;
515 
516     if (source == EShSourceHlsl) {
517         version = 500;          // shader model; currently a characteristic of glslang, not the input
518         profile = ECoreProfile; // allow doubles in prototype parsing
519         return correct;
520     }
521 
522     // Get a version...
523     if (version == 0) {
524         version = defaultVersion;
525         // infoSink.info.message(EPrefixWarning, "#version: statement missing; use #version on first line of shader");
526     }
527 
528     // Get a good profile...
529     if (profile == ENoProfile) {
530         if (version == 300 || version == 310 || version == 320) {
531             correct = false;
532             infoSink.info.message(EPrefixError, "#version: versions 300, 310, and 320 require specifying the 'es' profile");
533             profile = EEsProfile;
534         } else if (version == 100)
535             profile = EEsProfile;
536         else if (version >= FirstProfileVersion)
537             profile = ECoreProfile;
538         else
539             profile = ENoProfile;
540     } else {
541         // a profile was provided...
542         if (version < 150) {
543             correct = false;
544             infoSink.info.message(EPrefixError, "#version: versions before 150 do not allow a profile token");
545             if (version == 100)
546                 profile = EEsProfile;
547             else
548                 profile = ENoProfile;
549         } else if (version == 300 || version == 310 || version == 320) {
550             if (profile != EEsProfile) {
551                 correct = false;
552                 infoSink.info.message(EPrefixError, "#version: versions 300, 310, and 320 support only the es profile");
553             }
554             profile = EEsProfile;
555         } else {
556             if (profile == EEsProfile) {
557                 correct = false;
558                 infoSink.info.message(EPrefixError, "#version: only version 300, 310, and 320 support the es profile");
559                 if (version >= FirstProfileVersion)
560                     profile = ECoreProfile;
561                 else
562                     profile = ENoProfile;
563             }
564             // else: typical desktop case... e.g., "#version 410 core"
565         }
566     }
567 
568     // Fix version...
569     switch (version) {
570     // ES versions
571     case 100: break;
572     case 300: break;
573     case 310: break;
574     case 320: break;
575 
576     // desktop versions
577     case 110: break;
578     case 120: break;
579     case 130: break;
580     case 140: break;
581     case 150: break;
582     case 330: break;
583     case 400: break;
584     case 410: break;
585     case 420: break;
586     case 430: break;
587     case 440: break;
588     case 450: break;
589     case 460: break;
590 
591     // unknown version
592     default:
593         correct = false;
594         infoSink.info.message(EPrefixError, "version not supported");
595         if (profile == EEsProfile)
596             version = 310;
597         else {
598             version = 450;
599             profile = ECoreProfile;
600         }
601         break;
602     }
603 
604     // Correct for stage type...
605     switch (stage) {
606     case EShLangGeometry:
607         if ((profile == EEsProfile && version < 310) ||
608             (profile != EEsProfile && version < 150)) {
609             correct = false;
610             infoSink.info.message(EPrefixError, "#version: geometry shaders require es profile with version 310 or non-es profile with version 150 or above");
611             version = (profile == EEsProfile) ? 310 : 150;
612             if (profile == EEsProfile || profile == ENoProfile)
613                 profile = ECoreProfile;
614         }
615         break;
616     case EShLangTessControl:
617     case EShLangTessEvaluation:
618         if ((profile == EEsProfile && version < 310) ||
619             (profile != EEsProfile && version < 150)) {
620             correct = false;
621             infoSink.info.message(EPrefixError, "#version: tessellation shaders require es profile with version 310 or non-es profile with version 150 or above");
622             version = (profile == EEsProfile) ? 310 : 400; // 150 supports the extension, correction is to 400 which does not
623             if (profile == EEsProfile || profile == ENoProfile)
624                 profile = ECoreProfile;
625         }
626         break;
627     case EShLangCompute:
628         if ((profile == EEsProfile && version < 310) ||
629             (profile != EEsProfile && version < 420)) {
630             correct = false;
631             infoSink.info.message(EPrefixError, "#version: compute shaders require es profile with version 310 or above, or non-es profile with version 420 or above");
632             version = profile == EEsProfile ? 310 : 420;
633         }
634         break;
635     case EShLangRayGen:
636     case EShLangIntersect:
637     case EShLangAnyHit:
638     case EShLangClosestHit:
639     case EShLangMiss:
640     case EShLangCallable:
641         if (profile == EEsProfile || version < 460) {
642             correct = false;
643             infoSink.info.message(EPrefixError, "#version: ray tracing shaders require non-es profile with version 460 or above");
644             version = 460;
645         }
646         break;
647     case EShLangMesh:
648     case EShLangTask:
649         if ((profile == EEsProfile && version < 320) ||
650             (profile != EEsProfile && version < 450)) {
651             correct = false;
652             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");
653             version = profile == EEsProfile ? 320 : 450;
654         }
655         break;
656     default:
657         break;
658     }
659 
660     if (profile == EEsProfile && version >= 300 && versionNotFirst) {
661         correct = false;
662         infoSink.info.message(EPrefixError, "#version: statement must appear first in es-profile shader; before comments or newlines");
663     }
664 
665     // Check for SPIR-V compatibility
666     if (spvVersion.spv != 0) {
667         switch (profile) {
668         case EEsProfile:
669             if (version < 310) {
670                 correct = false;
671                 infoSink.info.message(EPrefixError, "#version: ES shaders for SPIR-V require version 310 or higher");
672                 version = 310;
673             }
674             break;
675         case ECompatibilityProfile:
676             infoSink.info.message(EPrefixError, "#version: compilation for SPIR-V does not support the compatibility profile");
677             break;
678         default:
679             if (spvVersion.vulkan > 0 && version < 140) {
680                 correct = false;
681                 infoSink.info.message(EPrefixError, "#version: Desktop shaders for Vulkan SPIR-V require version 140 or higher");
682                 version = 140;
683             }
684             if (spvVersion.openGl >= 100 && version < 330) {
685                 correct = false;
686                 infoSink.info.message(EPrefixError, "#version: Desktop shaders for OpenGL SPIR-V require version 330 or higher");
687                 version = 330;
688             }
689             break;
690         }
691     }
692 
693     return correct;
694 }
695 
696 // There are multiple paths in for setting environment stuff.
697 // TEnvironment takes precedence, for what it sets, so sort all this out.
698 // Ideally, the internal code could be made to use TEnvironment, but for
699 // now, translate it to the historically used parameters.
TranslateEnvironment(const TEnvironment * environment,EShMessages & messages,EShSource & source,EShLanguage & stage,SpvVersion & spvVersion)700 void TranslateEnvironment(const TEnvironment* environment, EShMessages& messages, EShSource& source,
701                           EShLanguage& stage, SpvVersion& spvVersion)
702 {
703     // Set up environmental defaults, first ignoring 'environment'.
704     if (messages & EShMsgSpvRules)
705         spvVersion.spv = EShTargetSpv_1_0;
706     if (messages & EShMsgVulkanRules) {
707         spvVersion.vulkan = EShTargetVulkan_1_0;
708         spvVersion.vulkanGlsl = 100;
709     } else if (spvVersion.spv != 0)
710         spvVersion.openGl = 100;
711 
712     // Now, override, based on any content set in 'environment'.
713     // 'environment' must be cleared to ESh*None settings when items
714     // are not being set.
715     if (environment != nullptr) {
716         // input language
717         if (environment->input.languageFamily != EShSourceNone) {
718             stage = environment->input.stage;
719             switch (environment->input.dialect) {
720             case EShClientNone:
721                 break;
722             case EShClientVulkan:
723                 spvVersion.vulkanGlsl = environment->input.dialectVersion;
724                 spvVersion.vulkanRelaxed = environment->input.vulkanRulesRelaxed;
725                 break;
726             case EShClientOpenGL:
727                 spvVersion.openGl = environment->input.dialectVersion;
728                 break;
729             case EShClientCount:
730                 assert(0);
731                 break;
732             }
733             switch (environment->input.languageFamily) {
734             case EShSourceNone:
735                 break;
736             case EShSourceGlsl:
737                 source = EShSourceGlsl;
738                 messages = static_cast<EShMessages>(messages & ~EShMsgReadHlsl);
739                 break;
740             case EShSourceHlsl:
741                 source = EShSourceHlsl;
742                 messages = static_cast<EShMessages>(messages | EShMsgReadHlsl);
743                 break;
744             case EShSourceCount:
745                 assert(0);
746                 break;
747             }
748         }
749 
750         // client
751         switch (environment->client.client) {
752         case EShClientVulkan:
753             spvVersion.vulkan = environment->client.version;
754             break;
755         default:
756             break;
757         }
758 
759         // generated code
760         switch (environment->target.language) {
761         case EshTargetSpv:
762             spvVersion.spv = environment->target.version;
763             break;
764         default:
765             break;
766         }
767     }
768 }
769 
770 // Most processes are recorded when set in the intermediate representation,
771 // These are the few that are not.
RecordProcesses(TIntermediate & intermediate,EShMessages messages,const std::string & sourceEntryPointName)772 void RecordProcesses(TIntermediate& intermediate, EShMessages messages, const std::string& sourceEntryPointName)
773 {
774     if ((messages & EShMsgRelaxedErrors) != 0)
775         intermediate.addProcess("relaxed-errors");
776     if ((messages & EShMsgSuppressWarnings) != 0)
777         intermediate.addProcess("suppress-warnings");
778     if ((messages & EShMsgKeepUncalled) != 0)
779         intermediate.addProcess("keep-uncalled");
780     if (sourceEntryPointName.size() > 0) {
781         intermediate.addProcess("source-entrypoint");
782         intermediate.addProcessArgument(sourceEntryPointName);
783     }
784 }
785 
786 // This is the common setup and cleanup code for PreprocessDeferred and
787 // CompileDeferred.
788 // It takes any callable with a signature of
789 //  bool (TParseContextBase& parseContext, TPpContext& ppContext,
790 //                  TInputScanner& input, bool versionWillBeError,
791 //                  TSymbolTable& , TIntermediate& ,
792 //                  EShOptimizationLevel , EShMessages );
793 // Which returns false if a failure was detected and true otherwise.
794 //
795 template<typename ProcessingContext>
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,EProfile defaultProfile,bool forceDefaultVersionAndProfile,int overrideVersion,bool forwardCompatible,EShMessages messages,TIntermediate & intermediate,ProcessingContext & processingContext,bool requireNonempty,TShader::Includer & includer,const std::string sourceEntryPointName="",const TEnvironment * environment=nullptr,bool compileOnly=false)796 bool ProcessDeferred(
797     TCompiler* compiler,
798     const char* const shaderStrings[],
799     const int numStrings,
800     const int* inputLengths,
801     const char* const stringNames[],
802     const char* customPreamble,
803     const EShOptimizationLevel optLevel,
804     const TBuiltInResource* resources,
805     int defaultVersion,  // use 100 for ES environment, 110 for desktop; this is the GLSL version, not SPIR-V or Vulkan
806     EProfile defaultProfile,
807     // set version/profile to defaultVersion/defaultProfile regardless of the #version
808     // directive in the source code
809     bool forceDefaultVersionAndProfile,
810     int overrideVersion, // overrides version specified by #version or default version
811     bool forwardCompatible,     // give errors for use of deprecated features
812     EShMessages messages,       // warnings/errors/AST; things to print out
813     TIntermediate& intermediate, // returned tree, etc.
814     ProcessingContext& processingContext,
815     bool requireNonempty,
816     TShader::Includer& includer,
817     const std::string sourceEntryPointName = "",
818     const TEnvironment* environment = nullptr,  // optional way of fully setting all versions, overriding the above
819     bool compileOnly = false)
820 {
821     // This must be undone (.pop()) by the caller, after it finishes consuming the created tree.
822     GetThreadPoolAllocator().push();
823 
824     if (numStrings == 0)
825         return true;
826 
827     // Move to length-based strings, rather than null-terminated strings.
828     // Also, add strings to include the preamble and to ensure the shader is not null,
829     // which lets the grammar accept what was a null (post preprocessing) shader.
830     //
831     // Shader will look like
832     //   string 0:                system preamble
833     //   string 1:                custom preamble
834     //   string 2...numStrings+1: user's shader
835     //   string numStrings+2:     "int;"
836     const int numPre = 2;
837     const int numPost = requireNonempty? 1 : 0;
838     const int numTotal = numPre + numStrings + numPost;
839     std::unique_ptr<size_t[]> lengths(new size_t[numTotal]);
840     std::unique_ptr<const char*[]> strings(new const char*[numTotal]);
841     std::unique_ptr<const char*[]> names(new const char*[numTotal]);
842     for (int s = 0; s < numStrings; ++s) {
843         strings[s + numPre] = shaderStrings[s];
844         if (inputLengths == nullptr || inputLengths[s] < 0)
845             lengths[s + numPre] = strlen(shaderStrings[s]);
846         else
847             lengths[s + numPre] = inputLengths[s];
848     }
849     if (stringNames != nullptr) {
850         for (int s = 0; s < numStrings; ++s)
851             names[s + numPre] = stringNames[s];
852     } else {
853         for (int s = 0; s < numStrings; ++s)
854             names[s + numPre] = nullptr;
855     }
856 
857     // Get all the stages, languages, clients, and other environment
858     // stuff sorted out.
859     EShSource sourceGuess = (messages & EShMsgReadHlsl) != 0 ? EShSourceHlsl : EShSourceGlsl;
860     SpvVersion spvVersion;
861     EShLanguage stage = compiler->getLanguage();
862     TranslateEnvironment(environment, messages, sourceGuess, stage, spvVersion);
863 #ifdef ENABLE_HLSL
864     EShSource source = sourceGuess;
865     if (environment != nullptr && environment->target.hlslFunctionality1)
866         intermediate.setHlslFunctionality1();
867 #else
868     const EShSource source = EShSourceGlsl;
869 #endif
870     // First, without using the preprocessor or parser, find the #version, so we know what
871     // symbol tables, processing rules, etc. to set up.  This does not need the extra strings
872     // outlined above, just the user shader, after the system and user preambles.
873     glslang::TInputScanner userInput(numStrings, &strings[numPre], &lengths[numPre]);
874     int version = 0;
875     EProfile profile = ENoProfile;
876     bool versionNotFirstToken = false;
877     bool versionNotFirst = (source == EShSourceHlsl)
878                                 ? true
879                                 : userInput.scanVersion(version, profile, versionNotFirstToken);
880     bool versionNotFound = version == 0;
881     if (forceDefaultVersionAndProfile && source == EShSourceGlsl) {
882         if (! (messages & EShMsgSuppressWarnings) && ! versionNotFound &&
883             (version != defaultVersion || profile != defaultProfile)) {
884             compiler->infoSink.info << "Warning, (version, profile) forced to be ("
885                                     << defaultVersion << ", " << ProfileName(defaultProfile)
886                                     << "), while in source code it is ("
887                                     << version << ", " << ProfileName(profile) << ")\n";
888         }
889 
890         if (versionNotFound) {
891             versionNotFirstToken = false;
892             versionNotFirst = false;
893             versionNotFound = false;
894         }
895         version = defaultVersion;
896         profile = defaultProfile;
897     }
898     if (source == EShSourceGlsl && overrideVersion != 0) {
899         version = overrideVersion;
900     }
901 
902     bool goodVersion = DeduceVersionProfile(compiler->infoSink, stage,
903                                             versionNotFirst, defaultVersion, source, version, profile, spvVersion);
904     bool versionWillBeError = (versionNotFound || (profile == EEsProfile && version >= 300 && versionNotFirst));
905     bool warnVersionNotFirst = false;
906     if (! versionWillBeError && versionNotFirstToken) {
907         if (messages & EShMsgRelaxedErrors)
908             warnVersionNotFirst = true;
909         else
910             versionWillBeError = true;
911     }
912 
913     intermediate.setSource(source);
914     intermediate.setVersion(version);
915     intermediate.setProfile(profile);
916     intermediate.setSpv(spvVersion);
917     RecordProcesses(intermediate, messages, sourceEntryPointName);
918     if (spvVersion.vulkan > 0)
919         intermediate.setOriginUpperLeft();
920 #ifdef ENABLE_HLSL
921     if ((messages & EShMsgHlslOffsets) || source == EShSourceHlsl)
922         intermediate.setHlslOffsets();
923 #endif
924     if (messages & EShMsgDebugInfo) {
925         intermediate.setSourceFile(names[numPre]);
926         for (int s = 0; s < numStrings; ++s) {
927             // The string may not be null-terminated, so make sure we provide
928             // the length along with the string.
929             intermediate.addSourceText(strings[numPre + s], lengths[numPre + s]);
930         }
931     }
932     if (!SetupBuiltinSymbolTable(version, profile, spvVersion, source)) {
933         return false;
934     }
935 
936     TSymbolTable* cachedTable = SharedSymbolTables[MapVersionToIndex(version)]
937                                                   [MapSpvVersionToIndex(spvVersion)]
938                                                   [MapProfileToIndex(profile)]
939                                                   [MapSourceToIndex(source)]
940                                                   [stage];
941 
942     // Dynamically allocate the symbol table so we can control when it is deallocated WRT the pool.
943     std::unique_ptr<TSymbolTable> symbolTable(new TSymbolTable);
944     if (cachedTable)
945         symbolTable->adoptLevels(*cachedTable);
946 
947     if (intermediate.getUniqueId() != 0)
948         symbolTable->overwriteUniqueId(intermediate.getUniqueId());
949 
950     // Add built-in symbols that are potentially context dependent;
951     // they get popped again further down.
952     if (! AddContextSpecificSymbols(resources, compiler->infoSink, *symbolTable, version, profile, spvVersion,
953                                     stage, source)) {
954         return false;
955     }
956 
957     if (messages & EShMsgBuiltinSymbolTable)
958         DumpBuiltinSymbolTable(compiler->infoSink, *symbolTable);
959 
960     //
961     // Now we can process the full shader under proper symbols and rules.
962     //
963 
964     std::unique_ptr<TParseContextBase> parseContext(CreateParseContext(*symbolTable, intermediate, version, profile, source,
965                                                     stage, compiler->infoSink,
966                                                     spvVersion, forwardCompatible, messages, false, sourceEntryPointName));
967     parseContext->compileOnly = compileOnly;
968     TPpContext ppContext(*parseContext, names[numPre] ? names[numPre] : "", includer);
969 
970     // only GLSL (bison triggered, really) needs an externally set scan context
971     glslang::TScanContext scanContext(*parseContext);
972     if (source == EShSourceGlsl)
973         parseContext->setScanContext(&scanContext);
974 
975     parseContext->setPpContext(&ppContext);
976     parseContext->setLimits(*resources);
977     if (! goodVersion)
978         parseContext->addError();
979     if (warnVersionNotFirst) {
980         TSourceLoc loc;
981         loc.init();
982         parseContext->warn(loc, "Illegal to have non-comment, non-whitespace tokens before #version", "#version", "");
983     }
984 
985     parseContext->initializeExtensionBehavior();
986 
987     // Fill in the strings as outlined above.
988     std::string preamble;
989     parseContext->getPreamble(preamble);
990     strings[0] = preamble.c_str();
991     lengths[0] = strlen(strings[0]);
992     names[0] = nullptr;
993     strings[1] = customPreamble;
994     lengths[1] = strlen(strings[1]);
995     names[1] = nullptr;
996     assert(2 == numPre);
997     if (requireNonempty) {
998         const int postIndex = numStrings + numPre;
999         strings[postIndex] = "\n int;";
1000         lengths[postIndex] = strlen(strings[numStrings + numPre]);
1001         names[postIndex] = nullptr;
1002     }
1003     TInputScanner fullInput(numStrings + numPre + numPost, strings.get(), lengths.get(), names.get(), numPre, numPost);
1004 
1005     // Push a new symbol allocation scope that will get used for the shader's globals.
1006     symbolTable->push();
1007 
1008     bool success = processingContext(*parseContext, ppContext, fullInput,
1009                                      versionWillBeError, *symbolTable,
1010                                      intermediate, optLevel, messages);
1011     intermediate.setUniqueId(symbolTable->getMaxSymbolId());
1012     return success;
1013 }
1014 
1015 // Responsible for keeping track of the most recent source string and line in
1016 // the preprocessor and outputting newlines appropriately if the source string
1017 // or line changes.
1018 class SourceLineSynchronizer {
1019 public:
SourceLineSynchronizer(const std::function<int ()> & lastSourceIndex,std::string * output)1020     SourceLineSynchronizer(const std::function<int()>& lastSourceIndex,
1021                            std::string* output)
1022       : getLastSourceIndex(lastSourceIndex), output(output), lastSource(-1), lastLine(0) {}
1023 //    SourceLineSynchronizer(const SourceLineSynchronizer&) = delete;
1024 //    SourceLineSynchronizer& operator=(const SourceLineSynchronizer&) = delete;
1025 
1026     // Sets the internally tracked source string index to that of the most
1027     // recently read token. If we switched to a new source string, returns
1028     // true and inserts a newline. Otherwise, returns false and outputs nothing.
syncToMostRecentString()1029     bool syncToMostRecentString() {
1030         if (getLastSourceIndex() != lastSource) {
1031             // After switching to a new source string, we need to reset lastLine
1032             // because line number resets every time a new source string is
1033             // used. We also need to output a newline to separate the output
1034             // from the previous source string (if there is one).
1035             if (lastSource != -1 || lastLine != 0)
1036                 *output += '\n';
1037             lastSource = getLastSourceIndex();
1038             lastLine = -1;
1039             return true;
1040         }
1041         return false;
1042     }
1043 
1044     // Calls syncToMostRecentString() and then sets the internally tracked line
1045     // number to tokenLine. If we switched to a new line, returns true and inserts
1046     // newlines appropriately. Otherwise, returns false and outputs nothing.
syncToLine(int tokenLine)1047     bool syncToLine(int tokenLine) {
1048         syncToMostRecentString();
1049         const bool newLineStarted = lastLine < tokenLine;
1050         for (; lastLine < tokenLine; ++lastLine) {
1051             if (lastLine > 0) *output += '\n';
1052         }
1053         return newLineStarted;
1054     }
1055 
1056     // Sets the internally tracked line number to newLineNum.
setLineNum(int newLineNum)1057     void setLineNum(int newLineNum) { lastLine = newLineNum; }
1058 
1059 private:
1060     SourceLineSynchronizer& operator=(const SourceLineSynchronizer&);
1061 
1062     // A function for getting the index of the last valid source string we've
1063     // read tokens from.
1064     const std::function<int()> getLastSourceIndex;
1065     // output string for newlines.
1066     std::string* output;
1067     // lastSource is the source string index (starting from 0) of the last token
1068     // processed. It is tracked in order for newlines to be inserted when a new
1069     // source string starts. -1 means we haven't started processing any source
1070     // string.
1071     int lastSource;
1072     // lastLine is the line number (starting from 1) of the last token processed.
1073     // It is tracked in order for newlines to be inserted when a token appears
1074     // on a new line. 0 means we haven't started processing any line in the
1075     // current source string.
1076     int lastLine;
1077 };
1078 
1079 // DoPreprocessing is a valid ProcessingContext template argument,
1080 // which only performs the preprocessing step of compilation.
1081 // It places the result in the "string" argument to its constructor.
1082 //
1083 // This is not an officially supported or fully working path.
1084 struct DoPreprocessing {
DoPreprocessing__anon68e935d70111::DoPreprocessing1085     explicit DoPreprocessing(std::string* string): outputString(string) {}
operator ()__anon68e935d70111::DoPreprocessing1086     bool operator()(TParseContextBase& parseContext, TPpContext& ppContext,
1087                     TInputScanner& input, bool versionWillBeError,
1088                     TSymbolTable&, TIntermediate&,
1089                     EShOptimizationLevel, EShMessages)
1090     {
1091         // This is a list of tokens that do not require a space before or after.
1092         static const std::string noNeededSpaceBeforeTokens = ";)[].,";
1093         static const std::string noNeededSpaceAfterTokens = ".([";
1094         glslang::TPpToken ppToken;
1095 
1096         parseContext.setScanner(&input);
1097         ppContext.setInput(input, versionWillBeError);
1098 
1099         std::string outputBuffer;
1100         SourceLineSynchronizer lineSync(
1101             std::bind(&TInputScanner::getLastValidSourceIndex, &input), &outputBuffer);
1102 
1103         parseContext.setExtensionCallback([&lineSync, &outputBuffer](
1104             int line, const char* extension, const char* behavior) {
1105                 lineSync.syncToLine(line);
1106                 outputBuffer += "#extension ";
1107                 outputBuffer += extension;
1108                 outputBuffer += " : ";
1109                 outputBuffer += behavior;
1110         });
1111 
1112         parseContext.setLineCallback([&lineSync, &outputBuffer, &parseContext](
1113             int curLineNum, int newLineNum, bool hasSource, int sourceNum, const char* sourceName) {
1114             // SourceNum is the number of the source-string that is being parsed.
1115             lineSync.syncToLine(curLineNum);
1116             outputBuffer += "#line ";
1117             outputBuffer += std::to_string(newLineNum);
1118             if (hasSource) {
1119                 outputBuffer += ' ';
1120                 if (sourceName != nullptr) {
1121                     outputBuffer += '\"';
1122                     outputBuffer += sourceName;
1123                     outputBuffer += '\"';
1124                 } else {
1125                     outputBuffer += std::to_string(sourceNum);
1126                 }
1127             }
1128             if (parseContext.lineDirectiveShouldSetNextLine()) {
1129                 // newLineNum is the new line number for the line following the #line
1130                 // directive. So the new line number for the current line is
1131                 newLineNum -= 1;
1132             }
1133             outputBuffer += '\n';
1134             // And we are at the next line of the #line directive now.
1135             lineSync.setLineNum(newLineNum + 1);
1136         });
1137 
1138         parseContext.setVersionCallback(
1139             [&lineSync, &outputBuffer](int line, int version, const char* str) {
1140                 lineSync.syncToLine(line);
1141                 outputBuffer += "#version ";
1142                 outputBuffer += std::to_string(version);
1143                 if (str) {
1144                     outputBuffer += ' ';
1145                     outputBuffer += str;
1146                 }
1147             });
1148 
1149         parseContext.setPragmaCallback([&lineSync, &outputBuffer](
1150             int line, const glslang::TVector<glslang::TString>& ops) {
1151                 lineSync.syncToLine(line);
1152                 outputBuffer += "#pragma ";
1153                 for(size_t i = 0; i < ops.size(); ++i) {
1154                     outputBuffer += ops[i].c_str();
1155                 }
1156         });
1157 
1158         parseContext.setErrorCallback([&lineSync, &outputBuffer](
1159             int line, const char* errorMessage) {
1160                 lineSync.syncToLine(line);
1161                 outputBuffer += "#error ";
1162                 outputBuffer += errorMessage;
1163         });
1164 
1165         int lastToken = EndOfInput; // lastToken records the last token processed.
1166         std::string lastTokenName;
1167         do {
1168             int token = ppContext.tokenize(ppToken);
1169             if (token == EndOfInput)
1170                 break;
1171 
1172             bool isNewString = lineSync.syncToMostRecentString();
1173             bool isNewLine = lineSync.syncToLine(ppToken.loc.line);
1174 
1175             if (isNewLine) {
1176                 // Don't emit whitespace onto empty lines.
1177                 // Copy any whitespace characters at the start of a line
1178                 // from the input to the output.
1179                 outputBuffer += std::string(ppToken.loc.column - 1, ' ');
1180             }
1181 
1182             // Output a space in between tokens, but not at the start of a line,
1183             // and also not around special tokens. This helps with readability
1184             // and consistency.
1185             if (!isNewString && !isNewLine && lastToken != EndOfInput) {
1186                 // left parenthesis need a leading space, except it is in a function-call-like context.
1187                 // examples: `for (xxx)`, `a * (b + c)`, `vec(2.0)`, `foo(x, y, z)`
1188                 if (token == '(') {
1189                     if (lastToken != PpAtomIdentifier ||
1190                         lastTokenName == "if" ||
1191                         lastTokenName == "for" ||
1192                         lastTokenName == "while" ||
1193                         lastTokenName == "switch")
1194                         outputBuffer += ' ';
1195                 } else if ((noNeededSpaceBeforeTokens.find((char)token) == std::string::npos) &&
1196                     (noNeededSpaceAfterTokens.find((char)lastToken) == std::string::npos)) {
1197                     outputBuffer += ' ';
1198                 }
1199             }
1200             if (token == PpAtomIdentifier)
1201                 lastTokenName = ppToken.name;
1202             lastToken = token;
1203             if (token == PpAtomConstString)
1204                 outputBuffer += "\"";
1205             outputBuffer += ppToken.name;
1206             if (token == PpAtomConstString)
1207                 outputBuffer += "\"";
1208         } while (true);
1209         outputBuffer += '\n';
1210         *outputString = std::move(outputBuffer);
1211 
1212         bool success = true;
1213         if (parseContext.getNumErrors() > 0) {
1214             success = false;
1215             parseContext.infoSink.info.prefix(EPrefixError);
1216             parseContext.infoSink.info << parseContext.getNumErrors() << " compilation errors.  No code generated.\n\n";
1217         }
1218         return success;
1219     }
1220     std::string* outputString;
1221 };
1222 
1223 // DoFullParse is a valid ProcessingConext template argument for fully
1224 // parsing the shader.  It populates the "intermediate" with the AST.
1225 struct DoFullParse{
operator ()__anon68e935d70111::DoFullParse1226   bool operator()(TParseContextBase& parseContext, TPpContext& ppContext,
1227                   TInputScanner& fullInput, bool versionWillBeError,
1228                   TSymbolTable&, TIntermediate& intermediate,
1229                   EShOptimizationLevel optLevel, EShMessages messages)
1230     {
1231         bool success = true;
1232         // Parse the full shader.
1233         if (! parseContext.parseShaderStrings(ppContext, fullInput, versionWillBeError))
1234             success = false;
1235 
1236         if (success && intermediate.getTreeRoot()) {
1237             if (optLevel == EShOptNoGeneration)
1238                 parseContext.infoSink.info.message(EPrefixNone, "No errors.  No code generation or linking was requested.");
1239             else
1240                 success = intermediate.postProcess(intermediate.getTreeRoot(), parseContext.getLanguage());
1241         } else if (! success) {
1242             parseContext.infoSink.info.prefix(EPrefixError);
1243             parseContext.infoSink.info << parseContext.getNumErrors() << " compilation errors.  No code generated.\n\n";
1244         }
1245 
1246         if (messages & EShMsgAST)
1247             intermediate.output(parseContext.infoSink, true);
1248 
1249         return success;
1250     }
1251 };
1252 
1253 // Take a single compilation unit, and run the preprocessor on it.
1254 // Return: True if there were no issues found in preprocessing,
1255 //         False if during preprocessing any unknown version, pragmas or
1256 //         extensions were found.
1257 //
1258 // NOTE: Doing just preprocessing to obtain a correct preprocessed shader string
1259 // is not an officially supported or fully working path.
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,EProfile defaultProfile,bool forceDefaultVersionAndProfile,int overrideVersion,bool forwardCompatible,EShMessages messages,TShader::Includer & includer,TIntermediate & intermediate,std::string * outputString,TEnvironment * environment=nullptr)1260 bool PreprocessDeferred(
1261     TCompiler* compiler,
1262     const char* const shaderStrings[],
1263     const int numStrings,
1264     const int* inputLengths,
1265     const char* const stringNames[],
1266     const char* preamble,
1267     const EShOptimizationLevel optLevel,
1268     const TBuiltInResource* resources,
1269     int defaultVersion,         // use 100 for ES environment, 110 for desktop
1270     EProfile defaultProfile,
1271     bool forceDefaultVersionAndProfile,
1272     int overrideVersion,        // use 0 if not overriding GLSL version
1273     bool forwardCompatible,     // give errors for use of deprecated features
1274     EShMessages messages,       // warnings/errors/AST; things to print out
1275     TShader::Includer& includer,
1276     TIntermediate& intermediate, // returned tree, etc.
1277     std::string* outputString,
1278     TEnvironment* environment = nullptr)
1279 {
1280     DoPreprocessing parser(outputString);
1281     return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames,
1282                            preamble, optLevel, resources, defaultVersion,
1283                            defaultProfile, forceDefaultVersionAndProfile, overrideVersion,
1284                            forwardCompatible, messages, intermediate, parser,
1285                            false, includer, "", environment);
1286 }
1287 
1288 //
1289 // do a partial compile on the given strings for a single compilation unit
1290 // for a potential deferred link into a single stage (and deferred full compile of that
1291 // stage through machine-dependent compilation).
1292 //
1293 // all preprocessing, parsing, semantic checks, etc. for a single compilation unit
1294 // are done here.
1295 //
1296 // return:  the tree and other information is filled into the intermediate argument,
1297 //          and true is returned by the function for success.
1298 //
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,EProfile defaultProfile,bool forceDefaultVersionAndProfile,int overrideVersion,bool forwardCompatible,EShMessages messages,TIntermediate & intermediate,TShader::Includer & includer,const std::string sourceEntryPointName="",TEnvironment * environment=nullptr,bool compileOnly=false)1299 bool CompileDeferred(
1300     TCompiler* compiler,
1301     const char* const shaderStrings[],
1302     const int numStrings,
1303     const int* inputLengths,
1304     const char* const stringNames[],
1305     const char* preamble,
1306     const EShOptimizationLevel optLevel,
1307     const TBuiltInResource* resources,
1308     int defaultVersion,         // use 100 for ES environment, 110 for desktop
1309     EProfile defaultProfile,
1310     bool forceDefaultVersionAndProfile,
1311     int overrideVersion,        // use 0 if not overriding GLSL version
1312     bool forwardCompatible,     // give errors for use of deprecated features
1313     EShMessages messages,       // warnings/errors/AST; things to print out
1314     TIntermediate& intermediate,// returned tree, etc.
1315     TShader::Includer& includer,
1316     const std::string sourceEntryPointName = "",
1317     TEnvironment* environment = nullptr,
1318     bool compileOnly = false)
1319 {
1320     DoFullParse parser;
1321     return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames,
1322                            preamble, optLevel, resources, defaultVersion,
1323                            defaultProfile, forceDefaultVersionAndProfile, overrideVersion,
1324                            forwardCompatible, messages, intermediate, parser,
1325                            true, includer, sourceEntryPointName, environment, compileOnly);
1326 }
1327 
1328 } // end anonymous namespace for local functions
1329 
1330 //
1331 // ShInitialize() should be called exactly once per process, not per thread.
1332 //
ShInitialize()1333 int ShInitialize()
1334 {
1335 #ifndef DISABLE_THREAD_SUPPORT
1336     const std::lock_guard<std::mutex> lock(init_lock);
1337 #endif
1338     ++NumberOfClients;
1339 
1340     if (PerProcessGPA == nullptr)
1341         PerProcessGPA = new TPoolAllocator();
1342 
1343     return 1;
1344 }
1345 
1346 //
1347 // Driver calls these to create and destroy compiler/linker
1348 // objects.
1349 //
1350 
ShConstructCompiler(const EShLanguage language,int)1351 ShHandle ShConstructCompiler(const EShLanguage language, int /*debugOptions unused*/)
1352 {
1353     TShHandleBase* base = static_cast<TShHandleBase*>(ConstructCompiler(language, 0));
1354 
1355     return reinterpret_cast<void*>(base);
1356 }
1357 
ShConstructLinker(const EShExecutable executable,int)1358 ShHandle ShConstructLinker(const EShExecutable executable, int /*debugOptions unused*/)
1359 {
1360     TShHandleBase* base = static_cast<TShHandleBase*>(ConstructLinker(executable, 0));
1361 
1362     return reinterpret_cast<void*>(base);
1363 }
1364 
ShConstructUniformMap()1365 ShHandle ShConstructUniformMap()
1366 {
1367     TShHandleBase* base = static_cast<TShHandleBase*>(ConstructUniformMap());
1368 
1369     return reinterpret_cast<void*>(base);
1370 }
1371 
ShDestruct(ShHandle handle)1372 void ShDestruct(ShHandle handle)
1373 {
1374     if (handle == nullptr)
1375         return;
1376 
1377     TShHandleBase* base = static_cast<TShHandleBase*>(handle);
1378 
1379     if (base->getAsCompiler())
1380         DeleteCompiler(base->getAsCompiler());
1381     else if (base->getAsLinker())
1382         DeleteLinker(base->getAsLinker());
1383     else if (base->getAsUniformMap())
1384         DeleteUniformMap(base->getAsUniformMap());
1385 }
1386 
1387 //
1388 // Cleanup symbol tables
1389 //
ShFinalize()1390 int ShFinalize()
1391 {
1392 #ifndef DISABLE_THREAD_SUPPORT
1393     const std::lock_guard<std::mutex> lock(init_lock);
1394 #endif
1395     --NumberOfClients;
1396     assert(NumberOfClients >= 0);
1397     if (NumberOfClients > 0)
1398         return 1;
1399 
1400     for (int version = 0; version < VersionCount; ++version) {
1401         for (int spvVersion = 0; spvVersion < SpvVersionCount; ++spvVersion) {
1402             for (int p = 0; p < ProfileCount; ++p) {
1403                 for (int source = 0; source < SourceCount; ++source) {
1404                     for (int stage = 0; stage < EShLangCount; ++stage) {
1405                         delete SharedSymbolTables[version][spvVersion][p][source][stage];
1406                         SharedSymbolTables[version][spvVersion][p][source][stage] = nullptr;
1407                     }
1408                 }
1409             }
1410         }
1411     }
1412 
1413     for (int version = 0; version < VersionCount; ++version) {
1414         for (int spvVersion = 0; spvVersion < SpvVersionCount; ++spvVersion) {
1415             for (int p = 0; p < ProfileCount; ++p) {
1416                 for (int source = 0; source < SourceCount; ++source) {
1417                     for (int pc = 0; pc < EPcCount; ++pc) {
1418                         delete CommonSymbolTable[version][spvVersion][p][source][pc];
1419                         CommonSymbolTable[version][spvVersion][p][source][pc] = nullptr;
1420                     }
1421                 }
1422             }
1423         }
1424     }
1425 
1426     if (PerProcessGPA != nullptr) {
1427         delete PerProcessGPA;
1428         PerProcessGPA = nullptr;
1429     }
1430 
1431     return 1;
1432 }
1433 
1434 //
1435 // Do a full compile on the given strings for a single compilation unit
1436 // forming a complete stage.  The result of the machine dependent compilation
1437 // is left in the provided compile object.
1438 //
1439 // Return:  The return value is really boolean, indicating
1440 // success (1) or failure (0).
1441 //
ShCompile(const ShHandle handle,const char * const shaderStrings[],const int numStrings,const int * inputLengths,const EShOptimizationLevel optLevel,const TBuiltInResource * resources,int,int defaultVersion,bool forwardCompatible,EShMessages messages,const char * shaderFileName)1442 int ShCompile(
1443     const ShHandle handle,
1444     const char* const shaderStrings[],
1445     const int numStrings,
1446     const int* inputLengths,
1447     const EShOptimizationLevel optLevel,
1448     const TBuiltInResource* resources,
1449     int /*debugOptions*/,
1450     int defaultVersion,        // use 100 for ES environment, 110 for desktop
1451     bool forwardCompatible,    // give errors for use of deprecated features
1452     EShMessages messages,       // warnings/errors/AST; things to print out,
1453     const char *shaderFileName // the filename
1454     )
1455 {
1456     // Map the generic handle to the C++ object
1457     if (handle == nullptr)
1458         return 0;
1459 
1460     TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
1461     TCompiler* compiler = base->getAsCompiler();
1462     if (compiler == nullptr)
1463         return 0;
1464 
1465     SetThreadPoolAllocator(compiler->getPool());
1466 
1467     compiler->infoSink.info.erase();
1468     compiler->infoSink.debug.erase();
1469     compiler->infoSink.info.setShaderFileName(shaderFileName);
1470     compiler->infoSink.debug.setShaderFileName(shaderFileName);
1471 
1472 
1473     TIntermediate intermediate(compiler->getLanguage());
1474     TShader::ForbidIncluder includer;
1475     bool success = CompileDeferred(compiler, shaderStrings, numStrings, inputLengths, nullptr,
1476                                    "", optLevel, resources, defaultVersion, ENoProfile, false, 0,
1477                                    forwardCompatible, messages, intermediate, includer);
1478 
1479     //
1480     // Call the machine dependent compiler
1481     //
1482     if (success && intermediate.getTreeRoot() && optLevel != EShOptNoGeneration)
1483         success = compiler->compile(intermediate.getTreeRoot(), intermediate.getVersion(), intermediate.getProfile());
1484 
1485     intermediate.removeTree();
1486 
1487     // Throw away all the temporary memory used by the compilation process.
1488     // The push was done in the CompileDeferred() call above.
1489     GetThreadPoolAllocator().pop();
1490 
1491     return success ? 1 : 0;
1492 }
1493 
1494 //
1495 // Link the given compile objects.
1496 //
1497 // Return:  The return value of is really boolean, indicating
1498 // success or failure.
1499 //
ShLinkExt(const ShHandle linkHandle,const ShHandle compHandles[],const int numHandles)1500 int ShLinkExt(
1501     const ShHandle linkHandle,
1502     const ShHandle compHandles[],
1503     const int numHandles)
1504 {
1505     if (linkHandle == nullptr || numHandles == 0)
1506         return 0;
1507 
1508     THandleList cObjects;
1509 
1510     for (int i = 0; i < numHandles; ++i) {
1511         if (compHandles[i] == nullptr)
1512             return 0;
1513         TShHandleBase* base = reinterpret_cast<TShHandleBase*>(compHandles[i]);
1514         if (base->getAsLinker()) {
1515             cObjects.push_back(base->getAsLinker());
1516         }
1517         if (base->getAsCompiler())
1518             cObjects.push_back(base->getAsCompiler());
1519 
1520         if (cObjects[i] == nullptr)
1521             return 0;
1522     }
1523 
1524     TShHandleBase* base = reinterpret_cast<TShHandleBase*>(linkHandle);
1525     TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
1526 
1527     if (linker == nullptr)
1528         return 0;
1529 
1530     SetThreadPoolAllocator(linker->getPool());
1531     linker->infoSink.info.erase();
1532 
1533     for (int i = 0; i < numHandles; ++i) {
1534         if (cObjects[i]->getAsCompiler()) {
1535             if (! cObjects[i]->getAsCompiler()->linkable()) {
1536                 linker->infoSink.info.message(EPrefixError, "Not all shaders have valid object code.");
1537                 return 0;
1538             }
1539         }
1540     }
1541 
1542     bool ret = linker->link(cObjects);
1543 
1544     return ret ? 1 : 0;
1545 }
1546 
1547 //
1548 // ShSetEncrpytionMethod is a place-holder for specifying
1549 // how source code is encrypted.
1550 //
ShSetEncryptionMethod(ShHandle handle)1551 void ShSetEncryptionMethod(ShHandle handle)
1552 {
1553     if (handle == nullptr)
1554         return;
1555 }
1556 
1557 //
1558 // Return any compiler/linker/uniformmap log of messages for the application.
1559 //
ShGetInfoLog(const ShHandle handle)1560 const char* ShGetInfoLog(const ShHandle handle)
1561 {
1562     if (handle == nullptr)
1563         return nullptr;
1564 
1565     TShHandleBase* base = static_cast<TShHandleBase*>(handle);
1566     TInfoSink* infoSink;
1567 
1568     if (base->getAsCompiler())
1569         infoSink = &(base->getAsCompiler()->getInfoSink());
1570     else if (base->getAsLinker())
1571         infoSink = &(base->getAsLinker()->getInfoSink());
1572     else
1573         return nullptr;
1574 
1575     infoSink->info << infoSink->debug.c_str();
1576     return infoSink->info.c_str();
1577 }
1578 
1579 //
1580 // Return the resulting binary code from the link process.  Structure
1581 // is machine dependent.
1582 //
ShGetExecutable(const ShHandle handle)1583 const void* ShGetExecutable(const ShHandle handle)
1584 {
1585     if (handle == nullptr)
1586         return nullptr;
1587 
1588     TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
1589 
1590     TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
1591     if (linker == nullptr)
1592         return nullptr;
1593 
1594     return linker->getObjectCode();
1595 }
1596 
1597 //
1598 // Let the linker know where the application said it's attributes are bound.
1599 // The linker does not use these values, they are remapped by the ICD or
1600 // hardware.  It just needs them to know what's aliased.
1601 //
1602 // Return:  The return value of is really boolean, indicating
1603 // success or failure.
1604 //
ShSetVirtualAttributeBindings(const ShHandle handle,const ShBindingTable * table)1605 int ShSetVirtualAttributeBindings(const ShHandle handle, const ShBindingTable* table)
1606 {
1607     if (handle == nullptr)
1608         return 0;
1609 
1610     TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
1611     TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
1612 
1613     if (linker == nullptr)
1614         return 0;
1615 
1616     linker->setAppAttributeBindings(table);
1617 
1618     return 1;
1619 }
1620 
1621 //
1622 // Let the linker know where the predefined attributes have to live.
1623 //
ShSetFixedAttributeBindings(const ShHandle handle,const ShBindingTable * table)1624 int ShSetFixedAttributeBindings(const ShHandle handle, const ShBindingTable* table)
1625 {
1626     if (handle == nullptr)
1627         return 0;
1628 
1629     TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
1630     TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
1631 
1632     if (linker == nullptr)
1633         return 0;
1634 
1635     linker->setFixedAttributeBindings(table);
1636     return 1;
1637 }
1638 
1639 //
1640 // Some attribute locations are off-limits to the linker...
1641 //
ShExcludeAttributes(const ShHandle handle,int * attributes,int count)1642 int ShExcludeAttributes(const ShHandle handle, int *attributes, int count)
1643 {
1644     if (handle == nullptr)
1645         return 0;
1646 
1647     TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
1648     TLinker* linker = static_cast<TLinker*>(base->getAsLinker());
1649     if (linker == nullptr)
1650         return 0;
1651 
1652     linker->setExcludedAttributes(attributes, count);
1653 
1654     return 1;
1655 }
1656 
1657 //
1658 // Return the index for OpenGL to use for knowing where a uniform lives.
1659 //
1660 // Return:  The return value of is really boolean, indicating
1661 // success or failure.
1662 //
ShGetUniformLocation(const ShHandle handle,const char * name)1663 int ShGetUniformLocation(const ShHandle handle, const char* name)
1664 {
1665     if (handle == nullptr)
1666         return -1;
1667 
1668     TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle);
1669     TUniformMap* uniformMap= base->getAsUniformMap();
1670     if (uniformMap == nullptr)
1671         return -1;
1672 
1673     return uniformMap->getLocation(name);
1674 }
1675 
1676 ////////////////////////////////////////////////////////////////////////////////////////////
1677 //
1678 // Deferred-Lowering C++ Interface
1679 // -----------------------------------
1680 //
1681 // Below is a new alternate C++ interface that might potentially replace the above
1682 // opaque handle-based interface.
1683 //
1684 // See more detailed comment in ShaderLang.h
1685 //
1686 
1687 namespace glslang {
1688 
GetVersion()1689 Version GetVersion()
1690 {
1691     Version version;
1692     version.major = GLSLANG_VERSION_MAJOR;
1693     version.minor = GLSLANG_VERSION_MINOR;
1694     version.patch = GLSLANG_VERSION_PATCH;
1695     version.flavor = GLSLANG_VERSION_FLAVOR;
1696     return version;
1697 }
1698 
1699 #define QUOTE(s) #s
1700 #define STR(n) QUOTE(n)
1701 
GetEsslVersionString()1702 const char* GetEsslVersionString()
1703 {
1704     return "OpenGL ES GLSL 3.20 glslang Khronos. " STR(GLSLANG_VERSION_MAJOR) "." STR(GLSLANG_VERSION_MINOR) "." STR(
1705         GLSLANG_VERSION_PATCH) GLSLANG_VERSION_FLAVOR;
1706 }
1707 
GetGlslVersionString()1708 const char* GetGlslVersionString()
1709 {
1710     return "4.60 glslang Khronos. " STR(GLSLANG_VERSION_MAJOR) "." STR(GLSLANG_VERSION_MINOR) "." STR(
1711         GLSLANG_VERSION_PATCH) GLSLANG_VERSION_FLAVOR;
1712 }
1713 
GetKhronosToolId()1714 int GetKhronosToolId()
1715 {
1716     return 8;
1717 }
1718 
InitializeProcess()1719 bool InitializeProcess()
1720 {
1721     return ShInitialize() != 0;
1722 }
1723 
FinalizeProcess()1724 void FinalizeProcess()
1725 {
1726     ShFinalize();
1727 }
1728 
1729 class TDeferredCompiler : public TCompiler {
1730 public:
TDeferredCompiler(EShLanguage s,TInfoSink & i)1731     TDeferredCompiler(EShLanguage s, TInfoSink& i) : TCompiler(s, i) { }
compile(TIntermNode *,int=0,EProfile=ENoProfile)1732     virtual bool compile(TIntermNode*, int = 0, EProfile = ENoProfile) { return true; }
1733 };
1734 
GetGlslIoMapper()1735 TIoMapper* GetGlslIoMapper() {
1736     return static_cast<TIoMapper*>(new TGlslIoMapper());
1737 }
1738 
TShader(EShLanguage s)1739 TShader::TShader(EShLanguage s)
1740     : stage(s), lengths(nullptr), stringNames(nullptr), preamble(""), overrideVersion(0)
1741 {
1742     pool = new TPoolAllocator;
1743     infoSink = new TInfoSink;
1744     compiler = new TDeferredCompiler(stage, *infoSink);
1745     intermediate = new TIntermediate(s);
1746 
1747     // clear environment (avoid constructors in them for use in a C interface)
1748     environment.input.languageFamily = EShSourceNone;
1749     environment.input.dialect = EShClientNone;
1750     environment.input.vulkanRulesRelaxed = false;
1751     environment.client.client = EShClientNone;
1752     environment.target.language = EShTargetNone;
1753     environment.target.hlslFunctionality1 = false;
1754 }
1755 
~TShader()1756 TShader::~TShader()
1757 {
1758     delete infoSink;
1759     delete compiler;
1760     delete intermediate;
1761     delete pool;
1762 }
1763 
setStrings(const char * const * s,int n)1764 void TShader::setStrings(const char* const* s, int n)
1765 {
1766     strings = s;
1767     numStrings = n;
1768     lengths = nullptr;
1769 }
1770 
setStringsWithLengths(const char * const * s,const int * l,int n)1771 void TShader::setStringsWithLengths(const char* const* s, const int* l, int n)
1772 {
1773     strings = s;
1774     numStrings = n;
1775     lengths = l;
1776 }
1777 
setStringsWithLengthsAndNames(const char * const * s,const int * l,const char * const * names,int n)1778 void TShader::setStringsWithLengthsAndNames(
1779     const char* const* s, const int* l, const char* const* names, int n)
1780 {
1781     strings = s;
1782     numStrings = n;
1783     lengths = l;
1784     stringNames = names;
1785 }
1786 
setEntryPoint(const char * entryPoint)1787 void TShader::setEntryPoint(const char* entryPoint)
1788 {
1789     intermediate->setEntryPointName(entryPoint);
1790 }
1791 
setSourceEntryPoint(const char * name)1792 void TShader::setSourceEntryPoint(const char* name)
1793 {
1794     sourceEntryPointName = name;
1795 }
1796 
1797 // Log initial settings and transforms.
1798 // See comment for class TProcesses.
addProcesses(const std::vector<std::string> & p)1799 void TShader::addProcesses(const std::vector<std::string>& p)
1800 {
1801     intermediate->addProcesses(p);
1802 }
1803 
setUniqueId(unsigned long long id)1804 void  TShader::setUniqueId(unsigned long long id)
1805 {
1806     intermediate->setUniqueId(id);
1807 }
1808 
setOverrideVersion(int version)1809 void TShader::setOverrideVersion(int version)
1810 {
1811     overrideVersion = version;
1812 }
1813 
setDebugInfo(bool debugInfo)1814 void TShader::setDebugInfo(bool debugInfo)              { intermediate->setDebugInfo(debugInfo); }
setInvertY(bool invert)1815 void TShader::setInvertY(bool invert)                   { intermediate->setInvertY(invert); }
setDxPositionW(bool invert)1816 void TShader::setDxPositionW(bool invert)               { intermediate->setDxPositionW(invert); }
setEnhancedMsgs()1817 void TShader::setEnhancedMsgs()                         { intermediate->setEnhancedMsgs(); }
setNanMinMaxClamp(bool useNonNan)1818 void TShader::setNanMinMaxClamp(bool useNonNan)         { intermediate->setNanMinMaxClamp(useNonNan); }
1819 
1820 // Set binding base for given resource type
setShiftBinding(TResourceType res,unsigned int base)1821 void TShader::setShiftBinding(TResourceType res, unsigned int base) {
1822     intermediate->setShiftBinding(res, base);
1823 }
1824 
1825 // Set binding base for given resource type for a given binding set.
setShiftBindingForSet(TResourceType res,unsigned int base,unsigned int set)1826 void TShader::setShiftBindingForSet(TResourceType res, unsigned int base, unsigned int set) {
1827     intermediate->setShiftBindingForSet(res, base, set);
1828 }
1829 
1830 // Set binding base for sampler types
setShiftSamplerBinding(unsigned int base)1831 void TShader::setShiftSamplerBinding(unsigned int base) { setShiftBinding(EResSampler, base); }
1832 // Set binding base for texture types (SRV)
setShiftTextureBinding(unsigned int base)1833 void TShader::setShiftTextureBinding(unsigned int base) { setShiftBinding(EResTexture, base); }
1834 // Set binding base for image types
setShiftImageBinding(unsigned int base)1835 void TShader::setShiftImageBinding(unsigned int base)   { setShiftBinding(EResImage, base); }
1836 // Set binding base for uniform buffer objects (CBV)
setShiftUboBinding(unsigned int base)1837 void TShader::setShiftUboBinding(unsigned int base)     { setShiftBinding(EResUbo, base); }
1838 // Synonym for setShiftUboBinding, to match HLSL language.
setShiftCbufferBinding(unsigned int base)1839 void TShader::setShiftCbufferBinding(unsigned int base) { setShiftBinding(EResUbo, base); }
1840 // Set binding base for UAV (unordered access view)
setShiftUavBinding(unsigned int base)1841 void TShader::setShiftUavBinding(unsigned int base)     { setShiftBinding(EResUav, base); }
1842 // Set binding base for SSBOs
setShiftSsboBinding(unsigned int base)1843 void TShader::setShiftSsboBinding(unsigned int base)    { setShiftBinding(EResSsbo, base); }
1844 // Enables binding automapping using TIoMapper
setAutoMapBindings(bool map)1845 void TShader::setAutoMapBindings(bool map)              { intermediate->setAutoMapBindings(map); }
1846 // Enables position.Y output negation in vertex shader
1847 
1848 // Fragile: currently within one stage: simple auto-assignment of location
setAutoMapLocations(bool map)1849 void TShader::setAutoMapLocations(bool map)             { intermediate->setAutoMapLocations(map); }
addUniformLocationOverride(const char * name,int loc)1850 void TShader::addUniformLocationOverride(const char* name, int loc)
1851 {
1852     intermediate->addUniformLocationOverride(name, loc);
1853 }
setUniformLocationBase(int base)1854 void TShader::setUniformLocationBase(int base)
1855 {
1856     intermediate->setUniformLocationBase(base);
1857 }
setNoStorageFormat(bool useUnknownFormat)1858 void TShader::setNoStorageFormat(bool useUnknownFormat) { intermediate->setNoStorageFormat(useUnknownFormat); }
setResourceSetBinding(const std::vector<std::string> & base)1859 void TShader::setResourceSetBinding(const std::vector<std::string>& base)   { intermediate->setResourceSetBinding(base); }
setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode)1860 void TShader::setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode) { intermediate->setTextureSamplerTransformMode(mode); }
1861 
addBlockStorageOverride(const char * nameStr,TBlockStorageClass backing)1862 void TShader::addBlockStorageOverride(const char* nameStr, TBlockStorageClass backing) { intermediate->addBlockStorageOverride(nameStr, backing); }
1863 
setGlobalUniformBlockName(const char * name)1864 void TShader::setGlobalUniformBlockName(const char* name) { intermediate->setGlobalUniformBlockName(name); }
setGlobalUniformSet(unsigned int set)1865 void TShader::setGlobalUniformSet(unsigned int set) { intermediate->setGlobalUniformSet(set); }
setGlobalUniformBinding(unsigned int binding)1866 void TShader::setGlobalUniformBinding(unsigned int binding) { intermediate->setGlobalUniformBinding(binding); }
1867 
setAtomicCounterBlockName(const char * name)1868 void TShader::setAtomicCounterBlockName(const char* name) { intermediate->setAtomicCounterBlockName(name); }
setAtomicCounterBlockSet(unsigned int set)1869 void TShader::setAtomicCounterBlockSet(unsigned int set) { intermediate->setAtomicCounterBlockSet(set); }
1870 
addSourceText(const char * text,size_t len)1871 void TShader::addSourceText(const char* text, size_t len) { intermediate->addSourceText(text, len); }
setSourceFile(const char * file)1872 void TShader::setSourceFile(const char* file) { intermediate->setSourceFile(file); }
1873 
1874 #ifdef ENABLE_HLSL
1875 // See comment above TDefaultHlslIoMapper in iomapper.cpp:
setHlslIoMapping(bool hlslIoMap)1876 void TShader::setHlslIoMapping(bool hlslIoMap)          { intermediate->setHlslIoMapping(hlslIoMap); }
setFlattenUniformArrays(bool flatten)1877 void TShader::setFlattenUniformArrays(bool flatten)     { intermediate->setFlattenUniformArrays(flatten); }
1878 #endif
1879 
1880 //
1881 // Turn the shader strings into a parse tree in the TIntermediate.
1882 //
1883 // Returns true for success.
1884 //
parse(const TBuiltInResource * builtInResources,int defaultVersion,EProfile defaultProfile,bool forceDefaultVersionAndProfile,bool forwardCompatible,EShMessages messages,Includer & includer)1885 bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile,
1886                     bool forwardCompatible, EShMessages messages, Includer& includer)
1887 {
1888     SetThreadPoolAllocator(pool);
1889 
1890     if (! preamble)
1891         preamble = "";
1892 
1893     return CompileDeferred(compiler, strings, numStrings, lengths, stringNames,
1894                            preamble, EShOptNone, builtInResources, defaultVersion,
1895                            defaultProfile, forceDefaultVersionAndProfile, overrideVersion,
1896                            forwardCompatible, messages, *intermediate, includer, sourceEntryPointName,
1897                            &environment, compileOnly);
1898 }
1899 
1900 // Fill in a string with the result of preprocessing ShaderStrings
1901 // Returns true if all extensions, pragmas and version strings were valid.
1902 //
1903 // NOTE: Doing just preprocessing to obtain a correct preprocessed shader string
1904 // is not an officially supported or fully working path.
preprocess(const TBuiltInResource * builtInResources,int defaultVersion,EProfile defaultProfile,bool forceDefaultVersionAndProfile,bool forwardCompatible,EShMessages message,std::string * output_string,Includer & includer)1905 bool TShader::preprocess(const TBuiltInResource* builtInResources,
1906                          int defaultVersion, EProfile defaultProfile,
1907                          bool forceDefaultVersionAndProfile,
1908                          bool forwardCompatible, EShMessages message,
1909                          std::string* output_string,
1910                          Includer& includer)
1911 {
1912     SetThreadPoolAllocator(pool);
1913 
1914     if (! preamble)
1915         preamble = "";
1916 
1917     return PreprocessDeferred(compiler, strings, numStrings, lengths, stringNames, preamble,
1918                               EShOptNone, builtInResources, defaultVersion,
1919                               defaultProfile, forceDefaultVersionAndProfile, overrideVersion,
1920                               forwardCompatible, message, includer, *intermediate, output_string,
1921                               &environment);
1922 }
1923 
getInfoLog()1924 const char* TShader::getInfoLog()
1925 {
1926     return infoSink->info.c_str();
1927 }
1928 
getInfoDebugLog()1929 const char* TShader::getInfoDebugLog()
1930 {
1931     return infoSink->debug.c_str();
1932 }
1933 
TProgram()1934 TProgram::TProgram() : reflection(nullptr), linked(false)
1935 {
1936     pool = new TPoolAllocator;
1937     infoSink = new TInfoSink;
1938     for (int s = 0; s < EShLangCount; ++s) {
1939         intermediate[s] = nullptr;
1940         newedIntermediate[s] = false;
1941     }
1942 }
1943 
~TProgram()1944 TProgram::~TProgram()
1945 {
1946     delete infoSink;
1947     delete reflection;
1948 
1949     for (int s = 0; s < EShLangCount; ++s)
1950         if (newedIntermediate[s])
1951             delete intermediate[s];
1952 
1953     delete pool;
1954 }
1955 
1956 //
1957 // Merge the compilation units within each stage into a single TIntermediate.
1958 // All starting compilation units need to be the result of calling TShader::parse().
1959 //
1960 // Return true for success.
1961 //
link(EShMessages messages)1962 bool TProgram::link(EShMessages messages)
1963 {
1964     if (linked)
1965         return false;
1966     linked = true;
1967 
1968     bool error = false;
1969 
1970     SetThreadPoolAllocator(pool);
1971 
1972     for (int s = 0; s < EShLangCount; ++s) {
1973         if (! linkStage((EShLanguage)s, messages))
1974             error = true;
1975     }
1976 
1977     if (!error) {
1978         if (! crossStageCheck(messages))
1979             error = true;
1980     }
1981 
1982     return ! error;
1983 }
1984 
1985 //
1986 // Merge the compilation units within the given stage into a single TIntermediate.
1987 //
1988 // Return true for success.
1989 //
linkStage(EShLanguage stage,EShMessages messages)1990 bool TProgram::linkStage(EShLanguage stage, EShMessages messages)
1991 {
1992     if (stages[stage].size() == 0)
1993         return true;
1994 
1995     int numEsShaders = 0, numNonEsShaders = 0;
1996     for (auto it = stages[stage].begin(); it != stages[stage].end(); ++it) {
1997         if ((*it)->intermediate->getProfile() == EEsProfile) {
1998             numEsShaders++;
1999         } else {
2000             numNonEsShaders++;
2001         }
2002     }
2003 
2004     if (numEsShaders > 0 && numNonEsShaders > 0) {
2005         infoSink->info.message(EPrefixError, "Cannot mix ES profile with non-ES profile shaders");
2006         return false;
2007     } else if (numEsShaders > 1) {
2008         infoSink->info.message(EPrefixError, "Cannot attach multiple ES shaders of the same type to a single program");
2009         return false;
2010     }
2011 
2012     //
2013     // Be efficient for the common single compilation unit per stage case,
2014     // reusing it's TIntermediate instead of merging into a new one.
2015     //
2016     TIntermediate *firstIntermediate = stages[stage].front()->intermediate;
2017     if (stages[stage].size() == 1)
2018         intermediate[stage] = firstIntermediate;
2019     else {
2020         intermediate[stage] = new TIntermediate(stage,
2021                                                 firstIntermediate->getVersion(),
2022                                                 firstIntermediate->getProfile());
2023         intermediate[stage]->setLimits(firstIntermediate->getLimits());
2024         if (firstIntermediate->getEnhancedMsgs())
2025             intermediate[stage]->setEnhancedMsgs();
2026 
2027         // The new TIntermediate must use the same origin as the original TIntermediates.
2028         // Otherwise linking will fail due to different coordinate systems.
2029         if (firstIntermediate->getOriginUpperLeft()) {
2030             intermediate[stage]->setOriginUpperLeft();
2031         }
2032         intermediate[stage]->setSpv(firstIntermediate->getSpv());
2033 
2034         newedIntermediate[stage] = true;
2035     }
2036 
2037     if (messages & EShMsgAST)
2038         infoSink->info << "\nLinked " << StageName(stage) << " stage:\n\n";
2039 
2040     if (stages[stage].size() > 1) {
2041         std::list<TShader*>::const_iterator it;
2042         for (it = stages[stage].begin(); it != stages[stage].end(); ++it)
2043             intermediate[stage]->merge(*infoSink, *(*it)->intermediate);
2044     }
2045     intermediate[stage]->finalCheck(*infoSink, (messages & EShMsgKeepUncalled) != 0);
2046 
2047     if (messages & EShMsgAST)
2048         intermediate[stage]->output(*infoSink, true);
2049 
2050     return intermediate[stage]->getNumErrors() == 0;
2051 }
2052 
2053 //
2054 // Check that there are no errors in linker objects accross stages
2055 //
2056 // Return true if no errors.
2057 //
crossStageCheck(EShMessages messages)2058 bool TProgram::crossStageCheck(EShMessages messages) {
2059 
2060     // make temporary intermediates to hold the linkage symbols for each linking interface
2061     // while we do the checks
2062     // Independent interfaces are:
2063     //                  all uniform variables and blocks
2064     //                  all buffer blocks
2065     //                  all in/out on a stage boundary
2066 
2067     TVector<TIntermediate*> activeStages;
2068     for (int s = 0; s < EShLangCount; ++s) {
2069         if (intermediate[s])
2070             activeStages.push_back(intermediate[s]);
2071     }
2072 
2073     // no extra linking if there is only one stage
2074     if (! (activeStages.size() > 1))
2075         return true;
2076 
2077     // setup temporary tree to hold unfirom objects from different stages
2078     TIntermediate* firstIntermediate = activeStages.front();
2079     TIntermediate uniforms(EShLangCount,
2080                            firstIntermediate->getVersion(),
2081                            firstIntermediate->getProfile());
2082     uniforms.setSpv(firstIntermediate->getSpv());
2083 
2084     TIntermAggregate uniformObjects(EOpLinkerObjects);
2085     TIntermAggregate root(EOpSequence);
2086     root.getSequence().push_back(&uniformObjects);
2087     uniforms.setTreeRoot(&root);
2088 
2089     bool error = false;
2090 
2091     // merge uniforms from all stages into a single intermediate
2092     for (unsigned int i = 0; i < activeStages.size(); ++i) {
2093         uniforms.mergeUniformObjects(*infoSink, *activeStages[i]);
2094     }
2095     error |= uniforms.getNumErrors() != 0;
2096 
2097     // copy final definition of global block back into each stage
2098     for (unsigned int i = 0; i < activeStages.size(); ++i) {
2099         // We only want to merge into already existing global uniform blocks.
2100         // A stage that doesn't already know about the global doesn't care about it's content.
2101         // Otherwise we end up pointing to the same object between different stages
2102         // and that will break binding/set remappings
2103         bool mergeExistingOnly = true;
2104         activeStages[i]->mergeGlobalUniformBlocks(*infoSink, uniforms, mergeExistingOnly);
2105     }
2106 
2107     // compare cross stage symbols for each stage boundary
2108     for (unsigned int i = 1; i < activeStages.size(); ++i) {
2109         activeStages[i - 1]->checkStageIO(*infoSink, *activeStages[i]);
2110         error |= (activeStages[i - 1]->getNumErrors() != 0);
2111     }
2112 
2113     // if requested, optimize cross stage IO
2114     if (messages & EShMsgLinkTimeOptimization) {
2115         for (unsigned int i = 1; i < activeStages.size(); ++i) {
2116             activeStages[i - 1]->optimizeStageIO(*infoSink, *activeStages[i]);
2117         }
2118     }
2119 
2120     return !error;
2121 }
2122 
getInfoLog()2123 const char* TProgram::getInfoLog()
2124 {
2125     return infoSink->info.c_str();
2126 }
2127 
getInfoDebugLog()2128 const char* TProgram::getInfoDebugLog()
2129 {
2130     return infoSink->debug.c_str();
2131 }
2132 
2133 //
2134 // Reflection implementation.
2135 //
2136 
layoutLocation() const2137 unsigned int TObjectReflection::layoutLocation() const { return type->getQualifier().layoutLocation; }
2138 
buildReflection(int opts)2139 bool TProgram::buildReflection(int opts)
2140 {
2141     if (! linked || reflection != nullptr)
2142         return false;
2143 
2144     SetThreadPoolAllocator(pool);
2145 
2146     int firstStage = EShLangVertex, lastStage = EShLangFragment;
2147 
2148     if (opts & EShReflectionIntermediateIO) {
2149         // if we're reflecting intermediate I/O, determine the first and last stage linked and use those as the
2150         // boundaries for which stages generate pipeline inputs/outputs
2151         firstStage = EShLangCount;
2152         lastStage = 0;
2153         for (int s = 0; s < EShLangCount; ++s) {
2154             if (intermediate[s]) {
2155                 firstStage = std::min(firstStage, s);
2156                 lastStage = std::max(lastStage, s);
2157             }
2158         }
2159     }
2160 
2161     reflection = new TReflection((EShReflectionOptions)opts, (EShLanguage)firstStage, (EShLanguage)lastStage);
2162 
2163     for (int s = 0; s < EShLangCount; ++s) {
2164         if (intermediate[s]) {
2165             if (! reflection->addStage((EShLanguage)s, *intermediate[s]))
2166                 return false;
2167         }
2168     }
2169 
2170     return true;
2171 }
2172 
getLocalSize(int dim) const2173 unsigned TProgram::getLocalSize(int dim) const                        { return reflection->getLocalSize(dim); }
getReflectionIndex(const char * name) const2174 int TProgram::getReflectionIndex(const char* name) const              { return reflection->getIndex(name); }
getReflectionPipeIOIndex(const char * name,const bool inOrOut) const2175 int TProgram::getReflectionPipeIOIndex(const char* name, const bool inOrOut) const
2176                                                                       { return reflection->getPipeIOIndex(name, inOrOut); }
2177 
getNumUniformVariables() const2178 int TProgram::getNumUniformVariables() const                          { return reflection->getNumUniforms(); }
getUniform(int index) const2179 const TObjectReflection& TProgram::getUniform(int index) const        { return reflection->getUniform(index); }
getNumUniformBlocks() const2180 int TProgram::getNumUniformBlocks() const                             { return reflection->getNumUniformBlocks(); }
getUniformBlock(int index) const2181 const TObjectReflection& TProgram::getUniformBlock(int index) const   { return reflection->getUniformBlock(index); }
getNumPipeInputs() const2182 int TProgram::getNumPipeInputs() const                                { return reflection->getNumPipeInputs(); }
getPipeInput(int index) const2183 const TObjectReflection& TProgram::getPipeInput(int index) const      { return reflection->getPipeInput(index); }
getNumPipeOutputs() const2184 int TProgram::getNumPipeOutputs() const                               { return reflection->getNumPipeOutputs(); }
getPipeOutput(int index) const2185 const TObjectReflection& TProgram::getPipeOutput(int index) const     { return reflection->getPipeOutput(index); }
getNumBufferVariables() const2186 int TProgram::getNumBufferVariables() const                           { return reflection->getNumBufferVariables(); }
getBufferVariable(int index) const2187 const TObjectReflection& TProgram::getBufferVariable(int index) const { return reflection->getBufferVariable(index); }
getNumBufferBlocks() const2188 int TProgram::getNumBufferBlocks() const                              { return reflection->getNumStorageBuffers(); }
getBufferBlock(int index) const2189 const TObjectReflection& TProgram::getBufferBlock(int index) const    { return reflection->getStorageBufferBlock(index); }
getNumAtomicCounters() const2190 int TProgram::getNumAtomicCounters() const                            { return reflection->getNumAtomicCounters(); }
getAtomicCounter(int index) const2191 const TObjectReflection& TProgram::getAtomicCounter(int index) const  { return reflection->getAtomicCounter(index); }
dumpReflection()2192 void TProgram::dumpReflection() { if (reflection != nullptr) reflection->dump(); }
2193 
getGlslIoResolver(EShLanguage stage)2194 TIoMapResolver* TProgram::getGlslIoResolver(EShLanguage stage) {
2195     auto *intermediate = getIntermediate(stage);
2196     if (!intermediate)
2197         return NULL;
2198     return static_cast<TIoMapResolver*>(new TDefaultGlslIoResolver(*intermediate));
2199 }
2200 //
2201 // I/O mapping implementation.
2202 //
mapIO(TIoMapResolver * pResolver,TIoMapper * pIoMapper)2203 bool TProgram::mapIO(TIoMapResolver* pResolver, TIoMapper* pIoMapper)
2204 {
2205     if (! linked)
2206         return false;
2207 
2208     SetThreadPoolAllocator(pool);
2209 
2210     TIoMapper* ioMapper = nullptr;
2211     TIoMapper defaultIOMapper;
2212     if (pIoMapper == nullptr)
2213         ioMapper = &defaultIOMapper;
2214     else
2215         ioMapper = pIoMapper;
2216     for (int s = 0; s < EShLangCount; ++s) {
2217         if (intermediate[s]) {
2218             if (! ioMapper->addStage((EShLanguage)s, *intermediate[s], *infoSink, pResolver))
2219                 return false;
2220         }
2221     }
2222 
2223     return ioMapper->doMap(pResolver, *infoSink);
2224 }
2225 
2226 } // end namespace glslang
2227