Coverage Report

Created: 2025-07-11 07:07

/src/shaderc/third_party/glslang/glslang/MachineIndependent/iomapper.cpp
Line
Count
Source (jump to first uncovered line)
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.93k
    void clearNewAssignments() {
77
1.93k
        upgradedToPushConstantPacking = ElpNone;
78
1.93k
        newBinding = -1;
79
1.93k
        newSet = -1;
80
1.93k
        newLocation = -1;
81
1.93k
        newComponent = -1;
82
1.93k
        newIndex = -1;
83
1.93k
    }
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
2.26k
        inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r) {
96
2.26k
            const TQualifier& lq = l.symbol->getQualifier();
97
2.26k
            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
2.26k
            int lPoints = (lq.hasBinding() ? 2 : 0) + (lq.hasSet() ? 1 : 0);
104
2.26k
            int rPoints = (rq.hasBinding() ? 2 : 0) + (rq.hasSet() ? 1 : 0);
105
106
2.26k
            if (lPoints == rPoints)
107
2.01k
                return l.id < r.id;
108
250
            return lPoints > rPoints;
109
2.26k
        }
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.93k
    TVarLivePair(const std::pair<const TString, TVarEntryInfo>& _Right) : pair(_Right.first, _Right.second) {}
156
2.05k
    TVarLivePair& operator=(const TVarLivePair& _Right) {
157
2.05k
        const_cast<TString&>(first) = _Right.first;
158
2.05k
        second = _Right.second;
159
2.05k
        return (*this);
160
2.05k
    }
161
5.96k
    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
1.30k
      : TLiveTraverser(i, traverseDeadCode, true, true, false)
170
1.30k
      , inputList(inList)
171
1.30k
      , outputList(outList)
172
1.30k
      , uniformList(uniformList)
173
1.30k
    {
174
1.30k
    }
175
176
    virtual void visitSymbol(TIntermSymbol* base)
177
25.6k
    {
178
25.6k
        TVarLiveMap* target = nullptr;
179
25.6k
        if (base->getQualifier().storage == EvqVaryingIn)
180
3.37k
            target = &inputList;
181
22.2k
        else if (base->getQualifier().storage == EvqVaryingOut)
182
2.04k
            target = &outputList;
183
20.2k
        else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().isPushConstant() && !base->getQualifier().isShaderRecord())
184
5.76k
            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
14.4k
        else if (base->getQualifier().storage == EvqGlobal)
188
1.45k
            addGlobalReference(base->getAccessName());
189
190
25.6k
        if (target) {
191
11.1k
            TVarEntryInfo ent = {base->getId(), base, ! traverseAll, {}, {}, {}, {}, {}, {}, {}};
192
11.1k
            ent.stage = intermediate.getStage();
193
11.1k
            TVarLiveMap::iterator at = target->find(
194
11.1k
                ent.symbol->getAccessName()); // std::lower_bound(target->begin(), target->end(), ent, TVarEntryInfo::TOrderById());
195
11.1k
            if (at != target->end() && at->second.id == ent.id)
196
9.25k
                at->second.live = at->second.live || ! traverseAll; // update live state
197
1.93k
            else
198
1.93k
                (*target)[ent.symbol->getAccessName()] = ent;
199
11.1k
        }
200
25.6k
    }
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
654
      : TLiveTraverser(i, true, true, true, false)
213
654
      , inputList(inList)
214
654
      , outputList(outList)
215
654
      , uniformList(uniformList)
216
654
    {
217
654
    }
218
219
14.0k
    virtual void visitSymbol(TIntermSymbol* base) {
220
14.0k
        const TVarLiveMap* source;
221
14.0k
        if (base->getQualifier().storage == EvqVaryingIn)
222
2.10k
            source = &inputList;
223
11.9k
        else if (base->getQualifier().storage == EvqVaryingOut)
224
1.18k
            source = &outputList;
225
10.7k
        else if (base->getQualifier().isUniformOrBuffer())
226
3.31k
            source = &uniformList;
227
7.45k
        else
228
7.45k
            return;
229
230
6.60k
        TVarEntryInfo ent = { base->getId(), {}, {}, {}, {}, {}, {}, {}, {}, {} };
231
        // Fix a defect, when block has no instance name, we need to find its block name
232
6.60k
        TVarLiveMap::const_iterator at = source->find(base->getAccessName());
233
6.60k
        if (at == source->end())
234
36
            return;
235
236
6.57k
        if (at->second.id != ent.id)
237
0
            return;
238
239
6.57k
        if (at->second.newBinding != -1)
240
3.23k
            base->getWritableType().getQualifier().layoutBinding = at->second.newBinding;
241
6.57k
        if (at->second.newSet != -1)
242
3.28k
            base->getWritableType().getQualifier().layoutSet = at->second.newSet;
243
6.57k
        if (at->second.newLocation != -1)
244
1.99k
            base->getWritableType().getQualifier().layoutLocation = at->second.newLocation;
245
6.57k
        if (at->second.newComponent != -1)
246
0
            base->getWritableType().getQualifier().layoutComponent = at->second.newComponent;
247
6.57k
        if (at->second.newIndex != -1)
248
0
            base->getWritableType().getQualifier().layoutIndex = at->second.newIndex;
249
6.57k
        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
6.57k
    }
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
654
      : stage(s)
268
654
      , resolver(r)
269
654
    {
270
654
    }
271
272
    inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
273
796
    {
274
796
        resolver.notifyBinding(stage, entKey.second);
275
796
    }
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
654
      : stage(s)
287
654
      , resolver(r)
288
654
    {
289
654
    }
290
291
    inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
292
1.13k
    {
293
1.13k
        resolver.notifyInOut(entKey.second.stage, entKey.second);
294
1.13k
    }
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
654
      : stage(s)
303
654
      , resolver(r)
304
654
      , infoSink(i)
305
654
      , error(e)
306
654
    {
307
654
        memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*)));
308
654
    }
309
310
796
    inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
311
796
        TVarEntryInfo& ent = entKey.second;
312
796
        ent.clearNewAssignments();
313
796
        const bool isValid = resolver.validateBinding(stage, ent);
314
796
        if (isValid) {
315
796
            resolver.resolveSet(ent.stage, ent);
316
796
            resolver.resolveBinding(ent.stage, ent);
317
796
            resolver.resolveUniformLocation(ent.stage, ent);
318
319
796
            if (ent.newBinding != -1) {
320
752
                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
752
                if (ent.symbol->getQualifier().hasBinding()) {
328
7.95k
                    for (uint32_t idx = EShLangVertex; idx < EShLangCount; ++idx) {
329
7.42k
                        if (idx == ent.stage || uniformVarMap[idx] == nullptr)
330
7.42k
                            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
530
                }
337
752
            }
338
796
            if (ent.newSet != -1) {
339
796
                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
796
                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
796
            }
356
796
        } 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
796
    }
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
654
      : stage(s)
377
654
      , resolver(r)
378
654
      , infoSink(i)
379
654
      , error(e)
380
654
    {
381
654
    }
382
383
    inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
384
1.13k
    {
385
1.13k
        TVarEntryInfo& ent = entKey.second;
386
1.13k
        ent.clearNewAssignments();
387
1.13k
        const bool isValid = resolver.validateInOut(ent.stage, ent);
388
1.13k
        if (isValid) {
389
1.13k
            resolver.resolveInOutLocation(stage, ent);
390
1.13k
            resolver.resolveInOutComponent(stage, ent);
391
1.13k
            resolver.resolveInOutIndex(stage, ent);
392
1.13k
        } 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
1.13k
    }
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
1.30k
    : referenceIntermediate(intermediate)
868
1.30k
    , nextUniformLocation(intermediate.getUniformLocationBase())
869
1.30k
    , nextInputLocation(0)
870
1.30k
    , nextOutputLocation(0)
871
1.30k
{
872
1.30k
    memset(stageMask, false, sizeof(bool) * (EShLangCount + 1));
873
1.30k
    memset(stageIntermediates, 0, sizeof(TIntermediate*) * (EShLangCount));
874
1.30k
    stageIntermediates[intermediate.getStage()] = &intermediate;
875
1.30k
}
876
877
752
int TDefaultIoResolverBase::getBaseBinding(EShLanguage stage, TResourceType res, unsigned int set) const {
878
752
    return stageIntermediates[stage] ? selectBaseBinding(stageIntermediates[stage]->getShiftBinding(res), stageIntermediates[stage]->getShiftBindingForSet(res, set))
879
752
                                     : selectBaseBinding(referenceIntermediate.getShiftBinding(res), referenceIntermediate.getShiftBindingForSet(res, set));
880
752
}
881
882
652
const std::vector<std::string>& TDefaultIoResolverBase::getResourceSetBinding(EShLanguage stage) const {
883
652
    return stageIntermediates[stage] ? stageIntermediates[stage]->getResourceSetBinding()
884
652
                                     : referenceIntermediate.getResourceSetBinding();
885
652
}
886
887
222
bool TDefaultIoResolverBase::doAutoBindingMapping() const { return referenceIntermediate.getAutoMapBindings(); }
888
889
1.93k
bool TDefaultIoResolverBase::doAutoLocationMapping() const { return referenceIntermediate.getAutoMapLocations(); }
890
891
974
TDefaultIoResolverBase::TSlotSet::iterator TDefaultIoResolverBase::findSlot(int set, int slot) {
892
974
    return std::lower_bound(slots[set].begin(), slots[set].end(), slot);
893
974
}
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
752
int TDefaultIoResolverBase::reserveSlot(int set, int slot, int size) {
901
752
    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.50k
    for (int i = 0; i < size; i++) {
905
752
        if (at == slots[set].end() || *at != slot + i)
906
722
            at = slots[set].insert(at, slot + i);
907
752
        ++at;
908
752
    }
909
752
    return slot;
910
752
}
911
912
222
int TDefaultIoResolverBase::getFreeSlot(int set, int base, int size) {
913
222
    TSlotSet::iterator at = findSlot(set, base);
914
222
    if (at == slots[set].end())
915
110
        return reserveSlot(set, base, size);
916
    // look for a big enough gap
917
560
    for (; at != slots[set].end(); ++at) {
918
448
        if (*at - base >= size)
919
0
            break;
920
448
        base = *at + 1;
921
448
    }
922
112
    return reserveSlot(set, base, size);
923
222
}
924
925
796
int TDefaultIoResolverBase::resolveSet(EShLanguage stage, TVarEntryInfo& ent) {
926
796
    const TType& type = ent.symbol->getType();
927
796
    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
652
    if (getResourceSetBinding(stage).size() == 1) {
932
0
        return ent.newSet = atoi(getResourceSetBinding(stage)[0].c_str());
933
0
    }
934
652
    return ent.newSet = 0;
935
652
}
936
937
796
int TDefaultIoResolverBase::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
938
796
    const TType& type = ent.symbol->getType();
939
796
    const char* name =  ent.symbol->getAccessName().c_str();
940
    // kick out of not doing this
941
796
    if (! doAutoLocationMapping()) {
942
4
        return ent.newLocation = -1;
943
4
    }
944
    // no locations added if already present, a built-in variable, a block, or an opaque
945
792
    if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock ||
946
792
        type.isAtomic() || type.isSpirvType() || (type.containsOpaque() && referenceIntermediate.getSpv().openGl == 0)) {
947
792
        return ent.newLocation = -1;
948
792
    }
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
1.13k
int TDefaultIoResolverBase::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
968
1.13k
    const TType& type = ent.symbol->getType();
969
    // kick out of not doing this
970
1.13k
    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
1.13k
    if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getQualifier().hasSpirvDecorate()) {
976
552
        return ent.newLocation = -1;
977
552
    }
978
979
    // no locations on blocks of built-in variables
980
584
    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
584
    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
584
    int location = nextLocation;
993
584
    int typeLocationSize;
994
    // Don’t take into account the outer-most array if the stage’s
995
    // interface is automatically an array.
996
584
    typeLocationSize = computeTypeLocationSize(type, stage);
997
584
    nextLocation += typeLocationSize;
998
584
    return ent.newLocation = location;
999
584
}
1000
1001
1.13k
int TDefaultIoResolverBase::resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) {
1002
1.13k
    return ent.newComponent = -1;
1003
1.13k
}
1004
1005
1.13k
int TDefaultIoResolverBase::resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) { return ent.newIndex = -1; }
1006
1007
584
uint32_t TDefaultIoResolverBase::computeTypeLocationSize(const TType& type, EShLanguage stage) {
1008
584
    int typeLocationSize;
1009
    // Don’t take into account the outer-most array if the stage’s
1010
    // interface is automatically an array.
1011
584
    if (type.getQualifier().isArrayedIo(stage)) {
1012
0
        TType elementType(type, 0);
1013
0
        typeLocationSize = TIntermediate::computeTypeLocationSize(elementType, stage);
1014
584
    } else {
1015
584
        typeLocationSize = TIntermediate::computeTypeLocationSize(type, stage);
1016
584
    }
1017
584
    return typeLocationSize;
1018
584
}
1019
1020
//TDefaultGlslIoResolver
1021
0
TResourceType TDefaultGlslIoResolver::getResourceType(const glslang::TType& type) {
1022
0
    if (isImageType(type)) {
1023
0
        return EResImage;
1024
0
    }
1025
0
    if (isTextureType(type)) {
1026
0
        return EResTexture;
1027
0
    }
1028
0
    if (isSsboType(type)) {
1029
0
        return EResSsbo;
1030
0
    }
1031
0
    if (isSamplerType(type)) {
1032
0
        return EResSampler;
1033
0
    }
1034
0
    if (isUboType(type)) {
1035
0
        return EResUbo;
1036
0
    }
1037
0
    return EResCount;
1038
0
}
1039
1040
TDefaultGlslIoResolver::TDefaultGlslIoResolver(const TIntermediate& intermediate)
1041
0
    : TDefaultIoResolverBase(intermediate)
1042
0
    , preStage(EShLangCount)
1043
0
    , currentStage(EShLangCount)
1044
0
{ }
1045
1046
0
int TDefaultGlslIoResolver::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
1047
0
    const TType& type = ent.symbol->getType();
1048
0
    const TString& name = ent.symbol->getAccessName();
1049
0
    if (currentStage != stage) {
1050
0
        preStage = currentStage;
1051
0
        currentStage = stage;
1052
0
    }
1053
    // kick out if not doing this
1054
0
    if (! doAutoLocationMapping()) {
1055
0
        return ent.newLocation = -1;
1056
0
    }
1057
    // expand the location to each element if the symbol is a struct or array
1058
0
    if (type.getQualifier().hasLocation()) {
1059
0
        return ent.newLocation = type.getQualifier().layoutLocation;
1060
0
    }
1061
    // no locations added if already present, a built-in variable, or a variable with SPIR-V decorate
1062
0
    if (type.isBuiltIn() || type.getQualifier().hasSpirvDecorate()) {
1063
0
        return ent.newLocation = -1;
1064
0
    }
1065
    // no locations on blocks of built-in variables
1066
0
    if (type.isStruct()) {
1067
0
        if (type.getStruct()->size() < 1) {
1068
0
            return ent.newLocation = -1;
1069
0
        }
1070
0
        if ((*type.getStruct())[0].type->isBuiltIn()) {
1071
0
            return ent.newLocation = -1;
1072
0
        }
1073
0
    }
1074
0
    int typeLocationSize = computeTypeLocationSize(type, stage);
1075
0
    int location = type.getQualifier().layoutLocation;
1076
0
    bool hasLocation = false;
1077
0
    EShLanguage keyStage(EShLangCount);
1078
0
    TStorageQualifier storage;
1079
0
    storage = EvqInOut;
1080
0
    if (type.getQualifier().isPipeInput()) {
1081
        // If this symbol is a input, search pre stage's out
1082
0
        keyStage = preStage;
1083
0
    }
1084
0
    if (type.getQualifier().isPipeOutput()) {
1085
        // If this symbol is a output, search next stage's in
1086
0
        keyStage = currentStage;
1087
0
    }
1088
    // The in/out in current stage is not declared with location, but it is possible declared
1089
    // with explicit location in other stages, find the storageSlotMap firstly to check whether
1090
    // the in/out has location
1091
0
    int resourceKey = buildStorageKey(keyStage, storage);
1092
0
    if (! storageSlotMap[resourceKey].empty()) {
1093
0
        TVarSlotMap::iterator iter = storageSlotMap[resourceKey].find(name);
1094
0
        if (iter != storageSlotMap[resourceKey].end()) {
1095
            // If interface resource be found, set it has location and this symbol's new location
1096
            // equal the symbol's explicit location declaration in pre or next stage.
1097
            //
1098
            // vs:    out vec4 a;
1099
            // fs:    layout(..., location = 3,...) in vec4 a;
1100
0
            hasLocation = true;
1101
0
            location = iter->second;
1102
            // if we want deal like that:
1103
            // vs:    layout(location=4) out vec4 a;
1104
            //        out vec4 b;
1105
            //
1106
            // fs:    in vec4 a;
1107
            //        layout(location = 4) in vec4 b;
1108
            // we need retraverse the map.
1109
0
        }
1110
0
        if (! hasLocation) {
1111
            // If interface resource note found, It's mean the location in two stage are both implicit declarat.
1112
            // So we should find a new slot for this interface.
1113
            //
1114
            // vs: out vec4 a;
1115
            // fs: in vec4 a;
1116
0
            location = getFreeSlot(resourceKey, 0, typeLocationSize);
1117
0
            storageSlotMap[resourceKey][name] = location;
1118
0
        }
1119
0
    } else {
1120
        // the first interface declarated in a program.
1121
0
        TVarSlotMap varSlotMap;
1122
0
        location = getFreeSlot(resourceKey, 0, typeLocationSize);
1123
0
        varSlotMap[name] = location;
1124
0
        storageSlotMap[resourceKey] = varSlotMap;
1125
0
    }
1126
    //Update location
1127
0
    return ent.newLocation = location;
1128
0
}
1129
1130
0
int TDefaultGlslIoResolver::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
1131
0
    const TType& type = ent.symbol->getType();
1132
0
    const TString& name = ent.symbol->getAccessName();
1133
    // kick out of not doing this
1134
0
    if (! doAutoLocationMapping()) {
1135
0
        return ent.newLocation = -1;
1136
0
    }
1137
    // expand the location to each element if the symbol is a struct or array
1138
0
    if (type.getQualifier().hasLocation() && (type.isStruct() || type.isArray())) {
1139
0
        return ent.newLocation = type.getQualifier().layoutLocation;
1140
0
    } else {
1141
        // no locations added if already present, a built-in variable, a block, or an opaque
1142
0
        if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock ||
1143
0
            type.isAtomic() || type.isSpirvType() ||
1144
0
            (type.containsOpaque() && referenceIntermediate.getSpv().openGl == 0)) {
1145
0
            return ent.newLocation = -1;
1146
0
        }
1147
        // no locations on blocks of built-in variables
1148
0
        if (type.isStruct()) {
1149
0
            if (type.getStruct()->size() < 1) {
1150
0
                return ent.newLocation = -1;
1151
0
            }
1152
0
            if ((*type.getStruct())[0].type->isBuiltIn()) {
1153
0
                return ent.newLocation = -1;
1154
0
            }
1155
0
        }
1156
0
    }
1157
0
    int location = referenceIntermediate.getUniformLocationOverride(name.c_str());
1158
0
    if (location != -1) {
1159
0
        return ent.newLocation = location;
1160
0
    }
1161
1162
0
    int size = TIntermediate::computeTypeUniformLocationSize(type);
1163
1164
    // The uniform in current stage is not declared with location, but it is possible declared
1165
    // with explicit location in other stages, find the storageSlotMap firstly to check whether
1166
    // the uniform has location
1167
0
    bool hasLocation = false;
1168
0
    int resourceKey = buildStorageKey(EShLangCount, EvqUniform);
1169
0
    TVarSlotMap& slotMap = storageSlotMap[resourceKey];
1170
    // Check dose shader program has uniform resource
1171
0
    if (! slotMap.empty()) {
1172
        // If uniform resource not empty, try find a same name uniform
1173
0
        TVarSlotMap::iterator iter = slotMap.find(name);
1174
0
        if (iter != slotMap.end()) {
1175
            // If uniform resource be found, set it has location and this symbol's new location
1176
            // equal the uniform's explicit location declaration in other stage.
1177
            //
1178
            // vs:    uniform vec4 a;
1179
            // fs:    layout(..., location = 3,...) uniform vec4 a;
1180
0
            hasLocation = true;
1181
0
            location = iter->second;
1182
0
        }
1183
0
        if (! hasLocation) {
1184
            // No explicit location declaration in other stage.
1185
            // So we should find a new slot for this uniform.
1186
            //
1187
            // vs:    uniform vec4 a;
1188
            // fs:    uniform vec4 a;
1189
0
            location = getFreeSlot(resourceKey, 0, computeTypeLocationSize(type, currentStage));
1190
0
            storageSlotMap[resourceKey][name] = location;
1191
0
        }
1192
0
    } else {
1193
        // the first uniform declaration in a program.
1194
0
        TVarSlotMap varSlotMap;
1195
0
        location = getFreeSlot(resourceKey, 0, size);
1196
0
        varSlotMap[name] = location;
1197
0
        storageSlotMap[resourceKey] = varSlotMap;
1198
0
    }
1199
0
    return ent.newLocation = location;
1200
0
}
1201
1202
0
int TDefaultGlslIoResolver::resolveBinding(EShLanguage stage, TVarEntryInfo& ent) {
1203
0
    const TType& type = ent.symbol->getType();
1204
0
    const TString& name = ent.symbol->getAccessName();
1205
    // On OpenGL arrays of opaque types take a separate binding for each element
1206
0
    int numBindings = referenceIntermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
1207
0
    TResourceType resource = getResourceType(type);
1208
    // don't need to handle uniform symbol, it will be handled in resolveUniformLocation
1209
0
    if (resource == EResUbo && type.getBasicType() != EbtBlock) {
1210
0
        return ent.newBinding = -1;
1211
0
    }
1212
    // There is no 'set' qualifier in OpenGL shading language, each resource has its own
1213
    // binding name space, so remap the 'set' to resource type which make each resource
1214
    // binding is valid from 0 to MAX_XXRESOURCE_BINDINGS
1215
0
    int set = referenceIntermediate.getSpv().openGl != 0 ? resource : ent.newSet;
1216
0
    int resourceKey = set;
1217
0
    if (resource < EResCount) {
1218
0
        if (type.getQualifier().hasBinding()) {
1219
0
            int newBinding = reserveSlot(resourceKey, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding, numBindings);
1220
0
            return ent.newBinding = newBinding;
1221
1222
0
        } else {
1223
            // The resource in current stage is not declared with binding, but it is possible declared
1224
            // with explicit binding in other stages, find the resourceSlotMap firstly to check whether
1225
            // the resource has binding, don't need to allocate if it already has a binding
1226
0
            bool hasBinding = false;
1227
0
            ent.newBinding = -1; // leave as -1 if it isn't set below
1228
1229
0
            if (! resourceSlotMap[resourceKey].empty()) {
1230
0
                TVarSlotMap::iterator iter = resourceSlotMap[resourceKey].find(name);
1231
0
                if (iter != resourceSlotMap[resourceKey].end()) {
1232
0
                    hasBinding = true;
1233
0
                    ent.newBinding = iter->second;
1234
0
                }
1235
0
            }
1236
0
            if (!hasBinding && (ent.live && doAutoBindingMapping())) {
1237
                // find free slot, the caller did make sure it passes all vars with binding
1238
                // first and now all are passed that do not have a binding and needs one
1239
0
                int binding = getFreeSlot(resourceKey, getBaseBinding(stage, resource, set), numBindings);
1240
0
                resourceSlotMap[resourceKey][name] = binding;
1241
0
                ent.newBinding = binding;
1242
0
            }
1243
0
            return ent.newBinding;
1244
0
        }
1245
0
    }
1246
0
    return ent.newBinding = -1;
1247
0
}
1248
1249
0
void TDefaultGlslIoResolver::beginResolve(EShLanguage stage) {
1250
    // reset stage state
1251
0
    if (stage == EShLangCount)
1252
0
        preStage = currentStage = stage;
1253
    // update stage state
1254
0
    else if (currentStage != stage) {
1255
0
        preStage = currentStage;
1256
0
        currentStage = stage;
1257
0
    }
1258
0
}
1259
1260
0
void TDefaultGlslIoResolver::endResolve(EShLanguage /*stage*/) {
1261
    // TODO nothing
1262
0
}
1263
1264
0
void TDefaultGlslIoResolver::beginCollect(EShLanguage stage) {
1265
    // reset stage state
1266
0
    if (stage == EShLangCount)
1267
0
        preStage = currentStage = stage;
1268
    // update stage state
1269
0
    else if (currentStage != stage) {
1270
0
        preStage = currentStage;
1271
0
        currentStage = stage;
1272
0
    }
1273
0
}
1274
1275
0
void TDefaultGlslIoResolver::endCollect(EShLanguage /*stage*/) {
1276
    // TODO nothing
1277
0
}
1278
1279
0
void TDefaultGlslIoResolver::reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
1280
0
    const TType& type = ent.symbol->getType();
1281
0
    const TString& name = ent.symbol->getAccessName();
1282
0
    TStorageQualifier storage = type.getQualifier().storage;
1283
0
    EShLanguage stage(EShLangCount);
1284
0
    switch (storage) {
1285
0
    case EvqUniform:
1286
0
        if (type.getBasicType() != EbtBlock && type.getQualifier().hasLocation()) {
1287
            //
1288
            // Reserve the slots for the uniforms who has explicit location
1289
0
            int storageKey = buildStorageKey(EShLangCount, EvqUniform);
1290
0
            int location = type.getQualifier().layoutLocation;
1291
0
            TVarSlotMap& varSlotMap = storageSlotMap[storageKey];
1292
0
            TVarSlotMap::iterator iter = varSlotMap.find(name);
1293
0
            if (iter == varSlotMap.end()) {
1294
0
                int numLocations = TIntermediate::computeTypeUniformLocationSize(type);
1295
0
                reserveSlot(storageKey, location, numLocations);
1296
0
                varSlotMap[name] = location;
1297
0
            } else {
1298
                // Allocate location by name for OpenGL driver, so the uniform in different
1299
                // stages should be declared with the same location
1300
0
                if (iter->second != location) {
1301
0
                    TString errorMsg = "Invalid location: " + name;
1302
0
                    infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
1303
0
                    hasError = true;
1304
0
                }
1305
0
            }
1306
0
        }
1307
0
        break;
1308
0
    case EvqVaryingIn:
1309
0
    case EvqVaryingOut:
1310
        //
1311
        // Reserve the slots for the inout who has explicit location
1312
0
        if (type.getQualifier().hasLocation()) {
1313
0
            stage = storage == EvqVaryingIn ? preStage : stage;
1314
0
            stage = storage == EvqVaryingOut ? currentStage : stage;
1315
0
            int storageKey = buildStorageKey(stage, EvqInOut);
1316
0
            int location = type.getQualifier().layoutLocation;
1317
0
            TVarSlotMap& varSlotMap = storageSlotMap[storageKey];
1318
0
            TVarSlotMap::iterator iter = varSlotMap.find(name);
1319
0
            if (iter == varSlotMap.end()) {
1320
0
                int numLocations = TIntermediate::computeTypeUniformLocationSize(type);
1321
0
                reserveSlot(storageKey, location, numLocations);
1322
0
                varSlotMap[name] = location;
1323
0
            } else {
1324
                // Allocate location by name for OpenGL driver, so the uniform in different
1325
                // stages should be declared with the same location
1326
0
                if (iter->second != location) {
1327
0
                    TString errorMsg = "Invalid location: " + name;
1328
0
                    infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
1329
0
                    hasError = true;
1330
0
                }
1331
0
            }
1332
0
        }
1333
0
        break;
1334
0
    default:
1335
0
        break;
1336
0
    }
1337
0
}
1338
1339
0
void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
1340
0
    const TType& type = ent.symbol->getType();
1341
0
    const TString& name = ent.symbol->getAccessName();
1342
0
    TResourceType resource = getResourceType(type);
1343
0
    int set = referenceIntermediate.getSpv().openGl != 0 ? resource : resolveSet(ent.stage, ent);
1344
0
    int resourceKey = set;
1345
1346
0
    if (type.getQualifier().hasBinding()) {
1347
0
        TVarSlotMap& varSlotMap = resourceSlotMap[resourceKey];
1348
0
        TVarSlotMap::iterator iter = varSlotMap.find(name);
1349
0
        int binding = type.getQualifier().layoutBinding + getBaseBinding(ent.stage, resource, set);
1350
1351
0
        if (iter == varSlotMap.end()) {
1352
            // Reserve the slots for the ubo, ssbo and opaques who has explicit binding
1353
0
            int numBindings = referenceIntermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
1354
0
            varSlotMap[name] = binding;
1355
0
            reserveSlot(resourceKey, binding, numBindings);
1356
0
        } else {
1357
            // Allocate binding by name for OpenGL driver, so the resource in different
1358
            // stages should be declared with the same binding
1359
0
            if (iter->second != binding) {
1360
0
                TString errorMsg = "Invalid binding: " + name;
1361
0
                infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
1362
0
                hasError = true;
1363
0
            }
1364
0
        }
1365
0
    }
1366
0
}
1367
1368
//TDefaultGlslIoResolver end
1369
1370
/*
1371
 * Basic implementation of glslang::TIoMapResolver that replaces the
1372
 * previous offset behavior.
1373
 * It does the same, uses the offsets for the corresponding uniform
1374
 * types. Also respects the EOptionAutoMapBindings flag and binds
1375
 * them if needed.
1376
 */
1377
/*
1378
 * Default resolver
1379
 */
1380
struct TDefaultIoResolver : public TDefaultIoResolverBase {
1381
654
    TDefaultIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
1382
1383
796
    bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
1384
1385
796
    TResourceType getResourceType(const glslang::TType& type) override {
1386
796
        if (isImageType(type)) {
1387
132
            return EResImage;
1388
132
        }
1389
664
        if (isTextureType(type)) {
1390
258
            return EResTexture;
1391
258
        }
1392
406
        if (isSsboType(type)) {
1393
134
            return EResSsbo;
1394
134
        }
1395
272
        if (isSamplerType(type)) {
1396
84
            return EResSampler;
1397
84
        }
1398
188
        if (isUboType(type)) {
1399
188
            return EResUbo;
1400
188
        }
1401
0
        return EResCount;
1402
188
    }
1403
1404
796
    int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) override {
1405
796
        const TType& type = ent.symbol->getType();
1406
796
        const int set = getLayoutSet(type);
1407
        // On OpenGL arrays of opaque types take a seperate binding for each element
1408
796
        int numBindings = referenceIntermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
1409
796
        TResourceType resource = getResourceType(type);
1410
796
        if (resource < EResCount) {
1411
796
            if (type.getQualifier().hasBinding()) {
1412
530
                return ent.newBinding = reserveSlot(
1413
530
                           set, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding, numBindings);
1414
530
            } else if (ent.live && doAutoBindingMapping()) {
1415
                // find free slot, the caller did make sure it passes all vars with binding
1416
                // first and now all are passed that do not have a binding and needs one
1417
222
                return ent.newBinding = getFreeSlot(set, getBaseBinding(stage, resource, set), numBindings);
1418
222
            }
1419
796
        }
1420
44
        return ent.newBinding = -1;
1421
796
    }
1422
};
1423
1424
#ifdef ENABLE_HLSL
1425
/********************************************************************************
1426
The following IO resolver maps types in HLSL register space, as follows:
1427
1428
t - for shader resource views (SRV)
1429
   TEXTURE1D
1430
   TEXTURE1DARRAY
1431
   TEXTURE2D
1432
   TEXTURE2DARRAY
1433
   TEXTURE3D
1434
   TEXTURECUBE
1435
   TEXTURECUBEARRAY
1436
   TEXTURE2DMS
1437
   TEXTURE2DMSARRAY
1438
   STRUCTUREDBUFFER
1439
   BYTEADDRESSBUFFER
1440
   BUFFER
1441
   TBUFFER
1442
1443
s - for samplers
1444
   SAMPLER
1445
   SAMPLER1D
1446
   SAMPLER2D
1447
   SAMPLER3D
1448
   SAMPLERCUBE
1449
   SAMPLERSTATE
1450
   SAMPLERCOMPARISONSTATE
1451
1452
u - for unordered access views (UAV)
1453
   RWBYTEADDRESSBUFFER
1454
   RWSTRUCTUREDBUFFER
1455
   APPENDSTRUCTUREDBUFFER
1456
   CONSUMESTRUCTUREDBUFFER
1457
   RWBUFFER
1458
   RWTEXTURE1D
1459
   RWTEXTURE1DARRAY
1460
   RWTEXTURE2D
1461
   RWTEXTURE2DARRAY
1462
   RWTEXTURE3D
1463
1464
b - for constant buffer views (CBV)
1465
   CBUFFER
1466
   CONSTANTBUFFER
1467
 ********************************************************************************/
1468
struct TDefaultHlslIoResolver : public TDefaultIoResolverBase {
1469
654
    TDefaultHlslIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
1470
1471
0
    bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
1472
1473
0
    TResourceType getResourceType(const glslang::TType& type) override {
1474
0
        if (isUavType(type)) {
1475
0
            return EResUav;
1476
0
        }
1477
0
        if (isSrvType(type)) {
1478
0
            return EResTexture;
1479
0
        }
1480
0
        if (isSamplerType(type)) {
1481
0
            return EResSampler;
1482
0
        }
1483
0
        if (isUboType(type)) {
1484
0
            return EResUbo;
1485
0
        }
1486
0
        return EResCount;
1487
0
    }
1488
1489
0
    int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) override {
1490
0
        const TType& type = ent.symbol->getType();
1491
0
        const int set = getLayoutSet(type);
1492
0
        TResourceType resource = getResourceType(type);
1493
0
        if (resource < EResCount) {
1494
0
            if (type.getQualifier().hasBinding()) {
1495
0
                return ent.newBinding = reserveSlot(set, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding);
1496
0
            } else if (ent.live && doAutoBindingMapping()) {
1497
                // find free slot, the caller did make sure it passes all vars with binding
1498
                // first and now all are passed that do not have a binding and needs one
1499
0
                return ent.newBinding = getFreeSlot(set, getBaseBinding(stage, resource, set));
1500
0
            }
1501
0
        }
1502
0
        return ent.newBinding = -1;
1503
0
    }
1504
};
1505
#endif
1506
1507
// Map I/O variables to provided offsets, and make bindings for
1508
// unbound but live variables.
1509
//
1510
// Returns false if the input is too malformed to do this.
1511
3.71k
bool TIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) {
1512
3.71k
    bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() ||
1513
3.71k
                         intermediate.getAutoMapLocations();
1514
    // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce
1515
    // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true.
1516
20.9k
    for (int res = 0; (res < EResCount && !somethingToDo); ++res) {
1517
17.2k
        somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
1518
17.2k
                        intermediate.hasShiftBindingForSet(TResourceType(res));
1519
17.2k
    }
1520
3.71k
    if (! somethingToDo && resolver == nullptr)
1521
2.87k
        return true;
1522
838
    if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive())
1523
184
        return false;
1524
654
    TIntermNode* root = intermediate.getTreeRoot();
1525
654
    if (root == nullptr)
1526
0
        return false;
1527
    // if no resolver is provided, use the default resolver with the given shifts and auto map settings
1528
654
    TDefaultIoResolver defaultResolver(intermediate);
1529
654
#ifdef ENABLE_HLSL
1530
654
    TDefaultHlslIoResolver defaultHlslResolver(intermediate);
1531
654
    if (resolver == nullptr) {
1532
        // TODO: use a passed in IO mapper for this
1533
654
        if (intermediate.usingHlslIoMapping())
1534
0
            resolver = &defaultHlslResolver;
1535
654
        else
1536
654
            resolver = &defaultResolver;
1537
654
    }
1538
#else
1539
    resolver = &defaultResolver;
1540
#endif
1541
654
    resolver->addStage(stage, intermediate);
1542
1543
654
    TVarLiveMap inVarMap, outVarMap, uniformVarMap;
1544
654
    TVarLiveVector inVector, outVector, uniformVector;
1545
654
    TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap);
1546
654
    TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap);
1547
654
    root->traverse(&iter_binding_all);
1548
654
    iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
1549
1.56k
    while (! iter_binding_live.destinations.empty()) {
1550
910
        TIntermNode* destination = iter_binding_live.destinations.back();
1551
910
        iter_binding_live.destinations.pop_back();
1552
910
        destination->traverse(&iter_binding_live);
1553
910
    }
1554
1555
    // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info.
1556
816
    for (auto& var : inVarMap) { inVector.push_back(var); }
1557
1.04k
    std::sort(inVector.begin(), inVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1558
1.04k
        return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1559
1.04k
    });
1560
654
    for (auto& var : outVarMap) { outVector.push_back(var); }
1561
654
    std::sort(outVector.begin(), outVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1562
18
        return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1563
18
    });
1564
796
    for (auto& var : uniformVarMap) { uniformVector.push_back(var); }
1565
1.21k
    std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1566
1.21k
        return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1567
1.21k
    });
1568
654
    bool hadError = false;
1569
654
    TVarLiveMap* dummyUniformVarMap[EShLangCount] = {};
1570
654
    TNotifyInOutAdaptor inOutNotify(stage, *resolver);
1571
654
    TNotifyUniformAdaptor uniformNotify(stage, *resolver);
1572
654
    TResolverUniformAdaptor uniformResolve(stage, *resolver, dummyUniformVarMap, infoSink, hadError);
1573
654
    TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError);
1574
654
    resolver->beginNotifications(stage);
1575
654
    std::for_each(inVector.begin(), inVector.end(), inOutNotify);
1576
654
    std::for_each(outVector.begin(), outVector.end(), inOutNotify);
1577
654
    std::for_each(uniformVector.begin(), uniformVector.end(), uniformNotify);
1578
654
    resolver->endNotifications(stage);
1579
654
    resolver->beginResolve(stage);
1580
816
    for (auto& var : inVector) { inOutResolve(var); }
1581
816
    std::for_each(inVector.begin(), inVector.end(), [&inVarMap](TVarLivePair p) {
1582
816
        auto at = inVarMap.find(p.second.symbol->getAccessName());
1583
816
        if (at != inVarMap.end() && p.second.id == at->second.id)
1584
816
            at->second = p.second;
1585
816
    });
1586
654
    for (auto& var : outVector) { inOutResolve(var); }
1587
654
    std::for_each(outVector.begin(), outVector.end(), [&outVarMap](TVarLivePair p) {
1588
320
        auto at = outVarMap.find(p.second.symbol->getAccessName());
1589
320
        if (at != outVarMap.end() && p.second.id == at->second.id)
1590
320
            at->second = p.second;
1591
320
    });
1592
654
    std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve);
1593
796
    std::for_each(uniformVector.begin(), uniformVector.end(), [&uniformVarMap](TVarLivePair p) {
1594
796
        auto at = uniformVarMap.find(p.second.symbol->getAccessName());
1595
796
        if (at != uniformVarMap.end() && p.second.id == at->second.id)
1596
796
            at->second = p.second;
1597
796
    });
1598
654
    resolver->endResolve(stage);
1599
654
    if (!hadError) {
1600
654
        TVarSetTraverser iter_iomap(intermediate, inVarMap, outVarMap, uniformVarMap);
1601
654
        root->traverse(&iter_iomap);
1602
654
    }
1603
654
    return !hadError;
1604
654
}
1605
1606
0
TGlslIoMapper::TGlslIoMapper() {
1607
0
    memset(inVarMaps,     0, sizeof(TVarLiveMap*)   * EShLangCount);
1608
0
    memset(outVarMaps,    0, sizeof(TVarLiveMap*)   * EShLangCount);
1609
0
    memset(uniformVarMap, 0, sizeof(TVarLiveMap*)   * EShLangCount);
1610
0
    memset(intermediates, 0, sizeof(TIntermediate*) * EShLangCount);
1611
0
    profile = ENoProfile;
1612
0
    version = 0;
1613
0
    autoPushConstantMaxSize = 128;
1614
0
    autoPushConstantBlockPacking = ElpStd430;
1615
0
}
1616
1617
0
TGlslIoMapper::~TGlslIoMapper() {
1618
0
    for (size_t stage = 0; stage < EShLangCount; stage++) {
1619
0
        if (inVarMaps[stage] != nullptr) {
1620
0
            delete inVarMaps[stage];
1621
0
            inVarMaps[stage] = nullptr;
1622
0
        }
1623
0
        if (outVarMaps[stage] != nullptr) {
1624
0
            delete outVarMaps[stage];
1625
0
            outVarMaps[stage] = nullptr;
1626
0
        }
1627
0
        if (uniformVarMap[stage] != nullptr) {
1628
0
            delete uniformVarMap[stage];
1629
0
            uniformVarMap[stage] = nullptr;
1630
0
        }
1631
0
        if (intermediates[stage] != nullptr)
1632
0
            intermediates[stage] = nullptr;
1633
0
    }
1634
0
}
1635
1636
// Map I/O variables to provided offsets, and make bindings for
1637
// unbound but live variables.
1638
//
1639
// Returns false if the input is too malformed to do this.
1640
0
bool TGlslIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) {
1641
0
    bool somethingToDo = !intermediate.getResourceSetBinding().empty() ||
1642
0
        intermediate.getAutoMapBindings() ||
1643
0
        intermediate.getAutoMapLocations();
1644
1645
    // Profile and version are use for symbol validate.
1646
0
    profile = intermediate.getProfile();
1647
0
    version = intermediate.getVersion();
1648
1649
    // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce
1650
    // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true.
1651
0
    for (int res = 0; (res < EResCount && !somethingToDo); ++res) {
1652
0
        somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
1653
0
                        intermediate.hasShiftBindingForSet(TResourceType(res));
1654
0
    }
1655
0
    if (! somethingToDo && resolver == nullptr) {
1656
0
        return true;
1657
0
    }
1658
0
    if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive()) {
1659
0
        return false;
1660
0
    }
1661
0
    TIntermNode* root = intermediate.getTreeRoot();
1662
0
    if (root == nullptr) {
1663
0
        return false;
1664
0
    }
1665
    // if no resolver is provided, use the default resolver with the given shifts and auto map settings
1666
0
    TDefaultGlslIoResolver defaultResolver(intermediate);
1667
0
#ifdef ENABLE_HLSL
1668
0
    TDefaultHlslIoResolver defaultHlslResolver(intermediate);
1669
0
    if (resolver == nullptr) {
1670
        // TODO: use a passed in IO mapper for this
1671
0
        if (intermediate.usingHlslIoMapping())
1672
0
            resolver = &defaultHlslResolver;
1673
0
        else
1674
0
            resolver = &defaultResolver;
1675
0
    }
1676
#else
1677
    if (resolver == nullptr) {
1678
        resolver = &defaultResolver;
1679
    }
1680
#endif
1681
0
    resolver->addStage(stage, intermediate);
1682
0
    inVarMaps[stage] = new TVarLiveMap(); outVarMaps[stage] = new TVarLiveMap(); uniformVarMap[stage] = new TVarLiveMap();
1683
0
    TVarGatherTraverser iter_binding_all(intermediate, true, *inVarMaps[stage], *outVarMaps[stage],
1684
0
                                         *uniformVarMap[stage]);
1685
0
    TVarGatherTraverser iter_binding_live(intermediate, false, *inVarMaps[stage], *outVarMaps[stage],
1686
0
                                          *uniformVarMap[stage]);
1687
0
    root->traverse(&iter_binding_all);
1688
0
    iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
1689
0
    while (! iter_binding_live.destinations.empty()) {
1690
0
        TIntermNode* destination = iter_binding_live.destinations.back();
1691
0
        iter_binding_live.destinations.pop_back();
1692
0
        destination->traverse(&iter_binding_live);
1693
0
    }
1694
1695
0
    TNotifyInOutAdaptor inOutNotify(stage, *resolver);
1696
0
    TNotifyUniformAdaptor uniformNotify(stage, *resolver);
1697
    // Resolve current stage input symbol location with previous stage output here,
1698
    // uniform symbol, ubo, ssbo and opaque symbols are per-program resource,
1699
    // will resolve uniform symbol location and ubo/ssbo/opaque binding in doMap()
1700
0
    resolver->beginNotifications(stage);
1701
0
    std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), inOutNotify);
1702
0
    std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), inOutNotify);
1703
0
    std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), uniformNotify);
1704
0
    resolver->endNotifications(stage);
1705
0
    TSlotCollector slotCollector(*resolver, infoSink);
1706
0
    resolver->beginCollect(stage);
1707
0
    std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), slotCollector);
1708
0
    std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), slotCollector);
1709
0
    std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), slotCollector);
1710
0
    resolver->endCollect(stage);
1711
0
    intermediates[stage] = &intermediate;
1712
0
    return !hadError;
1713
0
}
1714
1715
0
bool TGlslIoMapper::doMap(TIoMapResolver* resolver, TInfoSink& infoSink) {
1716
0
    resolver->endResolve(EShLangCount);
1717
0
    if (!hadError) {
1718
        //Resolve uniform location, ubo/ssbo/opaque bindings across stages
1719
0
        TResolverUniformAdaptor uniformResolve(EShLangCount, *resolver, uniformVarMap, infoSink, hadError);
1720
0
        TResolverInOutAdaptor inOutResolve(EShLangCount, *resolver, infoSink, hadError);
1721
0
        TSymbolValidater symbolValidater(*resolver, infoSink, inVarMaps,
1722
0
                                         outVarMaps, uniformVarMap, hadError, profile, version);
1723
1724
0
        TVarLiveVector inVectors[EShLangCount];
1725
0
        TVarLiveVector outVectors[EShLangCount];
1726
0
        TVarLiveVector uniformVector;
1727
1728
0
        resolver->beginResolve(EShLangCount);
1729
0
        for (int stage = EShLangVertex; stage < EShLangCount; stage++) {
1730
0
            if (inVarMaps[stage] != nullptr) {
1731
0
                inOutResolve.setStage(EShLanguage(stage));
1732
1733
                // copy vars into a sorted list
1734
0
                std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(),
1735
0
                        [&inVectors, stage](TVarLivePair p) { inVectors[stage].push_back(p); });
1736
0
                std::sort(inVectors[stage].begin(), inVectors[stage].end(),
1737
0
                        [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1738
0
                            return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1739
0
                });
1740
1741
0
                std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(),
1742
0
                        [&outVectors, stage](TVarLivePair p) { outVectors[stage].push_back(p); });
1743
0
                std::sort(outVectors[stage].begin(), outVectors[stage].end(),
1744
0
                        [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1745
0
                            return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1746
0
                });
1747
1748
0
                for (auto& var : inVectors[stage]) { symbolValidater(var); }
1749
0
                for (auto& var : inVectors[stage]) { inOutResolve(var); }
1750
0
                for (auto& var : outVectors[stage]) { symbolValidater(var); }
1751
0
                for (auto& var : outVectors[stage]) { inOutResolve(var); }
1752
1753
                // copy results back into maps
1754
0
                std::for_each(inVectors[stage].begin(), inVectors[stage].end(),
1755
0
                    [this, stage](TVarLivePair p) {
1756
0
                        auto at = inVarMaps[stage]->find(p.first);
1757
0
                        if (at != inVarMaps[stage]->end())
1758
0
                            at->second = p.second;
1759
0
                });
1760
1761
0
                std::for_each(outVectors[stage].begin(), outVectors[stage].end(),
1762
0
                    [this, stage](TVarLivePair p) {
1763
0
                        auto at = outVarMaps[stage]->find(p.first);
1764
0
                        if (at != outVarMaps[stage]->end())
1765
0
                            at->second = p.second;
1766
0
                });
1767
1768
0
            }
1769
0
            if (uniformVarMap[stage] != nullptr) {
1770
0
                uniformResolve.setStage(EShLanguage(stage));
1771
0
                for (auto& var : *(uniformVarMap[stage])) { uniformVector.push_back(var); }
1772
0
            }
1773
0
        }
1774
0
        std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1775
0
            return TVarEntryInfo::TOrderByPriorityAndLive()(p1.second, p2.second);
1776
0
        });
1777
0
        for (auto& var : uniformVector) { symbolValidater(var); }
1778
0
        for (auto& var : uniformVector) { uniformResolve(var); }
1779
0
        std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1780
0
            return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1781
0
        });
1782
0
        resolver->endResolve(EShLangCount);
1783
0
        if (autoPushConstantBlockName.length()) {
1784
0
            bool upgraded = false;
1785
0
            for (size_t stage = 0; stage < EShLangCount; stage++) {
1786
0
                if (intermediates[stage] != nullptr) {
1787
0
                    TVarLiveMap** pUniformVarMap = uniformResolve.uniformVarMap;
1788
0
                    auto at = pUniformVarMap[stage]->find(autoPushConstantBlockName);
1789
0
                    if (at == pUniformVarMap[stage]->end())
1790
0
                        continue;
1791
0
                    TQualifier& qualifier = at->second.symbol->getQualifier();
1792
0
                    if (!qualifier.isUniform())
1793
0
                        continue;
1794
0
                    TType& t = at->second.symbol->getWritableType();
1795
0
                    int size, stride;
1796
0
                    TIntermediate::getBaseAlignment(t, size, stride, autoPushConstantBlockPacking,
1797
0
                                                    qualifier.layoutMatrix == ElmRowMajor);
1798
0
                    if (size <= int(autoPushConstantMaxSize)) {
1799
0
                        qualifier.setBlockStorage(EbsPushConstant);
1800
0
                        qualifier.layoutPacking = autoPushConstantBlockPacking;
1801
                        // Push constants don't have set/binding etc. decorations, remove those.
1802
0
                        qualifier.layoutSet = TQualifier::layoutSetEnd;
1803
0
                        at->second.clearNewAssignments();
1804
1805
0
                        upgraded = true;
1806
0
                    }
1807
0
                }
1808
0
            }
1809
            // If it's been upgraded to push_constant, then set the flag so when its traversed
1810
            // in the next for loop, all references to this symbol will get their flag changed.
1811
            // so it doesn't get a set/binding assigned to it.
1812
0
            if (upgraded) {
1813
0
                std::for_each(uniformVector.begin(), uniformVector.end(),
1814
0
                                       [this](TVarLivePair& p) {
1815
0
                if (p.first == autoPushConstantBlockName) {
1816
0
                        p.second.upgradedToPushConstantPacking = autoPushConstantBlockPacking;
1817
0
                        p.second.newSet = TQualifier::layoutSetEnd;
1818
0
                    }
1819
0
                });
1820
0
            }
1821
0
        }
1822
0
        for (size_t stage = 0; stage < EShLangCount; stage++) {
1823
0
            if (intermediates[stage] != nullptr) {
1824
                // traverse each stage, set new location to each input/output and unifom symbol, set new binding to
1825
                // ubo, ssbo and opaque symbols. Assign push_constant upgrades as well.
1826
0
                TVarLiveMap** pUniformVarMap = uniformResolve.uniformVarMap;
1827
0
                std::for_each(uniformVector.begin(), uniformVector.end(), [pUniformVarMap, stage](TVarLivePair p) {
1828
0
                    auto at = pUniformVarMap[stage]->find(p.second.symbol->getAccessName());
1829
0
                    if (at != pUniformVarMap[stage]->end() && at->second.id == p.second.id){
1830
0
                        if (p.second.upgradedToPushConstantPacking != ElpNone) {
1831
0
                            at->second.upgradedToPushConstantPacking = p.second.upgradedToPushConstantPacking;
1832
0
                        } else {
1833
0
                            int resolvedBinding = at->second.newBinding;
1834
0
                            at->second = p.second;
1835
0
                            if (resolvedBinding > 0)
1836
0
                                at->second.newBinding = resolvedBinding;
1837
0
                        }
1838
0
                    }
1839
0
                });
1840
0
                TVarSetTraverser iter_iomap(*intermediates[stage], *inVarMaps[stage], *outVarMaps[stage],
1841
0
                                            *uniformResolve.uniformVarMap[stage]);
1842
0
                intermediates[stage]->getTreeRoot()->traverse(&iter_iomap);
1843
0
            }
1844
0
        }
1845
0
        return !hadError;
1846
0
    } else {
1847
0
        return false;
1848
0
    }
1849
0
}
1850
1851
} // end namespace glslang