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