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