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