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