• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (C) 2016-2017 LunarG, Inc.
3 //
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions
8 // are met:
9 //
10 //    Redistributions of source code must retain the above copyright
11 //    notice, this list of conditions and the following disclaimer.
12 //
13 //    Redistributions in binary form must reproduce the above
14 //    copyright notice, this list of conditions and the following
15 //    disclaimer in the documentation and/or other materials provided
16 //    with the distribution.
17 //
18 //    Neither the name of 3Dlabs Inc. Ltd. nor the names of its
19 //    contributors may be used to endorse or promote products derived
20 //    from this software without specific prior written permission.
21 //
22 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 // POSSIBILITY OF SUCH DAMAGE.
34 //
35 
36 #include "../Include/Common.h"
37 #include "../Include/InfoSink.h"
38 #include "iomapper.h"
39 #include "LiveTraverser.h"
40 #include "localintermediate.h"
41 
42 #include "gl_types.h"
43 
44 #include <unordered_set>
45 #include <unordered_map>
46 
47 //
48 // Map IO bindings.
49 //
50 // High-level algorithm for one stage:
51 //
52 // 1. Traverse all code (live+dead) to find the explicitly provided bindings.
53 //
54 // 2. Traverse (just) the live code to determine which non-provided bindings
55 //    require auto-numbering.  We do not auto-number dead ones.
56 //
57 // 3. Traverse all the code to apply the bindings:
58 //    a. explicitly given bindings are offset according to their type
59 //    b. implicit live bindings are auto-numbered into the holes, using
60 //       any open binding slot.
61 //    c. implicit dead bindings are left un-bound.
62 //
63 
64 
65 namespace glslang {
66 
67 struct TVarEntryInfo
68 {
69     int               id;
70     TIntermSymbol*    symbol;
71     bool              live;
72     int               newBinding;
73     int               newSet;
74     int               newLocation;
75     int               newComponent;
76     int               newIndex;
77 
78     struct TOrderById
79     {
operator ()glslang::TVarEntryInfo::TOrderById80       inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r)
81       {
82         return l.id < r.id;
83       }
84     };
85 
86     struct TOrderByPriority
87     {
88         // ordering:
89         // 1) has both binding and set
90         // 2) has binding but no set
91         // 3) has no binding but set
92         // 4) has no binding and no set
operator ()glslang::TVarEntryInfo::TOrderByPriority93         inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r)
94         {
95             const TQualifier& lq = l.symbol->getQualifier();
96             const TQualifier& rq = r.symbol->getQualifier();
97 
98             // simple rules:
99             // has binding gives 2 points
100             // has set gives 1 point
101             // who has the most points is more important.
102             int lPoints = (lq.hasBinding() ? 2 : 0) + (lq.hasSet() ? 1 : 0);
103             int rPoints = (rq.hasBinding() ? 2 : 0) + (rq.hasSet() ? 1 : 0);
104 
105             if (lPoints == rPoints)
106               return l.id < r.id;
107             return lPoints > rPoints;
108         }
109     };
110 };
111 
112 
113 
114 typedef std::vector<TVarEntryInfo> TVarLiveMap;
115 
116 class TVarGatherTraverser : public TLiveTraverser
117 {
118 public:
TVarGatherTraverser(const TIntermediate & i,bool traverseDeadCode,TVarLiveMap & inList,TVarLiveMap & outList,TVarLiveMap & uniformList)119     TVarGatherTraverser(const TIntermediate& i, bool traverseDeadCode, TVarLiveMap& inList, TVarLiveMap& outList, TVarLiveMap& uniformList)
120       : TLiveTraverser(i, traverseDeadCode, true, true, false)
121       , inputList(inList)
122       , outputList(outList)
123       , uniformList(uniformList)
124     {
125     }
126 
127 
visitSymbol(TIntermSymbol * base)128     virtual void visitSymbol(TIntermSymbol* base)
129     {
130         TVarLiveMap* target = nullptr;
131         if (base->getQualifier().storage == EvqVaryingIn)
132             target = &inputList;
133         else if (base->getQualifier().storage == EvqVaryingOut)
134             target = &outputList;
135         else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().layoutPushConstant)
136             target = &uniformList;
137 
138         if (target) {
139             TVarEntryInfo ent = { base->getId(), base, !traverseAll };
140             TVarLiveMap::iterator at = std::lower_bound(target->begin(), target->end(), ent, TVarEntryInfo::TOrderById());
141             if (at != target->end() && at->id == ent.id)
142               at->live = at->live || !traverseAll; // update live state
143             else
144               target->insert(at, ent);
145         }
146     }
147 
148 private:
149     TVarLiveMap&    inputList;
150     TVarLiveMap&    outputList;
151     TVarLiveMap&    uniformList;
152 };
153 
154 class TVarSetTraverser : public TLiveTraverser
155 {
156 public:
TVarSetTraverser(const TIntermediate & i,const TVarLiveMap & inList,const TVarLiveMap & outList,const TVarLiveMap & uniformList)157     TVarSetTraverser(const TIntermediate& i, const TVarLiveMap& inList, const TVarLiveMap& outList, const TVarLiveMap& uniformList)
158       : TLiveTraverser(i, true, true, true, false)
159       , inputList(inList)
160       , outputList(outList)
161       , uniformList(uniformList)
162     {
163     }
164 
165 
visitSymbol(TIntermSymbol * base)166     virtual void visitSymbol(TIntermSymbol* base)
167     {
168         const TVarLiveMap* source;
169         if (base->getQualifier().storage == EvqVaryingIn)
170             source = &inputList;
171         else if (base->getQualifier().storage == EvqVaryingOut)
172             source = &outputList;
173         else if (base->getQualifier().isUniformOrBuffer())
174             source = &uniformList;
175         else
176             return;
177 
178         TVarEntryInfo ent = { base->getId() };
179         TVarLiveMap::const_iterator at = std::lower_bound(source->begin(), source->end(), ent, TVarEntryInfo::TOrderById());
180         if (at == source->end())
181             return;
182 
183         if (at->id != ent.id)
184             return;
185 
186         if (at->newBinding != -1)
187             base->getWritableType().getQualifier().layoutBinding = at->newBinding;
188         if (at->newSet != -1)
189             base->getWritableType().getQualifier().layoutSet = at->newSet;
190         if (at->newLocation != -1)
191             base->getWritableType().getQualifier().layoutLocation = at->newLocation;
192         if (at->newComponent != -1)
193             base->getWritableType().getQualifier().layoutComponent = at->newComponent;
194         if (at->newIndex != -1)
195             base->getWritableType().getQualifier().layoutIndex = at->newIndex;
196     }
197 
198   private:
199     const TVarLiveMap&    inputList;
200     const TVarLiveMap&    outputList;
201     const TVarLiveMap&    uniformList;
202 };
203 
204 struct TNotifyUniformAdaptor
205 {
206     EShLanguage stage;
207     TIoMapResolver& resolver;
TNotifyUniformAdaptorglslang::TNotifyUniformAdaptor208     inline TNotifyUniformAdaptor(EShLanguage s, TIoMapResolver& r)
209       : stage(s)
210       , resolver(r)
211     {
212     }
operator ()glslang::TNotifyUniformAdaptor213     inline void operator()(TVarEntryInfo& ent)
214     {
215         resolver.notifyBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), ent.live);
216     }
217 private:
218     TNotifyUniformAdaptor& operator=(TNotifyUniformAdaptor&);
219 };
220 
221 struct TNotifyInOutAdaptor
222 {
223     EShLanguage stage;
224     TIoMapResolver& resolver;
TNotifyInOutAdaptorglslang::TNotifyInOutAdaptor225     inline TNotifyInOutAdaptor(EShLanguage s, TIoMapResolver& r)
226       : stage(s)
227       , resolver(r)
228     {
229     }
operator ()glslang::TNotifyInOutAdaptor230     inline void operator()(TVarEntryInfo& ent)
231     {
232         resolver.notifyInOut(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), ent.live);
233     }
234 private:
235     TNotifyInOutAdaptor& operator=(TNotifyInOutAdaptor&);
236 };
237 
238 struct TResolverUniformAdaptor
239 {
TResolverUniformAdaptorglslang::TResolverUniformAdaptor240     TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e, TIntermediate& interm)
241       : stage(s)
242       , resolver(r)
243       , infoSink(i)
244       , error(e)
245       , intermediate(interm)
246     {
247     }
248 
operator ()glslang::TResolverUniformAdaptor249     inline void operator()(TVarEntryInfo& ent)
250     {
251         ent.newLocation = -1;
252         ent.newComponent = -1;
253         ent.newBinding = -1;
254         ent.newSet = -1;
255         ent.newIndex = -1;
256         const bool isValid = resolver.validateBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(),
257                                                              ent.live);
258         if (isValid) {
259             ent.newBinding = resolver.resolveBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(),
260                                                             ent.live);
261             ent.newSet = resolver.resolveSet(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), ent.live);
262             ent.newLocation = resolver.resolveUniformLocation(stage, ent.symbol->getName().c_str(),
263                                                                      ent.symbol->getType(), ent.live);
264 
265             if (ent.newBinding != -1) {
266                 if (ent.newBinding >= int(TQualifier::layoutBindingEnd)) {
267                     TString err = "mapped binding out of range: " + ent.symbol->getName();
268 
269                     infoSink.info.message(EPrefixInternalError, err.c_str());
270                     error = true;
271                 }
272             }
273             if (ent.newSet != -1) {
274                 if (ent.newSet >= int(TQualifier::layoutSetEnd)) {
275                     TString err = "mapped set out of range: " + ent.symbol->getName();
276 
277                     infoSink.info.message(EPrefixInternalError, err.c_str());
278                     error = true;
279                 }
280             }
281         } else {
282             TString errorMsg = "Invalid binding: " + ent.symbol->getName();
283             infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
284             error = true;
285         }
286     }
287 
288     EShLanguage     stage;
289     TIoMapResolver& resolver;
290     TInfoSink&      infoSink;
291     bool&           error;
292     TIntermediate&  intermediate;
293 
294 private:
295     TResolverUniformAdaptor& operator=(TResolverUniformAdaptor&);
296 };
297 
298 struct TResolverInOutAdaptor
299 {
TResolverInOutAdaptorglslang::TResolverInOutAdaptor300     TResolverInOutAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e, TIntermediate& interm)
301       : stage(s)
302       , resolver(r)
303       , infoSink(i)
304       , error(e)
305       , intermediate(interm)
306     {
307     }
308 
operator ()glslang::TResolverInOutAdaptor309     inline void operator()(TVarEntryInfo& ent)
310     {
311         ent.newLocation = -1;
312         ent.newComponent = -1;
313         ent.newBinding = -1;
314         ent.newSet = -1;
315         ent.newIndex = -1;
316         const bool isValid = resolver.validateInOut(stage,
317                                                     ent.symbol->getName().c_str(),
318                                                     ent.symbol->getType(),
319                                                     ent.live);
320         if (isValid) {
321             ent.newLocation = resolver.resolveInOutLocation(stage,
322                                                             ent.symbol->getName().c_str(),
323                                                             ent.symbol->getType(),
324                                                             ent.live);
325             ent.newComponent = resolver.resolveInOutComponent(stage,
326                                                               ent.symbol->getName().c_str(),
327                                                               ent.symbol->getType(),
328                                                               ent.live);
329             ent.newIndex = resolver.resolveInOutIndex(stage,
330                                                       ent.symbol->getName().c_str(),
331                                                       ent.symbol->getType(),
332                                                       ent.live);
333         } else {
334             TString errorMsg;
335             if (ent.symbol->getType().getQualifier().semanticName != nullptr) {
336                 errorMsg = "Invalid shader In/Out variable semantic: ";
337                 errorMsg += ent.symbol->getType().getQualifier().semanticName;
338             } else {
339                 errorMsg = "Invalid shader In/Out variable: ";
340                 errorMsg += ent.symbol->getName();
341             }
342             infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
343             error = true;
344         }
345     }
346 
347     EShLanguage     stage;
348     TIoMapResolver& resolver;
349     TInfoSink&      infoSink;
350     bool&           error;
351     TIntermediate&  intermediate;
352 
353 private:
354     TResolverInOutAdaptor& operator=(TResolverInOutAdaptor&);
355 };
356 
357 // Base class for shared TIoMapResolver services, used by several derivations.
358 struct TDefaultIoResolverBase : public glslang::TIoMapResolver
359 {
TDefaultIoResolverBaseglslang::TDefaultIoResolverBase360     TDefaultIoResolverBase(const TIntermediate &intermediate) :
361         intermediate(intermediate),
362         nextUniformLocation(intermediate.getUniformLocationBase()),
363         nextInputLocation(0),
364         nextOutputLocation(0)
365     { }
366 
getBaseBindingglslang::TDefaultIoResolverBase367     int getBaseBinding(TResourceType res, unsigned int set) const {
368         return selectBaseBinding(intermediate.getShiftBinding(res),
369                                  intermediate.getShiftBindingForSet(res, set));
370     }
371 
getResourceSetBindingglslang::TDefaultIoResolverBase372     const std::vector<std::string>& getResourceSetBinding() const { return intermediate.getResourceSetBinding(); }
373 
doAutoBindingMappingglslang::TDefaultIoResolverBase374     bool doAutoBindingMapping() const { return intermediate.getAutoMapBindings(); }
doAutoLocationMappingglslang::TDefaultIoResolverBase375     bool doAutoLocationMapping() const { return intermediate.getAutoMapLocations(); }
376 
377     typedef std::vector<int> TSlotSet;
378     typedef std::unordered_map<int, TSlotSet> TSlotSetMap;
379     TSlotSetMap slots;
380 
findSlotglslang::TDefaultIoResolverBase381     TSlotSet::iterator findSlot(int set, int slot)
382     {
383         return std::lower_bound(slots[set].begin(), slots[set].end(), slot);
384     }
385 
checkEmptyglslang::TDefaultIoResolverBase386     bool checkEmpty(int set, int slot)
387     {
388         TSlotSet::iterator at = findSlot(set, slot);
389         return !(at != slots[set].end() && *at == slot);
390     }
391 
reserveSlotglslang::TDefaultIoResolverBase392     int reserveSlot(int set, int slot, int size = 1)
393     {
394         TSlotSet::iterator at = findSlot(set, slot);
395 
396         // tolerate aliasing, by not double-recording aliases
397         // (policy about appropriateness of the alias is higher up)
398         for (int i = 0; i < size; i++) {
399                 if (at == slots[set].end() || *at != slot + i)
400                         at = slots[set].insert(at, slot + i);
401                 ++at;
402         }
403 
404         return slot;
405     }
406 
getFreeSlotglslang::TDefaultIoResolverBase407     int getFreeSlot(int set, int base, int size = 1)
408     {
409         TSlotSet::iterator at = findSlot(set, base);
410         if (at == slots[set].end())
411             return reserveSlot(set, base, size);
412 
413         // look for a big enough gap
414         for (; at != slots[set].end(); ++at) {
415             if (*at - base >= size)
416                 break;
417             base = *at + 1;
418         }
419         return reserveSlot(set, base, size);
420     }
421 
422     virtual bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool /*is_live*/) override = 0;
423 
424     virtual int resolveBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool is_live) override = 0;
425 
resolveSetglslang::TDefaultIoResolverBase426     int resolveSet(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool /*is_live*/) override
427     {
428         if (type.getQualifier().hasSet())
429             return type.getQualifier().layoutSet;
430 
431         // If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN)
432         if (getResourceSetBinding().size() == 1)
433             return atoi(getResourceSetBinding()[0].c_str());
434 
435         return 0;
436     }
resolveUniformLocationglslang::TDefaultIoResolverBase437     int resolveUniformLocation(EShLanguage /*stage*/, const char* name, const glslang::TType& type, bool /*is_live*/) override
438     {
439         // kick out of not doing this
440         if (!doAutoLocationMapping())
441             return -1;
442 
443         // no locations added if already present, a built-in variable, a block, or an opaque
444         if (type.getQualifier().hasLocation() || type.isBuiltIn() ||
445             type.getBasicType() == EbtBlock ||
446             type.getBasicType() == EbtAtomicUint ||
447             (type.containsOpaque() && intermediate.getSpv().openGl == 0))
448             return -1;
449 
450         // no locations on blocks of built-in variables
451         if (type.isStruct()) {
452             if (type.getStruct()->size() < 1)
453                 return -1;
454             if ((*type.getStruct())[0].type->isBuiltIn())
455                 return -1;
456         }
457 
458         int location = intermediate.getUniformLocationOverride(name);
459         if (location != -1)
460             return location;
461 
462         location = nextUniformLocation;
463 
464         nextUniformLocation += TIntermediate::computeTypeUniformLocationSize(type);
465 
466         return location;
467     }
validateInOutglslang::TDefaultIoResolverBase468     bool validateInOut(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
469     {
470         return true;
471     }
resolveInOutLocationglslang::TDefaultIoResolverBase472     int resolveInOutLocation(EShLanguage stage, const char* /*name*/, const TType& type, bool /*is_live*/) override
473     {
474         // kick out of not doing this
475         if (!doAutoLocationMapping())
476             return -1;
477 
478         // no locations added if already present, or a built-in variable
479         if (type.getQualifier().hasLocation() || type.isBuiltIn())
480             return -1;
481 
482         // no locations on blocks of built-in variables
483         if (type.isStruct()) {
484             if (type.getStruct()->size() < 1)
485                 return -1;
486             if ((*type.getStruct())[0].type->isBuiltIn())
487                 return -1;
488         }
489 
490         // point to the right input or output location counter
491         int& nextLocation = type.getQualifier().isPipeInput() ? nextInputLocation : nextOutputLocation;
492 
493         // Placeholder. This does not do proper cross-stage lining up, nor
494         // work with mixed location/no-location declarations.
495         int location = nextLocation;
496         int typeLocationSize;
497         // Don’t take into account the outer-most array if the stage’s
498         // interface is automatically an array.
499         if (type.getQualifier().isArrayedIo(stage)) {
500                 TType elementType(type, 0);
501                 typeLocationSize = TIntermediate::computeTypeLocationSize(elementType, stage);
502         } else {
503                 typeLocationSize = TIntermediate::computeTypeLocationSize(type, stage);
504         }
505         nextLocation += typeLocationSize;
506 
507         return location;
508     }
resolveInOutComponentglslang::TDefaultIoResolverBase509     int resolveInOutComponent(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
510     {
511         return -1;
512     }
resolveInOutIndexglslang::TDefaultIoResolverBase513     int resolveInOutIndex(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
514     {
515         return -1;
516     }
517 
notifyBindingglslang::TDefaultIoResolverBase518     void notifyBinding(EShLanguage, const char* /*name*/, const TType&, bool /*is_live*/) override {}
notifyInOutglslang::TDefaultIoResolverBase519     void notifyInOut(EShLanguage, const char* /*name*/, const TType&, bool /*is_live*/) override {}
endNotificationsglslang::TDefaultIoResolverBase520     void endNotifications(EShLanguage) override {}
beginNotificationsglslang::TDefaultIoResolverBase521     void beginNotifications(EShLanguage) override {}
beginResolveglslang::TDefaultIoResolverBase522     void beginResolve(EShLanguage) override {}
endResolveglslang::TDefaultIoResolverBase523     void endResolve(EShLanguage) override {}
524 
525 protected:
526     TDefaultIoResolverBase(TDefaultIoResolverBase&);
527     TDefaultIoResolverBase& operator=(TDefaultIoResolverBase&);
528 
529     const TIntermediate &intermediate;
530     int nextUniformLocation;
531     int nextInputLocation;
532     int nextOutputLocation;
533 
534     // Return descriptor set specific base if there is one, and the generic base otherwise.
selectBaseBindingglslang::TDefaultIoResolverBase535     int selectBaseBinding(int base, int descriptorSetBase) const {
536         return descriptorSetBase != -1 ? descriptorSetBase : base;
537     }
538 
getLayoutSetglslang::TDefaultIoResolverBase539     static int getLayoutSet(const glslang::TType& type) {
540         if (type.getQualifier().hasSet())
541             return type.getQualifier().layoutSet;
542         else
543             return 0;
544     }
545 
isSamplerTypeglslang::TDefaultIoResolverBase546     static bool isSamplerType(const glslang::TType& type) {
547         return type.getBasicType() == glslang::EbtSampler && type.getSampler().isPureSampler();
548     }
549 
isTextureTypeglslang::TDefaultIoResolverBase550     static bool isTextureType(const glslang::TType& type) {
551         return (type.getBasicType() == glslang::EbtSampler &&
552                 (type.getSampler().isTexture() || type.getSampler().isSubpass()));
553     }
554 
isUboTypeglslang::TDefaultIoResolverBase555     static bool isUboType(const glslang::TType& type) {
556         return type.getQualifier().storage == EvqUniform;
557     }
558 };
559 
560 /*
561  * Basic implementation of glslang::TIoMapResolver that replaces the
562  * previous offset behavior.
563  * It does the same, uses the offsets for the corresponding uniform
564  * types. Also respects the EOptionAutoMapBindings flag and binds
565  * them if needed.
566  */
567 /*
568  * Default resolver
569  */
570 struct TDefaultIoResolver : public TDefaultIoResolverBase
571 {
TDefaultIoResolverglslang::TDefaultIoResolver572     TDefaultIoResolver(const TIntermediate &intermediate) : TDefaultIoResolverBase(intermediate) { }
573 
validateBindingglslang::TDefaultIoResolver574     bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& /*type*/, bool /*is_live*/) override
575     {
576         return true;
577     }
578 
resolveBindingglslang::TDefaultIoResolver579     int resolveBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool is_live) override
580     {
581         const int set = getLayoutSet(type);
582         // On OpenGL arrays of opaque types take a seperate binding for each element
583         int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
584 
585         if (type.getQualifier().hasBinding()) {
586             if (isImageType(type))
587                 return reserveSlot(set, getBaseBinding(EResImage, set) + type.getQualifier().layoutBinding, numBindings);
588 
589             if (isTextureType(type))
590                 return reserveSlot(set, getBaseBinding(EResTexture, set) + type.getQualifier().layoutBinding, numBindings);
591 
592             if (isSsboType(type))
593                 return reserveSlot(set, getBaseBinding(EResSsbo, set) + type.getQualifier().layoutBinding, numBindings);
594 
595             if (isSamplerType(type))
596                 return reserveSlot(set, getBaseBinding(EResSampler, set) + type.getQualifier().layoutBinding, numBindings);
597 
598             if (isUboType(type))
599                 return reserveSlot(set, getBaseBinding(EResUbo, set) + type.getQualifier().layoutBinding, numBindings);
600         } else if (is_live && doAutoBindingMapping()) {
601             // find free slot, the caller did make sure it passes all vars with binding
602             // first and now all are passed that do not have a binding and needs one
603 
604             if (isImageType(type))
605                 return getFreeSlot(set, getBaseBinding(EResImage, set), numBindings);
606 
607             if (isTextureType(type))
608                 return getFreeSlot(set, getBaseBinding(EResTexture, set), numBindings);
609 
610             if (isSsboType(type))
611                 return getFreeSlot(set, getBaseBinding(EResSsbo, set), numBindings);
612 
613             if (isSamplerType(type))
614                 return getFreeSlot(set, getBaseBinding(EResSampler, set), numBindings);
615 
616             if (isUboType(type))
617                 return getFreeSlot(set, getBaseBinding(EResUbo, set), numBindings);
618         }
619 
620         return -1;
621     }
622 
623 protected:
isImageTypeglslang::TDefaultIoResolver624     static bool isImageType(const glslang::TType& type) {
625         return type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage();
626     }
627 
isSsboTypeglslang::TDefaultIoResolver628     static bool isSsboType(const glslang::TType& type) {
629         return type.getQualifier().storage == EvqBuffer;
630     }
631 };
632 
633 /********************************************************************************
634 The following IO resolver maps types in HLSL register space, as follows:
635 
636 t - for shader resource views (SRV)
637    TEXTURE1D
638    TEXTURE1DARRAY
639    TEXTURE2D
640    TEXTURE2DARRAY
641    TEXTURE3D
642    TEXTURECUBE
643    TEXTURECUBEARRAY
644    TEXTURE2DMS
645    TEXTURE2DMSARRAY
646    STRUCTUREDBUFFER
647    BYTEADDRESSBUFFER
648    BUFFER
649    TBUFFER
650 
651 s - for samplers
652    SAMPLER
653    SAMPLER1D
654    SAMPLER2D
655    SAMPLER3D
656    SAMPLERCUBE
657    SAMPLERSTATE
658    SAMPLERCOMPARISONSTATE
659 
660 u - for unordered access views (UAV)
661    RWBYTEADDRESSBUFFER
662    RWSTRUCTUREDBUFFER
663    APPENDSTRUCTUREDBUFFER
664    CONSUMESTRUCTUREDBUFFER
665    RWBUFFER
666    RWTEXTURE1D
667    RWTEXTURE1DARRAY
668    RWTEXTURE2D
669    RWTEXTURE2DARRAY
670    RWTEXTURE3D
671 
672 b - for constant buffer views (CBV)
673    CBUFFER
674    CONSTANTBUFFER
675  ********************************************************************************/
676 struct TDefaultHlslIoResolver : public TDefaultIoResolverBase
677 {
TDefaultHlslIoResolverglslang::TDefaultHlslIoResolver678     TDefaultHlslIoResolver(const TIntermediate &intermediate) : TDefaultIoResolverBase(intermediate) { }
679 
validateBindingglslang::TDefaultHlslIoResolver680     bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& /*type*/, bool /*is_live*/) override
681     {
682         return true;
683     }
684 
resolveBindingglslang::TDefaultHlslIoResolver685     int resolveBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool is_live) override
686     {
687         const int set = getLayoutSet(type);
688 
689         if (type.getQualifier().hasBinding()) {
690             if (isUavType(type))
691                 return reserveSlot(set, getBaseBinding(EResUav, set) + type.getQualifier().layoutBinding);
692 
693             if (isSrvType(type))
694                 return reserveSlot(set, getBaseBinding(EResTexture, set) + type.getQualifier().layoutBinding);
695 
696             if (isSamplerType(type))
697                 return reserveSlot(set, getBaseBinding(EResSampler, set) + type.getQualifier().layoutBinding);
698 
699             if (isUboType(type))
700                 return reserveSlot(set, getBaseBinding(EResUbo, set) + type.getQualifier().layoutBinding);
701         } else if (is_live && doAutoBindingMapping()) {
702             // find free slot, the caller did make sure it passes all vars with binding
703             // first and now all are passed that do not have a binding and needs one
704 
705             if (isUavType(type))
706                 return getFreeSlot(set, getBaseBinding(EResUav, set));
707 
708             if (isSrvType(type))
709                 return getFreeSlot(set, getBaseBinding(EResTexture, set));
710 
711             if (isSamplerType(type))
712                 return getFreeSlot(set, getBaseBinding(EResSampler, set));
713 
714             if (isUboType(type))
715                 return getFreeSlot(set, getBaseBinding(EResUbo, set));
716         }
717 
718         return -1;
719     }
720 
721 protected:
722     // Return true if this is a SRV (shader resource view) type:
isSrvTypeglslang::TDefaultHlslIoResolver723     static bool isSrvType(const glslang::TType& type) {
724         return isTextureType(type) || type.getQualifier().storage == EvqBuffer;
725     }
726 
727     // Return true if this is a UAV (unordered access view) type:
isUavTypeglslang::TDefaultHlslIoResolver728     static bool isUavType(const glslang::TType& type) {
729         if (type.getQualifier().readonly)
730             return false;
731 
732         return (type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage()) ||
733             (type.getQualifier().storage == EvqBuffer);
734     }
735 };
736 
737 
738 // Map I/O variables to provided offsets, and make bindings for
739 // unbound but live variables.
740 //
741 // Returns false if the input is too malformed to do this.
addStage(EShLanguage stage,TIntermediate & intermediate,TInfoSink & infoSink,TIoMapResolver * resolver)742 bool TIoMapper::addStage(EShLanguage stage, TIntermediate &intermediate, TInfoSink &infoSink, TIoMapResolver *resolver)
743 {
744     bool somethingToDo = !intermediate.getResourceSetBinding().empty() ||
745         intermediate.getAutoMapBindings() ||
746         intermediate.getAutoMapLocations();
747 
748     for (int res = 0; res < EResCount; ++res) {
749         somethingToDo = somethingToDo ||
750             (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
751             intermediate.hasShiftBindingForSet(TResourceType(res));
752     }
753 
754     if (!somethingToDo && resolver == nullptr)
755         return true;
756 
757     if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive())
758         return false;
759 
760     TIntermNode* root = intermediate.getTreeRoot();
761     if (root == nullptr)
762         return false;
763 
764     // if no resolver is provided, use the default resolver with the given shifts and auto map settings
765     TDefaultIoResolver defaultResolver(intermediate);
766     TDefaultHlslIoResolver defaultHlslResolver(intermediate);
767 
768     if (resolver == nullptr) {
769         // TODO: use a passed in IO mapper for this
770         if (intermediate.usingHlslIoMapping())
771             resolver = &defaultHlslResolver;
772         else
773             resolver = &defaultResolver;
774     }
775 
776     TVarLiveMap inVarMap, outVarMap, uniformVarMap;
777     TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap);
778     TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap);
779 
780     root->traverse(&iter_binding_all);
781     iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
782 
783     while (!iter_binding_live.functions.empty()) {
784         TIntermNode* function = iter_binding_live.functions.back();
785         iter_binding_live.functions.pop_back();
786         function->traverse(&iter_binding_live);
787     }
788 
789     // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info.
790     std::sort(uniformVarMap.begin(), uniformVarMap.end(), TVarEntryInfo::TOrderByPriority());
791 
792     bool hadError = false;
793     TNotifyInOutAdaptor inOutNotify(stage, *resolver);
794     TNotifyUniformAdaptor uniformNotify(stage, *resolver);
795     TResolverUniformAdaptor uniformResolve(stage, *resolver, infoSink, hadError, intermediate);
796     TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError, intermediate);
797     resolver->beginNotifications(stage);
798     std::for_each(inVarMap.begin(), inVarMap.end(), inOutNotify);
799     std::for_each(outVarMap.begin(), outVarMap.end(), inOutNotify);
800     std::for_each(uniformVarMap.begin(), uniformVarMap.end(), uniformNotify);
801     resolver->endNotifications(stage);
802     resolver->beginResolve(stage);
803     std::for_each(inVarMap.begin(), inVarMap.end(), inOutResolve);
804     std::for_each(outVarMap.begin(), outVarMap.end(), inOutResolve);
805     std::for_each(uniformVarMap.begin(), uniformVarMap.end(), uniformResolve);
806     resolver->endResolve(stage);
807 
808     if (!hadError) {
809         // sort by id again, so we can use lower bound to find entries
810         std::sort(uniformVarMap.begin(), uniformVarMap.end(), TVarEntryInfo::TOrderById());
811         TVarSetTraverser iter_iomap(intermediate, inVarMap, outVarMap, uniformVarMap);
812         root->traverse(&iter_iomap);
813     }
814 
815     return !hadError;
816 }
817 
818 } // end namespace glslang
819