2 // Copyright (C) 2016-2017 LunarG, Inc.
4 // All rights reserved.
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions
10 // Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
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.
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.
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.
36 #if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
38 #include "../Include/Common.h"
39 #include "../Include/InfoSink.h"
40 #include "../Include/Types.h"
44 #include "SymbolTable.h"
49 // High-level algorithm for one stage:
51 // 1. Traverse all code (live+dead) to find the explicitly provided bindings.
53 // 2. Traverse (just) the live code to determine which non-provided bindings
54 // require auto-numbering. We do not auto-number dead ones.
56 // 3. Traverse all the code to apply the bindings:
57 // a. explicitly given bindings are offset according to their type
58 // b. implicit live bindings are auto-numbered into the holes, using
59 // any open binding slot.
60 // c. implicit dead bindings are left un-bound.
65 class TVarGatherTraverser : public TLiveTraverser {
67 TVarGatherTraverser(const TIntermediate& i, bool traverseDeadCode, TVarLiveMap& inList, TVarLiveMap& outList, TVarLiveMap& uniformList)
68 : TLiveTraverser(i, traverseDeadCode, true, true, false)
71 , uniformList(uniformList)
75 virtual void visitSymbol(TIntermSymbol* base)
77 TVarLiveMap* target = nullptr;
78 if (base->getQualifier().storage == EvqVaryingIn)
80 else if (base->getQualifier().storage == EvqVaryingOut)
82 else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().isPushConstant())
83 target = &uniformList;
84 // If a global is being visited, then we should also traverse it incase it's evaluation
85 // ends up visiting inputs we want to tag as live
86 else if (base->getQualifier().storage == EvqGlobal)
87 addGlobalReference(base->getAccessName());
90 TVarEntryInfo ent = {base->getId(), base, ! traverseAll};
91 ent.stage = intermediate.getStage();
92 TVarLiveMap::iterator at = target->find(
93 ent.symbol->getAccessName()); // std::lower_bound(target->begin(), target->end(), ent, TVarEntryInfo::TOrderById());
94 if (at != target->end() && at->second.id == ent.id)
95 at->second.live = at->second.live || ! traverseAll; // update live state
97 (*target)[ent.symbol->getAccessName()] = ent;
102 TVarLiveMap& inputList;
103 TVarLiveMap& outputList;
104 TVarLiveMap& uniformList;
107 class TVarSetTraverser : public TLiveTraverser
110 TVarSetTraverser(const TIntermediate& i, const TVarLiveMap& inList, const TVarLiveMap& outList, const TVarLiveMap& uniformList)
111 : TLiveTraverser(i, true, true, true, false)
113 , outputList(outList)
114 , uniformList(uniformList)
118 virtual void visitSymbol(TIntermSymbol* base) {
119 const TVarLiveMap* source;
120 if (base->getQualifier().storage == EvqVaryingIn)
122 else if (base->getQualifier().storage == EvqVaryingOut)
123 source = &outputList;
124 else if (base->getQualifier().isUniformOrBuffer())
125 source = &uniformList;
129 TVarEntryInfo ent = { base->getId() };
130 // Fix a defect, when block has no instance name, we need to find its block name
131 TVarLiveMap::const_iterator at = source->find(base->getAccessName());
132 if (at == source->end())
135 if (at->second.id != ent.id)
138 if (at->second.newBinding != -1)
139 base->getWritableType().getQualifier().layoutBinding = at->second.newBinding;
140 if (at->second.newSet != -1)
141 base->getWritableType().getQualifier().layoutSet = at->second.newSet;
142 if (at->second.newLocation != -1)
143 base->getWritableType().getQualifier().layoutLocation = at->second.newLocation;
144 if (at->second.newComponent != -1)
145 base->getWritableType().getQualifier().layoutComponent = at->second.newComponent;
146 if (at->second.newIndex != -1)
147 base->getWritableType().getQualifier().layoutIndex = at->second.newIndex;
151 const TVarLiveMap& inputList;
152 const TVarLiveMap& outputList;
153 const TVarLiveMap& uniformList;
156 struct TNotifyUniformAdaptor
159 TIoMapResolver& resolver;
160 inline TNotifyUniformAdaptor(EShLanguage s, TIoMapResolver& r)
166 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
168 resolver.notifyBinding(stage, entKey.second);
172 TNotifyUniformAdaptor& operator=(TNotifyUniformAdaptor&) = delete;
175 struct TNotifyInOutAdaptor
178 TIoMapResolver& resolver;
179 inline TNotifyInOutAdaptor(EShLanguage s, TIoMapResolver& r)
185 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
187 resolver.notifyInOut(entKey.second.stage, entKey.second);
191 TNotifyInOutAdaptor& operator=(TNotifyInOutAdaptor&) = delete;
194 struct TResolverUniformAdaptor {
195 TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TVarLiveMap* uniform[EShLangCount], TInfoSink& i, bool& e)
201 memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*)));
204 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
205 TVarEntryInfo& ent = entKey.second;
206 ent.newLocation = -1;
207 ent.newComponent = -1;
211 const bool isValid = resolver.validateBinding(stage, ent);
213 resolver.resolveSet(ent.stage, ent);
214 resolver.resolveBinding(ent.stage, ent);
215 resolver.resolveUniformLocation(ent.stage, ent);
217 if (ent.newBinding != -1) {
218 if (ent.newBinding >= int(TQualifier::layoutBindingEnd)) {
219 TString err = "mapped binding out of range: " + entKey.first;
221 infoSink.info.message(EPrefixInternalError, err.c_str());
225 if (ent.symbol->getQualifier().hasBinding()) {
226 for (uint32_t idx = EShLangVertex; idx < EShLangCount; ++idx) {
227 if (idx == ent.stage || uniformVarMap[idx] == nullptr)
229 auto entKey2 = uniformVarMap[idx]->find(entKey.first);
230 if (entKey2 != uniformVarMap[idx]->end()) {
231 entKey2->second.newBinding = ent.newBinding;
236 if (ent.newSet != -1) {
237 if (ent.newSet >= int(TQualifier::layoutSetEnd)) {
238 TString err = "mapped set out of range: " + entKey.first;
240 infoSink.info.message(EPrefixInternalError, err.c_str());
243 if (ent.symbol->getQualifier().hasSet()) {
244 for (uint32_t idx = EShLangVertex; idx < EShLangCount; ++idx) {
245 if ((idx == stage) || (uniformVarMap[idx] == nullptr))
247 auto entKey2 = uniformVarMap[idx]->find(entKey.first);
248 if (entKey2 != uniformVarMap[idx]->end()) {
249 entKey2->second.newSet = ent.newSet;
255 TString errorMsg = "Invalid binding: " + entKey.first;
256 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
261 inline void setStage(EShLanguage s) { stage = s; }
264 TIoMapResolver& resolver;
267 TVarLiveMap* uniformVarMap[EShLangCount];
269 TResolverUniformAdaptor& operator=(TResolverUniformAdaptor&) = delete;
272 struct TResolverInOutAdaptor {
273 TResolverInOutAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e)
281 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
283 TVarEntryInfo& ent = entKey.second;
284 ent.newLocation = -1;
285 ent.newComponent = -1;
289 const bool isValid = resolver.validateInOut(ent.stage, ent);
291 resolver.resolveInOutLocation(stage, ent);
292 resolver.resolveInOutComponent(stage, ent);
293 resolver.resolveInOutIndex(stage, ent);
296 if (ent.symbol->getType().getQualifier().semanticName != nullptr) {
297 errorMsg = "Invalid shader In/Out variable semantic: ";
298 errorMsg += ent.symbol->getType().getQualifier().semanticName;
300 errorMsg = "Invalid shader In/Out variable: ";
301 errorMsg += ent.symbol->getName();
303 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
308 inline void setStage(EShLanguage s) { stage = s; }
311 TIoMapResolver& resolver;
316 TResolverInOutAdaptor& operator=(TResolverInOutAdaptor&) = delete;
319 // The class is used for reserving explicit uniform locations and ubo/ssbo/opaque bindings
320 // xxTODO: maybe this logic should be moved into the resolver's "validateInOut" and "validateUniform"
322 struct TSymbolValidater
324 TSymbolValidater(TIoMapResolver& r, TInfoSink& i, TVarLiveMap* in[EShLangCount], TVarLiveMap* out[EShLangCount],
325 TVarLiveMap* uniform[EShLangCount], bool& hadError, EProfile profile, int version)
332 memcpy(inVarMaps, in, EShLangCount * (sizeof(TVarLiveMap*)));
333 memcpy(outVarMaps, out, EShLangCount * (sizeof(TVarLiveMap*)));
334 memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*)));
336 std::map<TString, TString> anonymousMemberMap;
337 std::vector<TRange> usedUniformLocation;
338 std::vector<TString> usedUniformName;
339 usedUniformLocation.clear();
340 usedUniformName.clear();
341 for (int i = 0; i < EShLangCount; i++) {
342 if (uniformVarMap[i]) {
343 for (auto uniformVar : *uniformVarMap[i])
345 TIntermSymbol* pSymbol = uniformVar.second.symbol;
346 TQualifier qualifier = uniformVar.second.symbol->getQualifier();
347 TString symbolName = pSymbol->getAccessName();
349 // All the uniform needs multi-stage location check (block/default)
350 int uniformLocation = qualifier.layoutLocation;
352 if (uniformLocation != TQualifier::layoutLocationEnd) {
353 // Total size of current uniform, could be block, struct or other types.
354 int size = TIntermediate::computeTypeUniformLocationSize(pSymbol->getType());
356 TRange locationRange(uniformLocation, uniformLocation + size - 1);
358 // Combine location and component ranges
359 int overlapLocation = -1;
360 bool diffLocation = false;
362 // Check for collisions, except for vertex inputs on desktop targeting OpenGL
363 overlapLocation = checkLocationOverlap(locationRange, usedUniformLocation, symbolName, usedUniformName, diffLocation);
365 // Overlap locations of uniforms, regardless of components (multi stages)
366 if (overlapLocation == -1) {
367 usedUniformLocation.push_back(locationRange);
368 usedUniformName.push_back(symbolName);
370 else if (overlapLocation >= 0) {
371 if (diffLocation == true) {
372 TString err = ("Uniform location should be equal for same uniforms: " +std::to_string(overlapLocation)).c_str();
373 infoSink.info.message(EPrefixInternalError, err.c_str());
378 TString err = ("Uniform location overlaps across stages: " + std::to_string(overlapLocation)).c_str();
379 infoSink.info.message(EPrefixInternalError, err.c_str());
386 if ((uniformVar.second.symbol->getBasicType() == EbtBlock) &&
387 IsAnonymous(uniformVar.second.symbol->getName()))
389 auto blockType = uniformVar.second.symbol->getType().getStruct();
390 for (size_t memberIdx = 0; memberIdx < blockType->size(); ++memberIdx) {
391 auto memberName = (*blockType)[memberIdx].type->getFieldName();
392 if (anonymousMemberMap.find(memberName) != anonymousMemberMap.end())
394 if (anonymousMemberMap[memberName] != uniformVar.second.symbol->getType().getTypeName())
396 TString err = "Invalid block member name: " + memberName;
397 infoSink.info.message(EPrefixInternalError, err.c_str());
404 anonymousMemberMap[memberName] = uniformVar.second.symbol->getType().getTypeName();
415 // In case we need to new an intermediate, which costs too much
416 int checkLocationOverlap(const TRange& locationRange, std::vector<TRange>& usedUniformLocation, const TString symbolName, std::vector<TString>& usedUniformName, bool& diffLocation)
418 for (size_t r = 0; r < usedUniformLocation.size(); ++r) {
419 if (usedUniformName[r] == symbolName) {
421 return (usedUniformLocation[r].start == locationRange.start &&
422 usedUniformLocation[r].last == locationRange.last)
423 ? -2 : std::max(locationRange.start, usedUniformLocation[r].start);
425 if (locationRange.overlap(usedUniformLocation[r])) {
426 // there is a collision; pick one
427 return std::max(locationRange.start, usedUniformLocation[r].start);
431 return -1; // no collision
434 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
435 TVarEntryInfo& ent1 = entKey.second;
436 TIntermSymbol* base = ent1.symbol;
437 const TType& type = ent1.symbol->getType();
438 const TString& name = entKey.first;
439 TString mangleName1, mangleName2;
440 EShLanguage stage = ent1.stage;
441 EShLanguage preStage, currentStage, nextStage;
443 preStage = EShLangCount;
444 for (int i = stage - 1; i >= 0; i--) {
445 if (inVarMaps[i] != nullptr) {
446 preStage = static_cast<EShLanguage>(i);
450 currentStage = stage;
451 nextStage = EShLangCount;
452 for (int i = stage + 1; i < EShLangCount; i++) {
453 if (inVarMaps[i] != nullptr) {
454 nextStage = static_cast<EShLanguage>(i);
459 if (type.getQualifier().isArrayedIo(stage)) {
460 TType subType(type, 0);
461 subType.appendMangledName(mangleName1);
463 type.appendMangledName(mangleName1);
467 // basic checking that symbols match
468 // more extensive checking in the link stage
469 if (base->getQualifier().storage == EvqVaryingIn) {
470 // validate stage in;
471 if (preStage == EShLangCount)
473 if (TSymbolTable::isBuiltInSymbol(base->getId()))
475 if (outVarMaps[preStage] != nullptr) {
476 auto ent2 = outVarMaps[preStage]->find(name);
477 uint32_t location = base->getType().getQualifier().layoutLocation;
478 if (ent2 == outVarMaps[preStage]->end() &&
479 location != glslang::TQualifier::layoutLocationEnd) {
480 for (auto var = outVarMaps[preStage]->begin(); var != ent2; var++) {
481 if (var->second.symbol->getType().getQualifier().layoutLocation == location) {
487 if (ent2 != outVarMaps[preStage]->end()) {
488 auto& type1 = base->getType();
489 auto& type2 = ent2->second.symbol->getType();
490 hadError = hadError || typeCheck(&type1, &type2, name.c_str(), false);
491 if (ent2->second.symbol->getType().getQualifier().isArrayedIo(preStage)) {
492 TType subType(ent2->second.symbol->getType(), 0);
493 subType.appendMangledName(mangleName2);
495 ent2->second.symbol->getType().appendMangledName(mangleName2);
498 if (mangleName1 == mangleName2) {
499 // For ES 3.0 only, other versions have no such restrictions
500 // According to ES 3.0 spec: The type and presence of the interpolation qualifiers and
501 // storage qualifiers of variables with the same name declared in all linked shaders must
502 // match, otherwise the link command will fail.
503 if (profile == EEsProfile && version == 300) {
504 // Don't need to check smooth qualifier, as it uses the default interpolation mode
505 if (ent1.stage == EShLangFragment && type1.isBuiltIn() == false) {
506 if (type1.getQualifier().flat != type2.getQualifier().flat ||
507 type1.getQualifier().nopersp != type2.getQualifier().nopersp) {
508 TString err = "Interpolation qualifier mismatch : " + entKey.first;
509 infoSink.info.message(EPrefixInternalError, err.c_str());
517 TString err = "Invalid In/Out variable type : " + entKey.first;
518 infoSink.info.message(EPrefixInternalError, err.c_str());
522 else if (!base->getType().isBuiltIn()) {
523 // According to spec: A link error is generated if any statically referenced input variable
524 // or block does not have a matching output
525 if (profile == EEsProfile && ent1.live) {
527 TString errorStr = name + ": not been declare as a output variable in pre shader stage.";
528 infoSink.info.message(EPrefixError, errorStr.c_str());
533 } else if (base->getQualifier().storage == EvqVaryingOut) {
534 // validate stage out;
535 if (nextStage == EShLangCount)
537 if (TSymbolTable::isBuiltInSymbol(base->getId()))
539 if (inVarMaps[nextStage] != nullptr) {
540 auto ent2 = inVarMaps[nextStage]->find(name);
541 if (ent2 != inVarMaps[nextStage]->end()) {
542 if (ent2->second.symbol->getType().getQualifier().isArrayedIo(nextStage)) {
543 TType subType(ent2->second.symbol->getType(), 0);
544 subType.appendMangledName(mangleName2);
546 ent2->second.symbol->getType().appendMangledName(mangleName2);
548 if (mangleName1 == mangleName2)
551 TString err = "Invalid In/Out variable type : " + entKey.first;
552 infoSink.info.message(EPrefixInternalError, err.c_str());
558 } else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().isPushConstant()) {
559 // validate uniform type;
560 for (int i = 0; i < EShLangCount; i++) {
561 if (i != currentStage && outVarMaps[i] != nullptr) {
562 auto ent2 = uniformVarMap[i]->find(name);
563 if (ent2 != uniformVarMap[i]->end()) {
564 ent2->second.symbol->getType().appendMangledName(mangleName2);
565 if (mangleName1 != mangleName2) {
566 ent2->second.symbol->getType().sameElementType(type);
567 TString err = "Invalid Uniform variable type : " + entKey.first;
568 infoSink.info.message(EPrefixInternalError, err.c_str());
573 // validate instance name of blocks
574 if (hadError == false &&
575 base->getType().getBasicType() == EbtBlock &&
576 IsAnonymous(base->getName()) != IsAnonymous(ent2->second.symbol->getName())) {
577 TString err = "Matched uniform block names must also either all be lacking "
578 "an instance name or all having an instance name: " + entKey.first;
579 infoSink.info.message(EPrefixInternalError, err.c_str());
583 // validate uniform block member qualifier and member names
584 auto& type1 = base->getType();
585 auto& type2 = ent2->second.symbol->getType();
586 if (hadError == false && base->getType().getBasicType() == EbtBlock) {
587 hadError = hadError || typeCheck(&type1, &type2, name.c_str(), true);
590 hadError = hadError || typeCheck(&type1, &type2, name.c_str(), false);
593 else if (base->getBasicType() == EbtBlock)
595 if (IsAnonymous(base->getName()))
597 // The name of anonymous block member can't same with default uniform variable.
598 auto blockType1 = base->getType().getStruct();
599 for (size_t memberIdx = 0; memberIdx < blockType1->size(); ++memberIdx) {
600 auto memberName = (*blockType1)[memberIdx].type->getFieldName();
601 if (uniformVarMap[i]->find(memberName) != uniformVarMap[i]->end())
603 TString err = "Invalid Uniform variable name : " + memberName;
604 infoSink.info.message(EPrefixInternalError, err.c_str());
616 TVarLiveMap *inVarMaps[EShLangCount], *outVarMaps[EShLangCount], *uniformVarMap[EShLangCount];
618 // Use for mark current shader stage for resolver
619 TIoMapResolver& resolver;
626 TSymbolValidater& operator=(TSymbolValidater&) = delete;
628 bool qualifierCheck(const TType* const type1, const TType* const type2, const std::string& name, bool isBlock)
630 bool hasError = false;
631 const TQualifier& qualifier1 = type1->getQualifier();
632 const TQualifier& qualifier2 = type2->getQualifier();
634 if (((isBlock == false) &&
635 (type1->getQualifier().storage == EvqUniform && type2->getQualifier().storage == EvqUniform)) ||
636 (type1->getQualifier().storage == EvqGlobal && type2->getQualifier().storage == EvqGlobal)) {
637 if (qualifier1.precision != qualifier2.precision) {
639 std::string errorStr = name + ": have precision conflict cross stage.";
640 infoSink.info.message(EPrefixError, errorStr.c_str());
642 if (qualifier1.hasFormat() && qualifier2.hasFormat()) {
643 if (qualifier1.layoutFormat != qualifier2.layoutFormat) {
645 std::string errorStr = name + ": have layout format conflict cross stage.";
646 infoSink.info.message(EPrefixError, errorStr.c_str());
652 if (isBlock == true) {
653 if (qualifier1.layoutPacking != qualifier2.layoutPacking) {
655 std::string errorStr = name + ": have layoutPacking conflict cross stage.";
656 infoSink.info.message(EPrefixError, errorStr.c_str());
658 if (qualifier1.layoutMatrix != qualifier2.layoutMatrix) {
660 std::string errorStr = name + ": have layoutMatrix conflict cross stage.";
661 infoSink.info.message(EPrefixError, errorStr.c_str());
663 if (qualifier1.layoutOffset != qualifier2.layoutOffset) {
665 std::string errorStr = name + ": have layoutOffset conflict cross stage.";
666 infoSink.info.message(EPrefixError, errorStr.c_str());
668 if (qualifier1.layoutAlign != qualifier2.layoutAlign) {
670 std::string errorStr = name + ": have layoutAlign conflict cross stage.";
671 infoSink.info.message(EPrefixError, errorStr.c_str());
678 bool typeCheck(const TType* const type1, const TType* const type2, const std::string& name, bool isBlock)
680 bool hasError = false;
681 if (!(type1->isStruct() && type2->isStruct())) {
682 hasError = hasError || qualifierCheck(type1, type2, name, isBlock);
685 if (type1->getBasicType() == EbtBlock && type2->getBasicType() == EbtBlock)
687 const TTypeList* typeList1 = type1->getStruct();
688 const TTypeList* typeList2 = type2->getStruct();
690 std::string newName = name;
691 size_t memberCount = typeList1->size();
693 for (size_t index = 0; index < memberCount; index++, index2++) {
694 // Skip inactive member
695 if (typeList1->at(index).type->getBasicType() == EbtVoid)
697 while (index2 < typeList2->size() && typeList2->at(index2).type->getBasicType() == EbtVoid) {
701 // TypeList1 has more members in list
702 if (index2 == typeList2->size()) {
703 std::string errorStr = name + ": struct mismatch.";
704 infoSink.info.message(EPrefixError, errorStr.c_str());
709 if (typeList1->at(index).type->getFieldName() != typeList2->at(index2).type->getFieldName()) {
710 std::string errorStr = name + ": member name mismatch.";
711 infoSink.info.message(EPrefixError, errorStr.c_str());
715 newName = typeList1->at(index).type->getFieldName().c_str();
717 hasError = hasError || typeCheck(typeList1->at(index).type, typeList2->at(index2).type, newName, isBlock);
720 while (index2 < typeList2->size())
722 // TypeList2 has more members
723 if (typeList2->at(index2).type->getBasicType() != EbtVoid) {
724 std::string errorStr = name + ": struct mismatch.";
725 infoSink.info.message(EPrefixError, errorStr.c_str());
736 struct TSlotCollector {
737 TSlotCollector(TIoMapResolver& r, TInfoSink& i) : resolver(r), infoSink(i) { }
739 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
740 resolver.reserverStorageSlot(entKey.second, infoSink);
741 resolver.reserverResourceSlot(entKey.second, infoSink);
743 TIoMapResolver& resolver;
747 TSlotCollector& operator=(TSlotCollector&) = delete;
750 TDefaultIoResolverBase::TDefaultIoResolverBase(const TIntermediate& intermediate)
751 : intermediate(intermediate)
752 , nextUniformLocation(intermediate.getUniformLocationBase())
753 , nextInputLocation(0)
754 , nextOutputLocation(0)
756 memset(stageMask, false, sizeof(bool) * (EShLangCount + 1));
757 memset(stageIntermediates, 0, sizeof(TIntermediate*) * (EShLangCount));
758 stageIntermediates[intermediate.getStage()] = &intermediate;
761 int TDefaultIoResolverBase::getBaseBinding(EShLanguage stage, TResourceType res, unsigned int set) const {
762 return stageIntermediates[stage] ? selectBaseBinding(stageIntermediates[stage]->getShiftBinding(res), stageIntermediates[stage]->getShiftBindingForSet(res, set))
763 : selectBaseBinding(intermediate.getShiftBinding(res), intermediate.getShiftBindingForSet(res, set));
766 const std::vector<std::string>& TDefaultIoResolverBase::getResourceSetBinding(EShLanguage stage) const {
767 return stageIntermediates[stage] ? stageIntermediates[stage]->getResourceSetBinding()
768 : intermediate.getResourceSetBinding();
771 bool TDefaultIoResolverBase::doAutoBindingMapping() const { return intermediate.getAutoMapBindings(); }
773 bool TDefaultIoResolverBase::doAutoLocationMapping() const { return intermediate.getAutoMapLocations(); }
775 TDefaultIoResolverBase::TSlotSet::iterator TDefaultIoResolverBase::findSlot(int set, int slot) {
776 return std::lower_bound(slots[set].begin(), slots[set].end(), slot);
779 bool TDefaultIoResolverBase::checkEmpty(int set, int slot) {
780 TSlotSet::iterator at = findSlot(set, slot);
781 return ! (at != slots[set].end() && *at == slot);
784 int TDefaultIoResolverBase::reserveSlot(int set, int slot, int size) {
785 TSlotSet::iterator at = findSlot(set, slot);
786 // tolerate aliasing, by not double-recording aliases
787 // (policy about appropriateness of the alias is higher up)
788 for (int i = 0; i < size; i++) {
789 if (at == slots[set].end() || *at != slot + i)
790 at = slots[set].insert(at, slot + i);
796 int TDefaultIoResolverBase::getFreeSlot(int set, int base, int size) {
797 TSlotSet::iterator at = findSlot(set, base);
798 if (at == slots[set].end())
799 return reserveSlot(set, base, size);
800 // look for a big enough gap
801 for (; at != slots[set].end(); ++at) {
802 if (*at - base >= size)
806 return reserveSlot(set, base, size);
809 int TDefaultIoResolverBase::resolveSet(EShLanguage stage, TVarEntryInfo& ent) {
810 const TType& type = ent.symbol->getType();
811 if (type.getQualifier().hasSet()) {
812 return ent.newSet = type.getQualifier().layoutSet;
814 // If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN)
815 if (getResourceSetBinding(stage).size() == 1) {
816 return ent.newSet = atoi(getResourceSetBinding(stage)[0].c_str());
818 return ent.newSet = 0;
821 int TDefaultIoResolverBase::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
822 const TType& type = ent.symbol->getType();
823 const char* name = ent.symbol->getAccessName().c_str();
824 // kick out of not doing this
825 if (! doAutoLocationMapping()) {
826 return ent.newLocation = -1;
828 // no locations added if already present, a built-in variable, a block, or an opaque
829 if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock ||
830 type.isAtomic() || (type.containsOpaque() && intermediate.getSpv().openGl == 0)) {
831 return ent.newLocation = -1;
833 // no locations on blocks of built-in variables
834 if (type.isStruct()) {
835 if (type.getStruct()->size() < 1) {
836 return ent.newLocation = -1;
838 if ((*type.getStruct())[0].type->isBuiltIn()) {
839 return ent.newLocation = -1;
842 int location = intermediate.getUniformLocationOverride(name);
843 if (location != -1) {
844 return ent.newLocation = location;
846 location = nextUniformLocation;
847 nextUniformLocation += TIntermediate::computeTypeUniformLocationSize(type);
848 return ent.newLocation = location;
851 int TDefaultIoResolverBase::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
852 const TType& type = ent.symbol->getType();
853 // kick out of not doing this
854 if (! doAutoLocationMapping()) {
855 return ent.newLocation = -1;
858 // no locations added if already present, or a built-in variable
859 if (type.getQualifier().hasLocation() || type.isBuiltIn()) {
860 return ent.newLocation = -1;
863 // no locations on blocks of built-in variables
864 if (type.isStruct()) {
865 if (type.getStruct()->size() < 1) {
866 return ent.newLocation = -1;
868 if ((*type.getStruct())[0].type->isBuiltIn()) {
869 return ent.newLocation = -1;
872 // point to the right input or output location counter
873 int& nextLocation = type.getQualifier().isPipeInput() ? nextInputLocation : nextOutputLocation;
874 // Placeholder. This does not do proper cross-stage lining up, nor
875 // work with mixed location/no-location declarations.
876 int location = nextLocation;
877 int typeLocationSize;
878 // Don’t take into account the outer-most array if the stage’s
879 // interface is automatically an array.
880 typeLocationSize = computeTypeLocationSize(type, stage);
881 nextLocation += typeLocationSize;
882 return ent.newLocation = location;
885 int TDefaultIoResolverBase::resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) {
886 return ent.newComponent = -1;
889 int TDefaultIoResolverBase::resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) { return ent.newIndex = -1; }
891 uint32_t TDefaultIoResolverBase::computeTypeLocationSize(const TType& type, EShLanguage stage) {
892 int typeLocationSize;
893 // Don’t take into account the outer-most array if the stage’s
894 // interface is automatically an array.
895 if (type.getQualifier().isArrayedIo(stage)) {
896 TType elementType(type, 0);
897 typeLocationSize = TIntermediate::computeTypeLocationSize(elementType, stage);
899 typeLocationSize = TIntermediate::computeTypeLocationSize(type, stage);
901 return typeLocationSize;
904 //TDefaultGlslIoResolver
905 TResourceType TDefaultGlslIoResolver::getResourceType(const glslang::TType& type) {
906 if (isImageType(type)) {
909 if (isTextureType(type)) {
912 if (isSsboType(type)) {
915 if (isSamplerType(type)) {
918 if (isUboType(type)) {
924 TDefaultGlslIoResolver::TDefaultGlslIoResolver(const TIntermediate& intermediate)
925 : TDefaultIoResolverBase(intermediate)
926 , preStage(EShLangCount)
927 , currentStage(EShLangCount)
930 int TDefaultGlslIoResolver::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
931 const TType& type = ent.symbol->getType();
932 const TString& name = ent.symbol->getAccessName();
933 if (currentStage != stage) {
934 preStage = currentStage;
935 currentStage = stage;
937 // kick out if not doing this
938 if (! doAutoLocationMapping()) {
939 return ent.newLocation = -1;
941 // expand the location to each element if the symbol is a struct or array
942 if (type.getQualifier().hasLocation()) {
943 return ent.newLocation = type.getQualifier().layoutLocation;
945 // no locations added if already present, or a built-in variable
946 if (type.isBuiltIn()) {
947 return ent.newLocation = -1;
949 // no locations on blocks of built-in variables
950 if (type.isStruct()) {
951 if (type.getStruct()->size() < 1) {
952 return ent.newLocation = -1;
954 if ((*type.getStruct())[0].type->isBuiltIn()) {
955 return ent.newLocation = -1;
958 int typeLocationSize = computeTypeLocationSize(type, stage);
959 int location = type.getQualifier().layoutLocation;
960 bool hasLocation = false;
961 EShLanguage keyStage(EShLangCount);
962 TStorageQualifier storage;
964 if (type.getQualifier().isPipeInput()) {
965 // If this symbol is a input, search pre stage's out
968 if (type.getQualifier().isPipeOutput()) {
969 // If this symbol is a output, search next stage's in
970 keyStage = currentStage;
972 // The in/out in current stage is not declared with location, but it is possible declared
973 // with explicit location in other stages, find the storageSlotMap firstly to check whether
974 // the in/out has location
975 int resourceKey = buildStorageKey(keyStage, storage);
976 if (! storageSlotMap[resourceKey].empty()) {
977 TVarSlotMap::iterator iter = storageSlotMap[resourceKey].find(name);
978 if (iter != storageSlotMap[resourceKey].end()) {
979 // If interface resource be found, set it has location and this symbol's new location
980 // equal the symbol's explicit location declaration in pre or next stage.
983 // fs: layout(..., location = 3,...) in vec4 a;
985 location = iter->second;
986 // if we want deal like that:
987 // vs: layout(location=4) out vec4 a;
991 // layout(location = 4) in vec4 b;
992 // we need retraverse the map.
995 // If interface resource note found, It's mean the location in two stage are both implicit declarat.
996 // So we should find a new slot for this interface.
1000 location = getFreeSlot(resourceKey, 0, typeLocationSize);
1001 storageSlotMap[resourceKey][name] = location;
1004 // the first interface declarated in a program.
1005 TVarSlotMap varSlotMap;
1006 location = getFreeSlot(resourceKey, 0, typeLocationSize);
1007 varSlotMap[name] = location;
1008 storageSlotMap[resourceKey] = varSlotMap;
1011 return ent.newLocation = location;
1014 int TDefaultGlslIoResolver::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
1015 const TType& type = ent.symbol->getType();
1016 const TString& name = ent.symbol->getAccessName();
1017 // kick out of not doing this
1018 if (! doAutoLocationMapping()) {
1019 return ent.newLocation = -1;
1021 // expand the location to each element if the symbol is a struct or array
1022 if (type.getQualifier().hasLocation() && (type.isStruct() || type.isArray())) {
1023 return ent.newLocation = type.getQualifier().layoutLocation;
1025 // no locations added if already present, a built-in variable, a block, or an opaque
1026 if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock ||
1027 type.isAtomic() || (type.containsOpaque() && intermediate.getSpv().openGl == 0)) {
1028 return ent.newLocation = -1;
1030 // no locations on blocks of built-in variables
1031 if (type.isStruct()) {
1032 if (type.getStruct()->size() < 1) {
1033 return ent.newLocation = -1;
1035 if ((*type.getStruct())[0].type->isBuiltIn()) {
1036 return ent.newLocation = -1;
1040 int location = intermediate.getUniformLocationOverride(name.c_str());
1041 if (location != -1) {
1042 return ent.newLocation = location;
1045 int size = TIntermediate::computeTypeUniformLocationSize(type);
1047 // The uniform in current stage is not declared with location, but it is possible declared
1048 // with explicit location in other stages, find the storageSlotMap firstly to check whether
1049 // the uniform has location
1050 bool hasLocation = false;
1051 int resourceKey = buildStorageKey(EShLangCount, EvqUniform);
1052 TVarSlotMap& slotMap = storageSlotMap[resourceKey];
1053 // Check dose shader program has uniform resource
1054 if (! slotMap.empty()) {
1055 // If uniform resource not empty, try find a same name uniform
1056 TVarSlotMap::iterator iter = slotMap.find(name);
1057 if (iter != slotMap.end()) {
1058 // If uniform resource be found, set it has location and this symbol's new location
1059 // equal the uniform's explicit location declaration in other stage.
1061 // vs: uniform vec4 a;
1062 // fs: layout(..., location = 3,...) uniform vec4 a;
1064 location = iter->second;
1066 if (! hasLocation) {
1067 // No explicit location declaration in other stage.
1068 // So we should find a new slot for this uniform.
1070 // vs: uniform vec4 a;
1071 // fs: uniform vec4 a;
1072 location = getFreeSlot(resourceKey, 0, computeTypeLocationSize(type, currentStage));
1073 storageSlotMap[resourceKey][name] = location;
1076 // the first uniform declaration in a program.
1077 TVarSlotMap varSlotMap;
1078 location = getFreeSlot(resourceKey, 0, size);
1079 varSlotMap[name] = location;
1080 storageSlotMap[resourceKey] = varSlotMap;
1082 return ent.newLocation = location;
1085 int TDefaultGlslIoResolver::resolveBinding(EShLanguage stage, TVarEntryInfo& ent) {
1086 const TType& type = ent.symbol->getType();
1087 const TString& name = ent.symbol->getAccessName();
1088 // On OpenGL arrays of opaque types take a separate binding for each element
1089 int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
1090 TResourceType resource = getResourceType(type);
1091 // don't need to handle uniform symbol, it will be handled in resolveUniformLocation
1092 if (resource == EResUbo && type.getBasicType() != EbtBlock) {
1093 return ent.newBinding = -1;
1095 // There is no 'set' qualifier in OpenGL shading language, each resource has its own
1096 // binding name space, so remap the 'set' to resource type which make each resource
1097 // binding is valid from 0 to MAX_XXRESOURCE_BINDINGS
1098 int set = intermediate.getSpv().openGl != 0 ? resource : ent.newSet;
1099 int resourceKey = set;
1100 if (resource < EResCount) {
1101 if (type.getQualifier().hasBinding()) {
1102 int newBinding = reserveSlot(resourceKey, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding, numBindings);
1103 return ent.newBinding = newBinding;
1106 // The resource in current stage is not declared with binding, but it is possible declared
1107 // with explicit binding in other stages, find the resourceSlotMap firstly to check whether
1108 // the resource has binding, don't need to allocate if it already has a binding
1109 bool hasBinding = false;
1110 ent.newBinding = -1; // leave as -1 if it isn't set below
1112 if (! resourceSlotMap[resourceKey].empty()) {
1113 TVarSlotMap::iterator iter = resourceSlotMap[resourceKey].find(name);
1114 if (iter != resourceSlotMap[resourceKey].end()) {
1116 ent.newBinding = iter->second;
1119 if (!hasBinding && (ent.live && doAutoBindingMapping())) {
1120 // find free slot, the caller did make sure it passes all vars with binding
1121 // first and now all are passed that do not have a binding and needs one
1122 int binding = getFreeSlot(resourceKey, getBaseBinding(stage, resource, set), numBindings);
1123 resourceSlotMap[resourceKey][name] = binding;
1124 ent.newBinding = binding;
1126 return ent.newBinding;
1129 return ent.newBinding = -1;
1132 void TDefaultGlslIoResolver::beginResolve(EShLanguage stage) {
1133 // reset stage state
1134 if (stage == EShLangCount)
1135 preStage = currentStage = stage;
1136 // update stage state
1137 else if (currentStage != stage) {
1138 preStage = currentStage;
1139 currentStage = stage;
1143 void TDefaultGlslIoResolver::endResolve(EShLanguage /*stage*/) {
1147 void TDefaultGlslIoResolver::beginCollect(EShLanguage stage) {
1148 // reset stage state
1149 if (stage == EShLangCount)
1150 preStage = currentStage = stage;
1151 // update stage state
1152 else if (currentStage != stage) {
1153 preStage = currentStage;
1154 currentStage = stage;
1158 void TDefaultGlslIoResolver::endCollect(EShLanguage /*stage*/) {
1162 void TDefaultGlslIoResolver::reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
1163 const TType& type = ent.symbol->getType();
1164 const TString& name = ent.symbol->getAccessName();
1165 TStorageQualifier storage = type.getQualifier().storage;
1166 EShLanguage stage(EShLangCount);
1169 if (type.getBasicType() != EbtBlock && type.getQualifier().hasLocation()) {
1171 // Reserve the slots for the uniforms who has explicit location
1172 int storageKey = buildStorageKey(EShLangCount, EvqUniform);
1173 int location = type.getQualifier().layoutLocation;
1174 TVarSlotMap& varSlotMap = storageSlotMap[storageKey];
1175 TVarSlotMap::iterator iter = varSlotMap.find(name);
1176 if (iter == varSlotMap.end()) {
1177 int numLocations = TIntermediate::computeTypeUniformLocationSize(type);
1178 reserveSlot(storageKey, location, numLocations);
1179 varSlotMap[name] = location;
1181 // Allocate location by name for OpenGL driver, so the uniform in different
1182 // stages should be declared with the same location
1183 if (iter->second != location) {
1184 TString errorMsg = "Invalid location: " + name;
1185 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
1194 // Reserve the slots for the inout who has explicit location
1195 if (type.getQualifier().hasLocation()) {
1196 stage = storage == EvqVaryingIn ? preStage : stage;
1197 stage = storage == EvqVaryingOut ? currentStage : stage;
1198 int storageKey = buildStorageKey(stage, EvqInOut);
1199 int location = type.getQualifier().layoutLocation;
1200 TVarSlotMap& varSlotMap = storageSlotMap[storageKey];
1201 TVarSlotMap::iterator iter = varSlotMap.find(name);
1202 if (iter == varSlotMap.end()) {
1203 int numLocations = TIntermediate::computeTypeUniformLocationSize(type);
1204 reserveSlot(storageKey, location, numLocations);
1205 varSlotMap[name] = location;
1207 // Allocate location by name for OpenGL driver, so the uniform in different
1208 // stages should be declared with the same location
1209 if (iter->second != location) {
1210 TString errorMsg = "Invalid location: " + name;
1211 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
1222 void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
1223 const TType& type = ent.symbol->getType();
1224 const TString& name = ent.symbol->getAccessName();
1225 TResourceType resource = getResourceType(type);
1226 int set = intermediate.getSpv().openGl != 0 ? resource : resolveSet(ent.stage, ent);
1227 int resourceKey = set;
1229 if (type.getQualifier().hasBinding()) {
1230 TVarSlotMap& varSlotMap = resourceSlotMap[resourceKey];
1231 TVarSlotMap::iterator iter = varSlotMap.find(name);
1232 int binding = type.getQualifier().layoutBinding + getBaseBinding(ent.stage, resource, set);
1234 if (iter == varSlotMap.end()) {
1235 // Reserve the slots for the ubo, ssbo and opaques who has explicit binding
1236 int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
1237 varSlotMap[name] = binding;
1238 reserveSlot(resourceKey, binding, numBindings);
1240 // Allocate binding by name for OpenGL driver, so the resource in different
1241 // stages should be declared with the same binding
1242 if (iter->second != binding) {
1243 TString errorMsg = "Invalid binding: " + name;
1244 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
1251 //TDefaultGlslIoResolver end
1254 * Basic implementation of glslang::TIoMapResolver that replaces the
1255 * previous offset behavior.
1256 * It does the same, uses the offsets for the corresponding uniform
1257 * types. Also respects the EOptionAutoMapBindings flag and binds
1263 struct TDefaultIoResolver : public TDefaultIoResolverBase {
1264 TDefaultIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
1266 bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
1268 TResourceType getResourceType(const glslang::TType& type) override {
1269 if (isImageType(type)) {
1272 if (isTextureType(type)) {
1275 if (isSsboType(type)) {
1278 if (isSamplerType(type)) {
1281 if (isUboType(type)) {
1287 int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) override {
1288 const TType& type = ent.symbol->getType();
1289 const int set = getLayoutSet(type);
1290 // On OpenGL arrays of opaque types take a seperate binding for each element
1291 int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
1292 TResourceType resource = getResourceType(type);
1293 if (resource < EResCount) {
1294 if (type.getQualifier().hasBinding()) {
1295 return ent.newBinding = reserveSlot(
1296 set, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding, numBindings);
1297 } else if (ent.live && doAutoBindingMapping()) {
1298 // find free slot, the caller did make sure it passes all vars with binding
1299 // first and now all are passed that do not have a binding and needs one
1300 return ent.newBinding = getFreeSlot(set, getBaseBinding(stage, resource, set), numBindings);
1303 return ent.newBinding = -1;
1308 /********************************************************************************
1309 The following IO resolver maps types in HLSL register space, as follows:
1311 t - for shader resource views (SRV)
1333 SAMPLERCOMPARISONSTATE
1335 u - for unordered access views (UAV)
1338 APPENDSTRUCTUREDBUFFER
1339 CONSUMESTRUCTUREDBUFFER
1347 b - for constant buffer views (CBV)
1350 ********************************************************************************/
1351 struct TDefaultHlslIoResolver : public TDefaultIoResolverBase {
1352 TDefaultHlslIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
1354 bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
1356 TResourceType getResourceType(const glslang::TType& type) override {
1357 if (isUavType(type)) {
1360 if (isSrvType(type)) {
1363 if (isSamplerType(type)) {
1366 if (isUboType(type)) {
1372 int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) override {
1373 const TType& type = ent.symbol->getType();
1374 const int set = getLayoutSet(type);
1375 TResourceType resource = getResourceType(type);
1376 if (resource < EResCount) {
1377 if (type.getQualifier().hasBinding()) {
1378 return ent.newBinding = reserveSlot(set, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding);
1379 } else if (ent.live && doAutoBindingMapping()) {
1380 // find free slot, the caller did make sure it passes all vars with binding
1381 // first and now all are passed that do not have a binding and needs one
1382 return ent.newBinding = getFreeSlot(set, getBaseBinding(stage, resource, set));
1385 return ent.newBinding = -1;
1390 // Map I/O variables to provided offsets, and make bindings for
1391 // unbound but live variables.
1393 // Returns false if the input is too malformed to do this.
1394 bool TIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) {
1395 bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() ||
1396 intermediate.getAutoMapLocations();
1397 // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce
1398 // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true.
1399 for (int res = 0; (res < EResCount && !somethingToDo); ++res) {
1400 somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
1401 intermediate.hasShiftBindingForSet(TResourceType(res));
1403 if (! somethingToDo && resolver == nullptr)
1405 if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive())
1407 TIntermNode* root = intermediate.getTreeRoot();
1408 if (root == nullptr)
1410 // if no resolver is provided, use the default resolver with the given shifts and auto map settings
1411 TDefaultIoResolver defaultResolver(intermediate);
1413 TDefaultHlslIoResolver defaultHlslResolver(intermediate);
1414 if (resolver == nullptr) {
1415 // TODO: use a passed in IO mapper for this
1416 if (intermediate.usingHlslIoMapping())
1417 resolver = &defaultHlslResolver;
1419 resolver = &defaultResolver;
1422 resolver = &defaultResolver;
1424 resolver->addStage(stage, intermediate);
1426 TVarLiveMap inVarMap, outVarMap, uniformVarMap;
1427 TVarLiveVector inVector, outVector, uniformVector;
1428 TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap);
1429 TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap);
1430 root->traverse(&iter_binding_all);
1431 iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
1432 while (! iter_binding_live.destinations.empty()) {
1433 TIntermNode* destination = iter_binding_live.destinations.back();
1434 iter_binding_live.destinations.pop_back();
1435 destination->traverse(&iter_binding_live);
1438 // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info.
1439 for (auto& var : inVarMap) { inVector.push_back(var); }
1440 std::sort(inVector.begin(), inVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1441 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1443 for (auto& var : outVarMap) { outVector.push_back(var); }
1444 std::sort(outVector.begin(), outVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1445 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1447 for (auto& var : uniformVarMap) { uniformVector.push_back(var); }
1448 std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1449 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1451 bool hadError = false;
1452 TVarLiveMap* dummyUniformVarMap[EShLangCount] = {};
1453 TNotifyInOutAdaptor inOutNotify(stage, *resolver);
1454 TNotifyUniformAdaptor uniformNotify(stage, *resolver);
1455 TResolverUniformAdaptor uniformResolve(stage, *resolver, dummyUniformVarMap, infoSink, hadError);
1456 TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError);
1457 resolver->beginNotifications(stage);
1458 std::for_each(inVector.begin(), inVector.end(), inOutNotify);
1459 std::for_each(outVector.begin(), outVector.end(), inOutNotify);
1460 std::for_each(uniformVector.begin(), uniformVector.end(), uniformNotify);
1461 resolver->endNotifications(stage);
1462 resolver->beginResolve(stage);
1463 for (auto& var : inVector) { inOutResolve(var); }
1464 std::for_each(inVector.begin(), inVector.end(), [&inVarMap](TVarLivePair p) {
1465 auto at = inVarMap.find(p.second.symbol->getAccessName());
1466 if (at != inVarMap.end() && p.second.id == at->second.id)
1467 at->second = p.second;
1469 for (auto& var : outVector) { inOutResolve(var); }
1470 std::for_each(outVector.begin(), outVector.end(), [&outVarMap](TVarLivePair p) {
1471 auto at = outVarMap.find(p.second.symbol->getAccessName());
1472 if (at != outVarMap.end() && p.second.id == at->second.id)
1473 at->second = p.second;
1475 std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve);
1476 std::for_each(uniformVector.begin(), uniformVector.end(), [&uniformVarMap](TVarLivePair p) {
1477 auto at = uniformVarMap.find(p.second.symbol->getAccessName());
1478 if (at != uniformVarMap.end() && p.second.id == at->second.id)
1479 at->second = p.second;
1481 resolver->endResolve(stage);
1483 TVarSetTraverser iter_iomap(intermediate, inVarMap, outVarMap, uniformVarMap);
1484 root->traverse(&iter_iomap);
1489 // Map I/O variables to provided offsets, and make bindings for
1490 // unbound but live variables.
1492 // Returns false if the input is too malformed to do this.
1493 bool TGlslIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) {
1494 bool somethingToDo = !intermediate.getResourceSetBinding().empty() ||
1495 intermediate.getAutoMapBindings() ||
1496 intermediate.getAutoMapLocations();
1498 // Profile and version are use for symbol validate.
1499 profile = intermediate.getProfile();
1500 version = intermediate.getVersion();
1502 // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce
1503 // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true.
1504 for (int res = 0; (res < EResCount && !somethingToDo); ++res) {
1505 somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
1506 intermediate.hasShiftBindingForSet(TResourceType(res));
1508 if (! somethingToDo && resolver == nullptr) {
1511 if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive()) {
1514 TIntermNode* root = intermediate.getTreeRoot();
1515 if (root == nullptr) {
1518 // if no resolver is provided, use the default resolver with the given shifts and auto map settings
1519 TDefaultGlslIoResolver defaultResolver(intermediate);
1521 TDefaultHlslIoResolver defaultHlslResolver(intermediate);
1522 if (resolver == nullptr) {
1523 // TODO: use a passed in IO mapper for this
1524 if (intermediate.usingHlslIoMapping())
1525 resolver = &defaultHlslResolver;
1527 resolver = &defaultResolver;
1530 if (resolver == nullptr) {
1531 resolver = &defaultResolver;
1534 resolver->addStage(stage, intermediate);
1535 inVarMaps[stage] = new TVarLiveMap(); outVarMaps[stage] = new TVarLiveMap(); uniformVarMap[stage] = new TVarLiveMap();
1536 TVarGatherTraverser iter_binding_all(intermediate, true, *inVarMaps[stage], *outVarMaps[stage],
1537 *uniformVarMap[stage]);
1538 TVarGatherTraverser iter_binding_live(intermediate, false, *inVarMaps[stage], *outVarMaps[stage],
1539 *uniformVarMap[stage]);
1540 root->traverse(&iter_binding_all);
1541 iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
1542 while (! iter_binding_live.destinations.empty()) {
1543 TIntermNode* destination = iter_binding_live.destinations.back();
1544 iter_binding_live.destinations.pop_back();
1545 destination->traverse(&iter_binding_live);
1548 TNotifyInOutAdaptor inOutNotify(stage, *resolver);
1549 TNotifyUniformAdaptor uniformNotify(stage, *resolver);
1550 // Resolve current stage input symbol location with previous stage output here,
1551 // uniform symbol, ubo, ssbo and opaque symbols are per-program resource,
1552 // will resolve uniform symbol location and ubo/ssbo/opaque binding in doMap()
1553 resolver->beginNotifications(stage);
1554 std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), inOutNotify);
1555 std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), inOutNotify);
1556 std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), uniformNotify);
1557 resolver->endNotifications(stage);
1558 TSlotCollector slotCollector(*resolver, infoSink);
1559 resolver->beginCollect(stage);
1560 std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), slotCollector);
1561 std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), slotCollector);
1562 std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), slotCollector);
1563 resolver->endCollect(stage);
1564 intermediates[stage] = &intermediate;
1568 bool TGlslIoMapper::doMap(TIoMapResolver* resolver, TInfoSink& infoSink) {
1569 resolver->endResolve(EShLangCount);
1571 //Resolve uniform location, ubo/ssbo/opaque bindings across stages
1572 TResolverUniformAdaptor uniformResolve(EShLangCount, *resolver, uniformVarMap, infoSink, hadError);
1573 TResolverInOutAdaptor inOutResolve(EShLangCount, *resolver, infoSink, hadError);
1574 TSymbolValidater symbolValidater(*resolver, infoSink, inVarMaps,
1575 outVarMaps, uniformVarMap, hadError, profile, version);
1577 TVarLiveVector inVectors[EShLangCount];
1578 TVarLiveVector outVectors[EShLangCount];
1579 TVarLiveVector uniformVector;
1581 resolver->beginResolve(EShLangCount);
1582 for (int stage = EShLangVertex; stage < EShLangCount; stage++) {
1583 if (inVarMaps[stage] != nullptr) {
1584 inOutResolve.setStage(EShLanguage(stage));
1586 // copy vars into a sorted list
1587 std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(),
1588 [&inVectors, stage](TVarLivePair p) { inVectors[stage].push_back(p); });
1589 std::sort(inVectors[stage].begin(), inVectors[stage].end(),
1590 [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1591 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1594 std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(),
1595 [&outVectors, stage](TVarLivePair p) { outVectors[stage].push_back(p); });
1596 std::sort(outVectors[stage].begin(), outVectors[stage].end(),
1597 [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1598 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1601 for (auto& var : inVectors[stage]) { symbolValidater(var); }
1602 for (auto& var : inVectors[stage]) { inOutResolve(var); }
1603 for (auto& var : outVectors[stage]) { symbolValidater(var); }
1604 for (auto& var : outVectors[stage]) { inOutResolve(var); }
1606 // copy results back into maps
1607 std::for_each(inVectors[stage].begin(), inVectors[stage].end(),
1608 [this, stage](TVarLivePair p) {
1609 auto at = inVarMaps[stage]->find(p.first);
1610 if (at != inVarMaps[stage]->end())
1611 at->second = p.second;
1614 std::for_each(outVectors[stage].begin(), outVectors[stage].end(),
1615 [this, stage](TVarLivePair p) {
1616 auto at = outVarMaps[stage]->find(p.first);
1617 if (at != outVarMaps[stage]->end())
1618 at->second = p.second;
1622 if (uniformVarMap[stage] != nullptr) {
1623 uniformResolve.setStage(EShLanguage(stage));
1624 for (auto& var : *(uniformVarMap[stage])) { uniformVector.push_back(var); }
1627 std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1628 return TVarEntryInfo::TOrderByPriorityAndLive()(p1.second, p2.second);
1630 for (auto& var : uniformVector) { symbolValidater(var); }
1631 for (auto& var : uniformVector) { uniformResolve(var); }
1632 std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1633 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1635 resolver->endResolve(EShLangCount);
1636 if (autoPushConstantBlockName.length()) {
1637 bool upgraded = false;
1638 for (size_t stage = 0; stage < EShLangCount; stage++) {
1639 if (intermediates[stage] != nullptr) {
1640 TVarLiveMap** pUniformVarMap = uniformResolve.uniformVarMap;
1641 auto at = pUniformVarMap[stage]->find(autoPushConstantBlockName);
1642 if (at == pUniformVarMap[stage]->end())
1644 TQualifier& qualifier = at->second.symbol->getQualifier();
1645 if (!qualifier.isUniform())
1647 TType& t = at->second.symbol->getWritableType();
1649 TIntermediate::getBaseAlignment(t, size, stride, autoPushConstantBlockPacking,
1650 qualifier.layoutMatrix == ElmRowMajor);
1651 if (size <= int(autoPushConstantMaxSize)) {
1652 qualifier.setBlockStorage(EbsPushConstant);
1653 qualifier.layoutPacking = autoPushConstantBlockPacking;
1658 // If it's been upgraded to push_constant, then remove it from the uniformVector
1659 // so it doesn't get a set/binding assigned to it.
1661 auto at = std::find_if(uniformVector.begin(), uniformVector.end(),
1662 [this](const TVarLivePair& p) { return p.first == autoPushConstantBlockName; });
1663 if (at != uniformVector.end())
1664 uniformVector.erase(at);
1667 for (size_t stage = 0; stage < EShLangCount; stage++) {
1668 if (intermediates[stage] != nullptr) {
1669 // traverse each stage, set new location to each input/output and unifom symbol, set new binding to
1670 // ubo, ssbo and opaque symbols
1671 TVarLiveMap** pUniformVarMap = uniformResolve.uniformVarMap;
1672 std::for_each(uniformVector.begin(), uniformVector.end(), [pUniformVarMap, stage](TVarLivePair p) {
1673 auto at = pUniformVarMap[stage]->find(p.second.symbol->getAccessName());
1674 if (at != pUniformVarMap[stage]->end() && at->second.id == p.second.id){
1675 int resolvedBinding = at->second.newBinding;
1676 at->second = p.second;
1677 if (resolvedBinding > 0)
1678 at->second.newBinding = resolvedBinding;
1681 TVarSetTraverser iter_iomap(*intermediates[stage], *inVarMaps[stage], *outVarMaps[stage],
1682 *uniformResolve.uniformVarMap[stage]);
1683 intermediates[stage]->getTreeRoot()->traverse(&iter_iomap);
1692 } // end namespace glslang
1694 #endif // !GLSLANG_WEB && !GLSLANG_ANGLE