• 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 #if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
37 
38 #include "../Include/Common.h"
39 #include "../Include/InfoSink.h"
40 
41 #include "gl_types.h"
42 #include "iomapper.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())
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->getName());
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->getName()); // 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->getName()] = 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         TVarLiveMap::const_iterator at = source->find(base->getName());
129         if (at == source->end())
130             return;
131 
132         if (at->second.id != ent.id)
133             return;
134 
135         if (at->second.newBinding != -1)
136             base->getWritableType().getQualifier().layoutBinding = at->second.newBinding;
137         if (at->second.newSet != -1)
138             base->getWritableType().getQualifier().layoutSet = at->second.newSet;
139         if (at->second.newLocation != -1)
140             base->getWritableType().getQualifier().layoutLocation = at->second.newLocation;
141         if (at->second.newComponent != -1)
142             base->getWritableType().getQualifier().layoutComponent = at->second.newComponent;
143         if (at->second.newIndex != -1)
144             base->getWritableType().getQualifier().layoutIndex = at->second.newIndex;
145     }
146 
147   private:
148     const TVarLiveMap&    inputList;
149     const TVarLiveMap&    outputList;
150     const TVarLiveMap&    uniformList;
151 };
152 
153 struct TNotifyUniformAdaptor
154 {
155     EShLanguage stage;
156     TIoMapResolver& resolver;
TNotifyUniformAdaptorglslang::TNotifyUniformAdaptor157     inline TNotifyUniformAdaptor(EShLanguage s, TIoMapResolver& r)
158       : stage(s)
159       , resolver(r)
160     {
161     }
162 
operator ()glslang::TNotifyUniformAdaptor163     inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
164     {
165         resolver.notifyBinding(stage, entKey.second);
166     }
167 
168 private:
169     TNotifyUniformAdaptor& operator=(TNotifyUniformAdaptor&) = delete;
170 };
171 
172 struct TNotifyInOutAdaptor
173 {
174     EShLanguage stage;
175     TIoMapResolver& resolver;
TNotifyInOutAdaptorglslang::TNotifyInOutAdaptor176     inline TNotifyInOutAdaptor(EShLanguage s, TIoMapResolver& r)
177       : stage(s)
178       , resolver(r)
179     {
180     }
181 
operator ()glslang::TNotifyInOutAdaptor182     inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
183     {
184         resolver.notifyInOut(stage, entKey.second);
185     }
186 
187 private:
188     TNotifyInOutAdaptor& operator=(TNotifyInOutAdaptor&) = delete;
189 };
190 
191 struct TResolverUniformAdaptor {
TResolverUniformAdaptorglslang::TResolverUniformAdaptor192     TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e)
193       : stage(s)
194       , resolver(r)
195       , infoSink(i)
196       , error(e)
197     {
198     }
199 
operator ()glslang::TResolverUniformAdaptor200     inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
201         TVarEntryInfo& ent = entKey.second;
202         ent.newLocation = -1;
203         ent.newComponent = -1;
204         ent.newBinding = -1;
205         ent.newSet = -1;
206         ent.newIndex = -1;
207         const bool isValid = resolver.validateBinding(stage, ent);
208         if (isValid) {
209             resolver.resolveBinding(stage, ent);
210             resolver.resolveSet(stage, ent);
211             resolver.resolveUniformLocation(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.newSet != -1) {
222                 if (ent.newSet >= int(TQualifier::layoutSetEnd)) {
223                     TString err = "mapped set out of range: " + entKey.first;
224 
225                     infoSink.info.message(EPrefixInternalError, err.c_str());
226                     error = true;
227                 }
228             }
229         } else {
230             TString errorMsg = "Invalid binding: " + entKey.first;
231             infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
232             error = true;
233         }
234     }
235 
setStageglslang::TResolverUniformAdaptor236     inline void setStage(EShLanguage s) { stage = s; }
237 
238     EShLanguage     stage;
239     TIoMapResolver& resolver;
240     TInfoSink&      infoSink;
241     bool&           error;
242 
243 private:
244     TResolverUniformAdaptor& operator=(TResolverUniformAdaptor&) = delete;
245 };
246 
247 struct TResolverInOutAdaptor {
TResolverInOutAdaptorglslang::TResolverInOutAdaptor248     TResolverInOutAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e)
249       : stage(s)
250       , resolver(r)
251       , infoSink(i)
252       , error(e)
253     {
254     }
255 
operator ()glslang::TResolverInOutAdaptor256     inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
257     {
258         TVarEntryInfo& ent = entKey.second;
259         ent.newLocation = -1;
260         ent.newComponent = -1;
261         ent.newBinding = -1;
262         ent.newSet = -1;
263         ent.newIndex = -1;
264         const bool isValid = resolver.validateInOut(stage, ent);
265         if (isValid) {
266             resolver.resolveInOutLocation(stage, ent);
267             resolver.resolveInOutComponent(stage, ent);
268             resolver.resolveInOutIndex(stage, ent);
269         } else {
270             TString errorMsg;
271             if (ent.symbol->getType().getQualifier().semanticName != nullptr) {
272                 errorMsg = "Invalid shader In/Out variable semantic: ";
273                 errorMsg += ent.symbol->getType().getQualifier().semanticName;
274             } else {
275                 errorMsg = "Invalid shader In/Out variable: ";
276                 errorMsg += ent.symbol->getName();
277             }
278             infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
279             error = true;
280         }
281     }
282 
setStageglslang::TResolverInOutAdaptor283     inline void setStage(EShLanguage s) { stage = s; }
284 
285     EShLanguage     stage;
286     TIoMapResolver& resolver;
287     TInfoSink&      infoSink;
288     bool&           error;
289 
290 private:
291     TResolverInOutAdaptor& operator=(TResolverInOutAdaptor&) = delete;
292 };
293 
294 // The class is used for reserving explicit uniform locations and ubo/ssbo/opaque bindings
295 
296 struct TSymbolValidater
297 {
TSymbolValidaterglslang::TSymbolValidater298     TSymbolValidater(TIoMapResolver& r, TInfoSink& i, TVarLiveMap* in[EShLangCount], TVarLiveMap* out[EShLangCount],
299                      TVarLiveMap* uniform[EShLangCount], bool& hadError)
300         : preStage(EShLangCount)
301         , currentStage(EShLangCount)
302         , nextStage(EShLangCount)
303         , resolver(r)
304         , infoSink(i)
305         , hadError(hadError)
306     {
307         memcpy(inVarMaps, in, EShLangCount * (sizeof(TVarLiveMap*)));
308         memcpy(outVarMaps, out, EShLangCount * (sizeof(TVarLiveMap*)));
309         memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*)));
310     }
311 
operator ()glslang::TSymbolValidater312     inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
313         TVarEntryInfo& ent1 = entKey.second;
314         TIntermSymbol* base = ent1.symbol;
315         const TType& type = ent1.symbol->getType();
316         const TString& name = entKey.first;
317         EShLanguage stage = ent1.stage;
318         TString mangleName1, mangleName2;
319         if (currentStage != stage) {
320             preStage = currentStage;
321             currentStage = stage;
322             nextStage = EShLangCount;
323             for (int i = currentStage + 1; i < EShLangCount; i++) {
324                 if (inVarMaps[i] != nullptr) {
325                     nextStage = static_cast<EShLanguage>(i);
326                     break;
327                 }
328             }
329         }
330 
331         if (type.getQualifier().isArrayedIo(stage)) {
332             TType subType(type, 0);
333             subType.appendMangledName(mangleName1);
334         } else {
335             type.appendMangledName(mangleName1);
336         }
337 
338         if (base->getQualifier().storage == EvqVaryingIn) {
339             // validate stage in;
340             if (preStage == EShLangCount)
341                 return;
342             if (name == "gl_PerVertex")
343                 return;
344             if (outVarMaps[preStage] != nullptr) {
345                 auto ent2 = outVarMaps[preStage]->find(name);
346                 if (ent2 != outVarMaps[preStage]->end()) {
347                     if (ent2->second.symbol->getType().getQualifier().isArrayedIo(preStage)) {
348                         TType subType(ent2->second.symbol->getType(), 0);
349                         subType.appendMangledName(mangleName2);
350                     }
351                     else {
352                         ent2->second.symbol->getType().appendMangledName(mangleName2);
353                     }
354                     if (mangleName1 == mangleName2)
355                         return;
356                     else {
357                         TString err = "Invalid In/Out variable type : " + entKey.first;
358                         infoSink.info.message(EPrefixInternalError, err.c_str());
359                         hadError = true;
360                     }
361                 }
362                 return;
363             }
364         } else if (base->getQualifier().storage == EvqVaryingOut) {
365             // validate stage out;
366             if (nextStage == EShLangCount)
367                 return;
368             if (name == "gl_PerVertex")
369                 return;
370             if (outVarMaps[nextStage] != nullptr) {
371                 auto ent2 = inVarMaps[nextStage]->find(name);
372                 if (ent2 != inVarMaps[nextStage]->end()) {
373                     if (ent2->second.symbol->getType().getQualifier().isArrayedIo(nextStage)) {
374                         TType subType(ent2->second.symbol->getType(), 0);
375                         subType.appendMangledName(mangleName2);
376                     }
377                     else {
378                         ent2->second.symbol->getType().appendMangledName(mangleName2);
379                     }
380                     if (mangleName1 == mangleName2)
381                         return;
382                     else {
383                         TString err = "Invalid In/Out variable type : " + entKey.first;
384                         infoSink.info.message(EPrefixInternalError, err.c_str());
385                         hadError = true;
386                     }
387                 }
388                 return;
389             }
390         } else if (base->getQualifier().isUniformOrBuffer() && ! base->getQualifier().isPushConstant()) {
391             // validate uniform type;
392             for (int i = 0; i < EShLangCount; i++) {
393                 if (i != currentStage && outVarMaps[i] != nullptr) {
394                     auto ent2 = uniformVarMap[i]->find(name);
395                     if (ent2 != uniformVarMap[i]->end()) {
396                         ent2->second.symbol->getType().appendMangledName(mangleName2);
397                         if (mangleName1 != mangleName2) {
398                             TString err = "Invalid Uniform variable type : " + entKey.first;
399                             infoSink.info.message(EPrefixInternalError, err.c_str());
400                             hadError = true;
401                         }
402                         mangleName2.clear();
403                     }
404                 }
405             }
406         }
407     }
408     TVarLiveMap *inVarMaps[EShLangCount], *outVarMaps[EShLangCount], *uniformVarMap[EShLangCount];
409     // Use for mark pre stage, to get more interface symbol information.
410     EShLanguage preStage, currentStage, nextStage;
411     // Use for mark current shader stage for resolver
412     TIoMapResolver& resolver;
413     TInfoSink& infoSink;
414     bool& hadError;
415 
416 private:
417     TSymbolValidater& operator=(TSymbolValidater&) = delete;
418 };
419 
420 struct TSlotCollector {
TSlotCollectorglslang::TSlotCollector421     TSlotCollector(TIoMapResolver& r, TInfoSink& i) : resolver(r), infoSink(i) { }
422 
operator ()glslang::TSlotCollector423     inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
424         resolver.reserverStorageSlot(entKey.second, infoSink);
425         resolver.reserverResourceSlot(entKey.second, infoSink);
426     }
427     TIoMapResolver& resolver;
428     TInfoSink& infoSink;
429 
430 private:
431     TSlotCollector& operator=(TSlotCollector&) = delete;
432 };
433 
TDefaultIoResolverBase(const TIntermediate & intermediate)434 TDefaultIoResolverBase::TDefaultIoResolverBase(const TIntermediate& intermediate)
435     : intermediate(intermediate)
436     , nextUniformLocation(intermediate.getUniformLocationBase())
437     , nextInputLocation(0)
438     , nextOutputLocation(0)
439 {
440     memset(stageMask, false, sizeof(bool) * (EShLangCount + 1));
441 }
442 
getBaseBinding(TResourceType res,unsigned int set) const443 int TDefaultIoResolverBase::getBaseBinding(TResourceType res, unsigned int set) const {
444     return selectBaseBinding(intermediate.getShiftBinding(res), intermediate.getShiftBindingForSet(res, set));
445 }
446 
getResourceSetBinding() const447 const std::vector<std::string>& TDefaultIoResolverBase::getResourceSetBinding() const {
448     return intermediate.getResourceSetBinding();
449 }
450 
doAutoBindingMapping() const451 bool TDefaultIoResolverBase::doAutoBindingMapping() const { return intermediate.getAutoMapBindings(); }
452 
doAutoLocationMapping() const453 bool TDefaultIoResolverBase::doAutoLocationMapping() const { return intermediate.getAutoMapLocations(); }
454 
findSlot(int set,int slot)455 TDefaultIoResolverBase::TSlotSet::iterator TDefaultIoResolverBase::findSlot(int set, int slot) {
456     return std::lower_bound(slots[set].begin(), slots[set].end(), slot);
457 }
458 
checkEmpty(int set,int slot)459 bool TDefaultIoResolverBase::checkEmpty(int set, int slot) {
460     TSlotSet::iterator at = findSlot(set, slot);
461     return ! (at != slots[set].end() && *at == slot);
462 }
463 
reserveSlot(int set,int slot,int size)464 int TDefaultIoResolverBase::reserveSlot(int set, int slot, int size) {
465     TSlotSet::iterator at = findSlot(set, slot);
466     // tolerate aliasing, by not double-recording aliases
467     // (policy about appropriateness of the alias is higher up)
468     for (int i = 0; i < size; i++) {
469         if (at == slots[set].end() || *at != slot + i)
470             at = slots[set].insert(at, slot + i);
471         ++at;
472     }
473     return slot;
474 }
475 
getFreeSlot(int set,int base,int size)476 int TDefaultIoResolverBase::getFreeSlot(int set, int base, int size) {
477     TSlotSet::iterator at = findSlot(set, base);
478     if (at == slots[set].end())
479         return reserveSlot(set, base, size);
480     // look for a big enough gap
481     for (; at != slots[set].end(); ++at) {
482         if (*at - base >= size)
483             break;
484         base = *at + 1;
485     }
486     return reserveSlot(set, base, size);
487 }
488 
resolveSet(EShLanguage,TVarEntryInfo & ent)489 int TDefaultIoResolverBase::resolveSet(EShLanguage /*stage*/, TVarEntryInfo& ent) {
490     const TType& type = ent.symbol->getType();
491     if (type.getQualifier().hasSet()) {
492         return ent.newSet = type.getQualifier().layoutSet;
493     }
494     // If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN)
495     if (getResourceSetBinding().size() == 1) {
496         return ent.newSet = atoi(getResourceSetBinding()[0].c_str());
497     }
498     return ent.newSet = 0;
499 }
500 
resolveUniformLocation(EShLanguage,TVarEntryInfo & ent)501 int TDefaultIoResolverBase::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
502     const TType& type = ent.symbol->getType();
503     const char* name = ent.symbol->getName().c_str();
504     // kick out of not doing this
505     if (! doAutoLocationMapping()) {
506         return ent.newLocation = -1;
507     }
508     // no locations added if already present, a built-in variable, a block, or an opaque
509     if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock ||
510         type.isAtomic() || (type.containsOpaque() && intermediate.getSpv().openGl == 0)) {
511         return ent.newLocation = -1;
512     }
513     // no locations on blocks of built-in variables
514     if (type.isStruct()) {
515         if (type.getStruct()->size() < 1) {
516             return ent.newLocation = -1;
517         }
518         if ((*type.getStruct())[0].type->isBuiltIn()) {
519             return ent.newLocation = -1;
520         }
521     }
522     int location = intermediate.getUniformLocationOverride(name);
523     if (location != -1) {
524         return ent.newLocation = location;
525     }
526     location = nextUniformLocation;
527     nextUniformLocation += TIntermediate::computeTypeUniformLocationSize(type);
528     return ent.newLocation = location;
529 }
530 
resolveInOutLocation(EShLanguage stage,TVarEntryInfo & ent)531 int TDefaultIoResolverBase::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
532     const TType& type = ent.symbol->getType();
533     // kick out of not doing this
534     if (! doAutoLocationMapping()) {
535         return ent.newLocation = -1;
536     }
537 
538     // no locations added if already present, or a built-in variable
539     if (type.getQualifier().hasLocation() || type.isBuiltIn()) {
540         return ent.newLocation = -1;
541     }
542 
543     // no locations on blocks of built-in variables
544     if (type.isStruct()) {
545         if (type.getStruct()->size() < 1) {
546             return ent.newLocation = -1;
547         }
548         if ((*type.getStruct())[0].type->isBuiltIn()) {
549             return ent.newLocation = -1;
550         }
551     }
552     // point to the right input or output location counter
553     int& nextLocation = type.getQualifier().isPipeInput() ? nextInputLocation : nextOutputLocation;
554     // Placeholder. This does not do proper cross-stage lining up, nor
555     // work with mixed location/no-location declarations.
556     int location = nextLocation;
557     int typeLocationSize;
558     // Don’t take into account the outer-most array if the stage’s
559     // interface is automatically an array.
560     typeLocationSize = computeTypeLocationSize(type, stage);
561     nextLocation += typeLocationSize;
562     return ent.newLocation = location;
563 }
564 
resolveInOutComponent(EShLanguage,TVarEntryInfo & ent)565 int TDefaultIoResolverBase::resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) {
566     return ent.newComponent = -1;
567 }
568 
resolveInOutIndex(EShLanguage,TVarEntryInfo & ent)569 int TDefaultIoResolverBase::resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) { return ent.newIndex = -1; }
570 
computeTypeLocationSize(const TType & type,EShLanguage stage)571 uint32_t TDefaultIoResolverBase::computeTypeLocationSize(const TType& type, EShLanguage stage) {
572     int typeLocationSize;
573     // Don’t take into account the outer-most array if the stage’s
574     // interface is automatically an array.
575     if (type.getQualifier().isArrayedIo(stage)) {
576         TType elementType(type, 0);
577         typeLocationSize = TIntermediate::computeTypeLocationSize(elementType, stage);
578     } else {
579         typeLocationSize = TIntermediate::computeTypeLocationSize(type, stage);
580     }
581     return typeLocationSize;
582 }
583 
584 //TDefaultGlslIoResolver
getResourceType(const glslang::TType & type)585 TResourceType TDefaultGlslIoResolver::getResourceType(const glslang::TType& type) {
586     if (isImageType(type)) {
587         return EResImage;
588     }
589     if (isTextureType(type)) {
590         return EResTexture;
591     }
592     if (isSsboType(type)) {
593         return EResSsbo;
594     }
595     if (isSamplerType(type)) {
596         return EResSampler;
597     }
598     if (isUboType(type)) {
599         return EResUbo;
600     }
601     return EResCount;
602 }
603 
TDefaultGlslIoResolver(const TIntermediate & intermediate)604 TDefaultGlslIoResolver::TDefaultGlslIoResolver(const TIntermediate& intermediate)
605     : TDefaultIoResolverBase(intermediate)
606     , preStage(EShLangCount)
607     , currentStage(EShLangCount)
608 { }
609 
resolveInOutLocation(EShLanguage stage,TVarEntryInfo & ent)610 int TDefaultGlslIoResolver::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
611     const TType& type = ent.symbol->getType();
612     const TString& name = getAccessName(ent.symbol);
613     if (currentStage != stage) {
614         preStage = currentStage;
615         currentStage = stage;
616     }
617     // kick out of not doing this
618     if (! doAutoLocationMapping()) {
619         return ent.newLocation = -1;
620     }
621     // expand the location to each element if the symbol is a struct or array
622     if (type.getQualifier().hasLocation()) {
623         return ent.newLocation = type.getQualifier().layoutLocation;
624     }
625     // no locations added if already present, or a built-in variable
626     if (type.isBuiltIn()) {
627         return ent.newLocation = -1;
628     }
629     // no locations on blocks of built-in variables
630     if (type.isStruct()) {
631         if (type.getStruct()->size() < 1) {
632             return ent.newLocation = -1;
633         }
634         if ((*type.getStruct())[0].type->isBuiltIn()) {
635             return ent.newLocation = -1;
636         }
637     }
638     int typeLocationSize = computeTypeLocationSize(type, stage);
639     int location = type.getQualifier().layoutLocation;
640     bool hasLocation = false;
641     EShLanguage keyStage(EShLangCount);
642     TStorageQualifier storage;
643     storage = EvqInOut;
644     if (type.getQualifier().isPipeInput()) {
645         // If this symbol is a input, search pre stage's out
646         keyStage = preStage;
647     }
648     if (type.getQualifier().isPipeOutput()) {
649         // If this symbol is a output, search next stage's in
650         keyStage = currentStage;
651     }
652     // The in/out in current stage is not declared with location, but it is possible declared
653     // with explicit location in other stages, find the storageSlotMap firstly to check whether
654     // the in/out has location
655     int resourceKey = buildStorageKey(keyStage, storage);
656     if (! storageSlotMap[resourceKey].empty()) {
657         TVarSlotMap::iterator iter = storageSlotMap[resourceKey].find(name);
658         if (iter != storageSlotMap[resourceKey].end()) {
659             // If interface resource be found, set it has location and this symbol's new location
660             // equal the symbol's explicit location declaration in pre or next stage.
661             //
662             // vs:    out vec4 a;
663             // fs:    layout(..., location = 3,...) in vec4 a;
664             hasLocation = true;
665             location = iter->second;
666             // if we want deal like that:
667             // vs:    layout(location=4) out vec4 a;
668             //        out vec4 b;
669             //
670             // fs:    in vec4 a;
671             //        layout(location = 4) in vec4 b;
672             // we need retraverse the map.
673         }
674         if (! hasLocation) {
675             // If interface resource note found, It's mean the location in two stage are both implicit declarat.
676             // So we should find a new slot for this interface.
677             //
678             // vs: out vec4 a;
679             // fs: in vec4 a;
680             location = getFreeSlot(resourceKey, 0, typeLocationSize);
681             storageSlotMap[resourceKey][name] = location;
682         }
683     } else {
684         // the first interface declarated in a program.
685         TVarSlotMap varSlotMap;
686         location = getFreeSlot(resourceKey, 0, typeLocationSize);
687         varSlotMap[name] = location;
688         storageSlotMap[resourceKey] = varSlotMap;
689     }
690     //Update location
691     return ent.newLocation = location;
692 }
693 
resolveUniformLocation(EShLanguage,TVarEntryInfo & ent)694 int TDefaultGlslIoResolver::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
695     const TType& type = ent.symbol->getType();
696     const TString& name = getAccessName(ent.symbol);
697     // kick out of not doing this
698     if (! doAutoLocationMapping()) {
699         return ent.newLocation = -1;
700     }
701     // expand the location to each element if the symbol is a struct or array
702     if (type.getQualifier().hasLocation() && (type.isStruct() || type.isArray())) {
703         return ent.newLocation = type.getQualifier().layoutLocation;
704     } else {
705         // no locations added if already present, a built-in variable, a block, or an opaque
706         if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock ||
707             type.isAtomic() || (type.containsOpaque() && intermediate.getSpv().openGl == 0)) {
708             return ent.newLocation = -1;
709         }
710         // no locations on blocks of built-in variables
711         if (type.isStruct()) {
712             if (type.getStruct()->size() < 1) {
713                 return ent.newLocation = -1;
714             }
715             if ((*type.getStruct())[0].type->isBuiltIn()) {
716                 return ent.newLocation = -1;
717             }
718         }
719     }
720     int location = intermediate.getUniformLocationOverride(name.c_str());
721     if (location != -1) {
722         return ent.newLocation = location;
723     }
724 
725     int size = TIntermediate::computeTypeUniformLocationSize(type);
726 
727     // The uniform in current stage is not declared with location, but it is possible declared
728     // with explicit location in other stages, find the storageSlotMap firstly to check whether
729     // the uniform has location
730     bool hasLocation = false;
731     int resourceKey = buildStorageKey(EShLangCount, EvqUniform);
732     TVarSlotMap& slotMap = storageSlotMap[resourceKey];
733     // Check dose shader program has uniform resource
734     if (! slotMap.empty()) {
735         // If uniform resource not empty, try find a same name uniform
736         TVarSlotMap::iterator iter = slotMap.find(name);
737         if (iter != slotMap.end()) {
738             // If uniform resource be found, set it has location and this symbol's new location
739             // equal the uniform's explicit location declaration in other stage.
740             //
741             // vs:    uniform vec4 a;
742             // fs:    layout(..., location = 3,...) uniform vec4 a;
743             hasLocation = true;
744             location = iter->second;
745         }
746         if (! hasLocation) {
747             // No explicit location declaration in other stage.
748             // So we should find a new slot for this uniform.
749             //
750             // vs:    uniform vec4 a;
751             // fs:    uniform vec4 a;
752             location = getFreeSlot(resourceKey, 0, computeTypeLocationSize(type, currentStage));
753             storageSlotMap[resourceKey][name] = location;
754         }
755     } else {
756         // the first uniform declaration in a program.
757         TVarSlotMap varSlotMap;
758         location = getFreeSlot(resourceKey, 0, size);
759         varSlotMap[name] = location;
760         storageSlotMap[resourceKey] = varSlotMap;
761     }
762     return ent.newLocation = location;
763 }
764 
resolveBinding(EShLanguage,TVarEntryInfo & ent)765 int TDefaultGlslIoResolver::resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) {
766     const TType& type = ent.symbol->getType();
767     const TString& name = getAccessName(ent.symbol);
768     // On OpenGL arrays of opaque types take a separate binding for each element
769     int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
770     TResourceType resource = getResourceType(type);
771     // don't need to handle uniform symbol, it will be handled in resolveUniformLocation
772     if (resource == EResUbo && type.getBasicType() != EbtBlock) {
773         return ent.newBinding = -1;
774     }
775     // There is no 'set' qualifier in OpenGL shading language, each resource has its own
776     // binding name space, so remap the 'set' to resource type which make each resource
777     // binding is valid from 0 to MAX_XXRESOURCE_BINDINGS
778     int set = resource;
779     if (resource < EResCount) {
780         if (type.getQualifier().hasBinding()) {
781             ent.newBinding = reserveSlot(set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding, numBindings);
782             return ent.newBinding;
783         } else if (ent.live && doAutoBindingMapping()) {
784             // The resource in current stage is not declared with binding, but it is possible declared
785             // with explicit binding in other stages, find the resourceSlotMap firstly to check whether
786             // the resource has binding, don't need to allocate if it already has a binding
787             bool hasBinding = false;
788             if (! resourceSlotMap[resource].empty()) {
789                 TVarSlotMap::iterator iter = resourceSlotMap[resource].find(name);
790                 if (iter != resourceSlotMap[resource].end()) {
791                     hasBinding = true;
792                     ent.newBinding = iter->second;
793                 }
794             }
795             if (! hasBinding) {
796                 TVarSlotMap varSlotMap;
797                 // find free slot, the caller did make sure it passes all vars with binding
798                 // first and now all are passed that do not have a binding and needs one
799                 int binding = getFreeSlot(resource, getBaseBinding(resource, set), numBindings);
800                 varSlotMap[name] = binding;
801                 resourceSlotMap[resource] = varSlotMap;
802                 ent.newBinding = binding;
803             }
804             return ent.newBinding;
805         }
806     }
807     return ent.newBinding = -1;
808 }
809 
beginResolve(EShLanguage stage)810 void TDefaultGlslIoResolver::beginResolve(EShLanguage stage) {
811     // reset stage state
812     if (stage == EShLangCount)
813         preStage = currentStage = stage;
814     // update stage state
815     else if (currentStage != stage) {
816         preStage = currentStage;
817         currentStage = stage;
818     }
819 }
820 
endResolve(EShLanguage)821 void TDefaultGlslIoResolver::endResolve(EShLanguage /*stage*/) {
822     // TODO nothing
823 }
824 
beginCollect(EShLanguage stage)825 void TDefaultGlslIoResolver::beginCollect(EShLanguage stage) {
826     // reset stage state
827     if (stage == EShLangCount)
828         preStage = currentStage = stage;
829     // update stage state
830     else if (currentStage != stage) {
831         preStage = currentStage;
832         currentStage = stage;
833     }
834 }
835 
endCollect(EShLanguage)836 void TDefaultGlslIoResolver::endCollect(EShLanguage /*stage*/) {
837     // TODO nothing
838 }
839 
reserverStorageSlot(TVarEntryInfo & ent,TInfoSink & infoSink)840 void TDefaultGlslIoResolver::reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
841     const TType& type = ent.symbol->getType();
842     const TString& name = getAccessName(ent.symbol);
843     TStorageQualifier storage = type.getQualifier().storage;
844     EShLanguage stage(EShLangCount);
845     switch (storage) {
846     case EvqUniform:
847         if (type.getBasicType() != EbtBlock && type.getQualifier().hasLocation()) {
848             //
849             // Reserve the slots for the uniforms who has explicit location
850             int storageKey = buildStorageKey(EShLangCount, EvqUniform);
851             int location = type.getQualifier().layoutLocation;
852             TVarSlotMap& varSlotMap = storageSlotMap[storageKey];
853             TVarSlotMap::iterator iter = varSlotMap.find(name);
854             if (iter == varSlotMap.end()) {
855                 int numLocations = TIntermediate::computeTypeUniformLocationSize(type);
856                 reserveSlot(storageKey, location, numLocations);
857                 varSlotMap[name] = location;
858             } else {
859                 // Allocate location by name for OpenGL driver, so the uniform in different
860                 // stages should be declared with the same location
861                 if (iter->second != location) {
862                     TString errorMsg = "Invalid location: " + name;
863                     infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
864                     hasError = true;
865                 }
866             }
867         }
868         break;
869     case EvqVaryingIn:
870     case EvqVaryingOut:
871         //
872         // Reserve the slots for the inout who has explicit location
873         if (type.getQualifier().hasLocation()) {
874             stage = storage == EvqVaryingIn ? preStage : stage;
875             stage = storage == EvqVaryingOut ? currentStage : stage;
876             int storageKey = buildStorageKey(stage, EvqInOut);
877             int location = type.getQualifier().layoutLocation;
878             TVarSlotMap& varSlotMap = storageSlotMap[storageKey];
879             TVarSlotMap::iterator iter = varSlotMap.find(name);
880             if (iter == varSlotMap.end()) {
881                 int numLocations = TIntermediate::computeTypeUniformLocationSize(type);
882                 reserveSlot(storageKey, location, numLocations);
883                 varSlotMap[name] = location;
884             } else {
885                 // Allocate location by name for OpenGL driver, so the uniform in different
886                 // stages should be declared with the same location
887                 if (iter->second != location) {
888                     TString errorMsg = "Invalid location: " + name;
889                     infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
890                     hasError = true;
891                 }
892             }
893         }
894         break;
895     default:
896         break;
897     }
898 }
899 
reserverResourceSlot(TVarEntryInfo & ent,TInfoSink & infoSink)900 void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
901     const TType& type = ent.symbol->getType();
902     const TString& name = getAccessName(ent.symbol);
903     int resource = getResourceType(type);
904     if (type.getQualifier().hasBinding()) {
905         TVarSlotMap& varSlotMap = resourceSlotMap[resource];
906         TVarSlotMap::iterator iter = varSlotMap.find(name);
907         int binding = type.getQualifier().layoutBinding;
908         if (iter == varSlotMap.end()) {
909             // Reserve the slots for the ubo, ssbo and opaques who has explicit binding
910             int numBindings = type.isSizedArray() ? type.getCumulativeArraySize() : 1;
911             varSlotMap[name] = binding;
912             reserveSlot(resource, binding, numBindings);
913         } else {
914             // Allocate binding by name for OpenGL driver, so the resource in different
915             // stages should be declared with the same binding
916             if (iter->second != binding) {
917                 TString errorMsg = "Invalid binding: " + name;
918                 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
919                 hasError = true;
920             }
921         }
922     }
923 }
924 
getAccessName(const TIntermSymbol * symbol)925 const TString& TDefaultGlslIoResolver::getAccessName(const TIntermSymbol* symbol)
926 {
927     return symbol->getBasicType() == EbtBlock ?
928         symbol->getType().getTypeName() :
929         symbol->getName();
930 }
931 
932 //TDefaultGlslIoResolver end
933 
934 /*
935  * Basic implementation of glslang::TIoMapResolver that replaces the
936  * previous offset behavior.
937  * It does the same, uses the offsets for the corresponding uniform
938  * types. Also respects the EOptionAutoMapBindings flag and binds
939  * them if needed.
940  */
941 /*
942  * Default resolver
943  */
944 struct TDefaultIoResolver : public TDefaultIoResolverBase {
TDefaultIoResolverglslang::TDefaultIoResolver945     TDefaultIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
946 
validateBindingglslang::TDefaultIoResolver947     bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
948 
getResourceTypeglslang::TDefaultIoResolver949     TResourceType getResourceType(const glslang::TType& type) override {
950         if (isImageType(type)) {
951             return EResImage;
952         }
953         if (isTextureType(type)) {
954             return EResTexture;
955         }
956         if (isSsboType(type)) {
957             return EResSsbo;
958         }
959         if (isSamplerType(type)) {
960             return EResSampler;
961         }
962         if (isUboType(type)) {
963             return EResUbo;
964         }
965         return EResCount;
966     }
967 
resolveBindingglslang::TDefaultIoResolver968     int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override {
969         const TType& type = ent.symbol->getType();
970         const int set = getLayoutSet(type);
971         // On OpenGL arrays of opaque types take a seperate binding for each element
972         int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
973         TResourceType resource = getResourceType(type);
974         if (resource < EResCount) {
975             if (type.getQualifier().hasBinding()) {
976                 return ent.newBinding = reserveSlot(
977                            set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding, numBindings);
978             } else if (ent.live && doAutoBindingMapping()) {
979                 // find free slot, the caller did make sure it passes all vars with binding
980                 // first and now all are passed that do not have a binding and needs one
981                 return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set), numBindings);
982             }
983         }
984         return ent.newBinding = -1;
985     }
986 };
987 
988 #ifdef ENABLE_HLSL
989 /********************************************************************************
990 The following IO resolver maps types in HLSL register space, as follows:
991 
992 t - for shader resource views (SRV)
993    TEXTURE1D
994    TEXTURE1DARRAY
995    TEXTURE2D
996    TEXTURE2DARRAY
997    TEXTURE3D
998    TEXTURECUBE
999    TEXTURECUBEARRAY
1000    TEXTURE2DMS
1001    TEXTURE2DMSARRAY
1002    STRUCTUREDBUFFER
1003    BYTEADDRESSBUFFER
1004    BUFFER
1005    TBUFFER
1006 
1007 s - for samplers
1008    SAMPLER
1009    SAMPLER1D
1010    SAMPLER2D
1011    SAMPLER3D
1012    SAMPLERCUBE
1013    SAMPLERSTATE
1014    SAMPLERCOMPARISONSTATE
1015 
1016 u - for unordered access views (UAV)
1017    RWBYTEADDRESSBUFFER
1018    RWSTRUCTUREDBUFFER
1019    APPENDSTRUCTUREDBUFFER
1020    CONSUMESTRUCTUREDBUFFER
1021    RWBUFFER
1022    RWTEXTURE1D
1023    RWTEXTURE1DARRAY
1024    RWTEXTURE2D
1025    RWTEXTURE2DARRAY
1026    RWTEXTURE3D
1027 
1028 b - for constant buffer views (CBV)
1029    CBUFFER
1030    CONSTANTBUFFER
1031  ********************************************************************************/
1032 struct TDefaultHlslIoResolver : public TDefaultIoResolverBase {
TDefaultHlslIoResolverglslang::TDefaultHlslIoResolver1033     TDefaultHlslIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
1034 
validateBindingglslang::TDefaultHlslIoResolver1035     bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
1036 
getResourceTypeglslang::TDefaultHlslIoResolver1037     TResourceType getResourceType(const glslang::TType& type) override {
1038         if (isUavType(type)) {
1039             return EResUav;
1040         }
1041         if (isSrvType(type)) {
1042             return EResTexture;
1043         }
1044         if (isSamplerType(type)) {
1045             return EResSampler;
1046         }
1047         if (isUboType(type)) {
1048             return EResUbo;
1049         }
1050         return EResCount;
1051     }
1052 
resolveBindingglslang::TDefaultHlslIoResolver1053     int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override {
1054         const TType& type = ent.symbol->getType();
1055         const int set = getLayoutSet(type);
1056         TResourceType resource = getResourceType(type);
1057         if (resource < EResCount) {
1058             if (type.getQualifier().hasBinding()) {
1059                 return ent.newBinding = reserveSlot(set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding);
1060             } else if (ent.live && doAutoBindingMapping()) {
1061                 // find free slot, the caller did make sure it passes all vars with binding
1062                 // first and now all are passed that do not have a binding and needs one
1063                 return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set));
1064             }
1065         }
1066         return ent.newBinding = -1;
1067     }
1068 };
1069 #endif
1070 
1071 // Map I/O variables to provided offsets, and make bindings for
1072 // unbound but live variables.
1073 //
1074 // Returns false if the input is too malformed to do this.
addStage(EShLanguage stage,TIntermediate & intermediate,TInfoSink & infoSink,TIoMapResolver * resolver)1075 bool TIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) {
1076     bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() ||
1077                          intermediate.getAutoMapLocations();
1078     // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce
1079     // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true.
1080     for (int res = 0; (res < EResCount && !somethingToDo); ++res) {
1081         somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
1082                         intermediate.hasShiftBindingForSet(TResourceType(res));
1083     }
1084     if (! somethingToDo && resolver == nullptr)
1085         return true;
1086     if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive())
1087         return false;
1088     TIntermNode* root = intermediate.getTreeRoot();
1089     if (root == nullptr)
1090         return false;
1091     // if no resolver is provided, use the default resolver with the given shifts and auto map settings
1092     TDefaultIoResolver defaultResolver(intermediate);
1093 #ifdef ENABLE_HLSL
1094     TDefaultHlslIoResolver defaultHlslResolver(intermediate);
1095     if (resolver == nullptr) {
1096         // TODO: use a passed in IO mapper for this
1097         if (intermediate.usingHlslIoMapping())
1098             resolver = &defaultHlslResolver;
1099         else
1100             resolver = &defaultResolver;
1101     }
1102     resolver->addStage(stage);
1103 #else
1104     resolver = &defaultResolver;
1105 #endif
1106 
1107     TVarLiveMap inVarMap, outVarMap, uniformVarMap;
1108     TVarLiveVector inVector, outVector, uniformVector;
1109     TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap);
1110     TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap);
1111     root->traverse(&iter_binding_all);
1112     iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
1113     while (! iter_binding_live.destinations.empty()) {
1114         TIntermNode* destination = iter_binding_live.destinations.back();
1115         iter_binding_live.destinations.pop_back();
1116         destination->traverse(&iter_binding_live);
1117     }
1118 
1119     // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info.
1120     std::for_each(inVarMap.begin(), inVarMap.end(),
1121                   [&inVector](TVarLivePair p) { inVector.push_back(p); });
1122     std::sort(inVector.begin(), inVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1123         return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1124     });
1125     std::for_each(outVarMap.begin(), outVarMap.end(),
1126                   [&outVector](TVarLivePair p) { outVector.push_back(p); });
1127     std::sort(outVector.begin(), outVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1128         return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1129     });
1130     std::for_each(uniformVarMap.begin(), uniformVarMap.end(),
1131                   [&uniformVector](TVarLivePair p) { uniformVector.push_back(p); });
1132     std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1133         return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1134     });
1135     bool hadError = false;
1136     TNotifyInOutAdaptor inOutNotify(stage, *resolver);
1137     TNotifyUniformAdaptor uniformNotify(stage, *resolver);
1138     TResolverUniformAdaptor uniformResolve(stage, *resolver, infoSink, hadError);
1139     TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError);
1140     resolver->beginNotifications(stage);
1141     std::for_each(inVector.begin(), inVector.end(), inOutNotify);
1142     std::for_each(outVector.begin(), outVector.end(), inOutNotify);
1143     std::for_each(uniformVector.begin(), uniformVector.end(), uniformNotify);
1144     resolver->endNotifications(stage);
1145     resolver->beginResolve(stage);
1146     std::for_each(inVector.begin(), inVector.end(), inOutResolve);
1147     std::for_each(inVector.begin(), inVector.end(), [&inVarMap](TVarLivePair p) {
1148         auto at = inVarMap.find(p.second.symbol->getName());
1149         if (at != inVarMap.end())
1150             at->second = p.second;
1151     });
1152     std::for_each(outVector.begin(), outVector.end(), inOutResolve);
1153     std::for_each(outVector.begin(), outVector.end(), [&outVarMap](TVarLivePair p) {
1154         auto at = outVarMap.find(p.second.symbol->getName());
1155         if (at != outVarMap.end())
1156             at->second = p.second;
1157     });
1158     std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve);
1159     std::for_each(uniformVector.begin(), uniformVector.end(), [&uniformVarMap](TVarLivePair p) {
1160         auto at = uniformVarMap.find(p.second.symbol->getName());
1161         if (at != uniformVarMap.end())
1162             at->second = p.second;
1163     });
1164     resolver->endResolve(stage);
1165     if (!hadError) {
1166         TVarSetTraverser iter_iomap(intermediate, inVarMap, outVarMap, uniformVarMap);
1167         root->traverse(&iter_iomap);
1168     }
1169     return !hadError;
1170 }
1171 
1172 // Map I/O variables to provided offsets, and make bindings for
1173 // unbound but live variables.
1174 //
1175 // Returns false if the input is too malformed to do this.
addStage(EShLanguage stage,TIntermediate & intermediate,TInfoSink & infoSink,TIoMapResolver * resolver)1176 bool TGlslIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) {
1177 
1178     bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() ||
1179                          intermediate.getAutoMapLocations();
1180     // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce
1181     // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true.
1182     for (int res = 0; (res < EResCount && !somethingToDo); ++res) {
1183         somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
1184                         intermediate.hasShiftBindingForSet(TResourceType(res));
1185     }
1186     if (! somethingToDo && resolver == nullptr) {
1187         return true;
1188     }
1189     if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive()) {
1190         return false;
1191     }
1192     TIntermNode* root = intermediate.getTreeRoot();
1193     if (root == nullptr) {
1194         return false;
1195     }
1196     // if no resolver is provided, use the default resolver with the given shifts and auto map settings
1197     TDefaultGlslIoResolver defaultResolver(intermediate);
1198     if (resolver == nullptr) {
1199         resolver = &defaultResolver;
1200     }
1201     resolver->addStage(stage);
1202     inVarMaps[stage] = new TVarLiveMap(); outVarMaps[stage] = new TVarLiveMap(); uniformVarMap[stage] = new TVarLiveMap();
1203     TVarGatherTraverser iter_binding_all(intermediate, true, *inVarMaps[stage], *outVarMaps[stage],
1204                                          *uniformVarMap[stage]);
1205     TVarGatherTraverser iter_binding_live(intermediate, false, *inVarMaps[stage], *outVarMaps[stage],
1206                                           *uniformVarMap[stage]);
1207     root->traverse(&iter_binding_all);
1208     iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
1209     while (! iter_binding_live.destinations.empty()) {
1210         TIntermNode* destination = iter_binding_live.destinations.back();
1211         iter_binding_live.destinations.pop_back();
1212         destination->traverse(&iter_binding_live);
1213     }
1214 
1215     TNotifyInOutAdaptor inOutNotify(stage, *resolver);
1216     TNotifyUniformAdaptor uniformNotify(stage, *resolver);
1217     // Resolve current stage input symbol location with previous stage output here,
1218     // uniform symbol, ubo, ssbo and opaque symbols are per-program resource,
1219     // will resolve uniform symbol location and ubo/ssbo/opaque binding in doMap()
1220     resolver->beginNotifications(stage);
1221     std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), inOutNotify);
1222     std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), inOutNotify);
1223     std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), uniformNotify);
1224     resolver->endNotifications(stage);
1225     TSlotCollector slotCollector(*resolver, infoSink);
1226     resolver->beginCollect(stage);
1227     std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), slotCollector);
1228     std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), slotCollector);
1229     std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), slotCollector);
1230     resolver->endCollect(stage);
1231     intermediates[stage] = &intermediate;
1232     return !hadError;
1233 }
1234 
doMap(TIoMapResolver * resolver,TInfoSink & infoSink)1235 bool TGlslIoMapper::doMap(TIoMapResolver* resolver, TInfoSink& infoSink) {
1236     resolver->endResolve(EShLangCount);
1237     if (!hadError) {
1238         //Resolve uniform location, ubo/ssbo/opaque bindings across stages
1239         TResolverUniformAdaptor uniformResolve(EShLangCount, *resolver, infoSink, hadError);
1240         TResolverInOutAdaptor inOutResolve(EShLangCount, *resolver, infoSink, hadError);
1241         TSymbolValidater symbolValidater(*resolver, infoSink, inVarMaps, outVarMaps, uniformVarMap, hadError);
1242         TVarLiveVector uniformVector;
1243         resolver->beginResolve(EShLangCount);
1244         for (int stage = EShLangVertex; stage < EShLangCount; stage++) {
1245             if (inVarMaps[stage] != nullptr) {
1246                 inOutResolve.setStage(EShLanguage(stage));
1247                 std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), symbolValidater);
1248                 std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), inOutResolve);
1249                 std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), symbolValidater);
1250                 std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), inOutResolve);
1251             }
1252             if (uniformVarMap[stage] != nullptr) {
1253                 uniformResolve.setStage(EShLanguage(stage));
1254                 // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info.
1255                 std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(),
1256                               [&uniformVector](TVarLivePair p) { uniformVector.push_back(p); });
1257             }
1258         }
1259         std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1260             return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1261         });
1262         std::for_each(uniformVector.begin(), uniformVector.end(), symbolValidater);
1263         std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve);
1264         std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1265             return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1266         });
1267         resolver->endResolve(EShLangCount);
1268         for (size_t stage = 0; stage < EShLangCount; stage++) {
1269             if (intermediates[stage] != nullptr) {
1270                 // traverse each stage, set new location to each input/output and unifom symbol, set new binding to
1271                 // ubo, ssbo and opaque symbols
1272                 TVarLiveMap** pUniformVarMap = uniformVarMap;
1273                 std::for_each(uniformVector.begin(), uniformVector.end(), [pUniformVarMap, stage](TVarLivePair p) {
1274                     auto at = pUniformVarMap[stage]->find(p.second.symbol->getName());
1275                     if (at != pUniformVarMap[stage]->end())
1276                         at->second = p.second;
1277                 });
1278                 TVarSetTraverser iter_iomap(*intermediates[stage], *inVarMaps[stage], *outVarMaps[stage],
1279                                             *uniformVarMap[stage]);
1280                 intermediates[stage]->getTreeRoot()->traverse(&iter_iomap);
1281             }
1282         }
1283         return !hadError;
1284     } else {
1285         return false;
1286     }
1287 }
1288 
1289 } // end namespace glslang
1290 
1291 #endif // !GLSLANG_WEB && !GLSLANG_ANGLE
1292