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