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.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 // Deal with input/output pairs where one is a block member but the other is loose,
518 // e.g. with ARB_separate_shader_objects
519 if (type1.getBasicType() == EbtBlock &&
520 type1.isStruct() && !type2.isStruct()) {
521 // Iterate through block members tracking layout
522 glslang::TString name;
523 type1.getStruct()->begin()->type->appendMangledName(name);
524 if (name == mangleName2
525 && type1.getQualifier().layoutLocation == type2.getQualifier().layoutLocation) return;
527 if (type2.getBasicType() == EbtBlock &&
528 type2.isStruct() && !type1.isStruct()) {
529 // Iterate through block members tracking layout
530 glslang::TString name;
531 type2.getStruct()->begin()->type->appendMangledName(name);
532 if (name == mangleName1
533 && type1.getQualifier().layoutLocation == type2.getQualifier().layoutLocation) return;
535 TString err = "Invalid In/Out variable type : " + entKey.first;
536 infoSink.info.message(EPrefixInternalError, err.c_str());
540 else if (!base->getType().isBuiltIn()) {
541 // According to spec: A link error is generated if any statically referenced input variable
542 // or block does not have a matching output
543 if (profile == EEsProfile && ent1.live) {
545 TString errorStr = name + ": not been declare as a output variable in pre shader stage.";
546 infoSink.info.message(EPrefixError, errorStr.c_str());
551 } else if (base->getQualifier().storage == EvqVaryingOut) {
552 // validate stage out;
553 if (nextStage == EShLangCount)
555 if (TSymbolTable::isBuiltInSymbol(base->getId()))
557 if (inVarMaps[nextStage] != nullptr) {
558 auto ent2 = inVarMaps[nextStage]->find(name);
559 if (ent2 != inVarMaps[nextStage]->end()) {
560 if (ent2->second.symbol->getType().getQualifier().isArrayedIo(nextStage)) {
561 TType subType(ent2->second.symbol->getType(), 0);
562 subType.appendMangledName(mangleName2);
564 ent2->second.symbol->getType().appendMangledName(mangleName2);
566 if (mangleName1 == mangleName2)
569 TString err = "Invalid In/Out variable type : " + entKey.first;
570 infoSink.info.message(EPrefixInternalError, err.c_str());
576 } else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().isPushConstant()) {
577 // validate uniform type;
578 for (int i = 0; i < EShLangCount; i++) {
579 if (i != currentStage && outVarMaps[i] != nullptr) {
580 auto ent2 = uniformVarMap[i]->find(name);
581 if (ent2 != uniformVarMap[i]->end()) {
582 ent2->second.symbol->getType().appendMangledName(mangleName2);
583 if (mangleName1 != mangleName2) {
584 ent2->second.symbol->getType().sameElementType(type);
585 TString err = "Invalid Uniform variable type : " + entKey.first;
586 infoSink.info.message(EPrefixInternalError, err.c_str());
591 // validate instance name of blocks
592 if (hadError == false &&
593 base->getType().getBasicType() == EbtBlock &&
594 IsAnonymous(base->getName()) != IsAnonymous(ent2->second.symbol->getName())) {
595 TString err = "Matched uniform block names must also either all be lacking "
596 "an instance name or all having an instance name: " + entKey.first;
597 infoSink.info.message(EPrefixInternalError, err.c_str());
601 // validate uniform block member qualifier and member names
602 auto& type1 = base->getType();
603 auto& type2 = ent2->second.symbol->getType();
604 if (hadError == false && base->getType().getBasicType() == EbtBlock) {
605 hadError = hadError || typeCheck(&type1, &type2, name.c_str(), true);
608 hadError = hadError || typeCheck(&type1, &type2, name.c_str(), false);
611 else if (base->getBasicType() == EbtBlock)
613 if (IsAnonymous(base->getName()))
615 // The name of anonymous block member can't same with default uniform variable.
616 auto blockType1 = base->getType().getStruct();
617 for (size_t memberIdx = 0; memberIdx < blockType1->size(); ++memberIdx) {
618 auto memberName = (*blockType1)[memberIdx].type->getFieldName();
619 if (uniformVarMap[i]->find(memberName) != uniformVarMap[i]->end())
621 TString err = "Invalid Uniform variable name : " + memberName;
622 infoSink.info.message(EPrefixInternalError, err.c_str());
634 TVarLiveMap *inVarMaps[EShLangCount], *outVarMaps[EShLangCount], *uniformVarMap[EShLangCount];
636 // Use for mark current shader stage for resolver
637 TIoMapResolver& resolver;
644 TSymbolValidater& operator=(TSymbolValidater&) = delete;
646 bool qualifierCheck(const TType* const type1, const TType* const type2, const std::string& name, bool isBlock)
648 bool hasError = false;
649 const TQualifier& qualifier1 = type1->getQualifier();
650 const TQualifier& qualifier2 = type2->getQualifier();
652 if (((isBlock == false) &&
653 (type1->getQualifier().storage == EvqUniform && type2->getQualifier().storage == EvqUniform)) ||
654 (type1->getQualifier().storage == EvqGlobal && type2->getQualifier().storage == EvqGlobal)) {
655 if (qualifier1.precision != qualifier2.precision) {
657 std::string errorStr = name + ": have precision conflict cross stage.";
658 infoSink.info.message(EPrefixError, errorStr.c_str());
660 if (qualifier1.hasFormat() && qualifier2.hasFormat()) {
661 if (qualifier1.layoutFormat != qualifier2.layoutFormat) {
663 std::string errorStr = name + ": have layout format conflict cross stage.";
664 infoSink.info.message(EPrefixError, errorStr.c_str());
670 if (isBlock == true) {
671 if (qualifier1.layoutPacking != qualifier2.layoutPacking) {
673 std::string errorStr = name + ": have layoutPacking conflict cross stage.";
674 infoSink.info.message(EPrefixError, errorStr.c_str());
676 if (qualifier1.layoutMatrix != qualifier2.layoutMatrix) {
678 std::string errorStr = name + ": have layoutMatrix conflict cross stage.";
679 infoSink.info.message(EPrefixError, errorStr.c_str());
681 if (qualifier1.layoutOffset != qualifier2.layoutOffset) {
683 std::string errorStr = name + ": have layoutOffset conflict cross stage.";
684 infoSink.info.message(EPrefixError, errorStr.c_str());
686 if (qualifier1.layoutAlign != qualifier2.layoutAlign) {
688 std::string errorStr = name + ": have layoutAlign conflict cross stage.";
689 infoSink.info.message(EPrefixError, errorStr.c_str());
696 bool typeCheck(const TType* const type1, const TType* const type2, const std::string& name, bool isBlock)
698 bool hasError = false;
699 if (!(type1->isStruct() && type2->isStruct())) {
700 hasError = hasError || qualifierCheck(type1, type2, name, isBlock);
703 if (type1->getBasicType() == EbtBlock && type2->getBasicType() == EbtBlock)
705 const TTypeList* typeList1 = type1->getStruct();
706 const TTypeList* typeList2 = type2->getStruct();
708 std::string newName = name;
709 size_t memberCount = typeList1->size();
711 for (size_t index = 0; index < memberCount; index++, index2++) {
712 // Skip inactive member
713 if (typeList1->at(index).type->getBasicType() == EbtVoid)
715 while (index2 < typeList2->size() && typeList2->at(index2).type->getBasicType() == EbtVoid) {
719 // TypeList1 has more members in list
720 if (index2 == typeList2->size()) {
721 std::string errorStr = name + ": struct mismatch.";
722 infoSink.info.message(EPrefixError, errorStr.c_str());
727 if (typeList1->at(index).type->getFieldName() != typeList2->at(index2).type->getFieldName()) {
728 std::string errorStr = name + ": member name mismatch.";
729 infoSink.info.message(EPrefixError, errorStr.c_str());
733 newName = typeList1->at(index).type->getFieldName().c_str();
735 hasError = hasError || typeCheck(typeList1->at(index).type, typeList2->at(index2).type, newName, isBlock);
738 while (index2 < typeList2->size())
740 // TypeList2 has more members
741 if (typeList2->at(index2).type->getBasicType() != EbtVoid) {
742 std::string errorStr = name + ": struct mismatch.";
743 infoSink.info.message(EPrefixError, errorStr.c_str());
754 struct TSlotCollector {
755 TSlotCollector(TIoMapResolver& r, TInfoSink& i) : resolver(r), infoSink(i) { }
757 inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
758 resolver.reserverStorageSlot(entKey.second, infoSink);
759 resolver.reserverResourceSlot(entKey.second, infoSink);
761 TIoMapResolver& resolver;
765 TSlotCollector& operator=(TSlotCollector&) = delete;
768 TDefaultIoResolverBase::TDefaultIoResolverBase(const TIntermediate& intermediate)
769 : referenceIntermediate(intermediate)
770 , nextUniformLocation(intermediate.getUniformLocationBase())
771 , nextInputLocation(0)
772 , nextOutputLocation(0)
774 memset(stageMask, false, sizeof(bool) * (EShLangCount + 1));
775 memset(stageIntermediates, 0, sizeof(TIntermediate*) * (EShLangCount));
776 stageIntermediates[intermediate.getStage()] = &intermediate;
779 int TDefaultIoResolverBase::getBaseBinding(EShLanguage stage, TResourceType res, unsigned int set) const {
780 return stageIntermediates[stage] ? selectBaseBinding(stageIntermediates[stage]->getShiftBinding(res), stageIntermediates[stage]->getShiftBindingForSet(res, set))
781 : selectBaseBinding(referenceIntermediate.getShiftBinding(res), referenceIntermediate.getShiftBindingForSet(res, set));
784 const std::vector<std::string>& TDefaultIoResolverBase::getResourceSetBinding(EShLanguage stage) const {
785 return stageIntermediates[stage] ? stageIntermediates[stage]->getResourceSetBinding()
786 : referenceIntermediate.getResourceSetBinding();
789 bool TDefaultIoResolverBase::doAutoBindingMapping() const { return referenceIntermediate.getAutoMapBindings(); }
791 bool TDefaultIoResolverBase::doAutoLocationMapping() const { return referenceIntermediate.getAutoMapLocations(); }
793 TDefaultIoResolverBase::TSlotSet::iterator TDefaultIoResolverBase::findSlot(int set, int slot) {
794 return std::lower_bound(slots[set].begin(), slots[set].end(), slot);
797 bool TDefaultIoResolverBase::checkEmpty(int set, int slot) {
798 TSlotSet::iterator at = findSlot(set, slot);
799 return ! (at != slots[set].end() && *at == slot);
802 int TDefaultIoResolverBase::reserveSlot(int set, int slot, int size) {
803 TSlotSet::iterator at = findSlot(set, slot);
804 // tolerate aliasing, by not double-recording aliases
805 // (policy about appropriateness of the alias is higher up)
806 for (int i = 0; i < size; i++) {
807 if (at == slots[set].end() || *at != slot + i)
808 at = slots[set].insert(at, slot + i);
814 int TDefaultIoResolverBase::getFreeSlot(int set, int base, int size) {
815 TSlotSet::iterator at = findSlot(set, base);
816 if (at == slots[set].end())
817 return reserveSlot(set, base, size);
818 // look for a big enough gap
819 for (; at != slots[set].end(); ++at) {
820 if (*at - base >= size)
824 return reserveSlot(set, base, size);
827 int TDefaultIoResolverBase::resolveSet(EShLanguage stage, TVarEntryInfo& ent) {
828 const TType& type = ent.symbol->getType();
829 if (type.getQualifier().hasSet()) {
830 return ent.newSet = type.getQualifier().layoutSet;
832 // If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN)
833 if (getResourceSetBinding(stage).size() == 1) {
834 return ent.newSet = atoi(getResourceSetBinding(stage)[0].c_str());
836 return ent.newSet = 0;
839 int TDefaultIoResolverBase::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
840 const TType& type = ent.symbol->getType();
841 const char* name = ent.symbol->getAccessName().c_str();
842 // kick out of not doing this
843 if (! doAutoLocationMapping()) {
844 return ent.newLocation = -1;
846 // no locations added if already present, a built-in variable, a block, or an opaque
847 if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock ||
848 type.isAtomic() || type.isSpirvType() || (type.containsOpaque() && referenceIntermediate.getSpv().openGl == 0)) {
849 return ent.newLocation = -1;
851 // no locations on blocks of built-in variables
852 if (type.isStruct()) {
853 if (type.getStruct()->size() < 1) {
854 return ent.newLocation = -1;
856 if ((*type.getStruct())[0].type->isBuiltIn()) {
857 return ent.newLocation = -1;
860 int location = referenceIntermediate.getUniformLocationOverride(name);
861 if (location != -1) {
862 return ent.newLocation = location;
864 location = nextUniformLocation;
865 nextUniformLocation += TIntermediate::computeTypeUniformLocationSize(type);
866 return ent.newLocation = location;
869 int TDefaultIoResolverBase::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
870 const TType& type = ent.symbol->getType();
871 // kick out of not doing this
872 if (! doAutoLocationMapping()) {
873 return ent.newLocation = -1;
876 // no locations added if already present, a built-in variable, or a variable with SPIR-V decorate
877 if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getQualifier().hasSprivDecorate()) {
878 return ent.newLocation = -1;
881 // no locations on blocks of built-in variables
882 if (type.isStruct()) {
883 if (type.getStruct()->size() < 1) {
884 return ent.newLocation = -1;
886 if ((*type.getStruct())[0].type->isBuiltIn()) {
887 return ent.newLocation = -1;
890 // point to the right input or output location counter
891 int& nextLocation = type.getQualifier().isPipeInput() ? nextInputLocation : nextOutputLocation;
892 // Placeholder. This does not do proper cross-stage lining up, nor
893 // work with mixed location/no-location declarations.
894 int location = nextLocation;
895 int typeLocationSize;
896 // Don’t take into account the outer-most array if the stage’s
897 // interface is automatically an array.
898 typeLocationSize = computeTypeLocationSize(type, stage);
899 nextLocation += typeLocationSize;
900 return ent.newLocation = location;
903 int TDefaultIoResolverBase::resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) {
904 return ent.newComponent = -1;
907 int TDefaultIoResolverBase::resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) { return ent.newIndex = -1; }
909 uint32_t TDefaultIoResolverBase::computeTypeLocationSize(const TType& type, EShLanguage stage) {
910 int typeLocationSize;
911 // Don’t take into account the outer-most array if the stage’s
912 // interface is automatically an array.
913 if (type.getQualifier().isArrayedIo(stage)) {
914 TType elementType(type, 0);
915 typeLocationSize = TIntermediate::computeTypeLocationSize(elementType, stage);
917 typeLocationSize = TIntermediate::computeTypeLocationSize(type, stage);
919 return typeLocationSize;
922 //TDefaultGlslIoResolver
923 TResourceType TDefaultGlslIoResolver::getResourceType(const glslang::TType& type) {
924 if (isImageType(type)) {
927 if (isTextureType(type)) {
930 if (isSsboType(type)) {
933 if (isSamplerType(type)) {
936 if (isUboType(type)) {
942 TDefaultGlslIoResolver::TDefaultGlslIoResolver(const TIntermediate& intermediate)
943 : TDefaultIoResolverBase(intermediate)
944 , preStage(EShLangCount)
945 , currentStage(EShLangCount)
948 int TDefaultGlslIoResolver::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
949 const TType& type = ent.symbol->getType();
950 const TString& name = ent.symbol->getAccessName();
951 if (currentStage != stage) {
952 preStage = currentStage;
953 currentStage = stage;
955 // kick out if not doing this
956 if (! doAutoLocationMapping()) {
957 return ent.newLocation = -1;
959 // expand the location to each element if the symbol is a struct or array
960 if (type.getQualifier().hasLocation()) {
961 return ent.newLocation = type.getQualifier().layoutLocation;
963 // no locations added if already present, a built-in variable, or a variable with SPIR-V decorate
964 if (type.isBuiltIn() || type.getQualifier().hasSprivDecorate()) {
965 return ent.newLocation = -1;
967 // no locations on blocks of built-in variables
968 if (type.isStruct()) {
969 if (type.getStruct()->size() < 1) {
970 return ent.newLocation = -1;
972 if ((*type.getStruct())[0].type->isBuiltIn()) {
973 return ent.newLocation = -1;
976 int typeLocationSize = computeTypeLocationSize(type, stage);
977 int location = type.getQualifier().layoutLocation;
978 bool hasLocation = false;
979 EShLanguage keyStage(EShLangCount);
980 TStorageQualifier storage;
982 if (type.getQualifier().isPipeInput()) {
983 // If this symbol is a input, search pre stage's out
986 if (type.getQualifier().isPipeOutput()) {
987 // If this symbol is a output, search next stage's in
988 keyStage = currentStage;
990 // The in/out in current stage is not declared with location, but it is possible declared
991 // with explicit location in other stages, find the storageSlotMap firstly to check whether
992 // the in/out has location
993 int resourceKey = buildStorageKey(keyStage, storage);
994 if (! storageSlotMap[resourceKey].empty()) {
995 TVarSlotMap::iterator iter = storageSlotMap[resourceKey].find(name);
996 if (iter != storageSlotMap[resourceKey].end()) {
997 // If interface resource be found, set it has location and this symbol's new location
998 // equal the symbol's explicit location declaration in pre or next stage.
1001 // fs: layout(..., location = 3,...) in vec4 a;
1003 location = iter->second;
1004 // if we want deal like that:
1005 // vs: layout(location=4) out vec4 a;
1009 // layout(location = 4) in vec4 b;
1010 // we need retraverse the map.
1012 if (! hasLocation) {
1013 // If interface resource note found, It's mean the location in two stage are both implicit declarat.
1014 // So we should find a new slot for this interface.
1018 location = getFreeSlot(resourceKey, 0, typeLocationSize);
1019 storageSlotMap[resourceKey][name] = location;
1022 // the first interface declarated in a program.
1023 TVarSlotMap varSlotMap;
1024 location = getFreeSlot(resourceKey, 0, typeLocationSize);
1025 varSlotMap[name] = location;
1026 storageSlotMap[resourceKey] = varSlotMap;
1029 return ent.newLocation = location;
1032 int TDefaultGlslIoResolver::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
1033 const TType& type = ent.symbol->getType();
1034 const TString& name = ent.symbol->getAccessName();
1035 // kick out of not doing this
1036 if (! doAutoLocationMapping()) {
1037 return ent.newLocation = -1;
1039 // expand the location to each element if the symbol is a struct or array
1040 if (type.getQualifier().hasLocation() && (type.isStruct() || type.isArray())) {
1041 return ent.newLocation = type.getQualifier().layoutLocation;
1043 // no locations added if already present, a built-in variable, a block, or an opaque
1044 if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock ||
1045 type.isAtomic() || type.isSpirvType() ||
1046 (type.containsOpaque() && referenceIntermediate.getSpv().openGl == 0)) {
1047 return ent.newLocation = -1;
1049 // no locations on blocks of built-in variables
1050 if (type.isStruct()) {
1051 if (type.getStruct()->size() < 1) {
1052 return ent.newLocation = -1;
1054 if ((*type.getStruct())[0].type->isBuiltIn()) {
1055 return ent.newLocation = -1;
1059 int location = referenceIntermediate.getUniformLocationOverride(name.c_str());
1060 if (location != -1) {
1061 return ent.newLocation = location;
1064 int size = TIntermediate::computeTypeUniformLocationSize(type);
1066 // The uniform in current stage is not declared with location, but it is possible declared
1067 // with explicit location in other stages, find the storageSlotMap firstly to check whether
1068 // the uniform has location
1069 bool hasLocation = false;
1070 int resourceKey = buildStorageKey(EShLangCount, EvqUniform);
1071 TVarSlotMap& slotMap = storageSlotMap[resourceKey];
1072 // Check dose shader program has uniform resource
1073 if (! slotMap.empty()) {
1074 // If uniform resource not empty, try find a same name uniform
1075 TVarSlotMap::iterator iter = slotMap.find(name);
1076 if (iter != slotMap.end()) {
1077 // If uniform resource be found, set it has location and this symbol's new location
1078 // equal the uniform's explicit location declaration in other stage.
1080 // vs: uniform vec4 a;
1081 // fs: layout(..., location = 3,...) uniform vec4 a;
1083 location = iter->second;
1085 if (! hasLocation) {
1086 // No explicit location declaration in other stage.
1087 // So we should find a new slot for this uniform.
1089 // vs: uniform vec4 a;
1090 // fs: uniform vec4 a;
1091 location = getFreeSlot(resourceKey, 0, computeTypeLocationSize(type, currentStage));
1092 storageSlotMap[resourceKey][name] = location;
1095 // the first uniform declaration in a program.
1096 TVarSlotMap varSlotMap;
1097 location = getFreeSlot(resourceKey, 0, size);
1098 varSlotMap[name] = location;
1099 storageSlotMap[resourceKey] = varSlotMap;
1101 return ent.newLocation = location;
1104 int TDefaultGlslIoResolver::resolveBinding(EShLanguage stage, TVarEntryInfo& ent) {
1105 const TType& type = ent.symbol->getType();
1106 const TString& name = ent.symbol->getAccessName();
1107 // On OpenGL arrays of opaque types take a separate binding for each element
1108 int numBindings = referenceIntermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
1109 TResourceType resource = getResourceType(type);
1110 // don't need to handle uniform symbol, it will be handled in resolveUniformLocation
1111 if (resource == EResUbo && type.getBasicType() != EbtBlock) {
1112 return ent.newBinding = -1;
1114 // There is no 'set' qualifier in OpenGL shading language, each resource has its own
1115 // binding name space, so remap the 'set' to resource type which make each resource
1116 // binding is valid from 0 to MAX_XXRESOURCE_BINDINGS
1117 int set = referenceIntermediate.getSpv().openGl != 0 ? resource : ent.newSet;
1118 int resourceKey = set;
1119 if (resource < EResCount) {
1120 if (type.getQualifier().hasBinding()) {
1121 int newBinding = reserveSlot(resourceKey, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding, numBindings);
1122 return ent.newBinding = newBinding;
1125 // The resource in current stage is not declared with binding, but it is possible declared
1126 // with explicit binding in other stages, find the resourceSlotMap firstly to check whether
1127 // the resource has binding, don't need to allocate if it already has a binding
1128 bool hasBinding = false;
1129 ent.newBinding = -1; // leave as -1 if it isn't set below
1131 if (! resourceSlotMap[resourceKey].empty()) {
1132 TVarSlotMap::iterator iter = resourceSlotMap[resourceKey].find(name);
1133 if (iter != resourceSlotMap[resourceKey].end()) {
1135 ent.newBinding = iter->second;
1138 if (!hasBinding && (ent.live && doAutoBindingMapping())) {
1139 // find free slot, the caller did make sure it passes all vars with binding
1140 // first and now all are passed that do not have a binding and needs one
1141 int binding = getFreeSlot(resourceKey, getBaseBinding(stage, resource, set), numBindings);
1142 resourceSlotMap[resourceKey][name] = binding;
1143 ent.newBinding = binding;
1145 return ent.newBinding;
1148 return ent.newBinding = -1;
1151 void TDefaultGlslIoResolver::beginResolve(EShLanguage stage) {
1152 // reset stage state
1153 if (stage == EShLangCount)
1154 preStage = currentStage = stage;
1155 // update stage state
1156 else if (currentStage != stage) {
1157 preStage = currentStage;
1158 currentStage = stage;
1162 void TDefaultGlslIoResolver::endResolve(EShLanguage /*stage*/) {
1166 void TDefaultGlslIoResolver::beginCollect(EShLanguage stage) {
1167 // reset stage state
1168 if (stage == EShLangCount)
1169 preStage = currentStage = stage;
1170 // update stage state
1171 else if (currentStage != stage) {
1172 preStage = currentStage;
1173 currentStage = stage;
1177 void TDefaultGlslIoResolver::endCollect(EShLanguage /*stage*/) {
1181 void TDefaultGlslIoResolver::reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
1182 const TType& type = ent.symbol->getType();
1183 const TString& name = ent.symbol->getAccessName();
1184 TStorageQualifier storage = type.getQualifier().storage;
1185 EShLanguage stage(EShLangCount);
1188 if (type.getBasicType() != EbtBlock && type.getQualifier().hasLocation()) {
1190 // Reserve the slots for the uniforms who has explicit location
1191 int storageKey = buildStorageKey(EShLangCount, EvqUniform);
1192 int location = type.getQualifier().layoutLocation;
1193 TVarSlotMap& varSlotMap = storageSlotMap[storageKey];
1194 TVarSlotMap::iterator iter = varSlotMap.find(name);
1195 if (iter == varSlotMap.end()) {
1196 int numLocations = TIntermediate::computeTypeUniformLocationSize(type);
1197 reserveSlot(storageKey, location, numLocations);
1198 varSlotMap[name] = location;
1200 // Allocate location by name for OpenGL driver, so the uniform in different
1201 // stages should be declared with the same location
1202 if (iter->second != location) {
1203 TString errorMsg = "Invalid location: " + name;
1204 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
1213 // Reserve the slots for the inout who has explicit location
1214 if (type.getQualifier().hasLocation()) {
1215 stage = storage == EvqVaryingIn ? preStage : stage;
1216 stage = storage == EvqVaryingOut ? currentStage : stage;
1217 int storageKey = buildStorageKey(stage, EvqInOut);
1218 int location = type.getQualifier().layoutLocation;
1219 TVarSlotMap& varSlotMap = storageSlotMap[storageKey];
1220 TVarSlotMap::iterator iter = varSlotMap.find(name);
1221 if (iter == varSlotMap.end()) {
1222 int numLocations = TIntermediate::computeTypeUniformLocationSize(type);
1223 reserveSlot(storageKey, location, numLocations);
1224 varSlotMap[name] = location;
1226 // Allocate location by name for OpenGL driver, so the uniform in different
1227 // stages should be declared with the same location
1228 if (iter->second != location) {
1229 TString errorMsg = "Invalid location: " + name;
1230 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
1241 void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
1242 const TType& type = ent.symbol->getType();
1243 const TString& name = ent.symbol->getAccessName();
1244 TResourceType resource = getResourceType(type);
1245 int set = referenceIntermediate.getSpv().openGl != 0 ? resource : resolveSet(ent.stage, ent);
1246 int resourceKey = set;
1248 if (type.getQualifier().hasBinding()) {
1249 TVarSlotMap& varSlotMap = resourceSlotMap[resourceKey];
1250 TVarSlotMap::iterator iter = varSlotMap.find(name);
1251 int binding = type.getQualifier().layoutBinding + getBaseBinding(ent.stage, resource, set);
1253 if (iter == varSlotMap.end()) {
1254 // Reserve the slots for the ubo, ssbo and opaques who has explicit binding
1255 int numBindings = referenceIntermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
1256 varSlotMap[name] = binding;
1257 reserveSlot(resourceKey, binding, numBindings);
1259 // Allocate binding by name for OpenGL driver, so the resource in different
1260 // stages should be declared with the same binding
1261 if (iter->second != binding) {
1262 TString errorMsg = "Invalid binding: " + name;
1263 infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
1270 //TDefaultGlslIoResolver end
1273 * Basic implementation of glslang::TIoMapResolver that replaces the
1274 * previous offset behavior.
1275 * It does the same, uses the offsets for the corresponding uniform
1276 * types. Also respects the EOptionAutoMapBindings flag and binds
1282 struct TDefaultIoResolver : public TDefaultIoResolverBase {
1283 TDefaultIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
1285 bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
1287 TResourceType getResourceType(const glslang::TType& type) override {
1288 if (isImageType(type)) {
1291 if (isTextureType(type)) {
1294 if (isSsboType(type)) {
1297 if (isSamplerType(type)) {
1300 if (isUboType(type)) {
1306 int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) override {
1307 const TType& type = ent.symbol->getType();
1308 const int set = getLayoutSet(type);
1309 // On OpenGL arrays of opaque types take a seperate binding for each element
1310 int numBindings = referenceIntermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
1311 TResourceType resource = getResourceType(type);
1312 if (resource < EResCount) {
1313 if (type.getQualifier().hasBinding()) {
1314 return ent.newBinding = reserveSlot(
1315 set, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding, numBindings);
1316 } else if (ent.live && doAutoBindingMapping()) {
1317 // find free slot, the caller did make sure it passes all vars with binding
1318 // first and now all are passed that do not have a binding and needs one
1319 return ent.newBinding = getFreeSlot(set, getBaseBinding(stage, resource, set), numBindings);
1322 return ent.newBinding = -1;
1327 /********************************************************************************
1328 The following IO resolver maps types in HLSL register space, as follows:
1330 t - for shader resource views (SRV)
1352 SAMPLERCOMPARISONSTATE
1354 u - for unordered access views (UAV)
1357 APPENDSTRUCTUREDBUFFER
1358 CONSUMESTRUCTUREDBUFFER
1366 b - for constant buffer views (CBV)
1369 ********************************************************************************/
1370 struct TDefaultHlslIoResolver : public TDefaultIoResolverBase {
1371 TDefaultHlslIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
1373 bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
1375 TResourceType getResourceType(const glslang::TType& type) override {
1376 if (isUavType(type)) {
1379 if (isSrvType(type)) {
1382 if (isSamplerType(type)) {
1385 if (isUboType(type)) {
1391 int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) override {
1392 const TType& type = ent.symbol->getType();
1393 const int set = getLayoutSet(type);
1394 TResourceType resource = getResourceType(type);
1395 if (resource < EResCount) {
1396 if (type.getQualifier().hasBinding()) {
1397 return ent.newBinding = reserveSlot(set, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding);
1398 } else if (ent.live && doAutoBindingMapping()) {
1399 // find free slot, the caller did make sure it passes all vars with binding
1400 // first and now all are passed that do not have a binding and needs one
1401 return ent.newBinding = getFreeSlot(set, getBaseBinding(stage, resource, set));
1404 return ent.newBinding = -1;
1409 // Map I/O variables to provided offsets, and make bindings for
1410 // unbound but live variables.
1412 // Returns false if the input is too malformed to do this.
1413 bool TIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) {
1414 bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() ||
1415 intermediate.getAutoMapLocations();
1416 // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce
1417 // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true.
1418 for (int res = 0; (res < EResCount && !somethingToDo); ++res) {
1419 somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
1420 intermediate.hasShiftBindingForSet(TResourceType(res));
1422 if (! somethingToDo && resolver == nullptr)
1424 if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive())
1426 TIntermNode* root = intermediate.getTreeRoot();
1427 if (root == nullptr)
1429 // if no resolver is provided, use the default resolver with the given shifts and auto map settings
1430 TDefaultIoResolver defaultResolver(intermediate);
1432 TDefaultHlslIoResolver defaultHlslResolver(intermediate);
1433 if (resolver == nullptr) {
1434 // TODO: use a passed in IO mapper for this
1435 if (intermediate.usingHlslIoMapping())
1436 resolver = &defaultHlslResolver;
1438 resolver = &defaultResolver;
1441 resolver = &defaultResolver;
1443 resolver->addStage(stage, intermediate);
1445 TVarLiveMap inVarMap, outVarMap, uniformVarMap;
1446 TVarLiveVector inVector, outVector, uniformVector;
1447 TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap);
1448 TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap);
1449 root->traverse(&iter_binding_all);
1450 iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
1451 while (! iter_binding_live.destinations.empty()) {
1452 TIntermNode* destination = iter_binding_live.destinations.back();
1453 iter_binding_live.destinations.pop_back();
1454 destination->traverse(&iter_binding_live);
1457 // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info.
1458 for (auto& var : inVarMap) { inVector.push_back(var); }
1459 std::sort(inVector.begin(), inVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1460 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1462 for (auto& var : outVarMap) { outVector.push_back(var); }
1463 std::sort(outVector.begin(), outVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1464 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1466 for (auto& var : uniformVarMap) { uniformVector.push_back(var); }
1467 std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1468 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1470 bool hadError = false;
1471 TVarLiveMap* dummyUniformVarMap[EShLangCount] = {};
1472 TNotifyInOutAdaptor inOutNotify(stage, *resolver);
1473 TNotifyUniformAdaptor uniformNotify(stage, *resolver);
1474 TResolverUniformAdaptor uniformResolve(stage, *resolver, dummyUniformVarMap, infoSink, hadError);
1475 TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError);
1476 resolver->beginNotifications(stage);
1477 std::for_each(inVector.begin(), inVector.end(), inOutNotify);
1478 std::for_each(outVector.begin(), outVector.end(), inOutNotify);
1479 std::for_each(uniformVector.begin(), uniformVector.end(), uniformNotify);
1480 resolver->endNotifications(stage);
1481 resolver->beginResolve(stage);
1482 for (auto& var : inVector) { inOutResolve(var); }
1483 std::for_each(inVector.begin(), inVector.end(), [&inVarMap](TVarLivePair p) {
1484 auto at = inVarMap.find(p.second.symbol->getAccessName());
1485 if (at != inVarMap.end() && p.second.id == at->second.id)
1486 at->second = p.second;
1488 for (auto& var : outVector) { inOutResolve(var); }
1489 std::for_each(outVector.begin(), outVector.end(), [&outVarMap](TVarLivePair p) {
1490 auto at = outVarMap.find(p.second.symbol->getAccessName());
1491 if (at != outVarMap.end() && p.second.id == at->second.id)
1492 at->second = p.second;
1494 std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve);
1495 std::for_each(uniformVector.begin(), uniformVector.end(), [&uniformVarMap](TVarLivePair p) {
1496 auto at = uniformVarMap.find(p.second.symbol->getAccessName());
1497 if (at != uniformVarMap.end() && p.second.id == at->second.id)
1498 at->second = p.second;
1500 resolver->endResolve(stage);
1502 TVarSetTraverser iter_iomap(intermediate, inVarMap, outVarMap, uniformVarMap);
1503 root->traverse(&iter_iomap);
1508 // Map I/O variables to provided offsets, and make bindings for
1509 // unbound but live variables.
1511 // Returns false if the input is too malformed to do this.
1512 bool TGlslIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) {
1513 bool somethingToDo = !intermediate.getResourceSetBinding().empty() ||
1514 intermediate.getAutoMapBindings() ||
1515 intermediate.getAutoMapLocations();
1517 // Profile and version are use for symbol validate.
1518 profile = intermediate.getProfile();
1519 version = intermediate.getVersion();
1521 // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce
1522 // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true.
1523 for (int res = 0; (res < EResCount && !somethingToDo); ++res) {
1524 somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
1525 intermediate.hasShiftBindingForSet(TResourceType(res));
1527 if (! somethingToDo && resolver == nullptr) {
1530 if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive()) {
1533 TIntermNode* root = intermediate.getTreeRoot();
1534 if (root == nullptr) {
1537 // if no resolver is provided, use the default resolver with the given shifts and auto map settings
1538 TDefaultGlslIoResolver defaultResolver(intermediate);
1540 TDefaultHlslIoResolver defaultHlslResolver(intermediate);
1541 if (resolver == nullptr) {
1542 // TODO: use a passed in IO mapper for this
1543 if (intermediate.usingHlslIoMapping())
1544 resolver = &defaultHlslResolver;
1546 resolver = &defaultResolver;
1549 if (resolver == nullptr) {
1550 resolver = &defaultResolver;
1553 resolver->addStage(stage, intermediate);
1554 inVarMaps[stage] = new TVarLiveMap(); outVarMaps[stage] = new TVarLiveMap(); uniformVarMap[stage] = new TVarLiveMap();
1555 TVarGatherTraverser iter_binding_all(intermediate, true, *inVarMaps[stage], *outVarMaps[stage],
1556 *uniformVarMap[stage]);
1557 TVarGatherTraverser iter_binding_live(intermediate, false, *inVarMaps[stage], *outVarMaps[stage],
1558 *uniformVarMap[stage]);
1559 root->traverse(&iter_binding_all);
1560 iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
1561 while (! iter_binding_live.destinations.empty()) {
1562 TIntermNode* destination = iter_binding_live.destinations.back();
1563 iter_binding_live.destinations.pop_back();
1564 destination->traverse(&iter_binding_live);
1567 TNotifyInOutAdaptor inOutNotify(stage, *resolver);
1568 TNotifyUniformAdaptor uniformNotify(stage, *resolver);
1569 // Resolve current stage input symbol location with previous stage output here,
1570 // uniform symbol, ubo, ssbo and opaque symbols are per-program resource,
1571 // will resolve uniform symbol location and ubo/ssbo/opaque binding in doMap()
1572 resolver->beginNotifications(stage);
1573 std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), inOutNotify);
1574 std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), inOutNotify);
1575 std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), uniformNotify);
1576 resolver->endNotifications(stage);
1577 TSlotCollector slotCollector(*resolver, infoSink);
1578 resolver->beginCollect(stage);
1579 std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), slotCollector);
1580 std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), slotCollector);
1581 std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), slotCollector);
1582 resolver->endCollect(stage);
1583 intermediates[stage] = &intermediate;
1587 bool TGlslIoMapper::doMap(TIoMapResolver* resolver, TInfoSink& infoSink) {
1588 resolver->endResolve(EShLangCount);
1590 //Resolve uniform location, ubo/ssbo/opaque bindings across stages
1591 TResolverUniformAdaptor uniformResolve(EShLangCount, *resolver, uniformVarMap, infoSink, hadError);
1592 TResolverInOutAdaptor inOutResolve(EShLangCount, *resolver, infoSink, hadError);
1593 TSymbolValidater symbolValidater(*resolver, infoSink, inVarMaps,
1594 outVarMaps, uniformVarMap, hadError, profile, version);
1596 TVarLiveVector inVectors[EShLangCount];
1597 TVarLiveVector outVectors[EShLangCount];
1598 TVarLiveVector uniformVector;
1600 resolver->beginResolve(EShLangCount);
1601 for (int stage = EShLangVertex; stage < EShLangCount; stage++) {
1602 if (inVarMaps[stage] != nullptr) {
1603 inOutResolve.setStage(EShLanguage(stage));
1605 // copy vars into a sorted list
1606 std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(),
1607 [&inVectors, stage](TVarLivePair p) { inVectors[stage].push_back(p); });
1608 std::sort(inVectors[stage].begin(), inVectors[stage].end(),
1609 [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1610 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1613 std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(),
1614 [&outVectors, stage](TVarLivePair p) { outVectors[stage].push_back(p); });
1615 std::sort(outVectors[stage].begin(), outVectors[stage].end(),
1616 [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1617 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1620 for (auto& var : inVectors[stage]) { symbolValidater(var); }
1621 for (auto& var : inVectors[stage]) { inOutResolve(var); }
1622 for (auto& var : outVectors[stage]) { symbolValidater(var); }
1623 for (auto& var : outVectors[stage]) { inOutResolve(var); }
1625 // copy results back into maps
1626 std::for_each(inVectors[stage].begin(), inVectors[stage].end(),
1627 [this, stage](TVarLivePair p) {
1628 auto at = inVarMaps[stage]->find(p.first);
1629 if (at != inVarMaps[stage]->end())
1630 at->second = p.second;
1633 std::for_each(outVectors[stage].begin(), outVectors[stage].end(),
1634 [this, stage](TVarLivePair p) {
1635 auto at = outVarMaps[stage]->find(p.first);
1636 if (at != outVarMaps[stage]->end())
1637 at->second = p.second;
1641 if (uniformVarMap[stage] != nullptr) {
1642 uniformResolve.setStage(EShLanguage(stage));
1643 for (auto& var : *(uniformVarMap[stage])) { uniformVector.push_back(var); }
1646 std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1647 return TVarEntryInfo::TOrderByPriorityAndLive()(p1.second, p2.second);
1649 for (auto& var : uniformVector) { symbolValidater(var); }
1650 for (auto& var : uniformVector) { uniformResolve(var); }
1651 std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
1652 return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
1654 resolver->endResolve(EShLangCount);
1655 if (autoPushConstantBlockName.length()) {
1656 bool upgraded = false;
1657 for (size_t stage = 0; stage < EShLangCount; stage++) {
1658 if (intermediates[stage] != nullptr) {
1659 TVarLiveMap** pUniformVarMap = uniformResolve.uniformVarMap;
1660 auto at = pUniformVarMap[stage]->find(autoPushConstantBlockName);
1661 if (at == pUniformVarMap[stage]->end())
1663 TQualifier& qualifier = at->second.symbol->getQualifier();
1664 if (!qualifier.isUniform())
1666 TType& t = at->second.symbol->getWritableType();
1668 TIntermediate::getBaseAlignment(t, size, stride, autoPushConstantBlockPacking,
1669 qualifier.layoutMatrix == ElmRowMajor);
1670 if (size <= int(autoPushConstantMaxSize)) {
1671 qualifier.setBlockStorage(EbsPushConstant);
1672 qualifier.layoutPacking = autoPushConstantBlockPacking;
1677 // If it's been upgraded to push_constant, then remove it from the uniformVector
1678 // so it doesn't get a set/binding assigned to it.
1680 auto at = std::find_if(uniformVector.begin(), uniformVector.end(),
1681 [this](const TVarLivePair& p) { return p.first == autoPushConstantBlockName; });
1682 if (at != uniformVector.end())
1683 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