Coverage Report

Created: 2026-06-04 06:49

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