Coverage Report

Created: 2026-01-09 07:16

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