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