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