Coverage Report

Created: 2025-11-19 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/shaderc/third_party/glslang/glslang/MachineIndependent/iomapper.cpp
Line
Count
Source
1
//
2
// Copyright (C) 2016-2017 LunarG, Inc.
3
//
4
// All rights reserved.
5
//
6
// Redistribution and use in source and binary forms, with or without
7
// modification, are permitted provided that the following conditions
8
// are met:
9
//
10
//    Redistributions of source code must retain the above copyright
11
//    notice, this list of conditions and the following disclaimer.
12
//
13
//    Redistributions in binary form must reproduce the above
14
//    copyright notice, this list of conditions and the following
15
//    disclaimer in the documentation and/or other materials provided
16
//    with the distribution.
17
//
18
//    Neither the name of 3Dlabs Inc. Ltd. nor the names of its
19
//    contributors may be used to endorse or promote products derived
20
//    from this software without specific prior written permission.
21
//
22
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33
// POSSIBILITY OF SUCH DAMAGE.
34
//
35
36
#include "../Include/Common.h"
37
#include "../Include/InfoSink.h"
38
#include "../Include/Types.h"
39
40
#include "gl_types.h"
41
#include "iomapper.h"
42
#include "LiveTraverser.h"
43
#include "SymbolTable.h"
44
45
//
46
// Map IO bindings.
47
//
48
// High-level algorithm for one stage:
49
//
50
// 1. Traverse all code (live+dead) to find the explicitly provided bindings.
51
//
52
// 2. Traverse (just) the live code to determine which non-provided bindings
53
//    require auto-numbering.  We do not auto-number dead ones.
54
//
55
// 3. Traverse all the code to apply the bindings:
56
//    a. explicitly given bindings are offset according to their type
57
//    b. implicit live bindings are auto-numbered into the holes, using
58
//       any open binding slot.
59
//    c. implicit dead bindings are left un-bound.
60
//
61
62
namespace glslang {
63
64
struct TVarEntryInfo {
65
    long long id;
66
    TIntermSymbol* symbol;
67
    bool live;
68
    TLayoutPacking upgradedToPushConstantPacking; // ElpNone means it hasn't been upgraded
69
    int newBinding;
70
    int newSet;
71
    int newLocation;
72
    int newComponent;
73
    int newIndex;
74
    EShLanguage stage;
75
76
1.15k
    void clearNewAssignments() {
77
1.15k
        upgradedToPushConstantPacking = ElpNone;
78
1.15k
        newBinding = -1;
79
1.15k
        newSet = -1;
80
1.15k
        newLocation = -1;
81
1.15k
        newComponent = -1;
82
1.15k
        newIndex = -1;
83
1.15k
    }
84
85
    struct TOrderById {
86
0
        inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r) { return l.id < r.id; }
87
    };
88
89
    struct TOrderByPriority {
90
        // ordering:
91
        // 1) has both binding and set
92
        // 2) has binding but no set
93
        // 3) has no binding but set
94
        // 4) has no binding and no set
95
1.23k
        inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r) {
96
1.23k
            const TQualifier& lq = l.symbol->getQualifier();
97
1.23k
            const TQualifier& rq = r.symbol->getQualifier();
98
99
            // simple rules:
100
            // has binding gives 2 points
101
            // has set gives 1 point
102
            // who has the most points is more important.
103
1.23k
            int lPoints = (lq.hasBinding() ? 2 : 0) + (lq.hasSet() ? 1 : 0);
104
1.23k
            int rPoints = (rq.hasBinding() ? 2 : 0) + (rq.hasSet() ? 1 : 0);
105
106
1.23k
            if (lPoints == rPoints)
107
1.14k
                return l.id < r.id;
108
90
            return lPoints > rPoints;
109
1.23k
        }
110
    };
111
112
    struct TOrderByPriorityAndLive {
113
        // ordering:
114
        // 1) do live variables first
115
        // 2) has both binding and set
116
        // 3) has binding but no set
117
        // 4) has no binding but set
118
        // 5) has no binding and no set
119
0
        inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r) {
120
121
0
            const TQualifier& lq = l.symbol->getQualifier();
122
0
            const TQualifier& rq = r.symbol->getQualifier();
123
124
            // simple rules:
125
            // has binding gives 2 points
126
            // has set gives 1 point
127
            // who has the most points is more important.
128
0
            int lPoints = (lq.hasBinding() ? 2 : 0) + (lq.hasSet() ? 1 : 0);
129
0
            int rPoints = (rq.hasBinding() ? 2 : 0) + (rq.hasSet() ? 1 : 0);
130
131
0
            if (l.live != r.live)
132
0
                return l.live > r.live;
133
134
0
            if (lPoints != rPoints)
135
0
                return lPoints > rPoints;
136
137
0
            return l.id < r.id;
138
0
        }
139
    };
140
};
141
142
// override function "operator=", if a vector<const _Kty, _Ty> being sort,
143
// when use vc++, the sort function will call :
144
// pair& operator=(const pair<_Other1, _Other2>& _Right)
145
// {
146
//     first = _Right.first;
147
//     second = _Right.second;
148
//     return (*this);
149
// }
150
// that will make a const type handing on left.
151
// override this function can avoid a compiler error.
152
// In the future, if the vc++ compiler can handle such a situation,
153
// this part of the code will be removed.
154
struct TVarLivePair : std::pair<const TString, TVarEntryInfo> {
155
1.15k
    TVarLivePair(const std::pair<const TString, TVarEntryInfo>& _Right) : pair(_Right.first, _Right.second) {}
156
1.07k
    TVarLivePair& operator=(const TVarLivePair& _Right) {
157
1.07k
        const_cast<TString&>(first) = _Right.first;
158
1.07k
        second = _Right.second;
159
1.07k
        return (*this);
160
1.07k
    }
161
3.42k
    TVarLivePair(const TVarLivePair& src) : pair(src) { }
162
};
163
typedef std::vector<TVarLivePair> TVarLiveVector;
164
165
166
class TVarGatherTraverser : public TLiveTraverser {
167
public:
168
    TVarGatherTraverser(const TIntermediate& i, bool traverseDeadCode, TVarLiveMap& inList, TVarLiveMap& outList, TVarLiveMap& uniformList)
169
784
      : TLiveTraverser(i, traverseDeadCode, true, true, false, false)
170
784
      , inputList(inList)
171
784
      , outputList(outList)
172
784
      , uniformList(uniformList)
173
784
    {
174
784
    }
175
176
    virtual void visitSymbol(TIntermSymbol* base)
177
16.2k
    {
178
16.2k
        TVarLiveMap* target = nullptr;
179
16.2k
        if (base->getQualifier().storage == EvqVaryingIn)
180
1.15k
            target = &inputList;
181
15.0k
        else if (base->getQualifier().storage == EvqVaryingOut)
182
830
            target = &outputList;
183
14.2k
        else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().isPushConstant() && !base->getQualifier().isShaderRecord())
184
4.67k
            target = &uniformList;
185
        // If a global is being visited, then we should also traverse it incase it's evaluation
186
        // ends up visiting inputs we want to tag as live
187
9.54k
        else if (base->getQualifier().storage == EvqGlobal)
188
1.24k
            addGlobalReference(base->getAccessName());
189
190
16.2k
        if (target) {
191
6.65k
            TVarEntryInfo ent = {base->getId(), base, ! traverseAll, {}, {}, {}, {}, {}, {}, {}};
192
6.65k
            ent.stage = intermediate.getStage();
193
6.65k
            TVarLiveMap::iterator at = target->find(
194
6.65k
                ent.symbol->getAccessName()); // std::lower_bound(target->begin(), target->end(), ent, TVarEntryInfo::TOrderById());
195
6.65k
            if (at != target->end() && at->second.id == ent.id)
196
5.50k
                at->second.live = at->second.live || ! traverseAll; // update live state
197
1.15k
            else
198
1.15k
                (*target)[ent.symbol->getAccessName()] = ent;
199
6.65k
        }
200
16.2k
    }
201
202
private:
203
    TVarLiveMap&    inputList;
204
    TVarLiveMap&    outputList;
205
    TVarLiveMap&    uniformList;
206
};
207
208
class TVarSetTraverser : public TLiveTraverser
209
{
210
public:
211
    TVarSetTraverser(const TIntermediate& i, const TVarLiveMap& inList, const TVarLiveMap& outList, const TVarLiveMap& uniformList)
212
392
      : TLiveTraverser(i, true, true, true, false, true)
213
392
      , inputList(inList)
214
392
      , outputList(outList)
215
392
      , uniformList(uniformList)
216
392
    {
217
392
    }
218
219
8.88k
    virtual void visitSymbol(TIntermSymbol* base) {
220
8.88k
        const TVarLiveMap* source;
221
8.88k
        if (base->getQualifier().storage == EvqVaryingIn)
222
742
            source = &inputList;
223
8.14k
        else if (base->getQualifier().storage == EvqVaryingOut)
224
512
            source = &outputList;
225
7.63k
        else if (base->getQualifier().isUniformOrBuffer())
226
2.68k
            source = &uniformList;
227
4.95k
        else
228
4.95k
            return;
229
230
3.93k
        TVarEntryInfo ent = { base->getId(), {}, {}, {}, {}, {}, {}, {}, {}, {} };
231
        // Fix a defect, when block has no instance name, we need to find its block name
232
3.93k
        TVarLiveMap::const_iterator at = source->find(base->getAccessName());
233
3.93k
        if (at == source->end())
234
24
            return;
235
236
3.91k
        if (at->second.id != ent.id)
237
0
            return;
238
239
3.91k
        if (at->second.newBinding != -1)
240
2.63k
            base->getWritableType().getQualifier().layoutBinding = at->second.newBinding;
241
3.91k
        if (at->second.newSet != -1)
242
2.66k
            base->getWritableType().getQualifier().layoutSet = at->second.newSet;
243
3.91k
        if (at->second.newLocation != -1)
244
440
            base->getWritableType().getQualifier().layoutLocation = at->second.newLocation;
245
3.91k
        if (at->second.newComponent != -1)
246
0
            base->getWritableType().getQualifier().layoutComponent = at->second.newComponent;
247
3.91k
        if (at->second.newIndex != -1)
248
0
            base->getWritableType().getQualifier().layoutIndex = at->second.newIndex;
249
3.91k
        if (at->second.upgradedToPushConstantPacking != ElpNone) {
250
0
            base->getWritableType().getQualifier().layoutPushConstant = true;
251
0
            base->getWritableType().getQualifier().setBlockStorage(EbsPushConstant);
252
0
            base->getWritableType().getQualifier().layoutPacking = at->second.upgradedToPushConstantPacking;
253
0
        }
254
3.91k
    }
255
256
  private:
257
    const TVarLiveMap&    inputList;
258
    const TVarLiveMap&    outputList;
259
    const TVarLiveMap&    uniformList;
260
};
261
262
struct TNotifyUniformAdaptor
263
{
264
    EShLanguage stage;
265
    TIoMapResolver& resolver;
266
    inline TNotifyUniformAdaptor(EShLanguage s, TIoMapResolver& r)
267
392
      : stage(s)
268
392
      , resolver(r)
269
392
    {
270
392
    }
271
272
    inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
273
650
    {
274
650
        resolver.notifyBinding(stage, entKey.second);
275
650
    }
276
277
private:
278
    TNotifyUniformAdaptor& operator=(TNotifyUniformAdaptor&) = delete;
279
};
280
281
struct TNotifyInOutAdaptor
282
{
283
    EShLanguage stage;
284
    TIoMapResolver& resolver;
285
    inline TNotifyInOutAdaptor(EShLanguage s, TIoMapResolver& r)
286
392
      : stage(s)
287
392
      , resolver(r)
288
392
    {
289
392
    }
290
291
    inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
292
508
    {
293
508
        resolver.notifyInOut(entKey.second.stage, entKey.second);
294
508
    }
295
296
private:
297
    TNotifyInOutAdaptor& operator=(TNotifyInOutAdaptor&) = delete;
298
};
299
300
struct TResolverUniformAdaptor {
301
    TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TVarLiveMap* uniform[EShLangCount], TInfoSink& i, bool& e)
302
392
      : stage(s)
303
392
      , resolver(r)
304
392
      , infoSink(i)
305
392
      , error(e)
306
392
    {
307
392
        memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*)));
308
392
    }
309
310
650
    inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
311
650
        TVarEntryInfo& ent = entKey.second;
312
650
        ent.clearNewAssignments();
313
650
        const bool isValid = resolver.validateBinding(stage, ent);
314
650
        if (isValid) {
315
650
            resolver.resolveSet(ent.stage, ent);
316
650
            resolver.resolveBinding(ent.stage, ent);
317
650
            resolver.resolveUniformLocation(ent.stage, ent);
318
319
650
            if (ent.newBinding != -1) {
320
624
                if (ent.newBinding >= int(TQualifier::layoutBindingEnd)) {
321
0
                    TString err = "mapped binding out of range: " + entKey.first;
322
323
0
                    infoSink.info.message(EPrefixInternalError, err.c_str());
324
0
                    error = true;
325
0
                }
326
327
624
                if (ent.symbol->getQualifier().hasBinding()) {
328
6.93k
                    for (uint32_t idx = EShLangVertex; idx < EShLangCount; ++idx) {
329
6.46k
                        if (idx == ent.stage || uniformVarMap[idx] == nullptr)
330
6.46k
                            continue;
331
0
                        auto entKey2 = uniformVarMap[idx]->find(entKey.first);
332
0
                        if (entKey2 != uniformVarMap[idx]->end()) {
333
0
                            entKey2->second.newBinding = ent.newBinding;
334
0
                        }
335
0
                    }
336
462
                }
337
624
            }
338
650
            if (ent.newSet != -1) {
339
650
                if (ent.newSet >= int(TQualifier::layoutSetEnd)) {
340
0
                    TString err = "mapped set out of range: " + entKey.first;
341
342
0
                    infoSink.info.message(EPrefixInternalError, err.c_str());
343
0
                    error = true;
344
0
                }
345
650
                if (ent.symbol->getQualifier().hasSet()) {
346
2.16k
                    for (uint32_t idx = EShLangVertex; idx < EShLangCount; ++idx) {
347
2.01k
                        if ((idx == stage) || (uniformVarMap[idx] == nullptr))
348
2.01k
                            continue;
349
0
                        auto entKey2 = uniformVarMap[idx]->find(entKey.first);
350
0
                        if (entKey2 != uniformVarMap[idx]->end()) {
351
0
                            entKey2->second.newSet = ent.newSet;
352
0
                        }
353
0
                    }
354
144
                }
355
650
            }
356
650
        } else {
357
0
            TString errorMsg = "Invalid binding: " + entKey.first;
358
0
            infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
359
0
            error = true;
360
0
        }
361
650
    }
362
363
0
    inline void setStage(EShLanguage s) { stage = s; }
364
365
    EShLanguage     stage;
366
    TIoMapResolver& resolver;
367
    TInfoSink&      infoSink;
368
    bool&           error;
369
    TVarLiveMap*    uniformVarMap[EShLangCount];
370
private:
371
    TResolverUniformAdaptor& operator=(TResolverUniformAdaptor&) = delete;
372
};
373
374
struct TResolverInOutAdaptor {
375
    TResolverInOutAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e)
376
392
      : stage(s)
377
392
      , resolver(r)
378
392
      , infoSink(i)
379
392
      , error(e)
380
392
    {
381
392
    }
382
383
    inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
384
508
    {
385
508
        TVarEntryInfo& ent = entKey.second;
386
508
        ent.clearNewAssignments();
387
508
        const bool isValid = resolver.validateInOut(ent.stage, ent);
388
508
        if (isValid) {
389
508
            resolver.resolveInOutLocation(stage, ent);
390
508
            resolver.resolveInOutComponent(stage, ent);
391
508
            resolver.resolveInOutIndex(stage, ent);
392
508
        } else {
393
0
            TString errorMsg;
394
0
            if (ent.symbol->getType().getQualifier().semanticName != nullptr) {
395
0
                errorMsg = "Invalid shader In/Out variable semantic: ";
396
0
                errorMsg += ent.symbol->getType().getQualifier().semanticName;
397
0
            } else {
398
0
                errorMsg = "Invalid shader In/Out variable: ";
399
0
                errorMsg += ent.symbol->getName();
400
0
            }
401
0
            infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
402
0
            error = true;
403
0
        }
404
508
    }
405
406
0
    inline void setStage(EShLanguage s) { stage = s; }
407
408
    EShLanguage     stage;
409
    TIoMapResolver& resolver;
410
    TInfoSink&      infoSink;
411
    bool&           error;
412
413
private:
414
    TResolverInOutAdaptor& operator=(TResolverInOutAdaptor&) = delete;
415
};
416
417
// The class is used for reserving explicit uniform locations and ubo/ssbo/opaque bindings
418
// xxTODO: maybe this logic should be moved into the resolver's "validateInOut" and "validateUniform"
419
420
struct TSymbolValidater
421
{
422
    TSymbolValidater(TIoMapResolver& r, TInfoSink& i, TVarLiveMap* in[EShLangCount], TVarLiveMap* out[EShLangCount],
423
                     TVarLiveMap* uniform[EShLangCount], bool& hadError, EProfile profile, int version)
424
0
        : resolver(r)
425
0
        , infoSink(i)
426
0
        , hadError(hadError)
427
0
        , profile(profile)
428
0
        , version(version)
429
0
    {
430
0
        memcpy(inVarMaps, in, EShLangCount * (sizeof(TVarLiveMap*)));
431
0
        memcpy(outVarMaps, out, EShLangCount * (sizeof(TVarLiveMap*)));
432
0
        memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*)));
433
434
0
        std::map<TString, TString> anonymousMemberMap;
435
0
        std::vector<TRange> usedUniformLocation;
436
0
        std::vector<TString> usedUniformName;
437
0
        usedUniformLocation.clear();
438
0
        usedUniformName.clear();
439
0
        for (int i = 0; i < EShLangCount; i++) {
440
0
            if (uniformVarMap[i]) {
441
0
                for (auto uniformVar : *uniformVarMap[i])
442
0
                {
443
0
                    TIntermSymbol* pSymbol = uniformVar.second.symbol;
444
0
                    TQualifier qualifier = uniformVar.second.symbol->getQualifier();
445
0
                    TString symbolName = pSymbol->getAccessName();
446
447
                    // All the uniform needs multi-stage location check (block/default)
448
0
                    int uniformLocation = qualifier.layoutLocation;
449
450
0
                    if (uniformLocation != TQualifier::layoutLocationEnd) {
451
                        // Total size of current uniform, could be block, struct or other types.
452
0
                        int size = TIntermediate::computeTypeUniformLocationSize(pSymbol->getType());
453
454
0
                        TRange locationRange(uniformLocation, uniformLocation + size - 1);
455
456
                        // Combine location and component ranges
457
0
                        int overlapLocation = -1;
458
0
                        bool diffLocation = false;
459
460
                        // Check for collisions, except for vertex inputs on desktop targeting OpenGL
461
0
                        overlapLocation = checkLocationOverlap(locationRange, usedUniformLocation, symbolName, usedUniformName, diffLocation);
462
463
                        // Overlap locations of uniforms, regardless of components (multi stages)
464
0
                        if (overlapLocation == -1) {
465
0
                            usedUniformLocation.push_back(locationRange);
466
0
                            usedUniformName.push_back(symbolName);
467
0
                        }
468
0
                        else if (overlapLocation >= 0) {
469
0
                            if (diffLocation == true) {
470
0
                                TString err = ("Uniform location should be equal for same uniforms: " +std::to_string(overlapLocation)).c_str();
471
0
                                infoSink.info.message(EPrefixInternalError, err.c_str());
472
0
                                hadError = true;
473
0
                                break;
474
0
                            }
475
0
                            else {
476
0
                                TString err = ("Uniform location overlaps across stages: " + std::to_string(overlapLocation)).c_str();
477
0
                                infoSink.info.message(EPrefixInternalError, err.c_str());
478
0
                                hadError = true;
479
0
                                break;
480
0
                            }
481
0
                        }
482
0
                    }
483
484
0
                    if ((uniformVar.second.symbol->getBasicType() == EbtBlock) &&
485
0
                        IsAnonymous(uniformVar.second.symbol->getName()))
486
0
                    {
487
0
                        auto blockType = uniformVar.second.symbol->getType().getStruct();
488
0
                        for (size_t memberIdx = 0; memberIdx < blockType->size(); ++memberIdx) {
489
0
                            auto memberName = (*blockType)[memberIdx].type->getFieldName();
490
0
                            if (anonymousMemberMap.find(memberName) != anonymousMemberMap.end())
491
0
                            {
492
0
                                if (anonymousMemberMap[memberName] != uniformVar.second.symbol->getType().getTypeName())
493
0
                                {
494
0
                                    TString err = "Invalid block member name: " + memberName;
495
0
                                    infoSink.info.message(EPrefixInternalError, err.c_str());
496
0
                                    hadError = true;
497
0
                                    break;
498
0
                                }
499
0
                            }
500
0
                            else
501
0
                            {
502
0
                                anonymousMemberMap[memberName] = uniformVar.second.symbol->getType().getTypeName();
503
0
                            }
504
0
                        }
505
0
                    }
506
0
                    if (hadError)
507
0
                        break;
508
0
                }
509
0
            }
510
0
        }
511
0
    }
512
513
    // In case we need to new an intermediate, which costs too much
514
    int checkLocationOverlap(const TRange& locationRange, std::vector<TRange>& usedUniformLocation, const TString symbolName, std::vector<TString>& usedUniformName, bool& diffLocation)
515
0
    {
516
0
        for (size_t r = 0; r < usedUniformLocation.size(); ++r) {
517
0
            if (usedUniformName[r] == symbolName) {
518
0
                diffLocation = true;
519
0
                return (usedUniformLocation[r].start == locationRange.start &&
520
0
                        usedUniformLocation[r].last == locationRange.last)
521
0
                       ? -2 : std::max(locationRange.start, usedUniformLocation[r].start);
522
0
            }
523
0
            if (locationRange.overlap(usedUniformLocation[r])) {
524
                // there is a collision; pick one
525
0
                return std::max(locationRange.start, usedUniformLocation[r].start);
526
0
            }
527
0
        }
528
529
0
        return -1; // no collision
530
0
    }
531
532
0
    inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
533
0
        TVarEntryInfo& ent1 = entKey.second;
534
0
        TIntermSymbol* base = ent1.symbol;
535
0
        const TType& type = ent1.symbol->getType();
536
0
        const TString& name = entKey.first;
537
0
        TString mangleName1, mangleName2;
538
0
        EShLanguage stage = ent1.stage;
539
0
        EShLanguage preStage, currentStage, nextStage;
540
541
0
        preStage = EShLangCount;
542
0
        for (int i = stage - 1; i >= 0; i--) {
543
0
            if (inVarMaps[i] != nullptr) {
544
0
                preStage = static_cast<EShLanguage>(i);
545
0
                break;
546
0
            }
547
0
        }
548
0
        currentStage = stage;
549
0
        nextStage = EShLangCount;
550
0
        for (int i = stage + 1; i < EShLangCount; i++) {
551
0
            if (inVarMaps[i] != nullptr) {
552
0
                nextStage = static_cast<EShLanguage>(i);
553
0
                break;
554
0
            }
555
0
        }
556
557
0
        if (type.getQualifier().isArrayedIo(stage)) {
558
0
            TType subType(type, 0);
559
0
            subType.appendMangledName(mangleName1);
560
0
        } else {
561
0
            type.appendMangledName(mangleName1);
562
0
        }
563
564
565
        // basic checking that symbols match
566
        // more extensive checking in the link stage
567
0
        if (base->getQualifier().storage == EvqVaryingIn) {
568
            // validate stage in;
569
0
            if (preStage == EShLangCount)
570
0
                return;
571
0
            if (TSymbolTable::isBuiltInSymbol(base->getId()))
572
0
                return;
573
0
            if (outVarMaps[preStage] != nullptr) {
574
0
                auto ent2 = outVarMaps[preStage]->find(name);
575
0
                uint32_t location = base->getType().getQualifier().layoutLocation;
576
0
                if (ent2 == outVarMaps[preStage]->end() &&
577
0
                    location != glslang::TQualifier::layoutLocationEnd) {
578
0
                    for (auto var = outVarMaps[preStage]->begin(); var != ent2; var++) {
579
0
                        if (var->second.symbol->getType().getQualifier().layoutLocation == location) {
580
0
                            ent2 = var;
581
0
                            break;
582
0
                        }
583
0
                    }
584
0
                }
585
0
                if (ent2 != outVarMaps[preStage]->end()) {
586
0
                    auto& type1 = base->getType();
587
0
                    auto& type2 = ent2->second.symbol->getType();
588
0
                    hadError = hadError || typeCheck(&type1, &type2, name.c_str(), false);
589
0
                    if (ent2->second.symbol->getType().getQualifier().isArrayedIo(preStage)) {
590
0
                        TType subType(ent2->second.symbol->getType(), 0);
591
0
                        subType.appendMangledName(mangleName2);
592
0
                    } else {
593
0
                        ent2->second.symbol->getType().appendMangledName(mangleName2);
594
0
                    }
595
596
0
                    if (mangleName1 == mangleName2) {
597
                        // For ES 3.0 only, other versions have no such restrictions
598
                        // According to ES 3.0 spec: The type and presence of the interpolation qualifiers and
599
                        // storage qualifiers  of variables with the same name declared in all linked shaders must
600
                        // match, otherwise the link command will fail.
601
0
                        if (profile == EEsProfile && version == 300) {
602
                            // Don't need to check smooth qualifier, as it uses the default interpolation mode
603
0
                            if (ent1.stage == EShLangFragment && type1.isBuiltIn() == false) {
604
0
                                if (type1.getQualifier().flat != type2.getQualifier().flat ||
605
0
                                    type1.getQualifier().nopersp != type2.getQualifier().nopersp) {
606
0
                                    TString err = "Interpolation qualifier mismatch : " + entKey.first;
607
0
                                    infoSink.info.message(EPrefixInternalError, err.c_str());
608
0
                                    hadError = true;
609
0
                                }
610
0
                            }
611
0
                        }
612
0
                        return;
613
0
                    }
614
0
                    else {
615
                        // Deal with input/output pairs where one is a block member but the other is loose,
616
                        // e.g. with ARB_separate_shader_objects
617
0
                        if (type1.getBasicType() == EbtBlock &&
618
0
                            type1.isStruct() && !type2.isStruct()) {
619
                            // Iterate through block members tracking layout
620
0
                            glslang::TString name;
621
0
                            type1.getStruct()->begin()->type->appendMangledName(name);
622
0
                            if (name == mangleName2
623
0
                                && type1.getQualifier().layoutLocation == type2.getQualifier().layoutLocation) return;
624
0
                        }
625
0
                        if (type2.getBasicType() == EbtBlock &&
626
0
                            type2.isStruct() && !type1.isStruct()) {
627
                            // Iterate through block members tracking layout
628
0
                            glslang::TString name;
629
0
                            type2.getStruct()->begin()->type->appendMangledName(name);
630
0
                            if (name == mangleName1
631
0
                                && type1.getQualifier().layoutLocation == type2.getQualifier().layoutLocation) return;
632
0
                        }
633
0
                        TString err = "Invalid In/Out variable type : " + entKey.first;
634
0
                        infoSink.info.message(EPrefixInternalError, err.c_str());
635
0
                        hadError = true;
636
0
                    }
637
0
                }
638
0
                else if (!base->getType().isBuiltIn()) {
639
                    // According to spec: A link error is generated if any statically referenced input variable
640
                    // or block does not have a matching output
641
0
                    if (profile == EEsProfile && ent1.live) {
642
0
                        hadError = true;
643
0
                        TString errorStr = name + ": not been declare as a output variable in pre shader stage.";
644
0
                        infoSink.info.message(EPrefixError, errorStr.c_str());
645
0
                    }
646
0
                }
647
0
                return;
648
0
            }
649
0
        } else if (base->getQualifier().storage == EvqVaryingOut) {
650
            // validate stage out;
651
0
            if (nextStage == EShLangCount)
652
0
                return;
653
0
            if (TSymbolTable::isBuiltInSymbol(base->getId()))
654
0
                return;
655
0
            if (inVarMaps[nextStage] != nullptr) {
656
0
                auto ent2 = inVarMaps[nextStage]->find(name);
657
0
                if (ent2 != inVarMaps[nextStage]->end()) {
658
0
                    if (ent2->second.symbol->getType().getQualifier().isArrayedIo(nextStage)) {
659
0
                        TType subType(ent2->second.symbol->getType(), 0);
660
0
                        subType.appendMangledName(mangleName2);
661
0
                    } else {
662
0
                        ent2->second.symbol->getType().appendMangledName(mangleName2);
663
0
                    }
664
0
                    if (mangleName1 == mangleName2)
665
0
                        return;
666
0
                    else {
667
0
                        TString err = "Invalid In/Out variable type : " + entKey.first;
668
0
                        infoSink.info.message(EPrefixInternalError, err.c_str());
669
0
                        hadError = true;
670
0
                    }
671
0
                }
672
0
                return;
673
0
            }
674
0
        } else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().isPushConstant()) {
675
            // validate uniform type;
676
0
            for (int i = 0; i < EShLangCount; i++) {
677
0
                if (i != currentStage && outVarMaps[i] != nullptr) {
678
0
                    auto ent2 = uniformVarMap[i]->find(name);
679
0
                    if (ent2 != uniformVarMap[i]->end()) {
680
0
                        ent2->second.symbol->getType().appendMangledName(mangleName2);
681
0
                        if (mangleName1 != mangleName2) {
682
0
                            ent2->second.symbol->getType().sameElementType(type);
683
0
                            TString err = "Invalid Uniform variable type : " + entKey.first;
684
0
                            infoSink.info.message(EPrefixInternalError, err.c_str());
685
0
                            hadError = true;
686
0
                        }
687
0
                        mangleName2.clear();
688
689
                        // validate instance name of blocks
690
0
                        if (hadError == false &&
691
0
                            base->getType().getBasicType() == EbtBlock &&
692
0
                            IsAnonymous(base->getName()) != IsAnonymous(ent2->second.symbol->getName())) {
693
0
                            TString err = "Matched uniform block names must also either all be lacking "
694
0
                                          "an instance name or all having an instance name: " + entKey.first;
695
0
                            infoSink.info.message(EPrefixInternalError, err.c_str());
696
0
                            hadError = true;
697
0
                        }
698
699
                        // validate uniform block member qualifier and member names
700
0
                        auto& type1 = base->getType();
701
0
                        auto& type2 = ent2->second.symbol->getType();
702
0
                        if (hadError == false && base->getType().getBasicType() == EbtBlock) {
703
0
                            hadError = hadError || typeCheck(&type1, &type2, name.c_str(), true);
704
0
                        }
705
0
                        else {
706
0
                            hadError = hadError || typeCheck(&type1, &type2, name.c_str(), false);
707
0
                        }
708
0
                    }
709
0
                    else if (base->getBasicType() == EbtBlock)
710
0
                    {
711
0
                        if (IsAnonymous(base->getName()))
712
0
                        {
713
                            // The name of anonymous block member can't same with default uniform variable.
714
0
                            auto blockType1 = base->getType().getStruct();
715
0
                            for (size_t memberIdx = 0; memberIdx < blockType1->size(); ++memberIdx) {
716
0
                                auto memberName = (*blockType1)[memberIdx].type->getFieldName();
717
0
                                if (uniformVarMap[i]->find(memberName) != uniformVarMap[i]->end())
718
0
                                {
719
0
                                    TString err = "Invalid Uniform variable name : " + memberName;
720
0
                                    infoSink.info.message(EPrefixInternalError, err.c_str());
721
0
                                    hadError = true;
722
0
                                    break;
723
0
                                }
724
0
                            }
725
0
                        }
726
0
                    }
727
0
                }
728
0
            }
729
0
        }
730
0
    }
731
732
    TVarLiveMap *inVarMaps[EShLangCount], *outVarMaps[EShLangCount], *uniformVarMap[EShLangCount];
733
734
    // Use for mark current shader stage for resolver
735
    TIoMapResolver& resolver;
736
    TInfoSink& infoSink;
737
    bool& hadError;
738
    EProfile profile;
739
    int version;
740
741
private:
742
    TSymbolValidater& operator=(TSymbolValidater&) = delete;
743
744
    bool qualifierCheck(const TType* const type1, const TType* const type2, const std::string& name, bool isBlock)
745
0
    {
746
0
        bool hasError = false;
747
0
        const TQualifier& qualifier1 = type1->getQualifier();
748
0
        const TQualifier& qualifier2 = type2->getQualifier();
749
750
0
        if (((isBlock == false) &&
751
0
            (type1->getQualifier().storage == EvqUniform && type2->getQualifier().storage == EvqUniform)) ||
752
0
            (type1->getQualifier().storage == EvqGlobal && type2->getQualifier().storage == EvqGlobal)) {
753
0
            if (qualifier1.precision != qualifier2.precision) {
754
0
                hasError = true;
755
0
                std::string errorStr = name + ": have precision conflict cross stage.";
756
0
                infoSink.info.message(EPrefixError, errorStr.c_str());
757
0
            }
758
0
            if (qualifier1.hasFormat() && qualifier2.hasFormat()) {
759
0
                if (qualifier1.layoutFormat != qualifier2.layoutFormat) {
760
0
                    hasError = true;
761
0
                    std::string errorStr = name + ": have layout format conflict cross stage.";
762
0
                    infoSink.info.message(EPrefixError, errorStr.c_str());
763
0
                }
764
765
0
            }
766
0
        }
767
768
0
        if (isBlock == true) {
769
0
            if (qualifier1.layoutPacking != qualifier2.layoutPacking) {
770
0
                hasError = true;
771
0
                std::string errorStr = name + ": have layoutPacking conflict cross stage.";
772
0
                infoSink.info.message(EPrefixError, errorStr.c_str());
773
0
            }
774
0
            if (qualifier1.layoutMatrix != qualifier2.layoutMatrix) {
775
0
                hasError = true;
776
0
                std::string errorStr = name + ": have layoutMatrix conflict cross stage.";
777
0
                infoSink.info.message(EPrefixError, errorStr.c_str());
778
0
            }
779
0
            if (qualifier1.layoutOffset != qualifier2.layoutOffset) {
780
0
                hasError = true;
781
0
                std::string errorStr = name + ": have layoutOffset conflict cross stage.";
782
0
                infoSink.info.message(EPrefixError, errorStr.c_str());
783
0
            }
784
0
            if (qualifier1.layoutAlign != qualifier2.layoutAlign) {
785
0
                hasError = true;
786
0
                std::string errorStr = name + ": have layoutAlign conflict cross stage.";
787
0
                infoSink.info.message(EPrefixError, errorStr.c_str());
788
0
            }
789
0
        }
790
791
0
        return hasError;
792
0
    }
793
794
    bool typeCheck(const TType* const type1, const TType* const type2, const std::string& name, bool isBlock)
795
0
    {
796
0
        bool hasError = false;
797
0
        if (!(type1->isStruct() && type2->isStruct())) {
798
0
            hasError = hasError || qualifierCheck(type1, type2, name, isBlock);
799
0
        }
800
0
        else {
801
0
            if (type1->getBasicType() == EbtBlock && type2->getBasicType() == EbtBlock)
802
0
                isBlock = true;
803
0
            const TTypeList* typeList1 = type1->getStruct();
804
0
            const TTypeList* typeList2 = type2->getStruct();
805
806
0
            std::string newName = name;
807
0
            size_t memberCount = typeList1->size();
808
0
            size_t index2 = 0;
809
0
            for (size_t index = 0; index < memberCount; index++, index2++) {
810
                // Skip inactive member
811
0
                if (typeList1->at(index).type->getBasicType() == EbtVoid)
812
0
                    continue;
813
0
                while (index2 < typeList2->size() && typeList2->at(index2).type->getBasicType() == EbtVoid) {
814
0
                    ++index2;
815
0
                }
816
817
                // TypeList1 has more members in list
818
0
                if (index2 == typeList2->size()) {
819
0
                    std::string errorStr = name + ": struct mismatch.";
820
0
                    infoSink.info.message(EPrefixError, errorStr.c_str());
821
0
                    hasError = true;
822
0
                    break;
823
0
                }
824
825
0
                if (typeList1->at(index).type->getFieldName() != typeList2->at(index2).type->getFieldName()) {
826
0
                    std::string errorStr = name + ": member name mismatch.";
827
0
                    infoSink.info.message(EPrefixError, errorStr.c_str());
828
0
                    hasError = true;
829
0
                }
830
0
                else {
831
0
                    newName = typeList1->at(index).type->getFieldName().c_str();
832
0
                }
833
0
                hasError = hasError || typeCheck(typeList1->at(index).type, typeList2->at(index2).type, newName, isBlock);
834
0
            }
835
836
0
            while (index2 < typeList2->size())
837
0
            {
838
                // TypeList2 has more members
839
0
                if (typeList2->at(index2).type->getBasicType() != EbtVoid) {
840
0
                    std::string errorStr = name + ": struct mismatch.";
841
0
                    infoSink.info.message(EPrefixError, errorStr.c_str());
842
0
                    hasError = true;
843
0
                    break;
844
0
                }
845
0
                ++index2;
846
0
            }
847
0
        }
848
0
        return hasError;
849
0
    }
850
};
851
852
struct TSlotCollector {
853
0
    TSlotCollector(TIoMapResolver& r, TInfoSink& i) : resolver(r), infoSink(i) { }
854
855
0
    inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
856
0
        resolver.reserverStorageSlot(entKey.second, infoSink);
857
0
        resolver.reserverResourceSlot(entKey.second, infoSink);
858
0
    }
859
    TIoMapResolver& resolver;
860
    TInfoSink& infoSink;
861
862
private:
863
    TSlotCollector& operator=(TSlotCollector&) = delete;
864
};
865
866
TDefaultIoResolverBase::TDefaultIoResolverBase(const TIntermediate& intermediate)
867
784
    : referenceIntermediate(intermediate)
868
784
    , nextUniformLocation(intermediate.getUniformLocationBase())
869
784
    , nextInputLocation(0)
870
784
    , nextOutputLocation(0)
871
784
{
872
784
    memset(stageMask, false, sizeof(bool) * (EShLangCount + 1));
873
784
    memset(stageIntermediates, 0, sizeof(TIntermediate*) * (EShLangCount));
874
784
    stageIntermediates[intermediate.getStage()] = &intermediate;
875
784
}
876
877
624
int TDefaultIoResolverBase::getBaseBinding(EShLanguage stage, TResourceType res, unsigned int set) const {
878
624
    return stageIntermediates[stage] ? selectBaseBinding(stageIntermediates[stage]->getShiftBinding(res), stageIntermediates[stage]->getShiftBindingForSet(res, set))
879
624
                                     : selectBaseBinding(referenceIntermediate.getShiftBinding(res), referenceIntermediate.getShiftBindingForSet(res, set));
880
624
}
881
882
506
const std::vector<std::string>& TDefaultIoResolverBase::getResourceSetBinding(EShLanguage stage) const {
883
506
    return stageIntermediates[stage] ? stageIntermediates[stage]->getResourceSetBinding()
884
506
                                     : referenceIntermediate.getResourceSetBinding();
885
506
}
886
887
162
bool TDefaultIoResolverBase::doAutoBindingMapping() const { return referenceIntermediate.getAutoMapBindings(); }
888
889
1.15k
bool TDefaultIoResolverBase::doAutoLocationMapping() const { return referenceIntermediate.getAutoMapLocations(); }
890
891
786
TDefaultIoResolverBase::TSlotSet::iterator TDefaultIoResolverBase::findSlot(int set, int slot) {
892
786
    return std::lower_bound(slots[set].begin(), slots[set].end(), slot);
893
786
}
894
895
0
bool TDefaultIoResolverBase::checkEmpty(int set, int slot) {
896
0
    TSlotSet::iterator at = findSlot(set, slot);
897
0
    return ! (at != slots[set].end() && *at == slot);
898
0
}
899
900
624
int TDefaultIoResolverBase::reserveSlot(int set, int slot, int size) {
901
624
    TSlotSet::iterator at = findSlot(set, slot);
902
    // tolerate aliasing, by not double-recording aliases
903
    // (policy about appropriateness of the alias is higher up)
904
1.24k
    for (int i = 0; i < size; i++) {
905
624
        if (at == slots[set].end() || *at != slot + i)
906
602
            at = slots[set].insert(at, slot + i);
907
624
        ++at;
908
624
    }
909
624
    return slot;
910
624
}
911
912
162
int TDefaultIoResolverBase::getFreeSlot(int set, int base, int size) {
913
162
    TSlotSet::iterator at = findSlot(set, base);
914
162
    if (at == slots[set].end())
915
68
        return reserveSlot(set, base, size);
916
    // look for a big enough gap
917
386
    for (; at != slots[set].end(); ++at) {
918
292
        if (*at - base >= size)
919
0
            break;
920
292
        base = *at + 1;
921
292
    }
922
94
    return reserveSlot(set, base, size);
923
162
}
924
925
650
int TDefaultIoResolverBase::resolveSet(EShLanguage stage, TVarEntryInfo& ent) {
926
650
    const TType& type = ent.symbol->getType();
927
650
    if (type.getQualifier().hasSet()) {
928
144
        return ent.newSet = type.getQualifier().layoutSet;
929
144
    }
930
    // If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN)
931
506
    if (getResourceSetBinding(stage).size() == 1) {
932
0
        return ent.newSet = atoi(getResourceSetBinding(stage)[0].c_str());
933
0
    }
934
506
    return ent.newSet = 0;
935
506
}
936
937
650
int TDefaultIoResolverBase::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
938
650
    const TType& type = ent.symbol->getType();
939
650
    const char* name =  ent.symbol->getAccessName().c_str();
940
    // kick out of not doing this
941
650
    if (! doAutoLocationMapping()) {
942
2
        return ent.newLocation = -1;
943
2
    }
944
    // no locations added if already present, a built-in variable, a block, or an opaque
945
648
    if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock ||
946
648
        type.isAtomic() || type.isSpirvType() || (type.containsOpaque() && referenceIntermediate.getSpv().openGl == 0)) {
947
648
        return ent.newLocation = -1;
948
648
    }
949
    // no locations on blocks of built-in variables
950
0
    if (type.isStruct()) {
951
0
        if (type.getStruct()->size() < 1) {
952
0
            return ent.newLocation = -1;
953
0
        }
954
0
        if ((*type.getStruct())[0].type->isBuiltIn()) {
955
0
            return ent.newLocation = -1;
956
0
        }
957
0
    }
958
0
    int location = referenceIntermediate.getUniformLocationOverride(name);
959
0
    if (location != -1) {
960
0
        return ent.newLocation = location;
961
0
    }
962
0
    location = nextUniformLocation;
963
0
    nextUniformLocation += TIntermediate::computeTypeUniformLocationSize(type);
964
0
    return ent.newLocation = location;
965
0
}
966
967
508
int TDefaultIoResolverBase::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
968
508
    const TType& type = ent.symbol->getType();
969
    // kick out of not doing this
970
508
    if (! doAutoLocationMapping()) {
971
0
        return ent.newLocation = -1;
972
0
    }
973
974
    // no locations added if already present, a built-in variable, or a variable with SPIR-V decorate
975
508
    if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getQualifier().hasSpirvDecorate()) {
976
356
        return ent.newLocation = -1;
977
356
    }
978
979
    // no locations on blocks of built-in variables
980
152
    if (type.isStruct()) {
981
0
        if (type.getStruct()->size() < 1) {
982
0
            return ent.newLocation = -1;
983
0
        }
984
0
        if ((*type.getStruct())[0].type->isBuiltIn()) {
985
0
            return ent.newLocation = -1;
986
0
        }
987
0
    }
988
    // point to the right input or output location counter
989
152
    int& nextLocation = type.getQualifier().isPipeInput() ? nextInputLocation : nextOutputLocation;
990
    // Placeholder. This does not do proper cross-stage lining up, nor
991
    // work with mixed location/no-location declarations.
992
152
    int location = nextLocation;
993
152
    int typeLocationSize;
994
    // Don’t take into account the outer-most array if the stage’s
995
    // interface is automatically an array.
996
152
    typeLocationSize = computeTypeLocationSize(type, stage);
997
152
    nextLocation += typeLocationSize;
998
152
    return ent.newLocation = location;
999
152
}
1000
1001
508
int TDefaultIoResolverBase::resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) {
1002
508
    return ent.newComponent = -1;
1003
508
}
1004
1005
508
int TDefaultIoResolverBase::resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) { return ent.newIndex = -1; }
1006
1007
152
uint32_t TDefaultIoResolverBase::computeTypeLocationSize(const TType& type, EShLanguage stage) {
1008
152
    int typeLocationSize;
1009
    // Don’t take into account the outer-most array if the stage’s
1010
    // interface is automatically an array.
1011
152
    if (type.getQualifier().isArrayedIo(stage)) {
1012
0
        TType elementType(type, 0);
1013
0
        typeLocationSize = TIntermediate::computeTypeLocationSize(elementType, stage);
1014
152
    } else {
1015
152
        typeLocationSize = TIntermediate::computeTypeLocationSize(type, stage);
1016
152
    }
1017
152
    return typeLocationSize;
1018
152
}
1019
1020
//TDefaultGlslIoResolver
1021
0
TResourceType TDefaultGlslIoResolver::getResourceType(const glslang::TType& type) {
1022
0
    assert(isValidGlslType(type));
1023
0
    if (isImageType(type)) {
1024
0
        return EResImage;
1025
0
    }
1026
0
    if (isTextureType(type)) {
1027
0
        return EResTexture;
1028
0
    }
1029
0
    if (isSsboType(type)) {
1030
0
        return EResSsbo;
1031
0
    }
1032
0
    if (isSamplerType(type)) {
1033
0
        return EResSampler;
1034
0
    }
1035
0
    if (isUboType(type)) {
1036
0
        return EResUbo;
1037
0
    }
1038
0
    if (isCombinedSamplerType(type)) {
1039
0
        return EResCombinedSampler;
1040
0
    }
1041
0
    if (isAsType(type)) {
1042
0
        return EResAs;
1043
0
    }
1044
0
    if (isTensorType(type)) {
1045
0
        return EResTensor;
1046
0
    }
1047
0
    return EResCount;
1048
0
}
1049
1050
TDefaultGlslIoResolver::TDefaultGlslIoResolver(const TIntermediate& intermediate)
1051
0
    : TDefaultIoResolverBase(intermediate)
1052
0
    , preStage(EShLangCount)
1053
0
    , currentStage(EShLangCount)
1054
0
{ }
1055
1056
0
int TDefaultGlslIoResolver::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
1057
0
    const TType& type = ent.symbol->getType();
1058
0
    const TString& name = ent.symbol->getAccessName();
1059
0
    if (currentStage != stage) {
1060
0
        preStage = currentStage;
1061
0
        currentStage = stage;
1062
0
    }
1063
    // kick out if not doing this
1064
0
    if (! doAutoLocationMapping()) {
1065
0
        return ent.newLocation = -1;
1066
0
    }
1067
    // expand the location to each element if the symbol is a struct or array
1068
0
    if (type.getQualifier().hasLocation()) {
1069
0
        return ent.newLocation = type.getQualifier().layoutLocation;
1070
0
    }
1071
    // no locations added if already present, a built-in variable, or a variable with SPIR-V decorate
1072
0
    if (type.isBuiltIn() || type.getQualifier().hasSpirvDecorate()) {
1073
0
        return ent.newLocation = -1;
1074
0
    }
1075
    // no locations on blocks of built-in variables
1076
0
    if (type.isStruct()) {
1077
0
        if (type.getStruct()->size() < 1) {
1078
0
            return ent.newLocation = -1;
1079
0
        }
1080
0
        if ((*type.getStruct())[0].type->isBuiltIn()) {
1081
0
            return ent.newLocation = -1;
1082
0
        }
1083
0
    }
1084
0
    int typeLocationSize = computeTypeLocationSize(type, stage);
1085
0
    int location = type.getQualifier().layoutLocation;
1086
0
    bool hasLocation = false;
1087
0
    EShLanguage keyStage(EShLangCount);
1088
0
    TStorageQualifier storage;
1089
0
    storage = EvqInOut;
1090
0
    if (type.getQualifier().isPipeInput()) {
1091
        // If this symbol is a input, search pre stage's out
1092
0
        keyStage = preStage;
1093
0
    }
1094
0
    if (type.getQualifier().isPipeOutput()) {
1095
        // If this symbol is a output, search next stage's in
1096
0
        keyStage = currentStage;
1097
0
    }
1098
    // The in/out in current stage is not declared with location, but it is possible declared
1099
    // with explicit location in other stages, find the storageSlotMap firstly to check whether
1100
    // the in/out has location
1101
0
    int resourceKey = buildStorageKey(keyStage, storage);
1102
0
    if (! storageSlotMap[resourceKey].empty()) {
1103
0
        TVarSlotMap::iterator iter = storageSlotMap[resourceKey].find(name);
1104
0
        if (iter != storageSlotMap[resourceKey].end()) {
1105
            // If interface resource be found, set it has location and this symbol's new location
1106
            // equal the symbol's explicit location declaration in pre or next stage.
1107
            //
1108
            // vs:    out vec4 a;
1109
            // fs:    layout(..., location = 3,...) in vec4 a;
1110
0
            hasLocation = true;
1111
0
            location = iter->second;
1112
            // if we want deal like that:
1113
            // vs:    layout(location=4) out vec4 a;
1114
            //        out vec4 b;
1115
            //
1116
            // fs:    in vec4 a;
1117
            //        layout(location = 4) in vec4 b;
1118
            // we need retraverse the map.
1119
0
        }
1120
0
        if (! hasLocation) {
1121
            // If interface resource note found, It's mean the location in two stage are both implicit declarat.
1122
            // So we should find a new slot for this interface.
1123
            //
1124
            // vs: out vec4 a;
1125
            // fs: in vec4 a;
1126
0
            location = getFreeSlot(resourceKey, 0, typeLocationSize);
1127
0
            storageSlotMap[resourceKey][name] = location;
1128
0
        }
1129
0
    } else {
1130
        // the first interface declarated in a program.
1131
0
        TVarSlotMap varSlotMap;
1132
0
        location = getFreeSlot(resourceKey, 0, typeLocationSize);
1133
0
        varSlotMap[name] = location;
1134
0
        storageSlotMap[resourceKey] = varSlotMap;
1135
0
    }
1136
    //Update location
1137
0
    return ent.newLocation = location;
1138
0
}
1139
1140
0
int TDefaultGlslIoResolver::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
1141
0
    const TType& type = ent.symbol->getType();
1142
0
    const TString& name = ent.symbol->getAccessName();
1143
    // kick out of not doing this
1144
0
    if (! doAutoLocationMapping()) {
1145
0
        return ent.newLocation = -1;
1146
0
    }
1147
    // expand the location to each element if the symbol is a struct or array
1148
0
    if (type.getQualifier().hasLocation() && (type.isStruct() || type.isArray())) {
1149
0
        return ent.newLocation = type.getQualifier().layoutLocation;
1150
0
    } else {
1151
        // no locations added if already present, a built-in variable, a block, or an opaque
1152
0
        if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock ||
1153
0
            type.isAtomic() || type.isSpirvType() ||
1154
0
            (type.containsOpaque() && referenceIntermediate.getSpv().openGl == 0)) {
1155
0
            return ent.newLocation = -1;
1156
0
        }
1157
        // no locations on blocks of built-in variables
1158
0
        if (type.isStruct()) {
1159
0
            if (type.getStruct()->size() < 1) {
1160
0
                return ent.newLocation = -1;
1161
0
            }
1162
0
            if ((*type.getStruct())[0].type->isBuiltIn()) {
1163
0
                return ent.newLocation = -1;
1164
0
            }
1165
0
        }
1166
0
    }
1167
0
    int location = referenceIntermediate.getUniformLocationOverride(name.c_str());
1168
0
    if (location != -1) {
1169
0
        return ent.newLocation = location;
1170
0
    }
1171
1172
0
    int size = TIntermediate::computeTypeUniformLocationSize(type);
1173
1174
    // The uniform in current stage is not declared with location, but it is possible declared
1175
    // with explicit location in other stages, find the storageSlotMap firstly to check whether
1176
    // the uniform has location
1177
0
    bool hasLocation = false;
1178
0
    int resourceKey = buildStorageKey(EShLangCount, EvqUniform);
1179
0
    TVarSlotMap& slotMap = storageSlotMap[resourceKey];
1180
    // Check dose shader program has uniform resource
1181
0
    if (! slotMap.empty()) {
1182
        // If uniform resource not empty, try find a same name uniform
1183
0
        TVarSlotMap::iterator iter = slotMap.find(name);
1184
0
        if (iter != slotMap.end()) {
1185
            // If uniform resource be found, set it has location and this symbol's new location
1186
            // equal the uniform's explicit location declaration in other stage.
1187
            //
1188
            // vs:    uniform vec4 a;
1189
            // fs:    layout(..., location = 3,...) uniform vec4 a;
1190
0
            hasLocation = true;
1191
0
            location = iter->second;
1192
0
        }
1193
0
        if (! hasLocation) {
1194
            // No explicit location declaration in other stage.
1195
            // So we should find a new slot for this uniform.
1196
            //
1197
            // vs:    uniform vec4 a;
1198
            // fs:    uniform vec4 a;
1199
0
            location = getFreeSlot(resourceKey, 0, computeTypeLocationSize(type, currentStage));
1200
0
            storageSlotMap[resourceKey][name] = location;
1201
0
        }
1202
0
    } else {
1203
        // the first uniform declaration in a program.
1204
0
        TVarSlotMap varSlotMap;
1205
0
        location = getFreeSlot(resourceKey, 0, size);
1206
0
        varSlotMap[name] = location;
1207
0
        storageSlotMap[resourceKey] = varSlotMap;
1208
0
    }
1209
0
    return ent.newLocation = location;
1210
0
}
1211
1212
0
int TDefaultGlslIoResolver::resolveBinding(EShLanguage stage, TVarEntryInfo& ent) {
1213
0
    const TType& type = ent.symbol->getType();
1214
0
    const TString& name = ent.symbol->getAccessName();
1215
    // On OpenGL arrays of opaque types take a separate binding for each element
1216
0
    int numBindings = referenceIntermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
1217
0
    TResourceType resource = getResourceType(type);
1218
    // don't need to handle uniform symbol, it will be handled in resolveUniformLocation
1219
0
    if (resource == EResUbo && type.getBasicType() != EbtBlock) {
1220
0
        return ent.newBinding = -1;
1221
0
    }
1222
    // There is no 'set' qualifier in OpenGL shading language, each resource has its own
1223
    // binding name space, so remap the 'set' to resource type which make each resource
1224
    // binding is valid from 0 to MAX_XXRESOURCE_BINDINGS
1225
0
    int set = referenceIntermediate.getSpv().openGl != 0 ? resource : ent.newSet;
1226
0
    int resourceKey = set;
1227
0
    if (resource < EResCount) {
1228
0
        if (type.getQualifier().hasBinding()) {
1229
0
            int newBinding = reserveSlot(resourceKey, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding, numBindings);
1230
0
            return ent.newBinding = newBinding;
1231
1232
0
        } else {
1233
            // The resource in current stage is not declared with binding, but it is possible declared
1234
            // with explicit binding in other stages, find the resourceSlotMap firstly to check whether
1235
            // the resource has binding, don't need to allocate if it already has a binding
1236
0
            bool hasBinding = false;
1237
0
            ent.newBinding = -1; // leave as -1 if it isn't set below
1238
1239
0
            if (! resourceSlotMap[resourceKey].empty()) {
1240
0
                TVarSlotMap::iterator iter = resourceSlotMap[resourceKey].find(name);
1241
0
                if (iter != resourceSlotMap[resourceKey].end()) {
1242
0
                    hasBinding = true;
1243
0
                    ent.newBinding = iter->second;
1244
0
                }
1245
0
            }
1246
0
            if (!hasBinding && (ent.live && doAutoBindingMapping())) {
1247
                // find free slot, the caller did make sure it passes all vars with binding
1248
                // first and now all are passed that do not have a binding and needs one
1249
0
                int binding = getFreeSlot(resourceKey, getBaseBinding(stage, resource, set), numBindings);
1250
0
                resourceSlotMap[resourceKey][name] = binding;
1251
0
                ent.newBinding = binding;
1252
0
            }
1253
0
            return ent.newBinding;
1254
0
        }
1255
0
    }
1256
0
    return ent.newBinding = -1;
1257
0
}
1258
1259
0
void TDefaultGlslIoResolver::beginResolve(EShLanguage stage) {
1260
    // reset stage state
1261
0
    if (stage == EShLangCount)
1262
0
        preStage = currentStage = stage;
1263
    // update stage state
1264
0
    else if (currentStage != stage) {
1265
0
        preStage = currentStage;
1266
0
        currentStage = stage;
1267
0
    }
1268
0
}
1269
1270
0
void TDefaultGlslIoResolver::endResolve(EShLanguage /*stage*/) {
1271
    // TODO nothing
1272
0
}
1273
1274
0
void TDefaultGlslIoResolver::beginCollect(EShLanguage stage) {
1275
    // reset stage state
1276
0
    if (stage == EShLangCount)
1277
0
        preStage = currentStage = stage;
1278
    // update stage state
1279
0
    else if (currentStage != stage) {
1280
0
        preStage = currentStage;
1281
0
        currentStage = stage;
1282
0
    }
1283
0
}
1284
1285
0
void TDefaultGlslIoResolver::endCollect(EShLanguage /*stage*/) {
1286
    // TODO nothing
1287
0
}
1288
1289
0
void TDefaultGlslIoResolver::reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
1290
0
    const TType& type = ent.symbol->getType();
1291
0
    const TString& name = ent.symbol->getAccessName();
1292
0
    TStorageQualifier storage = type.getQualifier().storage;
1293
0
    EShLanguage stage(EShLangCount);
1294
0
    switch (storage) {
1295
0
    case EvqUniform:
1296
0
        if (type.getBasicType() != EbtBlock && type.getQualifier().hasLocation()) {
1297
            //
1298
            // Reserve the slots for the uniforms who has explicit location
1299
0
            int storageKey = buildStorageKey(EShLangCount, EvqUniform);
1300
0
            int location = type.getQualifier().layoutLocation;
1301
0
            TVarSlotMap& varSlotMap = storageSlotMap[storageKey];
1302
0
            TVarSlotMap::iterator iter = varSlotMap.find(name);
1303
0
            if (iter == varSlotMap.end()) {
1304
0
                int numLocations = TIntermediate::computeTypeUniformLocationSize(type);
1305
0
                reserveSlot(storageKey, location, numLocations);
1306
0
                varSlotMap[name] = location;
1307
0
            } else {
1308
                // Allocate location by name for OpenGL driver, so the uniform in different
1309
                // stages should be declared with the same location
1310
0
                if (iter->second != location) {
1311
0
                    TString errorMsg = "Invalid location: " + name;
1312
0
                    infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
1313
0
                    hasError = true;
1314
0
                }
1315
0
            }
1316
0
        }
1317
0
        break;
1318
0
    case EvqVaryingIn:
1319
0
    case EvqVaryingOut:
1320
        //
1321
        // Reserve the slots for the inout who has explicit location
1322
0
        if (type.getQualifier().hasLocation()) {
1323
0
            stage = storage == EvqVaryingIn ? preStage : stage;
1324
0
            stage = storage == EvqVaryingOut ? currentStage : stage;
1325
0
            int storageKey = buildStorageKey(stage, EvqInOut);
1326
0
            int location = type.getQualifier().layoutLocation;
1327
0
            TVarSlotMap& varSlotMap = storageSlotMap[storageKey];
1328
0
            TVarSlotMap::iterator iter = varSlotMap.find(name);
1329
0
            if (iter == varSlotMap.end()) {
1330
0
                int numLocations = TIntermediate::computeTypeUniformLocationSize(type);
1331
0
                reserveSlot(storageKey, location, numLocations);
1332
0
                varSlotMap[name] = location;
1333
0
            } else {
1334
                // Allocate location by name for OpenGL driver, so the uniform in different
1335
                // stages should be declared with the same location
1336
0
                if (iter->second != location) {
1337
0
                    TString errorMsg = "Invalid location: " + name;
1338
0
                    infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
1339
0
                    hasError = true;
1340
0
                }
1341
0
            }
1342
0
        }
1343
0
        break;
1344
0
    default:
1345
0
        break;
1346
0
    }
1347
0
}
1348
1349
0
void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
1350
0
    const TType& type = ent.symbol->getType();
1351
0
    const TString& name = ent.symbol->getAccessName();
1352
0
    TResourceType resource = getResourceType(type);
1353
0
    int set = referenceIntermediate.getSpv().openGl != 0 ? resource : resolveSet(ent.stage, ent);
1354
0
    int resourceKey = set;
1355
1356
0
    if (type.getQualifier().hasBinding()) {
1357
0
        TVarSlotMap& varSlotMap = resourceSlotMap[resourceKey];
1358
0
        TVarSlotMap::iterator iter = varSlotMap.find(name);
1359
0
        int binding = type.getQualifier().layoutBinding + getBaseBinding(ent.stage, resource, set);
1360
1361
0
        if (iter == varSlotMap.end()) {
1362
            // Reserve the slots for the ubo, ssbo and opaques who has explicit binding
1363
0
            int numBindings = referenceIntermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
1364
0
            varSlotMap[name] = binding;
1365
0
            reserveSlot(resourceKey, binding, numBindings);
1366
0
        } else {
1367
            // Allocate binding by name for OpenGL driver, so the resource in different
1368
            // stages should be declared with the same binding
1369
0
            if (iter->second != binding) {
1370
0
                TString errorMsg = "Invalid binding: " + name;
1371
0
                infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
1372
0
                hasError = true;
1373
0
            }
1374
0
        }
1375
0
    }
1376
0
}
1377
1378
//TDefaultGlslIoResolver end
1379
1380
/*
1381
 * Basic implementation of glslang::TIoMapResolver that replaces the
1382
 * previous offset behavior.
1383
 * It does the same, uses the offsets for the corresponding uniform
1384
 * types. Also respects the EOptionAutoMapBindings flag and binds
1385
 * them if needed.
1386
 */
1387
/*
1388
 * Default resolver
1389
 */
1390
struct TDefaultIoResolver : public TDefaultIoResolverBase {
1391
392
    TDefaultIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
1392
1393
650
    bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
1394
1395
650
    TResourceType getResourceType(const glslang::TType& type) override {
1396
650
        assert(isValidGlslType(type));
1397
650
        if (isImageType(type)) {
1398
140
            return EResImage;
1399
140
        }
1400
510
        if (isTextureType(type)) {
1401
100
            return EResTexture;
1402
100
        }
1403
410
        if (isSsboType(type)) {
1404
118
            return EResSsbo;
1405
118
        }
1406
292
        if (isSamplerType(type)) {
1407
54
            return EResSampler;
1408
54
        }
1409
238
        if (isUboType(type)) {
1410
144
            return EResUbo;
1411
144
        }
1412
94
        if (isCombinedSamplerType(type)) {
1413
88
            return EResCombinedSampler;
1414
88
        }
1415
6
        if (isAsType(type)) {
1416
6
            return EResAs;
1417
6
        }
1418
0
        if (isTensorType(type)) {
1419
0
            return EResTensor;
1420
0
        }
1421
0
        return EResCount;
1422
0
    }
1423
1424
650
    int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) override {
1425
650
        const TType& type = ent.symbol->getType();
1426
650
        const int set = getLayoutSet(type);
1427
        // On OpenGL arrays of opaque types take a seperate binding for each element
1428
650
        int numBindings = referenceIntermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
1429
650
        TResourceType resource = getResourceType(type);
1430
650
        if (resource < EResCount) {
1431
650
            if (type.getQualifier().hasBinding()) {
1432
462
                return ent.newBinding = reserveSlot(
1433
462
                           set, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding, numBindings);
1434
462
            } else if (ent.live && doAutoBindingMapping()) {
1435
                // find free slot, the caller did make sure it passes all vars with binding
1436
                // first and now all are passed that do not have a binding and needs one
1437
162
                return ent.newBinding = getFreeSlot(set, getBaseBinding(stage, resource, set), numBindings);
1438
162
            }
1439
650
        }
1440
26
        return ent.newBinding = -1;
1441
650
    }
1442
};
1443
1444
#ifdef ENABLE_HLSL
1445
/********************************************************************************
1446
The following IO resolver maps types in HLSL register space, as follows:
1447
1448
t - for shader resource views (SRV)
1449
   TEXTURE1D
1450
   TEXTURE1DARRAY
1451
   TEXTURE2D
1452
   TEXTURE2DARRAY
1453
   TEXTURE3D
1454
   TEXTURECUBE
1455
   TEXTURECUBEARRAY
1456
   TEXTURE2DMS
1457
   TEXTURE2DMSARRAY
1458
   STRUCTUREDBUFFER
1459
   BYTEADDRESSBUFFER
1460
   BUFFER
1461
   TBUFFER
1462
1463
s - for samplers
1464
   SAMPLER
1465
   SAMPLER1D
1466
   SAMPLER2D
1467
   SAMPLER3D
1468
   SAMPLERCUBE
1469
   SAMPLERSTATE
1470
   SAMPLERCOMPARISONSTATE
1471
1472
u - for unordered access views (UAV)
1473
   RWBYTEADDRESSBUFFER
1474
   RWSTRUCTUREDBUFFER
1475
   APPENDSTRUCTUREDBUFFER
1476
   CONSUMESTRUCTUREDBUFFER
1477
   RWBUFFER
1478
   RWTEXTURE1D
1479
   RWTEXTURE1DARRAY
1480
   RWTEXTURE2D
1481
   RWTEXTURE2DARRAY
1482
   RWTEXTURE3D
1483
1484
b - for constant buffer views (CBV)
1485
   CBUFFER
1486
   CONSTANTBUFFER
1487
 ********************************************************************************/
1488
struct TDefaultHlslIoResolver : public TDefaultIoResolverBase {
1489
392
    TDefaultHlslIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
1490
1491
0
    bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
1492
1493
0
    TResourceType getResourceType(const glslang::TType& type) override {
1494
0
        if (isUavType(type)) {
1495
0
            return EResUav;
1496
0
        }
1497
0
        if (isSrvType(type)) {
1498
0
            return EResTexture;
1499
0
        }
1500
0
        if (isSamplerType(type)) {
1501
0
            return EResSampler;
1502
0
        }
1503
0
        if (isUboType(type)) {
1504
0
            return EResUbo;
1505
0
        }
1506
        // no support for combined samplers in HLSL
1507
0
        if (isAsType(type)) {
1508
0
            return EResAs;
1509
0
        }
1510
        // no support for tensors in HLSL
1511
0
        return EResCount;
1512
0
    }
1513
1514
0
    int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) override {
1515
0
        const TType& type = ent.symbol->getType();
1516
0
        const int set = getLayoutSet(type);
1517
0
        TResourceType resource = getResourceType(type);
1518
0
        if (resource < EResCount) {
1519
0
            if (type.getQualifier().hasBinding()) {
1520
0
                return ent.newBinding = reserveSlot(set, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding);
1521
0
            } else if (ent.live && doAutoBindingMapping()) {
1522
                // find free slot, the caller did make sure it passes all vars with binding
1523
                // first and now all are passed that do not have a binding and needs one
1524
0
                return ent.newBinding = getFreeSlot(set, getBaseBinding(stage, resource, set));
1525
0
            }
1526
0
        }
1527
0
        return ent.newBinding = -1;
1528
0
    }
1529
};
1530
#endif
1531
1532
// Map I/O variables to provided offsets, and make bindings for
1533
// unbound but live variables.
1534
//
1535
// Returns false if the input is too malformed to do this.
1536
2.49k
bool TIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) {
1537
2.49k
    bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() ||
1538
2.07k
                         intermediate.getAutoMapLocations();
1539
    // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce
1540
    // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true.
1541
21.0k
    for (int res = 0; (res < EResCount && !somethingToDo); ++res) {
1542
18.5k
        somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
1543
18.5k
                        intermediate.hasShiftBindingForSet(TResourceType(res));
1544
18.5k
    }
1545
2.49k
    if (! somethingToDo && resolver == nullptr)
1546
2.06k
        return true;
1547
426
    if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive())
1548
34
        return false;
1549
392
    TIntermNode* root = intermediate.getTreeRoot();
1550
392
    if (root == nullptr)
1551
0
        return false;
1552
    // if no resolver is provided, use the default resolver with the given shifts and auto map settings
1553
392
    TDefaultIoResolver defaultResolver(intermediate);
1554
392
#ifdef ENABLE_HLSL
1555
392
    TDefaultHlslIoResolver defaultHlslResolver(intermediate);
1556
392
    if (resolver == nullptr) {
1557
        // TODO: use a passed in IO mapper for this
1558
392
        if (intermediate.usingHlslIoMapping())
1559
0
            resolver = &defaultHlslResolver;
1560
392
        else
1561
392
            resolver = &defaultResolver;
1562
392
    }
1563
#else
1564
    resolver = &defaultResolver;
1565
#endif
1566
392
    resolver->addStage(stage, intermediate);
1567
1568
392
    TVarLiveMap inVarMap, outVarMap, uniformVarMap;
1569
392
    TVarLiveVector inVector, outVector, uniformVector;
1570
392
    TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap);
1571
392
    TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap);
1572
392
    root->traverse(&iter_binding_all);
1573
392
    iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
1574
922
    while (! iter_binding_live.destinations.empty()) {
1575
530
        TIntermNode* destination = iter_binding_live.destinations.back();
1576
530
        iter_binding_live.destinations.pop_back();
1577
530
        destination->traverse(&iter_binding_live);
1578
530
    }
1579
1580
    // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info.
1581
392
    for (auto& var : inVarMap) { inVector.push_back(var); }
1582
392
    std::sort(inVector.begin(), inVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1583
250
        return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1584
250
    });
1585
392
    for (auto& var : outVarMap) { outVector.push_back(var); }
1586
392
    std::sort(outVector.begin(), outVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1587
6
        return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1588
6
    });
1589
650
    for (auto& var : uniformVarMap) { uniformVector.push_back(var); }
1590
978
    std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1591
978
        return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1592
978
    });
1593
392
    bool hadError = false;
1594
392
    TVarLiveMap* dummyUniformVarMap[EShLangCount] = {};
1595
392
    TNotifyInOutAdaptor inOutNotify(stage, *resolver);
1596
392
    TNotifyUniformAdaptor uniformNotify(stage, *resolver);
1597
392
    TResolverUniformAdaptor uniformResolve(stage, *resolver, dummyUniformVarMap, infoSink, hadError);
1598
392
    TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError);
1599
392
    resolver->beginNotifications(stage);
1600
392
    std::for_each(inVector.begin(), inVector.end(), inOutNotify);
1601
392
    std::for_each(outVector.begin(), outVector.end(), inOutNotify);
1602
392
    std::for_each(uniformVector.begin(), uniformVector.end(), uniformNotify);
1603
392
    resolver->endNotifications(stage);
1604
392
    resolver->beginResolve(stage);
1605
392
    for (auto& var : inVector) { inOutResolve(var); }
1606
392
    std::for_each(inVector.begin(), inVector.end(), [&inVarMap](TVarLivePair p) {
1607
314
        auto at = inVarMap.find(p.second.symbol->getAccessName());
1608
314
        if (at != inVarMap.end() && p.second.id == at->second.id)
1609
314
            at->second = p.second;
1610
314
    });
1611
392
    for (auto& var : outVector) { inOutResolve(var); }
1612
392
    std::for_each(outVector.begin(), outVector.end(), [&outVarMap](TVarLivePair p) {
1613
194
        auto at = outVarMap.find(p.second.symbol->getAccessName());
1614
194
        if (at != outVarMap.end() && p.second.id == at->second.id)
1615
194
            at->second = p.second;
1616
194
    });
1617
392
    std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve);
1618
650
    std::for_each(uniformVector.begin(), uniformVector.end(), [&uniformVarMap](TVarLivePair p) {
1619
650
        auto at = uniformVarMap.find(p.second.symbol->getAccessName());
1620
650
        if (at != uniformVarMap.end() && p.second.id == at->second.id)
1621
650
            at->second = p.second;
1622
650
    });
1623
392
    resolver->endResolve(stage);
1624
392
    if (!hadError) {
1625
392
        TVarSetTraverser iter_iomap(intermediate, inVarMap, outVarMap, uniformVarMap);
1626
392
        root->traverse(&iter_iomap);
1627
392
    }
1628
392
    return !hadError;
1629
392
}
1630
1631
0
TGlslIoMapper::TGlslIoMapper() {
1632
0
    memset(inVarMaps,     0, sizeof(TVarLiveMap*)   * EShLangCount);
1633
0
    memset(outVarMaps,    0, sizeof(TVarLiveMap*)   * EShLangCount);
1634
0
    memset(uniformVarMap, 0, sizeof(TVarLiveMap*)   * EShLangCount);
1635
0
    memset(intermediates, 0, sizeof(TIntermediate*) * EShLangCount);
1636
0
    profile = ENoProfile;
1637
0
    version = 0;
1638
0
    autoPushConstantMaxSize = 128;
1639
0
    autoPushConstantBlockPacking = ElpStd430;
1640
0
}
1641
1642
0
TGlslIoMapper::~TGlslIoMapper() {
1643
0
    for (size_t stage = 0; stage < EShLangCount; stage++) {
1644
0
        if (inVarMaps[stage] != nullptr) {
1645
0
            delete inVarMaps[stage];
1646
0
            inVarMaps[stage] = nullptr;
1647
0
        }
1648
0
        if (outVarMaps[stage] != nullptr) {
1649
0
            delete outVarMaps[stage];
1650
0
            outVarMaps[stage] = nullptr;
1651
0
        }
1652
0
        if (uniformVarMap[stage] != nullptr) {
1653
0
            delete uniformVarMap[stage];
1654
0
            uniformVarMap[stage] = nullptr;
1655
0
        }
1656
0
        if (intermediates[stage] != nullptr)
1657
0
            intermediates[stage] = nullptr;
1658
0
    }
1659
0
}
1660
1661
// Map I/O variables to provided offsets, and make bindings for
1662
// unbound but live variables.
1663
//
1664
// Returns false if the input is too malformed to do this.
1665
0
bool TGlslIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) {
1666
0
    bool somethingToDo = !intermediate.getResourceSetBinding().empty() ||
1667
0
        intermediate.getAutoMapBindings() ||
1668
0
        intermediate.getAutoMapLocations();
1669
1670
    // Profile and version are use for symbol validate.
1671
0
    profile = intermediate.getProfile();
1672
0
    version = intermediate.getVersion();
1673
1674
    // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce
1675
    // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true.
1676
0
    for (int res = 0; (res < EResCount && !somethingToDo); ++res) {
1677
0
        somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
1678
0
                        intermediate.hasShiftBindingForSet(TResourceType(res));
1679
0
    }
1680
0
    if (! somethingToDo && resolver == nullptr) {
1681
0
        return true;
1682
0
    }
1683
0
    if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive()) {
1684
0
        return false;
1685
0
    }
1686
0
    TIntermNode* root = intermediate.getTreeRoot();
1687
0
    if (root == nullptr) {
1688
0
        return false;
1689
0
    }
1690
    // if no resolver is provided, use the default resolver with the given shifts and auto map settings
1691
0
    TDefaultGlslIoResolver defaultResolver(intermediate);
1692
0
#ifdef ENABLE_HLSL
1693
0
    TDefaultHlslIoResolver defaultHlslResolver(intermediate);
1694
0
    if (resolver == nullptr) {
1695
        // TODO: use a passed in IO mapper for this
1696
0
        if (intermediate.usingHlslIoMapping())
1697
0
            resolver = &defaultHlslResolver;
1698
0
        else
1699
0
            resolver = &defaultResolver;
1700
0
    }
1701
#else
1702
    if (resolver == nullptr) {
1703
        resolver = &defaultResolver;
1704
    }
1705
#endif
1706
0
    resolver->addStage(stage, intermediate);
1707
0
    inVarMaps[stage] = new TVarLiveMap(); outVarMaps[stage] = new TVarLiveMap(); uniformVarMap[stage] = new TVarLiveMap();
1708
0
    TVarGatherTraverser iter_binding_all(intermediate, true, *inVarMaps[stage], *outVarMaps[stage],
1709
0
                                         *uniformVarMap[stage]);
1710
0
    TVarGatherTraverser iter_binding_live(intermediate, false, *inVarMaps[stage], *outVarMaps[stage],
1711
0
                                          *uniformVarMap[stage]);
1712
0
    root->traverse(&iter_binding_all);
1713
0
    iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
1714
0
    while (! iter_binding_live.destinations.empty()) {
1715
0
        TIntermNode* destination = iter_binding_live.destinations.back();
1716
0
        iter_binding_live.destinations.pop_back();
1717
0
        destination->traverse(&iter_binding_live);
1718
0
    }
1719
1720
0
    TNotifyInOutAdaptor inOutNotify(stage, *resolver);
1721
0
    TNotifyUniformAdaptor uniformNotify(stage, *resolver);
1722
    // Resolve current stage input symbol location with previous stage output here,
1723
    // uniform symbol, ubo, ssbo and opaque symbols are per-program resource,
1724
    // will resolve uniform symbol location and ubo/ssbo/opaque binding in doMap()
1725
0
    resolver->beginNotifications(stage);
1726
0
    std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), inOutNotify);
1727
0
    std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), inOutNotify);
1728
0
    std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), uniformNotify);
1729
0
    resolver->endNotifications(stage);
1730
0
    TSlotCollector slotCollector(*resolver, infoSink);
1731
0
    resolver->beginCollect(stage);
1732
0
    std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), slotCollector);
1733
0
    std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), slotCollector);
1734
0
    std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), slotCollector);
1735
0
    resolver->endCollect(stage);
1736
0
    intermediates[stage] = &intermediate;
1737
0
    return !hadError;
1738
0
}
1739
1740
0
bool TGlslIoMapper::doMap(TIoMapResolver* resolver, TInfoSink& infoSink) {
1741
0
    resolver->endResolve(EShLangCount);
1742
0
    if (!hadError) {
1743
        //Resolve uniform location, ubo/ssbo/opaque bindings across stages
1744
0
        TResolverUniformAdaptor uniformResolve(EShLangCount, *resolver, uniformVarMap, infoSink, hadError);
1745
0
        TResolverInOutAdaptor inOutResolve(EShLangCount, *resolver, infoSink, hadError);
1746
0
        TSymbolValidater symbolValidater(*resolver, infoSink, inVarMaps,
1747
0
                                         outVarMaps, uniformVarMap, hadError, profile, version);
1748
1749
0
        TVarLiveVector inVectors[EShLangCount];
1750
0
        TVarLiveVector outVectors[EShLangCount];
1751
0
        TVarLiveVector uniformVector;
1752
1753
0
        resolver->beginResolve(EShLangCount);
1754
0
        for (int stage = EShLangVertex; stage < EShLangCount; stage++) {
1755
0
            if (inVarMaps[stage] != nullptr) {
1756
0
                inOutResolve.setStage(EShLanguage(stage));
1757
1758
                // copy vars into a sorted list
1759
0
                std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(),
1760
0
                        [&inVectors, stage](TVarLivePair p) { inVectors[stage].push_back(p); });
1761
0
                std::sort(inVectors[stage].begin(), inVectors[stage].end(),
1762
0
                        [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1763
0
                            return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1764
0
                });
1765
1766
0
                std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(),
1767
0
                        [&outVectors, stage](TVarLivePair p) { outVectors[stage].push_back(p); });
1768
0
                std::sort(outVectors[stage].begin(), outVectors[stage].end(),
1769
0
                        [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1770
0
                            return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1771
0
                });
1772
1773
0
                for (auto& var : inVectors[stage]) { symbolValidater(var); }
1774
0
                for (auto& var : inVectors[stage]) { inOutResolve(var); }
1775
0
                for (auto& var : outVectors[stage]) { symbolValidater(var); }
1776
0
                for (auto& var : outVectors[stage]) { inOutResolve(var); }
1777
1778
                // copy results back into maps
1779
0
                std::for_each(inVectors[stage].begin(), inVectors[stage].end(),
1780
0
                    [this, stage](TVarLivePair p) {
1781
0
                        auto at = inVarMaps[stage]->find(p.first);
1782
0
                        if (at != inVarMaps[stage]->end())
1783
0
                            at->second = p.second;
1784
0
                });
1785
1786
0
                std::for_each(outVectors[stage].begin(), outVectors[stage].end(),
1787
0
                    [this, stage](TVarLivePair p) {
1788
0
                        auto at = outVarMaps[stage]->find(p.first);
1789
0
                        if (at != outVarMaps[stage]->end())
1790
0
                            at->second = p.second;
1791
0
                });
1792
1793
0
            }
1794
0
            if (uniformVarMap[stage] != nullptr) {
1795
0
                uniformResolve.setStage(EShLanguage(stage));
1796
0
                for (auto& var : *(uniformVarMap[stage])) { uniformVector.push_back(var); }
1797
0
            }
1798
0
        }
1799
0
        std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1800
0
            return TVarEntryInfo::TOrderByPriorityAndLive()(p1.second, p2.second);
1801
0
        });
1802
0
        for (auto& var : uniformVector) { symbolValidater(var); }
1803
0
        for (auto& var : uniformVector) { uniformResolve(var); }
1804
0
        std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1805
0
            return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1806
0
        });
1807
0
        resolver->endResolve(EShLangCount);
1808
0
        if (autoPushConstantBlockName.length()) {
1809
0
            bool upgraded = false;
1810
0
            for (size_t stage = 0; stage < EShLangCount; stage++) {
1811
0
                if (intermediates[stage] != nullptr) {
1812
0
                    TVarLiveMap** pUniformVarMap = uniformResolve.uniformVarMap;
1813
0
                    auto at = pUniformVarMap[stage]->find(autoPushConstantBlockName);
1814
0
                    if (at == pUniformVarMap[stage]->end())
1815
0
                        continue;
1816
0
                    TQualifier& qualifier = at->second.symbol->getQualifier();
1817
0
                    if (!qualifier.isUniform())
1818
0
                        continue;
1819
0
                    TType& t = at->second.symbol->getWritableType();
1820
0
                    int size, stride;
1821
0
                    TIntermediate::getBaseAlignment(t, size, stride, autoPushConstantBlockPacking,
1822
0
                                                    qualifier.layoutMatrix == ElmRowMajor);
1823
0
                    if (size <= int(autoPushConstantMaxSize)) {
1824
0
                        qualifier.setBlockStorage(EbsPushConstant);
1825
0
                        qualifier.layoutPacking = autoPushConstantBlockPacking;
1826
                        // Push constants don't have set/binding etc. decorations, remove those.
1827
0
                        qualifier.layoutSet = TQualifier::layoutSetEnd;
1828
0
                        at->second.clearNewAssignments();
1829
1830
0
                        upgraded = true;
1831
0
                    }
1832
0
                }
1833
0
            }
1834
            // If it's been upgraded to push_constant, then set the flag so when its traversed
1835
            // in the next for loop, all references to this symbol will get their flag changed.
1836
            // so it doesn't get a set/binding assigned to it.
1837
0
            if (upgraded) {
1838
0
                std::for_each(uniformVector.begin(), uniformVector.end(),
1839
0
                                       [this](TVarLivePair& p) {
1840
0
                if (p.first == autoPushConstantBlockName) {
1841
0
                        p.second.upgradedToPushConstantPacking = autoPushConstantBlockPacking;
1842
0
                        p.second.newSet = TQualifier::layoutSetEnd;
1843
0
                    }
1844
0
                });
1845
0
            }
1846
0
        }
1847
0
        for (size_t stage = 0; stage < EShLangCount; stage++) {
1848
0
            if (intermediates[stage] != nullptr) {
1849
                // traverse each stage, set new location to each input/output and unifom symbol, set new binding to
1850
                // ubo, ssbo and opaque symbols. Assign push_constant upgrades as well.
1851
0
                TVarLiveMap** pUniformVarMap = uniformResolve.uniformVarMap;
1852
0
                std::for_each(uniformVector.begin(), uniformVector.end(), [pUniformVarMap, stage](TVarLivePair p) {
1853
0
                    auto at = pUniformVarMap[stage]->find(p.second.symbol->getAccessName());
1854
0
                    if (at != pUniformVarMap[stage]->end() && at->second.id == p.second.id){
1855
0
                        if (p.second.upgradedToPushConstantPacking != ElpNone) {
1856
0
                            at->second.upgradedToPushConstantPacking = p.second.upgradedToPushConstantPacking;
1857
0
                        } else {
1858
0
                            int resolvedBinding = at->second.newBinding;
1859
0
                            at->second = p.second;
1860
0
                            if (resolvedBinding > 0)
1861
0
                                at->second.newBinding = resolvedBinding;
1862
0
                        }
1863
0
                    }
1864
0
                });
1865
0
                TVarSetTraverser iter_iomap(*intermediates[stage], *inVarMaps[stage], *outVarMaps[stage],
1866
0
                                            *uniformResolve.uniformVarMap[stage]);
1867
0
                intermediates[stage]->getTreeRoot()->traverse(&iter_iomap);
1868
0
            }
1869
0
        }
1870
0
        return !hadError;
1871
0
    } else {
1872
0
        return false;
1873
0
    }
1874
0
}
1875
1876
} // end namespace glslang