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() && !base->getQualifier().isShaderRecord())
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.clearNewAssignments();
207 const bool isValid = resolver.validateBinding(stage, ent);
209 resolver.resolveSet(ent.stage, ent);
210 resolver.resolveBinding(ent.stage, ent);
211 resolver.resolveUniformLocation(ent.stage, ent);
213 if (ent.newBinding != -1) {
214 if (ent.newBinding >= int(TQualifier::layoutBindingEnd)) {
215 TString err = "mapped binding out of range: " + entKey.first;
217 infoSink.info.message(EPrefixInternalError, err.c_str());
221 if (ent.symbol->getQualifier().hasBinding()) {
222 for (uint32_t idx = EShLangVertex; idx < EShLangCount; ++idx) {
223 if (idx == ent.stage || uniformVarMap[idx] == nullptr)
225 auto entKey2 = uniformVarMap[idx]->find(entKey.first);
226 if (entKey2 != uniformVarMap[idx]->end()) {
227 entKey2->second.newBinding = ent.newBinding;
232 if (ent.newSet != -1) {
233 if (ent.newSet >= int(TQualifier::layoutSetEnd)) {
234 TString err = "mapped set out of range: " + entKey.first;
236 infoSink.info.message(EPrefixInternalError, err.c_str());
239 if (ent.symbol->getQualifier().hasSet()) {
240 for (uint32_t idx = EShLangVertex; idx < EShLangCount; ++idx) {
241 if ((idx == stage) || (uniformVarMap[idx] == nullptr))
243 auto entKey2 = uniformVarMap[idx]->find(entKey.first);
244 if (entKey2 != uniformVarMap[idx]->end()) {
245 entKey2->second.newSet = ent.newSet;
251 TString errorMsg = "Invalid binding: " + entKey.first;
252 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
257 inline void setStage(EShLanguage s) { stage = s; }
260 TIoMapResolver& resolver;
263 TVarLiveMap* uniformVarMap[EShLangCount];
265 TResolverUniformAdaptor& operator=(TResolverUniformAdaptor&) = delete;
268 struct TResolverInOutAdaptor {
269 TResolverInOutAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e)
277 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
279 TVarEntryInfo& ent = entKey.second;
280 ent.clearNewAssignments();
281 const bool isValid = resolver.validateInOut(ent.stage, ent);
283 resolver.resolveInOutLocation(stage, ent);
284 resolver.resolveInOutComponent(stage, ent);
285 resolver.resolveInOutIndex(stage, ent);
288 if (ent.symbol->getType().getQualifier().semanticName != nullptr) {
289 errorMsg = "Invalid shader In/Out variable semantic: ";
290 errorMsg += ent.symbol->getType().getQualifier().semanticName;
292 errorMsg = "Invalid shader In/Out variable: ";
293 errorMsg += ent.symbol->getName();
295 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
300 inline void setStage(EShLanguage s) { stage = s; }
303 TIoMapResolver& resolver;
308 TResolverInOutAdaptor& operator=(TResolverInOutAdaptor&) = delete;
311 // The class is used for reserving explicit uniform locations and ubo/ssbo/opaque bindings
312 // xxTODO: maybe this logic should be moved into the resolver's "validateInOut" and "validateUniform"
314 struct TSymbolValidater
316 TSymbolValidater(TIoMapResolver& r, TInfoSink& i, TVarLiveMap* in[EShLangCount], TVarLiveMap* out[EShLangCount],
317 TVarLiveMap* uniform[EShLangCount], bool& hadError, EProfile profile, int version)
324 memcpy(inVarMaps, in, EShLangCount * (sizeof(TVarLiveMap*)));
325 memcpy(outVarMaps, out, EShLangCount * (sizeof(TVarLiveMap*)));
326 memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*)));
328 std::map<TString, TString> anonymousMemberMap;
329 std::vector<TRange> usedUniformLocation;
330 std::vector<TString> usedUniformName;
331 usedUniformLocation.clear();
332 usedUniformName.clear();
333 for (int i = 0; i < EShLangCount; i++) {
334 if (uniformVarMap[i]) {
335 for (auto uniformVar : *uniformVarMap[i])
337 TIntermSymbol* pSymbol = uniformVar.second.symbol;
338 TQualifier qualifier = uniformVar.second.symbol->getQualifier();
339 TString symbolName = pSymbol->getAccessName();
341 // All the uniform needs multi-stage location check (block/default)
342 int uniformLocation = qualifier.layoutLocation;
344 if (uniformLocation != TQualifier::layoutLocationEnd) {
345 // Total size of current uniform, could be block, struct or other types.
346 int size = TIntermediate::computeTypeUniformLocationSize(pSymbol->getType());
348 TRange locationRange(uniformLocation, uniformLocation + size - 1);
350 // Combine location and component ranges
351 int overlapLocation = -1;
352 bool diffLocation = false;
354 // Check for collisions, except for vertex inputs on desktop targeting OpenGL
355 overlapLocation = checkLocationOverlap(locationRange, usedUniformLocation, symbolName, usedUniformName, diffLocation);
357 // Overlap locations of uniforms, regardless of components (multi stages)
358 if (overlapLocation == -1) {
359 usedUniformLocation.push_back(locationRange);
360 usedUniformName.push_back(symbolName);
362 else if (overlapLocation >= 0) {
363 if (diffLocation == true) {
364 TString err = ("Uniform location should be equal for same uniforms: " +std::to_string(overlapLocation)).c_str();
365 infoSink.info.message(EPrefixInternalError, err.c_str());
370 TString err = ("Uniform location overlaps across stages: " + std::to_string(overlapLocation)).c_str();
371 infoSink.info.message(EPrefixInternalError, err.c_str());
378 if ((uniformVar.second.symbol->getBasicType() == EbtBlock) &&
379 IsAnonymous(uniformVar.second.symbol->getName()))
381 auto blockType = uniformVar.second.symbol->getType().getStruct();
382 for (size_t memberIdx = 0; memberIdx < blockType->size(); ++memberIdx) {
383 auto memberName = (*blockType)[memberIdx].type->getFieldName();
384 if (anonymousMemberMap.find(memberName) != anonymousMemberMap.end())
386 if (anonymousMemberMap[memberName] != uniformVar.second.symbol->getType().getTypeName())
388 TString err = "Invalid block member name: " + memberName;
389 infoSink.info.message(EPrefixInternalError, err.c_str());
396 anonymousMemberMap[memberName] = uniformVar.second.symbol->getType().getTypeName();
407 // In case we need to new an intermediate, which costs too much
408 int checkLocationOverlap(const TRange& locationRange, std::vector<TRange>& usedUniformLocation, const TString symbolName, std::vector<TString>& usedUniformName, bool& diffLocation)
410 for (size_t r = 0; r < usedUniformLocation.size(); ++r) {
411 if (usedUniformName[r] == symbolName) {
413 return (usedUniformLocation[r].start == locationRange.start &&
414 usedUniformLocation[r].last == locationRange.last)
415 ? -2 : std::max(locationRange.start, usedUniformLocation[r].start);
417 if (locationRange.overlap(usedUniformLocation[r])) {
418 // there is a collision; pick one
419 return std::max(locationRange.start, usedUniformLocation[r].start);
423 return -1; // no collision
426 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
427 TVarEntryInfo& ent1 = entKey.second;
428 TIntermSymbol* base = ent1.symbol;
429 const TType& type = ent1.symbol->getType();
430 const TString& name = entKey.first;
431 TString mangleName1, mangleName2;
432 EShLanguage stage = ent1.stage;
433 EShLanguage preStage, currentStage, nextStage;
435 preStage = EShLangCount;
436 for (int i = stage - 1; i >= 0; i--) {
437 if (inVarMaps[i] != nullptr) {
438 preStage = static_cast<EShLanguage>(i);
442 currentStage = stage;
443 nextStage = EShLangCount;
444 for (int i = stage + 1; i < EShLangCount; i++) {
445 if (inVarMaps[i] != nullptr) {
446 nextStage = static_cast<EShLanguage>(i);
451 if (type.getQualifier().isArrayedIo(stage)) {
452 TType subType(type, 0);
453 subType.appendMangledName(mangleName1);
455 type.appendMangledName(mangleName1);
459 // basic checking that symbols match
460 // more extensive checking in the link stage
461 if (base->getQualifier().storage == EvqVaryingIn) {
462 // validate stage in;
463 if (preStage == EShLangCount)
465 if (TSymbolTable::isBuiltInSymbol(base->getId()))
467 if (outVarMaps[preStage] != nullptr) {
468 auto ent2 = outVarMaps[preStage]->find(name);
469 uint32_t location = base->getType().getQualifier().layoutLocation;
470 if (ent2 == outVarMaps[preStage]->end() &&
471 location != glslang::TQualifier::layoutLocationEnd) {
472 for (auto var = outVarMaps[preStage]->begin(); var != ent2; var++) {
473 if (var->second.symbol->getType().getQualifier().layoutLocation == location) {
479 if (ent2 != outVarMaps[preStage]->end()) {
480 auto& type1 = base->getType();
481 auto& type2 = ent2->second.symbol->getType();
482 hadError = hadError || typeCheck(&type1, &type2, name.c_str(), false);
483 if (ent2->second.symbol->getType().getQualifier().isArrayedIo(preStage)) {
484 TType subType(ent2->second.symbol->getType(), 0);
485 subType.appendMangledName(mangleName2);
487 ent2->second.symbol->getType().appendMangledName(mangleName2);
490 if (mangleName1 == mangleName2) {
491 // For ES 3.0 only, other versions have no such restrictions
492 // According to ES 3.0 spec: The type and presence of the interpolation qualifiers and
493 // storage qualifiers of variables with the same name declared in all linked shaders must
494 // match, otherwise the link command will fail.
495 if (profile == EEsProfile && version == 300) {
496 // Don't need to check smooth qualifier, as it uses the default interpolation mode
497 if (ent1.stage == EShLangFragment && type1.isBuiltIn() == false) {
498 if (type1.getQualifier().flat != type2.getQualifier().flat ||
499 type1.getQualifier().nopersp != type2.getQualifier().nopersp) {
500 TString err = "Interpolation qualifier mismatch : " + entKey.first;
501 infoSink.info.message(EPrefixInternalError, err.c_str());
509 // Deal with input/output pairs where one is a block member but the other is loose,
510 // e.g. with ARB_separate_shader_objects
511 if (type1.getBasicType() == EbtBlock &&
512 type1.isStruct() && !type2.isStruct()) {
513 // Iterate through block members tracking layout
514 glslang::TString name;
515 type1.getStruct()->begin()->type->appendMangledName(name);
516 if (name == mangleName2
517 && type1.getQualifier().layoutLocation == type2.getQualifier().layoutLocation) return;
519 if (type2.getBasicType() == EbtBlock &&
520 type2.isStruct() && !type1.isStruct()) {
521 // Iterate through block members tracking layout
522 glslang::TString name;
523 type2.getStruct()->begin()->type->appendMangledName(name);
524 if (name == mangleName1
525 && type1.getQualifier().layoutLocation == type2.getQualifier().layoutLocation) return;
527 TString err = "Invalid In/Out variable type : " + entKey.first;
528 infoSink.info.message(EPrefixInternalError, err.c_str());
532 else if (!base->getType().isBuiltIn()) {
533 // According to spec: A link error is generated if any statically referenced input variable
534 // or block does not have a matching output
535 if (profile == EEsProfile && ent1.live) {
537 TString errorStr = name + ": not been declare as a output variable in pre shader stage.";
538 infoSink.info.message(EPrefixError, errorStr.c_str());
543 } else if (base->getQualifier().storage == EvqVaryingOut) {
544 // validate stage out;
545 if (nextStage == EShLangCount)
547 if (TSymbolTable::isBuiltInSymbol(base->getId()))
549 if (inVarMaps[nextStage] != nullptr) {
550 auto ent2 = inVarMaps[nextStage]->find(name);
551 if (ent2 != inVarMaps[nextStage]->end()) {
552 if (ent2->second.symbol->getType().getQualifier().isArrayedIo(nextStage)) {
553 TType subType(ent2->second.symbol->getType(), 0);
554 subType.appendMangledName(mangleName2);
556 ent2->second.symbol->getType().appendMangledName(mangleName2);
558 if (mangleName1 == mangleName2)
561 TString err = "Invalid In/Out variable type : " + entKey.first;
562 infoSink.info.message(EPrefixInternalError, err.c_str());
568 } else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().isPushConstant()) {
569 // validate uniform type;
570 for (int i = 0; i < EShLangCount; i++) {
571 if (i != currentStage && outVarMaps[i] != nullptr) {
572 auto ent2 = uniformVarMap[i]->find(name);
573 if (ent2 != uniformVarMap[i]->end()) {
574 ent2->second.symbol->getType().appendMangledName(mangleName2);
575 if (mangleName1 != mangleName2) {
576 ent2->second.symbol->getType().sameElementType(type);
577 TString err = "Invalid Uniform variable type : " + entKey.first;
578 infoSink.info.message(EPrefixInternalError, err.c_str());
583 // validate instance name of blocks
584 if (hadError == false &&
585 base->getType().getBasicType() == EbtBlock &&
586 IsAnonymous(base->getName()) != IsAnonymous(ent2->second.symbol->getName())) {
587 TString err = "Matched uniform block names must also either all be lacking "
588 "an instance name or all having an instance name: " + entKey.first;
589 infoSink.info.message(EPrefixInternalError, err.c_str());
593 // validate uniform block member qualifier and member names
594 auto& type1 = base->getType();
595 auto& type2 = ent2->second.symbol->getType();
596 if (hadError == false && base->getType().getBasicType() == EbtBlock) {
597 hadError = hadError || typeCheck(&type1, &type2, name.c_str(), true);
600 hadError = hadError || typeCheck(&type1, &type2, name.c_str(), false);
603 else if (base->getBasicType() == EbtBlock)
605 if (IsAnonymous(base->getName()))
607 // The name of anonymous block member can't same with default uniform variable.
608 auto blockType1 = base->getType().getStruct();
609 for (size_t memberIdx = 0; memberIdx < blockType1->size(); ++memberIdx) {
610 auto memberName = (*blockType1)[memberIdx].type->getFieldName();
611 if (uniformVarMap[i]->find(memberName) != uniformVarMap[i]->end())
613 TString err = "Invalid Uniform variable name : " + memberName;
614 infoSink.info.message(EPrefixInternalError, err.c_str());
626 TVarLiveMap *inVarMaps[EShLangCount], *outVarMaps[EShLangCount], *uniformVarMap[EShLangCount];
628 // Use for mark current shader stage for resolver
629 TIoMapResolver& resolver;
636 TSymbolValidater& operator=(TSymbolValidater&) = delete;
638 bool qualifierCheck(const TType* const type1, const TType* const type2, const std::string& name, bool isBlock)
640 bool hasError = false;
641 const TQualifier& qualifier1 = type1->getQualifier();
642 const TQualifier& qualifier2 = type2->getQualifier();
644 if (((isBlock == false) &&
645 (type1->getQualifier().storage == EvqUniform && type2->getQualifier().storage == EvqUniform)) ||
646 (type1->getQualifier().storage == EvqGlobal && type2->getQualifier().storage == EvqGlobal)) {
647 if (qualifier1.precision != qualifier2.precision) {
649 std::string errorStr = name + ": have precision conflict cross stage.";
650 infoSink.info.message(EPrefixError, errorStr.c_str());
652 if (qualifier1.hasFormat() && qualifier2.hasFormat()) {
653 if (qualifier1.layoutFormat != qualifier2.layoutFormat) {
655 std::string errorStr = name + ": have layout format conflict cross stage.";
656 infoSink.info.message(EPrefixError, errorStr.c_str());
662 if (isBlock == true) {
663 if (qualifier1.layoutPacking != qualifier2.layoutPacking) {
665 std::string errorStr = name + ": have layoutPacking conflict cross stage.";
666 infoSink.info.message(EPrefixError, errorStr.c_str());
668 if (qualifier1.layoutMatrix != qualifier2.layoutMatrix) {
670 std::string errorStr = name + ": have layoutMatrix conflict cross stage.";
671 infoSink.info.message(EPrefixError, errorStr.c_str());
673 if (qualifier1.layoutOffset != qualifier2.layoutOffset) {
675 std::string errorStr = name + ": have layoutOffset conflict cross stage.";
676 infoSink.info.message(EPrefixError, errorStr.c_str());
678 if (qualifier1.layoutAlign != qualifier2.layoutAlign) {
680 std::string errorStr = name + ": have layoutAlign conflict cross stage.";
681 infoSink.info.message(EPrefixError, errorStr.c_str());
688 bool typeCheck(const TType* const type1, const TType* const type2, const std::string& name, bool isBlock)
690 bool hasError = false;
691 if (!(type1->isStruct() && type2->isStruct())) {
692 hasError = hasError || qualifierCheck(type1, type2, name, isBlock);
695 if (type1->getBasicType() == EbtBlock && type2->getBasicType() == EbtBlock)
697 const TTypeList* typeList1 = type1->getStruct();
698 const TTypeList* typeList2 = type2->getStruct();
700 std::string newName = name;
701 size_t memberCount = typeList1->size();
703 for (size_t index = 0; index < memberCount; index++, index2++) {
704 // Skip inactive member
705 if (typeList1->at(index).type->getBasicType() == EbtVoid)
707 while (index2 < typeList2->size() && typeList2->at(index2).type->getBasicType() == EbtVoid) {
711 // TypeList1 has more members in list
712 if (index2 == typeList2->size()) {
713 std::string errorStr = name + ": struct mismatch.";
714 infoSink.info.message(EPrefixError, errorStr.c_str());
719 if (typeList1->at(index).type->getFieldName() != typeList2->at(index2).type->getFieldName()) {
720 std::string errorStr = name + ": member name mismatch.";
721 infoSink.info.message(EPrefixError, errorStr.c_str());
725 newName = typeList1->at(index).type->getFieldName().c_str();
727 hasError = hasError || typeCheck(typeList1->at(index).type, typeList2->at(index2).type, newName, isBlock);
730 while (index2 < typeList2->size())
732 // TypeList2 has more members
733 if (typeList2->at(index2).type->getBasicType() != EbtVoid) {
734 std::string errorStr = name + ": struct mismatch.";
735 infoSink.info.message(EPrefixError, errorStr.c_str());
746 struct TSlotCollector {
747 TSlotCollector(TIoMapResolver& r, TInfoSink& i) : resolver(r), infoSink(i) { }
749 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
750 resolver.reserverStorageSlot(entKey.second, infoSink);
751 resolver.reserverResourceSlot(entKey.second, infoSink);
753 TIoMapResolver& resolver;
757 TSlotCollector& operator=(TSlotCollector&) = delete;
760 TDefaultIoResolverBase::TDefaultIoResolverBase(const TIntermediate& intermediate)
761 : referenceIntermediate(intermediate)
762 , nextUniformLocation(intermediate.getUniformLocationBase())
763 , nextInputLocation(0)
764 , nextOutputLocation(0)
766 memset(stageMask, false, sizeof(bool) * (EShLangCount + 1));
767 memset(stageIntermediates, 0, sizeof(TIntermediate*) * (EShLangCount));
768 stageIntermediates[intermediate.getStage()] = &intermediate;
771 int TDefaultIoResolverBase::getBaseBinding(EShLanguage stage, TResourceType res, unsigned int set) const {
772 return stageIntermediates[stage] ? selectBaseBinding(stageIntermediates[stage]->getShiftBinding(res), stageIntermediates[stage]->getShiftBindingForSet(res, set))
773 : selectBaseBinding(referenceIntermediate.getShiftBinding(res), referenceIntermediate.getShiftBindingForSet(res, set));
776 const std::vector<std::string>& TDefaultIoResolverBase::getResourceSetBinding(EShLanguage stage) const {
777 return stageIntermediates[stage] ? stageIntermediates[stage]->getResourceSetBinding()
778 : referenceIntermediate.getResourceSetBinding();
781 bool TDefaultIoResolverBase::doAutoBindingMapping() const { return referenceIntermediate.getAutoMapBindings(); }
783 bool TDefaultIoResolverBase::doAutoLocationMapping() const { return referenceIntermediate.getAutoMapLocations(); }
785 TDefaultIoResolverBase::TSlotSet::iterator TDefaultIoResolverBase::findSlot(int set, int slot) {
786 return std::lower_bound(slots[set].begin(), slots[set].end(), slot);
789 bool TDefaultIoResolverBase::checkEmpty(int set, int slot) {
790 TSlotSet::iterator at = findSlot(set, slot);
791 return ! (at != slots[set].end() && *at == slot);
794 int TDefaultIoResolverBase::reserveSlot(int set, int slot, int size) {
795 TSlotSet::iterator at = findSlot(set, slot);
796 // tolerate aliasing, by not double-recording aliases
797 // (policy about appropriateness of the alias is higher up)
798 for (int i = 0; i < size; i++) {
799 if (at == slots[set].end() || *at != slot + i)
800 at = slots[set].insert(at, slot + i);
806 int TDefaultIoResolverBase::getFreeSlot(int set, int base, int size) {
807 TSlotSet::iterator at = findSlot(set, base);
808 if (at == slots[set].end())
809 return reserveSlot(set, base, size);
810 // look for a big enough gap
811 for (; at != slots[set].end(); ++at) {
812 if (*at - base >= size)
816 return reserveSlot(set, base, size);
819 int TDefaultIoResolverBase::resolveSet(EShLanguage stage, TVarEntryInfo& ent) {
820 const TType& type = ent.symbol->getType();
821 if (type.getQualifier().hasSet()) {
822 return ent.newSet = type.getQualifier().layoutSet;
824 // If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN)
825 if (getResourceSetBinding(stage).size() == 1) {
826 return ent.newSet = atoi(getResourceSetBinding(stage)[0].c_str());
828 return ent.newSet = 0;
831 int TDefaultIoResolverBase::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
832 const TType& type = ent.symbol->getType();
833 const char* name = ent.symbol->getAccessName().c_str();
834 // kick out of not doing this
835 if (! doAutoLocationMapping()) {
836 return ent.newLocation = -1;
838 // no locations added if already present, a built-in variable, a block, or an opaque
839 if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock ||
840 type.isAtomic() || type.isSpirvType() || (type.containsOpaque() && referenceIntermediate.getSpv().openGl == 0)) {
841 return ent.newLocation = -1;
843 // no locations on blocks of built-in variables
844 if (type.isStruct()) {
845 if (type.getStruct()->size() < 1) {
846 return ent.newLocation = -1;
848 if ((*type.getStruct())[0].type->isBuiltIn()) {
849 return ent.newLocation = -1;
852 int location = referenceIntermediate.getUniformLocationOverride(name);
853 if (location != -1) {
854 return ent.newLocation = location;
856 location = nextUniformLocation;
857 nextUniformLocation += TIntermediate::computeTypeUniformLocationSize(type);
858 return ent.newLocation = location;
861 int TDefaultIoResolverBase::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
862 const TType& type = ent.symbol->getType();
863 // kick out of not doing this
864 if (! doAutoLocationMapping()) {
865 return ent.newLocation = -1;
868 // no locations added if already present, a built-in variable, or a variable with SPIR-V decorate
869 if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getQualifier().hasSprivDecorate()) {
870 return ent.newLocation = -1;
873 // no locations on blocks of built-in variables
874 if (type.isStruct()) {
875 if (type.getStruct()->size() < 1) {
876 return ent.newLocation = -1;
878 if ((*type.getStruct())[0].type->isBuiltIn()) {
879 return ent.newLocation = -1;
882 // point to the right input or output location counter
883 int& nextLocation = type.getQualifier().isPipeInput() ? nextInputLocation : nextOutputLocation;
884 // Placeholder. This does not do proper cross-stage lining up, nor
885 // work with mixed location/no-location declarations.
886 int location = nextLocation;
887 int typeLocationSize;
888 // Don’t take into account the outer-most array if the stage’s
889 // interface is automatically an array.
890 typeLocationSize = computeTypeLocationSize(type, stage);
891 nextLocation += typeLocationSize;
892 return ent.newLocation = location;
895 int TDefaultIoResolverBase::resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) {
896 return ent.newComponent = -1;
899 int TDefaultIoResolverBase::resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) { return ent.newIndex = -1; }
901 uint32_t TDefaultIoResolverBase::computeTypeLocationSize(const TType& type, EShLanguage stage) {
902 int typeLocationSize;
903 // Don’t take into account the outer-most array if the stage’s
904 // interface is automatically an array.
905 if (type.getQualifier().isArrayedIo(stage)) {
906 TType elementType(type, 0);
907 typeLocationSize = TIntermediate::computeTypeLocationSize(elementType, stage);
909 typeLocationSize = TIntermediate::computeTypeLocationSize(type, stage);
911 return typeLocationSize;
914 //TDefaultGlslIoResolver
915 TResourceType TDefaultGlslIoResolver::getResourceType(const glslang::TType& type) {
916 if (isImageType(type)) {
919 if (isTextureType(type)) {
922 if (isSsboType(type)) {
925 if (isSamplerType(type)) {
928 if (isUboType(type)) {
934 TDefaultGlslIoResolver::TDefaultGlslIoResolver(const TIntermediate& intermediate)
935 : TDefaultIoResolverBase(intermediate)
936 , preStage(EShLangCount)
937 , currentStage(EShLangCount)
940 int TDefaultGlslIoResolver::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
941 const TType& type = ent.symbol->getType();
942 const TString& name = ent.symbol->getAccessName();
943 if (currentStage != stage) {
944 preStage = currentStage;
945 currentStage = stage;
947 // kick out if not doing this
948 if (! doAutoLocationMapping()) {
949 return ent.newLocation = -1;
951 // expand the location to each element if the symbol is a struct or array
952 if (type.getQualifier().hasLocation()) {
953 return ent.newLocation = type.getQualifier().layoutLocation;
955 // no locations added if already present, a built-in variable, or a variable with SPIR-V decorate
956 if (type.isBuiltIn() || type.getQualifier().hasSprivDecorate()) {
957 return ent.newLocation = -1;
959 // no locations on blocks of built-in variables
960 if (type.isStruct()) {
961 if (type.getStruct()->size() < 1) {
962 return ent.newLocation = -1;
964 if ((*type.getStruct())[0].type->isBuiltIn()) {
965 return ent.newLocation = -1;
968 int typeLocationSize = computeTypeLocationSize(type, stage);
969 int location = type.getQualifier().layoutLocation;
970 bool hasLocation = false;
971 EShLanguage keyStage(EShLangCount);
972 TStorageQualifier storage;
974 if (type.getQualifier().isPipeInput()) {
975 // If this symbol is a input, search pre stage's out
978 if (type.getQualifier().isPipeOutput()) {
979 // If this symbol is a output, search next stage's in
980 keyStage = currentStage;
982 // The in/out in current stage is not declared with location, but it is possible declared
983 // with explicit location in other stages, find the storageSlotMap firstly to check whether
984 // the in/out has location
985 int resourceKey = buildStorageKey(keyStage, storage);
986 if (! storageSlotMap[resourceKey].empty()) {
987 TVarSlotMap::iterator iter = storageSlotMap[resourceKey].find(name);
988 if (iter != storageSlotMap[resourceKey].end()) {
989 // If interface resource be found, set it has location and this symbol's new location
990 // equal the symbol's explicit location declaration in pre or next stage.
993 // fs: layout(..., location = 3,...) in vec4 a;
995 location = iter->second;
996 // if we want deal like that:
997 // vs: layout(location=4) out vec4 a;
1001 // layout(location = 4) in vec4 b;
1002 // we need retraverse the map.
1004 if (! hasLocation) {
1005 // If interface resource note found, It's mean the location in two stage are both implicit declarat.
1006 // So we should find a new slot for this interface.
1010 location = getFreeSlot(resourceKey, 0, typeLocationSize);
1011 storageSlotMap[resourceKey][name] = location;
1014 // the first interface declarated in a program.
1015 TVarSlotMap varSlotMap;
1016 location = getFreeSlot(resourceKey, 0, typeLocationSize);
1017 varSlotMap[name] = location;
1018 storageSlotMap[resourceKey] = varSlotMap;
1021 return ent.newLocation = location;
1024 int TDefaultGlslIoResolver::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
1025 const TType& type = ent.symbol->getType();
1026 const TString& name = ent.symbol->getAccessName();
1027 // kick out of not doing this
1028 if (! doAutoLocationMapping()) {
1029 return ent.newLocation = -1;
1031 // expand the location to each element if the symbol is a struct or array
1032 if (type.getQualifier().hasLocation() && (type.isStruct() || type.isArray())) {
1033 return ent.newLocation = type.getQualifier().layoutLocation;
1035 // no locations added if already present, a built-in variable, a block, or an opaque
1036 if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock ||
1037 type.isAtomic() || type.isSpirvType() ||
1038 (type.containsOpaque() && referenceIntermediate.getSpv().openGl == 0)) {
1039 return ent.newLocation = -1;
1041 // no locations on blocks of built-in variables
1042 if (type.isStruct()) {
1043 if (type.getStruct()->size() < 1) {
1044 return ent.newLocation = -1;
1046 if ((*type.getStruct())[0].type->isBuiltIn()) {
1047 return ent.newLocation = -1;
1051 int location = referenceIntermediate.getUniformLocationOverride(name.c_str());
1052 if (location != -1) {
1053 return ent.newLocation = location;
1056 int size = TIntermediate::computeTypeUniformLocationSize(type);
1058 // The uniform in current stage is not declared with location, but it is possible declared
1059 // with explicit location in other stages, find the storageSlotMap firstly to check whether
1060 // the uniform has location
1061 bool hasLocation = false;
1062 int resourceKey = buildStorageKey(EShLangCount, EvqUniform);
1063 TVarSlotMap& slotMap = storageSlotMap[resourceKey];
1064 // Check dose shader program has uniform resource
1065 if (! slotMap.empty()) {
1066 // If uniform resource not empty, try find a same name uniform
1067 TVarSlotMap::iterator iter = slotMap.find(name);
1068 if (iter != slotMap.end()) {
1069 // If uniform resource be found, set it has location and this symbol's new location
1070 // equal the uniform's explicit location declaration in other stage.
1072 // vs: uniform vec4 a;
1073 // fs: layout(..., location = 3,...) uniform vec4 a;
1075 location = iter->second;
1077 if (! hasLocation) {
1078 // No explicit location declaration in other stage.
1079 // So we should find a new slot for this uniform.
1081 // vs: uniform vec4 a;
1082 // fs: uniform vec4 a;
1083 location = getFreeSlot(resourceKey, 0, computeTypeLocationSize(type, currentStage));
1084 storageSlotMap[resourceKey][name] = location;
1087 // the first uniform declaration in a program.
1088 TVarSlotMap varSlotMap;
1089 location = getFreeSlot(resourceKey, 0, size);
1090 varSlotMap[name] = location;
1091 storageSlotMap[resourceKey] = varSlotMap;
1093 return ent.newLocation = location;
1096 int TDefaultGlslIoResolver::resolveBinding(EShLanguage stage, TVarEntryInfo& ent) {
1097 const TType& type = ent.symbol->getType();
1098 const TString& name = ent.symbol->getAccessName();
1099 // On OpenGL arrays of opaque types take a separate binding for each element
1100 int numBindings = referenceIntermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
1101 TResourceType resource = getResourceType(type);
1102 // don't need to handle uniform symbol, it will be handled in resolveUniformLocation
1103 if (resource == EResUbo && type.getBasicType() != EbtBlock) {
1104 return ent.newBinding = -1;
1106 // There is no 'set' qualifier in OpenGL shading language, each resource has its own
1107 // binding name space, so remap the 'set' to resource type which make each resource
1108 // binding is valid from 0 to MAX_XXRESOURCE_BINDINGS
1109 int set = referenceIntermediate.getSpv().openGl != 0 ? resource : ent.newSet;
1110 int resourceKey = set;
1111 if (resource < EResCount) {
1112 if (type.getQualifier().hasBinding()) {
1113 int newBinding = reserveSlot(resourceKey, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding, numBindings);
1114 return ent.newBinding = newBinding;
1117 // The resource in current stage is not declared with binding, but it is possible declared
1118 // with explicit binding in other stages, find the resourceSlotMap firstly to check whether
1119 // the resource has binding, don't need to allocate if it already has a binding
1120 bool hasBinding = false;
1121 ent.newBinding = -1; // leave as -1 if it isn't set below
1123 if (! resourceSlotMap[resourceKey].empty()) {
1124 TVarSlotMap::iterator iter = resourceSlotMap[resourceKey].find(name);
1125 if (iter != resourceSlotMap[resourceKey].end()) {
1127 ent.newBinding = iter->second;
1130 if (!hasBinding && (ent.live && doAutoBindingMapping())) {
1131 // find free slot, the caller did make sure it passes all vars with binding
1132 // first and now all are passed that do not have a binding and needs one
1133 int binding = getFreeSlot(resourceKey, getBaseBinding(stage, resource, set), numBindings);
1134 resourceSlotMap[resourceKey][name] = binding;
1135 ent.newBinding = binding;
1137 return ent.newBinding;
1140 return ent.newBinding = -1;
1143 void TDefaultGlslIoResolver::beginResolve(EShLanguage stage) {
1144 // reset stage state
1145 if (stage == EShLangCount)
1146 preStage = currentStage = stage;
1147 // update stage state
1148 else if (currentStage != stage) {
1149 preStage = currentStage;
1150 currentStage = stage;
1154 void TDefaultGlslIoResolver::endResolve(EShLanguage /*stage*/) {
1158 void TDefaultGlslIoResolver::beginCollect(EShLanguage stage) {
1159 // reset stage state
1160 if (stage == EShLangCount)
1161 preStage = currentStage = stage;
1162 // update stage state
1163 else if (currentStage != stage) {
1164 preStage = currentStage;
1165 currentStage = stage;
1169 void TDefaultGlslIoResolver::endCollect(EShLanguage /*stage*/) {
1173 void TDefaultGlslIoResolver::reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
1174 const TType& type = ent.symbol->getType();
1175 const TString& name = ent.symbol->getAccessName();
1176 TStorageQualifier storage = type.getQualifier().storage;
1177 EShLanguage stage(EShLangCount);
1180 if (type.getBasicType() != EbtBlock && type.getQualifier().hasLocation()) {
1182 // Reserve the slots for the uniforms who has explicit location
1183 int storageKey = buildStorageKey(EShLangCount, EvqUniform);
1184 int location = type.getQualifier().layoutLocation;
1185 TVarSlotMap& varSlotMap = storageSlotMap[storageKey];
1186 TVarSlotMap::iterator iter = varSlotMap.find(name);
1187 if (iter == varSlotMap.end()) {
1188 int numLocations = TIntermediate::computeTypeUniformLocationSize(type);
1189 reserveSlot(storageKey, location, numLocations);
1190 varSlotMap[name] = location;
1192 // Allocate location by name for OpenGL driver, so the uniform in different
1193 // stages should be declared with the same location
1194 if (iter->second != location) {
1195 TString errorMsg = "Invalid location: " + name;
1196 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
1205 // Reserve the slots for the inout who has explicit location
1206 if (type.getQualifier().hasLocation()) {
1207 stage = storage == EvqVaryingIn ? preStage : stage;
1208 stage = storage == EvqVaryingOut ? currentStage : stage;
1209 int storageKey = buildStorageKey(stage, EvqInOut);
1210 int location = type.getQualifier().layoutLocation;
1211 TVarSlotMap& varSlotMap = storageSlotMap[storageKey];
1212 TVarSlotMap::iterator iter = varSlotMap.find(name);
1213 if (iter == varSlotMap.end()) {
1214 int numLocations = TIntermediate::computeTypeUniformLocationSize(type);
1215 reserveSlot(storageKey, location, numLocations);
1216 varSlotMap[name] = location;
1218 // Allocate location by name for OpenGL driver, so the uniform in different
1219 // stages should be declared with the same location
1220 if (iter->second != location) {
1221 TString errorMsg = "Invalid location: " + name;
1222 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
1233 void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
1234 const TType& type = ent.symbol->getType();
1235 const TString& name = ent.symbol->getAccessName();
1236 TResourceType resource = getResourceType(type);
1237 int set = referenceIntermediate.getSpv().openGl != 0 ? resource : resolveSet(ent.stage, ent);
1238 int resourceKey = set;
1240 if (type.getQualifier().hasBinding()) {
1241 TVarSlotMap& varSlotMap = resourceSlotMap[resourceKey];
1242 TVarSlotMap::iterator iter = varSlotMap.find(name);
1243 int binding = type.getQualifier().layoutBinding + getBaseBinding(ent.stage, resource, set);
1245 if (iter == varSlotMap.end()) {
1246 // Reserve the slots for the ubo, ssbo and opaques who has explicit binding
1247 int numBindings = referenceIntermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
1248 varSlotMap[name] = binding;
1249 reserveSlot(resourceKey, binding, numBindings);
1251 // Allocate binding by name for OpenGL driver, so the resource in different
1252 // stages should be declared with the same binding
1253 if (iter->second != binding) {
1254 TString errorMsg = "Invalid binding: " + name;
1255 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
1262 //TDefaultGlslIoResolver end
1265 * Basic implementation of glslang::TIoMapResolver that replaces the
1266 * previous offset behavior.
1267 * It does the same, uses the offsets for the corresponding uniform
1268 * types. Also respects the EOptionAutoMapBindings flag and binds
1274 struct TDefaultIoResolver : public TDefaultIoResolverBase {
1275 TDefaultIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
1277 bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
1279 TResourceType getResourceType(const glslang::TType& type) override {
1280 if (isImageType(type)) {
1283 if (isTextureType(type)) {
1286 if (isSsboType(type)) {
1289 if (isSamplerType(type)) {
1292 if (isUboType(type)) {
1298 int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) override {
1299 const TType& type = ent.symbol->getType();
1300 const int set = getLayoutSet(type);
1301 // On OpenGL arrays of opaque types take a seperate binding for each element
1302 int numBindings = referenceIntermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
1303 TResourceType resource = getResourceType(type);
1304 if (resource < EResCount) {
1305 if (type.getQualifier().hasBinding()) {
1306 return ent.newBinding = reserveSlot(
1307 set, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding, numBindings);
1308 } else if (ent.live && doAutoBindingMapping()) {
1309 // find free slot, the caller did make sure it passes all vars with binding
1310 // first and now all are passed that do not have a binding and needs one
1311 return ent.newBinding = getFreeSlot(set, getBaseBinding(stage, resource, set), numBindings);
1314 return ent.newBinding = -1;
1319 /********************************************************************************
1320 The following IO resolver maps types in HLSL register space, as follows:
1322 t - for shader resource views (SRV)
1344 SAMPLERCOMPARISONSTATE
1346 u - for unordered access views (UAV)
1349 APPENDSTRUCTUREDBUFFER
1350 CONSUMESTRUCTUREDBUFFER
1358 b - for constant buffer views (CBV)
1361 ********************************************************************************/
1362 struct TDefaultHlslIoResolver : public TDefaultIoResolverBase {
1363 TDefaultHlslIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
1365 bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
1367 TResourceType getResourceType(const glslang::TType& type) override {
1368 if (isUavType(type)) {
1371 if (isSrvType(type)) {
1374 if (isSamplerType(type)) {
1377 if (isUboType(type)) {
1383 int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) override {
1384 const TType& type = ent.symbol->getType();
1385 const int set = getLayoutSet(type);
1386 TResourceType resource = getResourceType(type);
1387 if (resource < EResCount) {
1388 if (type.getQualifier().hasBinding()) {
1389 return ent.newBinding = reserveSlot(set, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding);
1390 } else if (ent.live && doAutoBindingMapping()) {
1391 // find free slot, the caller did make sure it passes all vars with binding
1392 // first and now all are passed that do not have a binding and needs one
1393 return ent.newBinding = getFreeSlot(set, getBaseBinding(stage, resource, set));
1396 return ent.newBinding = -1;
1401 // Map I/O variables to provided offsets, and make bindings for
1402 // unbound but live variables.
1404 // Returns false if the input is too malformed to do this.
1405 bool TIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) {
1406 bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() ||
1407 intermediate.getAutoMapLocations();
1408 // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce
1409 // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true.
1410 for (int res = 0; (res < EResCount && !somethingToDo); ++res) {
1411 somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
1412 intermediate.hasShiftBindingForSet(TResourceType(res));
1414 if (! somethingToDo && resolver == nullptr)
1416 if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive())
1418 TIntermNode* root = intermediate.getTreeRoot();
1419 if (root == nullptr)
1421 // if no resolver is provided, use the default resolver with the given shifts and auto map settings
1422 TDefaultIoResolver defaultResolver(intermediate);
1424 TDefaultHlslIoResolver defaultHlslResolver(intermediate);
1425 if (resolver == nullptr) {
1426 // TODO: use a passed in IO mapper for this
1427 if (intermediate.usingHlslIoMapping())
1428 resolver = &defaultHlslResolver;
1430 resolver = &defaultResolver;
1433 resolver = &defaultResolver;
1435 resolver->addStage(stage, intermediate);
1437 TVarLiveMap inVarMap, outVarMap, uniformVarMap;
1438 TVarLiveVector inVector, outVector, uniformVector;
1439 TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap);
1440 TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap);
1441 root->traverse(&iter_binding_all);
1442 iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
1443 while (! iter_binding_live.destinations.empty()) {
1444 TIntermNode* destination = iter_binding_live.destinations.back();
1445 iter_binding_live.destinations.pop_back();
1446 destination->traverse(&iter_binding_live);
1449 // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info.
1450 for (auto& var : inVarMap) { inVector.push_back(var); }
1451 std::sort(inVector.begin(), inVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1452 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1454 for (auto& var : outVarMap) { outVector.push_back(var); }
1455 std::sort(outVector.begin(), outVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1456 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1458 for (auto& var : uniformVarMap) { uniformVector.push_back(var); }
1459 std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1460 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1462 bool hadError = false;
1463 TVarLiveMap* dummyUniformVarMap[EShLangCount] = {};
1464 TNotifyInOutAdaptor inOutNotify(stage, *resolver);
1465 TNotifyUniformAdaptor uniformNotify(stage, *resolver);
1466 TResolverUniformAdaptor uniformResolve(stage, *resolver, dummyUniformVarMap, infoSink, hadError);
1467 TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError);
1468 resolver->beginNotifications(stage);
1469 std::for_each(inVector.begin(), inVector.end(), inOutNotify);
1470 std::for_each(outVector.begin(), outVector.end(), inOutNotify);
1471 std::for_each(uniformVector.begin(), uniformVector.end(), uniformNotify);
1472 resolver->endNotifications(stage);
1473 resolver->beginResolve(stage);
1474 for (auto& var : inVector) { inOutResolve(var); }
1475 std::for_each(inVector.begin(), inVector.end(), [&inVarMap](TVarLivePair p) {
1476 auto at = inVarMap.find(p.second.symbol->getAccessName());
1477 if (at != inVarMap.end() && p.second.id == at->second.id)
1478 at->second = p.second;
1480 for (auto& var : outVector) { inOutResolve(var); }
1481 std::for_each(outVector.begin(), outVector.end(), [&outVarMap](TVarLivePair p) {
1482 auto at = outVarMap.find(p.second.symbol->getAccessName());
1483 if (at != outVarMap.end() && p.second.id == at->second.id)
1484 at->second = p.second;
1486 std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve);
1487 std::for_each(uniformVector.begin(), uniformVector.end(), [&uniformVarMap](TVarLivePair p) {
1488 auto at = uniformVarMap.find(p.second.symbol->getAccessName());
1489 if (at != uniformVarMap.end() && p.second.id == at->second.id)
1490 at->second = p.second;
1492 resolver->endResolve(stage);
1494 TVarSetTraverser iter_iomap(intermediate, inVarMap, outVarMap, uniformVarMap);
1495 root->traverse(&iter_iomap);
1500 // Map I/O variables to provided offsets, and make bindings for
1501 // unbound but live variables.
1503 // Returns false if the input is too malformed to do this.
1504 bool TGlslIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) {
1505 bool somethingToDo = !intermediate.getResourceSetBinding().empty() ||
1506 intermediate.getAutoMapBindings() ||
1507 intermediate.getAutoMapLocations();
1509 // Profile and version are use for symbol validate.
1510 profile = intermediate.getProfile();
1511 version = intermediate.getVersion();
1513 // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce
1514 // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true.
1515 for (int res = 0; (res < EResCount && !somethingToDo); ++res) {
1516 somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
1517 intermediate.hasShiftBindingForSet(TResourceType(res));
1519 if (! somethingToDo && resolver == nullptr) {
1522 if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive()) {
1525 TIntermNode* root = intermediate.getTreeRoot();
1526 if (root == nullptr) {
1529 // if no resolver is provided, use the default resolver with the given shifts and auto map settings
1530 TDefaultGlslIoResolver defaultResolver(intermediate);
1532 TDefaultHlslIoResolver defaultHlslResolver(intermediate);
1533 if (resolver == nullptr) {
1534 // TODO: use a passed in IO mapper for this
1535 if (intermediate.usingHlslIoMapping())
1536 resolver = &defaultHlslResolver;
1538 resolver = &defaultResolver;
1541 if (resolver == nullptr) {
1542 resolver = &defaultResolver;
1545 resolver->addStage(stage, intermediate);
1546 inVarMaps[stage] = new TVarLiveMap(); outVarMaps[stage] = new TVarLiveMap(); uniformVarMap[stage] = new TVarLiveMap();
1547 TVarGatherTraverser iter_binding_all(intermediate, true, *inVarMaps[stage], *outVarMaps[stage],
1548 *uniformVarMap[stage]);
1549 TVarGatherTraverser iter_binding_live(intermediate, false, *inVarMaps[stage], *outVarMaps[stage],
1550 *uniformVarMap[stage]);
1551 root->traverse(&iter_binding_all);
1552 iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
1553 while (! iter_binding_live.destinations.empty()) {
1554 TIntermNode* destination = iter_binding_live.destinations.back();
1555 iter_binding_live.destinations.pop_back();
1556 destination->traverse(&iter_binding_live);
1559 TNotifyInOutAdaptor inOutNotify(stage, *resolver);
1560 TNotifyUniformAdaptor uniformNotify(stage, *resolver);
1561 // Resolve current stage input symbol location with previous stage output here,
1562 // uniform symbol, ubo, ssbo and opaque symbols are per-program resource,
1563 // will resolve uniform symbol location and ubo/ssbo/opaque binding in doMap()
1564 resolver->beginNotifications(stage);
1565 std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), inOutNotify);
1566 std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), inOutNotify);
1567 std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), uniformNotify);
1568 resolver->endNotifications(stage);
1569 TSlotCollector slotCollector(*resolver, infoSink);
1570 resolver->beginCollect(stage);
1571 std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), slotCollector);
1572 std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), slotCollector);
1573 std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), slotCollector);
1574 resolver->endCollect(stage);
1575 intermediates[stage] = &intermediate;
1579 bool TGlslIoMapper::doMap(TIoMapResolver* resolver, TInfoSink& infoSink) {
1580 resolver->endResolve(EShLangCount);
1582 //Resolve uniform location, ubo/ssbo/opaque bindings across stages
1583 TResolverUniformAdaptor uniformResolve(EShLangCount, *resolver, uniformVarMap, infoSink, hadError);
1584 TResolverInOutAdaptor inOutResolve(EShLangCount, *resolver, infoSink, hadError);
1585 TSymbolValidater symbolValidater(*resolver, infoSink, inVarMaps,
1586 outVarMaps, uniformVarMap, hadError, profile, version);
1588 TVarLiveVector inVectors[EShLangCount];
1589 TVarLiveVector outVectors[EShLangCount];
1590 TVarLiveVector uniformVector;
1592 resolver->beginResolve(EShLangCount);
1593 for (int stage = EShLangVertex; stage < EShLangCount; stage++) {
1594 if (inVarMaps[stage] != nullptr) {
1595 inOutResolve.setStage(EShLanguage(stage));
1597 // copy vars into a sorted list
1598 std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(),
1599 [&inVectors, stage](TVarLivePair p) { inVectors[stage].push_back(p); });
1600 std::sort(inVectors[stage].begin(), inVectors[stage].end(),
1601 [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1602 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1605 std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(),
1606 [&outVectors, stage](TVarLivePair p) { outVectors[stage].push_back(p); });
1607 std::sort(outVectors[stage].begin(), outVectors[stage].end(),
1608 [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1609 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1612 for (auto& var : inVectors[stage]) { symbolValidater(var); }
1613 for (auto& var : inVectors[stage]) { inOutResolve(var); }
1614 for (auto& var : outVectors[stage]) { symbolValidater(var); }
1615 for (auto& var : outVectors[stage]) { inOutResolve(var); }
1617 // copy results back into maps
1618 std::for_each(inVectors[stage].begin(), inVectors[stage].end(),
1619 [this, stage](TVarLivePair p) {
1620 auto at = inVarMaps[stage]->find(p.first);
1621 if (at != inVarMaps[stage]->end())
1622 at->second = p.second;
1625 std::for_each(outVectors[stage].begin(), outVectors[stage].end(),
1626 [this, stage](TVarLivePair p) {
1627 auto at = outVarMaps[stage]->find(p.first);
1628 if (at != outVarMaps[stage]->end())
1629 at->second = p.second;
1633 if (uniformVarMap[stage] != nullptr) {
1634 uniformResolve.setStage(EShLanguage(stage));
1635 for (auto& var : *(uniformVarMap[stage])) { uniformVector.push_back(var); }
1638 std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1639 return TVarEntryInfo::TOrderByPriorityAndLive()(p1.second, p2.second);
1641 for (auto& var : uniformVector) { symbolValidater(var); }
1642 for (auto& var : uniformVector) { uniformResolve(var); }
1643 std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1644 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1646 resolver->endResolve(EShLangCount);
1647 if (autoPushConstantBlockName.length()) {
1648 bool upgraded = false;
1649 for (size_t stage = 0; stage < EShLangCount; stage++) {
1650 if (intermediates[stage] != nullptr) {
1651 TVarLiveMap** pUniformVarMap = uniformResolve.uniformVarMap;
1652 auto at = pUniformVarMap[stage]->find(autoPushConstantBlockName);
1653 if (at == pUniformVarMap[stage]->end())
1655 TQualifier& qualifier = at->second.symbol->getQualifier();
1656 if (!qualifier.isUniform())
1658 TType& t = at->second.symbol->getWritableType();
1660 TIntermediate::getBaseAlignment(t, size, stride, autoPushConstantBlockPacking,
1661 qualifier.layoutMatrix == ElmRowMajor);
1662 if (size <= int(autoPushConstantMaxSize)) {
1663 qualifier.setBlockStorage(EbsPushConstant);
1664 qualifier.layoutPacking = autoPushConstantBlockPacking;
1665 // Push constants don't have set/binding etc. decorations, remove those.
1666 qualifier.layoutSet = TQualifier::layoutSetEnd;
1667 at->second.clearNewAssignments();
1673 // If it's been upgraded to push_constant, then remove it from the uniformVector
1674 // so it doesn't get a set/binding assigned to it.
1677 auto at = std::find_if(uniformVector.begin(), uniformVector.end(),
1678 [this](const TVarLivePair& p) { return p.first == autoPushConstantBlockName; });
1679 if (at != uniformVector.end())
1680 uniformVector.erase(at);
1686 for (size_t stage = 0; stage < EShLangCount; stage++) {
1687 if (intermediates[stage] != nullptr) {
1688 // traverse each stage, set new location to each input/output and unifom symbol, set new binding to
1689 // ubo, ssbo and opaque symbols
1690 TVarLiveMap** pUniformVarMap = uniformResolve.uniformVarMap;
1691 std::for_each(uniformVector.begin(), uniformVector.end(), [pUniformVarMap, stage](TVarLivePair p) {
1692 auto at = pUniformVarMap[stage]->find(p.second.symbol->getAccessName());
1693 if (at != pUniformVarMap[stage]->end() && at->second.id == p.second.id){
1694 int resolvedBinding = at->second.newBinding;
1695 at->second = p.second;
1696 if (resolvedBinding > 0)
1697 at->second.newBinding = resolvedBinding;
1700 TVarSetTraverser iter_iomap(*intermediates[stage], *inVarMaps[stage], *outVarMaps[stage],
1701 *uniformResolve.uniformVarMap[stage]);
1702 intermediates[stage]->getTreeRoot()->traverse(&iter_iomap);
1711 } // end namespace glslang
1713 #endif // !GLSLANG_WEB && !GLSLANG_ANGLE