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())
    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 = "Invalid shader In/Out variable semantic: ";
    335             errorMsg += ent.symbol->getType().getQualifier().semanticName;
    336             infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
    337             error = true;
    338         }
    339     }
    340 
    341     EShLanguage     stage;
    342     TIoMapResolver& resolver;
    343     TInfoSink&      infoSink;
    344     bool&           error;
    345     TIntermediate&  intermediate;
    346 
    347 private:
    348     TResolverInOutAdaptor& operator=(TResolverInOutAdaptor&);
    349 };
    350 
    351 // Base class for shared TIoMapResolver services, used by several derivations.
    352 struct TDefaultIoResolverBase : public glslang::TIoMapResolver
    353 {
    354     int baseSamplerBinding;
    355     int baseTextureBinding;
    356     int baseImageBinding;
    357     int baseUboBinding;
    358     int baseSsboBinding;
    359     int baseUavBinding;
    360     std::vector<std::string> baseResourceSetBinding;
    361     bool doAutoBindingMapping;
    362     bool doAutoLocationMapping;
    363     int nextUniformLocation;
    364     typedef std::vector<int> TSlotSet;
    365     typedef std::unordered_map<int, TSlotSet> TSlotSetMap;
    366     TSlotSetMap slots;
    367 
    368     TSlotSet::iterator findSlot(int set, int slot)
    369     {
    370         return std::lower_bound(slots[set].begin(), slots[set].end(), slot);
    371     }
    372 
    373     bool checkEmpty(int set, int slot)
    374     {
    375         TSlotSet::iterator at = findSlot(set, slot);
    376         return !(at != slots[set].end() && *at == slot);
    377     }
    378 
    379     int reserveSlot(int set, int slot)
    380     {
    381         TSlotSet::iterator at = findSlot(set, slot);
    382 
    383         // tolerate aliasing, by not double-recording aliases
    384         // (policy about appropriateness of the alias is higher up)
    385         if (at == slots[set].end() || *at != slot)
    386             slots[set].insert(at, slot);
    387 
    388         return slot;
    389     }
    390 
    391     int getFreeSlot(int set, int base)
    392     {
    393         TSlotSet::iterator at = findSlot(set, base);
    394         if (at == slots[set].end())
    395             return reserveSlot(set, base);
    396 
    397         // look in locksteps, if they not match, then there is a free slot
    398         for (; at != slots[set].end(); ++at, ++base)
    399             if (*at != base)
    400                 break;
    401         return reserveSlot(set, base);
    402     }
    403 
    404     virtual bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool /*is_live*/) override = 0;
    405 
    406     virtual int resolveBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool is_live) override = 0;
    407 
    408     int resolveSet(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool /*is_live*/) override
    409     {
    410         if (type.getQualifier().hasSet())
    411             return type.getQualifier().layoutSet;
    412 
    413         // If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN)
    414         if (baseResourceSetBinding.size() == 1)
    415             return atoi(baseResourceSetBinding[0].c_str());
    416 
    417         return 0;
    418     }
    419     int resolveUniformLocation(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool /*is_live*/) override
    420     {
    421         // kick out of not doing this
    422         if (!doAutoLocationMapping)
    423             return -1;
    424 
    425         // no locations added if already present, a built-in variable, a block, or an opaque
    426         if (type.getQualifier().hasLocation() || type.isBuiltIn() ||
    427             type.getBasicType() == EbtBlock || type.containsOpaque())
    428             return -1;
    429 
    430         // no locations on blocks of built-in variables
    431         if (type.isStruct()) {
    432             if (type.getStruct()->size() < 1)
    433                 return -1;
    434             if ((*type.getStruct())[0].type->isBuiltIn())
    435                 return -1;
    436         }
    437 
    438         return nextUniformLocation++;
    439     }
    440     bool validateInOut(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
    441     {
    442         return true;
    443     }
    444     int resolveInOutLocation(EShLanguage /*stage*/, const char* /*name*/, const TType& type, bool /*is_live*/) override
    445     {
    446         // kick out of not doing this
    447         if (!doAutoLocationMapping)
    448             return -1;
    449 
    450         // no locations added if already present, or a built-in variable
    451         if (type.getQualifier().hasLocation() || type.isBuiltIn())
    452             return -1;
    453 
    454         // no locations on blocks of built-in variables
    455         if (type.isStruct()) {
    456             if (type.getStruct()->size() < 1)
    457                 return -1;
    458             if ((*type.getStruct())[0].type->isBuiltIn())
    459                 return -1;
    460         }
    461 
    462         // Placeholder.
    463         // TODO: It would be nice to flesh this out using
    464         // intermediate->computeTypeLocationSize(type), or functions that call it like
    465         // intermediate->addUsedLocation()
    466         // These in turn would want the intermediate, which is not available here, but
    467         // is available in many places, and a lot of copying from it could be saved if
    468         // it were just available.
    469         return 0;
    470     }
    471     int resolveInOutComponent(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
    472     {
    473         return -1;
    474     }
    475     int resolveInOutIndex(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
    476     {
    477         return -1;
    478     }
    479 
    480     void notifyBinding(EShLanguage, const char* /*name*/, const TType&, bool /*is_live*/) override {}
    481     void notifyInOut(EShLanguage, const char* /*name*/, const TType&, bool /*is_live*/) override {}
    482     void endNotifications(EShLanguage) override {}
    483     void beginNotifications(EShLanguage) override {}
    484     void beginResolve(EShLanguage) override {}
    485     void endResolve(EShLanguage) override {}
    486 
    487 protected:
    488     static int getLayoutSet(const glslang::TType& type) {
    489         if (type.getQualifier().hasSet())
    490             return type.getQualifier().layoutSet;
    491         else
    492             return 0;
    493     }
    494 
    495     static bool isSamplerType(const glslang::TType& type) {
    496         return type.getBasicType() == glslang::EbtSampler && type.getSampler().isPureSampler();
    497     }
    498 
    499     static bool isTextureType(const glslang::TType& type) {
    500         return type.getBasicType() == glslang::EbtSampler && type.getSampler().isTexture();
    501     }
    502 
    503     static bool isUboType(const glslang::TType& type) {
    504         return type.getQualifier().storage == EvqUniform;
    505     }
    506 };
    507 
    508 /*
    509  * Basic implementation of glslang::TIoMapResolver that replaces the
    510  * previous offset behavior.
    511  * It does the same, uses the offsets for the corresponding uniform
    512  * types. Also respects the EOptionAutoMapBindings flag and binds
    513  * them if needed.
    514  */
    515 /*
    516  * Default resolver
    517  */
    518 struct TDefaultIoResolver : public TDefaultIoResolverBase
    519 {
    520     bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& /*type*/, bool /*is_live*/) override
    521     {
    522         return true;
    523     }
    524 
    525     int resolveBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool is_live) override
    526     {
    527         const int set = getLayoutSet(type);
    528 
    529         if (type.getQualifier().hasBinding()) {
    530             if (isImageType(type))
    531                 return reserveSlot(set, baseImageBinding + type.getQualifier().layoutBinding);
    532 
    533             if (isTextureType(type))
    534                 return reserveSlot(set, baseTextureBinding + type.getQualifier().layoutBinding);
    535 
    536             if (isSsboType(type))
    537                 return reserveSlot(set, baseSsboBinding + type.getQualifier().layoutBinding);
    538 
    539             if (isSamplerType(type))
    540                 return reserveSlot(set, baseSamplerBinding + type.getQualifier().layoutBinding);
    541 
    542             if (isUboType(type))
    543                 return reserveSlot(set, baseUboBinding + type.getQualifier().layoutBinding);
    544         } else if (is_live && doAutoBindingMapping) {
    545             // find free slot, the caller did make sure it passes all vars with binding
    546             // first and now all are passed that do not have a binding and needs one
    547 
    548             if (isImageType(type))
    549                 return getFreeSlot(set, baseImageBinding);
    550 
    551             if (isTextureType(type))
    552                 return getFreeSlot(set, baseTextureBinding);
    553 
    554             if (isSsboType(type))
    555                 return getFreeSlot(set, baseSsboBinding);
    556 
    557             if (isSamplerType(type))
    558                 return getFreeSlot(set, baseSamplerBinding);
    559 
    560             if (isUboType(type))
    561                 return getFreeSlot(set, baseUboBinding);
    562         }
    563 
    564         return -1;
    565     }
    566 
    567 protected:
    568     static bool isImageType(const glslang::TType& type) {
    569         return type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage();
    570     }
    571 
    572     static bool isSsboType(const glslang::TType& type) {
    573         return type.getQualifier().storage == EvqBuffer;
    574     }
    575 };
    576 
    577 /********************************************************************************
    578 The following IO resolver maps types in HLSL register space, as follows:
    579 
    580 t  for shader resource views (SRV)
    581    TEXTURE1D
    582    TEXTURE1DARRAY
    583    TEXTURE2D
    584    TEXTURE2DARRAY
    585    TEXTURE3D
    586    TEXTURECUBE
    587    TEXTURECUBEARRAY
    588    TEXTURE2DMS
    589    TEXTURE2DMSARRAY
    590    STRUCTUREDBUFFER
    591    BYTEADDRESSBUFFER
    592    BUFFER
    593    TBUFFER
    594 
    595 s  for samplers
    596    SAMPLER
    597    SAMPLER1D
    598    SAMPLER2D
    599    SAMPLER3D
    600    SAMPLERCUBE
    601    SAMPLERSTATE
    602    SAMPLERCOMPARISONSTATE
    603 
    604 u  for unordered access views (UAV)
    605    RWBYTEADDRESSBUFFER
    606    RWSTRUCTUREDBUFFER
    607    APPENDSTRUCTUREDBUFFER
    608    CONSUMESTRUCTUREDBUFFER
    609    RWBUFFER
    610    RWTEXTURE1D
    611    RWTEXTURE1DARRAY
    612    RWTEXTURE2D
    613    RWTEXTURE2DARRAY
    614    RWTEXTURE3D
    615 
    616 b  for constant buffer views (CBV)
    617    CBUFFER
    618    CONSTANTBUFFER
    619  ********************************************************************************/
    620 struct TDefaultHlslIoResolver : public TDefaultIoResolverBase
    621 {
    622     bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& /*type*/, bool /*is_live*/) override
    623     {
    624         return true;
    625     }
    626 
    627     int resolveBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool is_live) override
    628     {
    629         const int set = getLayoutSet(type);
    630 
    631         if (type.getQualifier().hasBinding()) {
    632             if (isUavType(type))
    633                 return reserveSlot(set, baseUavBinding + type.getQualifier().layoutBinding);
    634 
    635             if (isSrvType(type))
    636                 return reserveSlot(set, baseTextureBinding + type.getQualifier().layoutBinding);
    637 
    638             if (isSamplerType(type))
    639                 return reserveSlot(set, baseSamplerBinding + type.getQualifier().layoutBinding);
    640 
    641             if (isUboType(type))
    642                 return reserveSlot(set, baseUboBinding + type.getQualifier().layoutBinding);
    643         } else if (is_live && doAutoBindingMapping) {
    644             // find free slot, the caller did make sure it passes all vars with binding
    645             // first and now all are passed that do not have a binding and needs one
    646 
    647             if (isUavType(type))
    648                 return getFreeSlot(set, baseUavBinding);
    649 
    650             if (isSrvType(type))
    651                 return getFreeSlot(set, baseTextureBinding);
    652 
    653             if (isSamplerType(type))
    654                 return getFreeSlot(set, baseSamplerBinding);
    655 
    656             if (isUboType(type))
    657                 return getFreeSlot(set, baseUboBinding);
    658         }
    659 
    660         return -1;
    661     }
    662 
    663 protected:
    664     // Return true if this is a SRV (shader resource view) type:
    665     static bool isSrvType(const glslang::TType& type) {
    666         return isTextureType(type) || type.getQualifier().storage == EvqBuffer;
    667     }
    668 
    669     // Return true if this is a UAV (unordered access view) type:
    670     static bool isUavType(const glslang::TType& type) {
    671         if (type.getQualifier().readonly)
    672             return false;
    673 
    674         return (type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage()) ||
    675             (type.getQualifier().storage == EvqBuffer);
    676     }
    677 };
    678 
    679 
    680 // Map I/O variables to provided offsets, and make bindings for
    681 // unbound but live variables.
    682 //
    683 // Returns false if the input is too malformed to do this.
    684 bool TIoMapper::addStage(EShLanguage stage, TIntermediate &intermediate, TInfoSink &infoSink, TIoMapResolver *resolver)
    685 {
    686     // Trivial return if there is nothing to do.
    687     if (intermediate.getShiftSamplerBinding() == 0 &&
    688         intermediate.getShiftTextureBinding() == 0 &&
    689         intermediate.getShiftImageBinding() == 0 &&
    690         intermediate.getShiftUboBinding() == 0 &&
    691         intermediate.getShiftSsboBinding() == 0 &&
    692         intermediate.getShiftUavBinding() == 0 &&
    693         intermediate.getResourceSetBinding().empty() &&
    694         intermediate.getAutoMapBindings() == false &&
    695         intermediate.getAutoMapLocations() == false &&
    696         resolver == nullptr)
    697         return true;
    698 
    699     if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive())
    700         return false;
    701 
    702     TIntermNode* root = intermediate.getTreeRoot();
    703     if (root == nullptr)
    704         return false;
    705 
    706     // if no resolver is provided, use the default resolver with the given shifts and auto map settings
    707     TDefaultIoResolver defaultResolver;
    708     TDefaultHlslIoResolver defaultHlslResolver;
    709 
    710     if (resolver == nullptr) {
    711         TDefaultIoResolverBase* resolverBase;
    712 
    713         // TODO: use a passed in IO mapper for this
    714         if (intermediate.usingHlslIoMapping())
    715             resolverBase = &defaultHlslResolver;
    716         else
    717             resolverBase = &defaultResolver;
    718 
    719         resolverBase->baseSamplerBinding = intermediate.getShiftSamplerBinding();
    720         resolverBase->baseTextureBinding = intermediate.getShiftTextureBinding();
    721         resolverBase->baseImageBinding = intermediate.getShiftImageBinding();
    722         resolverBase->baseUboBinding = intermediate.getShiftUboBinding();
    723         resolverBase->baseSsboBinding = intermediate.getShiftSsboBinding();
    724         resolverBase->baseUavBinding = intermediate.getShiftUavBinding();
    725         resolverBase->baseResourceSetBinding = intermediate.getResourceSetBinding();
    726         resolverBase->doAutoBindingMapping = intermediate.getAutoMapBindings();
    727         resolverBase->doAutoLocationMapping = intermediate.getAutoMapLocations();
    728         resolverBase->nextUniformLocation = 0;
    729 
    730         resolver = resolverBase;
    731     }
    732 
    733     TVarLiveMap inVarMap, outVarMap, uniformVarMap;
    734     TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap);
    735     TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap);
    736 
    737     root->traverse(&iter_binding_all);
    738     iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
    739 
    740     while (!iter_binding_live.functions.empty()) {
    741         TIntermNode* function = iter_binding_live.functions.back();
    742         iter_binding_live.functions.pop_back();
    743         function->traverse(&iter_binding_live);
    744     }
    745 
    746     // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info.
    747     std::sort(uniformVarMap.begin(), uniformVarMap.end(), TVarEntryInfo::TOrderByPriority());
    748 
    749     bool hadError = false;
    750     TNotifyInOutAdaptor inOutNotify(stage, *resolver);
    751     TNotifyUniformAdaptor uniformNotify(stage, *resolver);
    752     TResolverUniformAdaptor uniformResolve(stage, *resolver, infoSink, hadError, intermediate);
    753     TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError, intermediate);
    754     resolver->beginNotifications(stage);
    755     std::for_each(inVarMap.begin(), inVarMap.end(), inOutNotify);
    756     std::for_each(outVarMap.begin(), outVarMap.end(), inOutNotify);
    757     std::for_each(uniformVarMap.begin(), uniformVarMap.end(), uniformNotify);
    758     resolver->endNotifications(stage);
    759     resolver->beginResolve(stage);
    760     std::for_each(inVarMap.begin(), inVarMap.end(), inOutResolve);
    761     std::for_each(outVarMap.begin(), outVarMap.end(), inOutResolve);
    762     std::for_each(uniformVarMap.begin(), uniformVarMap.end(), uniformResolve);
    763     resolver->endResolve(stage);
    764 
    765     if (!hadError) {
    766         // sort by id again, so we can use lower bound to find entries
    767         std::sort(uniformVarMap.begin(), uniformVarMap.end(), TVarEntryInfo::TOrderById());
    768         TVarSetTraverser iter_iomap(intermediate, inVarMap, outVarMap, uniformVarMap);
    769         root->traverse(&iter_iomap);
    770     }
    771 
    772     return !hadError;
    773 }
    774 
    775 } // end namespace glslang
    776