1 // 2 // Copyright (C) 2016 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 #ifndef _IOMAPPER_INCLUDED 37 #define _IOMAPPER_INCLUDED 38 39 #include <cstdint> 40 #include "LiveTraverser.h" 41 #include <unordered_map> 42 #include <unordered_set> 43 // 44 // A reflection database and its interface, consistent with the OpenGL API reflection queries. 45 // 46 47 class TInfoSink; 48 49 namespace glslang { 50 51 class TIntermediate; 52 struct TVarEntryInfo { 53 long long id; 54 TIntermSymbol* symbol; 55 bool live; 56 bool upgradedToPushConstant; 57 int newBinding; 58 int newSet; 59 int newLocation; 60 int newComponent; 61 int newIndex; 62 EShLanguage stage; 63 clearNewAssignmentsTVarEntryInfo64 void clearNewAssignments() { 65 upgradedToPushConstant = false; 66 newBinding = -1; 67 newSet = -1; 68 newLocation = -1; 69 newComponent = -1; 70 newIndex = -1; 71 } 72 73 struct TOrderById { operatorTVarEntryInfo::TOrderById74 inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r) { return l.id < r.id; } 75 }; 76 77 struct TOrderByPriority { 78 // ordering: 79 // 1) has both binding and set 80 // 2) has binding but no set 81 // 3) has no binding but set 82 // 4) has no binding and no set operatorTVarEntryInfo::TOrderByPriority83 inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r) { 84 const TQualifier& lq = l.symbol->getQualifier(); 85 const TQualifier& rq = r.symbol->getQualifier(); 86 87 // simple rules: 88 // has binding gives 2 points 89 // has set gives 1 point 90 // who has the most points is more important. 91 int lPoints = (lq.hasBinding() ? 2 : 0) + (lq.hasSet() ? 1 : 0); 92 int rPoints = (rq.hasBinding() ? 2 : 0) + (rq.hasSet() ? 1 : 0); 93 94 if (lPoints == rPoints) 95 return l.id < r.id; 96 return lPoints > rPoints; 97 } 98 }; 99 100 struct TOrderByPriorityAndLive { 101 // ordering: 102 // 1) do live variables first 103 // 2) has both binding and set 104 // 3) has binding but no set 105 // 4) has no binding but set 106 // 5) has no binding and no set operatorTVarEntryInfo::TOrderByPriorityAndLive107 inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r) { 108 109 const TQualifier& lq = l.symbol->getQualifier(); 110 const TQualifier& rq = r.symbol->getQualifier(); 111 112 // simple rules: 113 // has binding gives 2 points 114 // has set gives 1 point 115 // who has the most points is more important. 116 int lPoints = (lq.hasBinding() ? 2 : 0) + (lq.hasSet() ? 1 : 0); 117 int rPoints = (rq.hasBinding() ? 2 : 0) + (rq.hasSet() ? 1 : 0); 118 119 if (l.live != r.live) 120 return l.live > r.live; 121 122 if (lPoints != rPoints) 123 return lPoints > rPoints; 124 125 return l.id < r.id; 126 } 127 }; 128 }; 129 130 // Base class for shared TIoMapResolver services, used by several derivations. 131 struct TDefaultIoResolverBase : public glslang::TIoMapResolver { 132 public: 133 TDefaultIoResolverBase(const TIntermediate& intermediate); 134 typedef std::vector<int> TSlotSet; 135 typedef std::unordered_map<int, TSlotSet> TSlotSetMap; 136 137 // grow the reflection stage by stage notifyBindingTDefaultIoResolverBase138 void notifyBinding(EShLanguage, TVarEntryInfo& /*ent*/) override {} notifyInOutTDefaultIoResolverBase139 void notifyInOut(EShLanguage, TVarEntryInfo& /*ent*/) override {} beginNotificationsTDefaultIoResolverBase140 void beginNotifications(EShLanguage) override {} endNotificationsTDefaultIoResolverBase141 void endNotifications(EShLanguage) override {} beginResolveTDefaultIoResolverBase142 void beginResolve(EShLanguage) override {} endResolveTDefaultIoResolverBase143 void endResolve(EShLanguage) override {} beginCollectTDefaultIoResolverBase144 void beginCollect(EShLanguage) override {} endCollectTDefaultIoResolverBase145 void endCollect(EShLanguage) override {} reserverResourceSlotTDefaultIoResolverBase146 void reserverResourceSlot(TVarEntryInfo& /*ent*/, TInfoSink& /*infoSink*/) override {} reserverStorageSlotTDefaultIoResolverBase147 void reserverStorageSlot(TVarEntryInfo& /*ent*/, TInfoSink& /*infoSink*/) override {} 148 int getBaseBinding(EShLanguage stage, TResourceType res, unsigned int set) const; 149 const std::vector<std::string>& getResourceSetBinding(EShLanguage stage) const; 150 virtual TResourceType getResourceType(const glslang::TType& type) = 0; 151 bool doAutoBindingMapping() const; 152 bool doAutoLocationMapping() const; 153 TSlotSet::iterator findSlot(int set, int slot); 154 bool checkEmpty(int set, int slot); validateInOutTDefaultIoResolverBase155 bool validateInOut(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; } 156 int reserveSlot(int set, int slot, int size = 1); 157 int getFreeSlot(int set, int base, int size = 1); 158 int resolveSet(EShLanguage /*stage*/, TVarEntryInfo& ent) override; 159 int resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) override; 160 int resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) override; 161 int resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) override; 162 int resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) override; addStageTDefaultIoResolverBase163 void addStage(EShLanguage stage, TIntermediate& stageIntermediate) override { 164 if (stage < EShLangCount) { 165 stageMask[stage] = true; 166 stageIntermediates[stage] = &stageIntermediate; 167 } 168 } 169 uint32_t computeTypeLocationSize(const TType& type, EShLanguage stage); 170 171 TSlotSetMap slots; 172 bool hasError = false; 173 174 protected: 175 TDefaultIoResolverBase(TDefaultIoResolverBase&); 176 TDefaultIoResolverBase& operator=(TDefaultIoResolverBase&); 177 const TIntermediate& referenceIntermediate; 178 int nextUniformLocation; 179 int nextInputLocation; 180 int nextOutputLocation; 181 bool stageMask[EShLangCount + 1]; 182 const TIntermediate* stageIntermediates[EShLangCount]; 183 184 // Return descriptor set specific base if there is one, and the generic base otherwise. selectBaseBindingTDefaultIoResolverBase185 int selectBaseBinding(int base, int descriptorSetBase) const { 186 return descriptorSetBase != -1 ? descriptorSetBase : base; 187 } 188 getLayoutSetTDefaultIoResolverBase189 static int getLayoutSet(const glslang::TType& type) { 190 if (type.getQualifier().hasSet()) 191 return type.getQualifier().layoutSet; 192 else 193 return 0; 194 } 195 isSamplerTypeTDefaultIoResolverBase196 static bool isSamplerType(const glslang::TType& type) { 197 return type.getBasicType() == glslang::EbtSampler && type.getSampler().isPureSampler(); 198 } 199 isTextureTypeTDefaultIoResolverBase200 static bool isTextureType(const glslang::TType& type) { 201 return (type.getBasicType() == glslang::EbtSampler && 202 (type.getSampler().isTexture() || type.getSampler().isSubpass())); 203 } 204 isUboTypeTDefaultIoResolverBase205 static bool isUboType(const glslang::TType& type) { 206 return type.getQualifier().storage == EvqUniform; 207 } 208 isImageTypeTDefaultIoResolverBase209 static bool isImageType(const glslang::TType& type) { 210 return type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage(); 211 } 212 isSsboTypeTDefaultIoResolverBase213 static bool isSsboType(const glslang::TType& type) { 214 return type.getQualifier().storage == EvqBuffer; 215 } 216 217 // Return true if this is a SRV (shader resource view) type: isSrvTypeTDefaultIoResolverBase218 static bool isSrvType(const glslang::TType& type) { 219 return isTextureType(type) || type.getQualifier().storage == EvqBuffer; 220 } 221 222 // Return true if this is a UAV (unordered access view) type: isUavTypeTDefaultIoResolverBase223 static bool isUavType(const glslang::TType& type) { 224 if (type.getQualifier().isReadOnly()) 225 return false; 226 return (type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage()) || 227 (type.getQualifier().storage == EvqBuffer); 228 } 229 }; 230 231 // Default I/O resolver for OpenGL 232 struct TDefaultGlslIoResolver : public TDefaultIoResolverBase { 233 public: 234 typedef std::map<TString, int> TVarSlotMap; // <resourceName, location/binding> 235 typedef std::map<int, TVarSlotMap> TSlotMap; // <resourceKey, TVarSlotMap> 236 TDefaultGlslIoResolver(const TIntermediate& intermediate); validateBindingTDefaultGlslIoResolver237 bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; } 238 TResourceType getResourceType(const glslang::TType& type) override; 239 int resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) override; 240 int resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) override; 241 int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override; 242 void beginResolve(EShLanguage /*stage*/) override; 243 void endResolve(EShLanguage stage) override; 244 void beginCollect(EShLanguage) override; 245 void endCollect(EShLanguage) override; 246 void reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) override; 247 void reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) override; 248 // in/out symbol and uniform symbol are stored in the same resourceSlotMap, the storage key is used to identify each type of symbol. 249 // We use stage and storage qualifier to construct a storage key. it can help us identify the same storage resource used in different stage. 250 // if a resource is a program resource and we don't need know it usage stage, we can use same stage to build storage key. 251 // Note: both stage and type must less then 0xffff. buildStorageKeyTDefaultGlslIoResolver252 int buildStorageKey(EShLanguage stage, TStorageQualifier type) { 253 assert(static_cast<uint32_t>(stage) <= 0x0000ffff && static_cast<uint32_t>(type) <= 0x0000ffff); 254 return (stage << 16) | type; 255 } 256 257 protected: 258 // Use for mark pre stage, to get more interface symbol information. 259 EShLanguage preStage; 260 // Use for mark current shader stage for resolver 261 EShLanguage currentStage; 262 // Slot map for storage resource(location of uniform and interface symbol) It's a program share slot 263 TSlotMap resourceSlotMap; 264 // Slot map for other resource(image, ubo, ssbo), It's a program share slot. 265 TSlotMap storageSlotMap; 266 }; 267 268 typedef std::map<TString, TVarEntryInfo> TVarLiveMap; 269 270 // override function "operator=", if a vector<const _Kty, _Ty> being sort, 271 // when use vc++, the sort function will call : 272 // pair& operator=(const pair<_Other1, _Other2>& _Right) 273 // { 274 // first = _Right.first; 275 // second = _Right.second; 276 // return (*this); 277 // } 278 // that will make a const type handing on left. 279 // override this function can avoid a compiler error. 280 // In the future, if the vc++ compiler can handle such a situation, 281 // this part of the code will be removed. 282 struct TVarLivePair : std::pair<const TString, TVarEntryInfo> { TVarLivePairTVarLivePair283 TVarLivePair(const std::pair<const TString, TVarEntryInfo>& _Right) : pair(_Right.first, _Right.second) {} 284 TVarLivePair& operator=(const TVarLivePair& _Right) { 285 const_cast<TString&>(first) = _Right.first; 286 second = _Right.second; 287 return (*this); 288 } TVarLivePairTVarLivePair289 TVarLivePair(const TVarLivePair& src) : pair(src) { } 290 }; 291 typedef std::vector<TVarLivePair> TVarLiveVector; 292 293 // I/O mapper 294 class TIoMapper { 295 public: TIoMapper()296 TIoMapper() {} ~TIoMapper()297 virtual ~TIoMapper() {} 298 // grow the reflection stage by stage 299 bool virtual addStage(EShLanguage, TIntermediate&, TInfoSink&, TIoMapResolver*); doMap(TIoMapResolver *,TInfoSink &)300 bool virtual doMap(TIoMapResolver*, TInfoSink&) { return true; } 301 }; 302 303 // I/O mapper for GLSL 304 class TGlslIoMapper : public TIoMapper { 305 public: TGlslIoMapper()306 TGlslIoMapper() { 307 memset(inVarMaps, 0, sizeof(TVarLiveMap*) * (EShLangCount + 1)); 308 memset(outVarMaps, 0, sizeof(TVarLiveMap*) * (EShLangCount + 1)); 309 memset(uniformVarMap, 0, sizeof(TVarLiveMap*) * (EShLangCount + 1)); 310 memset(intermediates, 0, sizeof(TIntermediate*) * (EShLangCount + 1)); 311 profile = ENoProfile; 312 version = 0; 313 autoPushConstantMaxSize = 128; 314 autoPushConstantBlockPacking = ElpStd430; 315 } ~TGlslIoMapper()316 virtual ~TGlslIoMapper() { 317 for (size_t stage = 0; stage < EShLangCount; stage++) { 318 if (inVarMaps[stage] != nullptr) { 319 delete inVarMaps[stage]; 320 inVarMaps[stage] = nullptr; 321 } 322 if (outVarMaps[stage] != nullptr) { 323 delete outVarMaps[stage]; 324 outVarMaps[stage] = nullptr; 325 } 326 if (uniformVarMap[stage] != nullptr) { 327 delete uniformVarMap[stage]; 328 uniformVarMap[stage] = nullptr; 329 } 330 if (intermediates[stage] != nullptr) 331 intermediates[stage] = nullptr; 332 } 333 } 334 // If set, the uniform block with the given name will be changed to be backed by 335 // push_constant if it's size is <= maxSize setAutoPushConstantBlock(const char * name,unsigned int maxSize,TLayoutPacking packing)336 void setAutoPushConstantBlock(const char* name, unsigned int maxSize, TLayoutPacking packing) { 337 autoPushConstantBlockName = name; 338 autoPushConstantMaxSize = maxSize; 339 autoPushConstantBlockPacking = packing; 340 } 341 // grow the reflection stage by stage 342 bool addStage(EShLanguage, TIntermediate&, TInfoSink&, TIoMapResolver*) override; 343 bool doMap(TIoMapResolver*, TInfoSink&) override; 344 TVarLiveMap *inVarMaps[EShLangCount], *outVarMaps[EShLangCount], 345 *uniformVarMap[EShLangCount]; 346 TIntermediate* intermediates[EShLangCount]; 347 bool hadError = false; 348 EProfile profile; 349 int version; 350 351 private: 352 TString autoPushConstantBlockName; 353 unsigned int autoPushConstantMaxSize; 354 TLayoutPacking autoPushConstantBlockPacking; 355 }; 356 357 } // end namespace glslang 358 359 #endif // _IOMAPPER_INCLUDED 360