Home | History | Annotate | Download | only in MachineIndependent
      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     {
     80       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
     93         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:
    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 
    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:
    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 
    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;
    208     inline TNotifyUniformAdaptor(EShLanguage s, TIoMapResolver& r)
    209       : stage(s)
    210       , resolver(r)
    211     {
    212     }
    213     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;
    225     inline TNotifyInOutAdaptor(EShLanguage s, TIoMapResolver& r)
    226       : stage(s)
    227       , resolver(r)
    228     {
    229     }
    230     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 {
    240     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 
    249     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 {
    300     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 
    309     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 {
    360     TDefaultIoResolverBase(const TIntermediate &intermediate) :
    361         intermediate(intermediate),
    362         nextUniformLocation(intermediate.getUniformLocationBase()),
    363         nextInputLocation(0),
    364         nextOutputLocation(0)
    365     { }
    366 
    367     int getBaseBinding(TResourceType res, unsigned int set) const {
    368         return selectBaseBinding(intermediate.getShiftBinding(res),
    369                                  intermediate.getShiftBindingForSet(res, set));
    370     }
    371 
    372     const std::vector<std::string>& getResourceSetBinding() const { return intermediate.getResourceSetBinding(); }
    373 
    374     bool doAutoBindingMapping() const { return intermediate.getAutoMapBindings(); }
    375     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 
    381     TSlotSet::iterator findSlot(int set, int slot)
    382     {
    383         return std::lower_bound(slots[set].begin(), slots[set].end(), slot);
    384     }
    385 
    386     bool checkEmpty(int set, int slot)
    387     {
    388         TSlotSet::iterator at = findSlot(set, slot);
    389         return !(at != slots[set].end() && *at == slot);
    390     }
    391 
    392     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 
    407     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 
    426     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     }
    437     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     }
    468     bool validateInOut(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
    469     {
    470         return true;
    471     }
    472     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         // Dont take into account the outer-most array if the stages
    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     }
    509     int resolveInOutComponent(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
    510     {
    511         return -1;
    512     }
    513     int resolveInOutIndex(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
    514     {
    515         return -1;
    516     }
    517 
    518     void notifyBinding(EShLanguage, const char* /*name*/, const TType&, bool /*is_live*/) override {}
    519     void notifyInOut(EShLanguage, const char* /*name*/, const TType&, bool /*is_live*/) override {}
    520     void endNotifications(EShLanguage) override {}
    521     void beginNotifications(EShLanguage) override {}
    522     void beginResolve(EShLanguage) override {}
    523     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.
    535     int selectBaseBinding(int base, int descriptorSetBase) const {
    536         return descriptorSetBase != -1 ? descriptorSetBase : base;
    537     }
    538 
    539     static int getLayoutSet(const glslang::TType& type) {
    540         if (type.getQualifier().hasSet())
    541             return type.getQualifier().layoutSet;
    542         else
    543             return 0;
    544     }
    545 
    546     static bool isSamplerType(const glslang::TType& type) {
    547         return type.getBasicType() == glslang::EbtSampler && type.getSampler().isPureSampler();
    548     }
    549 
    550     static bool isTextureType(const glslang::TType& type) {
    551         return (type.getBasicType() == glslang::EbtSampler &&
    552                 (type.getSampler().isTexture() || type.getSampler().isSubpass()));
    553     }
    554 
    555     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 {
    572     TDefaultIoResolver(const TIntermediate &intermediate) : TDefaultIoResolverBase(intermediate) { }
    573 
    574     bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& /*type*/, bool /*is_live*/) override
    575     {
    576         return true;
    577     }
    578 
    579     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:
    624     static bool isImageType(const glslang::TType& type) {
    625         return type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage();
    626     }
    627 
    628     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 {
    678     TDefaultHlslIoResolver(const TIntermediate &intermediate) : TDefaultIoResolverBase(intermediate) { }
    679 
    680     bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& /*type*/, bool /*is_live*/) override
    681     {
    682         return true;
    683     }
    684 
    685     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:
    723     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:
    728     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.
    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