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