| // |
| // Copyright (C) 2016-2017 LunarG, Inc. |
| // |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions |
| // are met: |
| // |
| // Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // |
| // Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following |
| // disclaimer in the documentation and/or other materials provided |
| // with the distribution. |
| // |
| // Neither the name of 3Dlabs Inc. Ltd. nor the names of its |
| // contributors may be used to endorse or promote products derived |
| // from this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| // POSSIBILITY OF SUCH DAMAGE. |
| // |
| |
| #ifndef GLSLANG_WEB |
| |
| #include "../Include/Common.h" |
| #include "../Include/InfoSink.h" |
| |
| #include "gl_types.h" |
| #include "iomapper.h" |
| |
| // |
| // Map IO bindings. |
| // |
| // High-level algorithm for one stage: |
| // |
| // 1. Traverse all code (live+dead) to find the explicitly provided bindings. |
| // |
| // 2. Traverse (just) the live code to determine which non-provided bindings |
| // require auto-numbering. We do not auto-number dead ones. |
| // |
| // 3. Traverse all the code to apply the bindings: |
| // a. explicitly given bindings are offset according to their type |
| // b. implicit live bindings are auto-numbered into the holes, using |
| // any open binding slot. |
| // c. implicit dead bindings are left un-bound. |
| // |
| |
| namespace glslang { |
| |
| class TVarGatherTraverser : public TLiveTraverser { |
| public: |
| TVarGatherTraverser(const TIntermediate& i, bool traverseDeadCode, TVarLiveMap& inList, TVarLiveMap& outList, TVarLiveMap& uniformList) |
| : TLiveTraverser(i, traverseDeadCode, true, true, false) |
| , inputList(inList) |
| , outputList(outList) |
| , uniformList(uniformList) |
| { |
| } |
| |
| virtual void visitSymbol(TIntermSymbol* base) |
| { |
| TVarLiveMap* target = nullptr; |
| if (base->getQualifier().storage == EvqVaryingIn) |
| target = &inputList; |
| else if (base->getQualifier().storage == EvqVaryingOut) |
| target = &outputList; |
| else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().isPushConstant()) |
| target = &uniformList; |
| if (target) { |
| TVarEntryInfo ent = {base->getId(), base, ! traverseAll}; |
| ent.stage = intermediate.getStage(); |
| TVarLiveMap::iterator at = target->find( |
| ent.symbol->getName()); // std::lower_bound(target->begin(), target->end(), ent, TVarEntryInfo::TOrderById()); |
| if (at != target->end() && at->second.id == ent.id) |
| at->second.live = at->second.live || ! traverseAll; // update live state |
| else |
| (*target)[ent.symbol->getName()] = ent; |
| } |
| } |
| |
| private: |
| TVarLiveMap& inputList; |
| TVarLiveMap& outputList; |
| TVarLiveMap& uniformList; |
| }; |
| |
| class TVarSetTraverser : public TLiveTraverser |
| { |
| public: |
| TVarSetTraverser(const TIntermediate& i, const TVarLiveMap& inList, const TVarLiveMap& outList, const TVarLiveMap& uniformList) |
| : TLiveTraverser(i, true, true, true, false) |
| , inputList(inList) |
| , outputList(outList) |
| , uniformList(uniformList) |
| { |
| } |
| |
| virtual void visitSymbol(TIntermSymbol* base) { |
| const TVarLiveMap* source; |
| if (base->getQualifier().storage == EvqVaryingIn) |
| source = &inputList; |
| else if (base->getQualifier().storage == EvqVaryingOut) |
| source = &outputList; |
| else if (base->getQualifier().isUniformOrBuffer()) |
| source = &uniformList; |
| else |
| return; |
| |
| TVarEntryInfo ent = { base->getId() }; |
| TVarLiveMap::const_iterator at = source->find(base->getName()); |
| if (at == source->end()) |
| return; |
| |
| if (at->second.id != ent.id) |
| return; |
| |
| if (at->second.newBinding != -1) |
| base->getWritableType().getQualifier().layoutBinding = at->second.newBinding; |
| if (at->second.newSet != -1) |
| base->getWritableType().getQualifier().layoutSet = at->second.newSet; |
| if (at->second.newLocation != -1) |
| base->getWritableType().getQualifier().layoutLocation = at->second.newLocation; |
| if (at->second.newComponent != -1) |
| base->getWritableType().getQualifier().layoutComponent = at->second.newComponent; |
| if (at->second.newIndex != -1) |
| base->getWritableType().getQualifier().layoutIndex = at->second.newIndex; |
| } |
| |
| private: |
| const TVarLiveMap& inputList; |
| const TVarLiveMap& outputList; |
| const TVarLiveMap& uniformList; |
| }; |
| |
| struct TNotifyUniformAdaptor |
| { |
| EShLanguage stage; |
| TIoMapResolver& resolver; |
| inline TNotifyUniformAdaptor(EShLanguage s, TIoMapResolver& r) |
| : stage(s) |
| , resolver(r) |
| { |
| } |
| |
| inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) |
| { |
| resolver.notifyBinding(stage, entKey.second); |
| } |
| |
| private: |
| TNotifyUniformAdaptor& operator=(TNotifyUniformAdaptor&) = delete; |
| }; |
| |
| struct TNotifyInOutAdaptor |
| { |
| EShLanguage stage; |
| TIoMapResolver& resolver; |
| inline TNotifyInOutAdaptor(EShLanguage s, TIoMapResolver& r) |
| : stage(s) |
| , resolver(r) |
| { |
| } |
| |
| inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) |
| { |
| resolver.notifyInOut(stage, entKey.second); |
| } |
| |
| private: |
| TNotifyInOutAdaptor& operator=(TNotifyInOutAdaptor&) = delete; |
| }; |
| |
| struct TResolverUniformAdaptor { |
| TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e) |
| : stage(s) |
| , resolver(r) |
| , infoSink(i) |
| , error(e) |
| { |
| } |
| |
| inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) { |
| TVarEntryInfo& ent = entKey.second; |
| ent.newLocation = -1; |
| ent.newComponent = -1; |
| ent.newBinding = -1; |
| ent.newSet = -1; |
| ent.newIndex = -1; |
| const bool isValid = resolver.validateBinding(stage, ent); |
| if (isValid) { |
| resolver.resolveBinding(stage, ent); |
| resolver.resolveSet(stage, ent); |
| resolver.resolveUniformLocation(stage, ent); |
| |
| if (ent.newBinding != -1) { |
| if (ent.newBinding >= int(TQualifier::layoutBindingEnd)) { |
| TString err = "mapped binding out of range: " + entKey.first; |
| |
| infoSink.info.message(EPrefixInternalError, err.c_str()); |
| error = true; |
| } |
| } |
| if (ent.newSet != -1) { |
| if (ent.newSet >= int(TQualifier::layoutSetEnd)) { |
| TString err = "mapped set out of range: " + entKey.first; |
| |
| infoSink.info.message(EPrefixInternalError, err.c_str()); |
| error = true; |
| } |
| } |
| } else { |
| TString errorMsg = "Invalid binding: " + entKey.first; |
| infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); |
| error = true; |
| } |
| } |
| |
| inline void setStage(EShLanguage s) { stage = s; } |
| |
| EShLanguage stage; |
| TIoMapResolver& resolver; |
| TInfoSink& infoSink; |
| bool& error; |
| |
| private: |
| TResolverUniformAdaptor& operator=(TResolverUniformAdaptor&) = delete; |
| }; |
| |
| struct TResolverInOutAdaptor { |
| TResolverInOutAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e) |
| : stage(s) |
| , resolver(r) |
| , infoSink(i) |
| , error(e) |
| { |
| } |
| |
| inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) |
| { |
| TVarEntryInfo& ent = entKey.second; |
| ent.newLocation = -1; |
| ent.newComponent = -1; |
| ent.newBinding = -1; |
| ent.newSet = -1; |
| ent.newIndex = -1; |
| const bool isValid = resolver.validateInOut(stage, ent); |
| if (isValid) { |
| resolver.resolveInOutLocation(stage, ent); |
| resolver.resolveInOutComponent(stage, ent); |
| resolver.resolveInOutIndex(stage, ent); |
| } else { |
| TString errorMsg; |
| if (ent.symbol->getType().getQualifier().semanticName != nullptr) { |
| errorMsg = "Invalid shader In/Out variable semantic: "; |
| errorMsg += ent.symbol->getType().getQualifier().semanticName; |
| } else { |
| errorMsg = "Invalid shader In/Out variable: "; |
| errorMsg += ent.symbol->getName(); |
| } |
| infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); |
| error = true; |
| } |
| } |
| |
| inline void setStage(EShLanguage s) { stage = s; } |
| |
| EShLanguage stage; |
| TIoMapResolver& resolver; |
| TInfoSink& infoSink; |
| bool& error; |
| |
| private: |
| TResolverInOutAdaptor& operator=(TResolverInOutAdaptor&) = delete; |
| }; |
| |
| // The class is used for reserving explicit uniform locations and ubo/ssbo/opaque bindings |
| |
| struct TSymbolValidater |
| { |
| TSymbolValidater(TIoMapResolver& r, TInfoSink& i, TVarLiveMap* in[EShLangCount], TVarLiveMap* out[EShLangCount], |
| TVarLiveMap* uniform[EShLangCount], bool& hadError) |
| : preStage(EShLangCount) |
| , currentStage(EShLangCount) |
| , nextStage(EShLangCount) |
| , resolver(r) |
| , infoSink(i) |
| , hadError(hadError) |
| { |
| memcpy(inVarMaps, in, EShLangCount * (sizeof(TVarLiveMap*))); |
| memcpy(outVarMaps, out, EShLangCount * (sizeof(TVarLiveMap*))); |
| memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*))); |
| } |
| |
| inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) { |
| TVarEntryInfo& ent1 = entKey.second; |
| TIntermSymbol* base = ent1.symbol; |
| const TType& type = ent1.symbol->getType(); |
| const TString& name = entKey.first; |
| TString mangleName1, mangleName2; |
| type.appendMangledName(mangleName1); |
| EShLanguage stage = ent1.stage; |
| if (currentStage != stage) { |
| preStage = currentStage; |
| currentStage = stage; |
| nextStage = EShLangCount; |
| for (int i = currentStage + 1; i < EShLangCount; i++) { |
| if (inVarMaps[i] != nullptr) |
| nextStage = static_cast<EShLanguage>(i); |
| } |
| } |
| if (base->getQualifier().storage == EvqVaryingIn) { |
| // validate stage in; |
| if (preStage == EShLangCount) |
| return; |
| if (outVarMaps[preStage] != nullptr) { |
| auto ent2 = outVarMaps[preStage]->find(name); |
| if (ent2 != outVarMaps[preStage]->end()) { |
| ent2->second.symbol->getType().appendMangledName(mangleName2); |
| if (mangleName1 == mangleName2) |
| return; |
| else { |
| TString err = "Invalid In/Out variable type : " + entKey.first; |
| infoSink.info.message(EPrefixInternalError, err.c_str()); |
| hadError = true; |
| } |
| } |
| return; |
| } |
| } else if (base->getQualifier().storage == EvqVaryingOut) { |
| // validate stage out; |
| if (nextStage == EShLangCount) |
| return; |
| if (outVarMaps[nextStage] != nullptr) { |
| auto ent2 = inVarMaps[nextStage]->find(name); |
| if (ent2 != inVarMaps[nextStage]->end()) { |
| ent2->second.symbol->getType().appendMangledName(mangleName2); |
| if (mangleName1 == mangleName2) |
| return; |
| else { |
| TString err = "Invalid In/Out variable type : " + entKey.first; |
| infoSink.info.message(EPrefixInternalError, err.c_str()); |
| hadError = true; |
| } |
| } |
| return; |
| } |
| } else if (base->getQualifier().isUniformOrBuffer() && ! base->getQualifier().isPushConstant()) { |
| // validate uniform type; |
| for (int i = 0; i < EShLangCount; i++) { |
| if (i != currentStage && outVarMaps[i] != nullptr) { |
| auto ent2 = uniformVarMap[i]->find(name); |
| if (ent2 != uniformVarMap[i]->end()) { |
| ent2->second.symbol->getType().appendMangledName(mangleName2); |
| if (mangleName1 != mangleName2) { |
| TString err = "Invalid Uniform variable type : " + entKey.first; |
| infoSink.info.message(EPrefixInternalError, err.c_str()); |
| hadError = true; |
| } |
| mangleName2.clear(); |
| } |
| } |
| } |
| } |
| } |
| TVarLiveMap *inVarMaps[EShLangCount], *outVarMaps[EShLangCount], *uniformVarMap[EShLangCount]; |
| // Use for mark pre stage, to get more interface symbol information. |
| EShLanguage preStage, currentStage, nextStage; |
| // Use for mark current shader stage for resolver |
| TIoMapResolver& resolver; |
| TInfoSink& infoSink; |
| bool& hadError; |
| |
| private: |
| TSymbolValidater& operator=(TSymbolValidater&) = delete; |
| }; |
| |
| struct TSlotCollector { |
| TSlotCollector(TIoMapResolver& r, TInfoSink& i) : resolver(r), infoSink(i) { } |
| |
| inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) { |
| resolver.reserverStorageSlot(entKey.second, infoSink); |
| resolver.reserverResourceSlot(entKey.second, infoSink); |
| } |
| TIoMapResolver& resolver; |
| TInfoSink& infoSink; |
| |
| private: |
| TSlotCollector& operator=(TSlotCollector&) = delete; |
| }; |
| |
| TDefaultIoResolverBase::TDefaultIoResolverBase(const TIntermediate& intermediate) |
| : intermediate(intermediate) |
| , nextUniformLocation(intermediate.getUniformLocationBase()) |
| , nextInputLocation(0) |
| , nextOutputLocation(0) |
| { |
| memset(stageMask, false, sizeof(bool) * (EShLangCount + 1)); |
| } |
| |
| int TDefaultIoResolverBase::getBaseBinding(TResourceType res, unsigned int set) const { |
| return selectBaseBinding(intermediate.getShiftBinding(res), intermediate.getShiftBindingForSet(res, set)); |
| } |
| |
| const std::vector<std::string>& TDefaultIoResolverBase::getResourceSetBinding() const { |
| return intermediate.getResourceSetBinding(); |
| } |
| |
| bool TDefaultIoResolverBase::doAutoBindingMapping() const { return intermediate.getAutoMapBindings(); } |
| |
| bool TDefaultIoResolverBase::doAutoLocationMapping() const { return intermediate.getAutoMapLocations(); } |
| |
| TDefaultIoResolverBase::TSlotSet::iterator TDefaultIoResolverBase::findSlot(int set, int slot) { |
| return std::lower_bound(slots[set].begin(), slots[set].end(), slot); |
| } |
| |
| bool TDefaultIoResolverBase::checkEmpty(int set, int slot) { |
| TSlotSet::iterator at = findSlot(set, slot); |
| return ! (at != slots[set].end() && *at == slot); |
| } |
| |
| int TDefaultIoResolverBase::reserveSlot(int set, int slot, int size) { |
| TSlotSet::iterator at = findSlot(set, slot); |
| // tolerate aliasing, by not double-recording aliases |
| // (policy about appropriateness of the alias is higher up) |
| for (int i = 0; i < size; i++) { |
| if (at == slots[set].end() || *at != slot + i) |
| at = slots[set].insert(at, slot + i); |
| ++at; |
| } |
| return slot; |
| } |
| |
| int TDefaultIoResolverBase::getFreeSlot(int set, int base, int size) { |
| TSlotSet::iterator at = findSlot(set, base); |
| if (at == slots[set].end()) |
| return reserveSlot(set, base, size); |
| // look for a big enough gap |
| for (; at != slots[set].end(); ++at) { |
| if (*at - base >= size) |
| break; |
| base = *at + 1; |
| } |
| return reserveSlot(set, base, size); |
| } |
| |
| int TDefaultIoResolverBase::resolveSet(EShLanguage /*stage*/, TVarEntryInfo& ent) { |
| const TType& type = ent.symbol->getType(); |
| if (type.getQualifier().hasSet()) { |
| return ent.newSet = type.getQualifier().layoutSet; |
| } |
| // If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN) |
| if (getResourceSetBinding().size() == 1) { |
| return ent.newSet = atoi(getResourceSetBinding()[0].c_str()); |
| } |
| return ent.newSet = 0; |
| } |
| |
| int TDefaultIoResolverBase::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) { |
| const TType& type = ent.symbol->getType(); |
| const char* name = ent.symbol->getName().c_str(); |
| // kick out of not doing this |
| if (! doAutoLocationMapping()) { |
| return ent.newLocation = -1; |
| } |
| // no locations added if already present, a built-in variable, a block, or an opaque |
| if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock || |
| type.isAtomic() || (type.containsOpaque() && intermediate.getSpv().openGl == 0)) { |
| return ent.newLocation = -1; |
| } |
| // no locations on blocks of built-in variables |
| if (type.isStruct()) { |
| if (type.getStruct()->size() < 1) { |
| return ent.newLocation = -1; |
| } |
| if ((*type.getStruct())[0].type->isBuiltIn()) { |
| return ent.newLocation = -1; |
| } |
| } |
| int location = intermediate.getUniformLocationOverride(name); |
| if (location != -1) { |
| return ent.newLocation = location; |
| } |
| location = nextUniformLocation; |
| nextUniformLocation += TIntermediate::computeTypeUniformLocationSize(type); |
| return ent.newLocation = location; |
| } |
| |
| int TDefaultIoResolverBase::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) { |
| const TType& type = ent.symbol->getType(); |
| // kick out of not doing this |
| if (! doAutoLocationMapping()) { |
| return ent.newLocation = -1; |
| } |
| |
| // no locations added if already present, or a built-in variable |
| if (type.getQualifier().hasLocation() || type.isBuiltIn()) { |
| return ent.newLocation = -1; |
| } |
| |
| // no locations on blocks of built-in variables |
| if (type.isStruct()) { |
| if (type.getStruct()->size() < 1) { |
| return ent.newLocation = -1; |
| } |
| if ((*type.getStruct())[0].type->isBuiltIn()) { |
| return ent.newLocation = -1; |
| } |
| } |
| // point to the right input or output location counter |
| int& nextLocation = type.getQualifier().isPipeInput() ? nextInputLocation : nextOutputLocation; |
| // Placeholder. This does not do proper cross-stage lining up, nor |
| // work with mixed location/no-location declarations. |
| int location = nextLocation; |
| int typeLocationSize; |
| // Don’t take into account the outer-most array if the stage’s |
| // interface is automatically an array. |
| typeLocationSize = computeTypeLocationSize(type, stage); |
| nextLocation += typeLocationSize; |
| return ent.newLocation = location; |
| } |
| |
| int TDefaultIoResolverBase::resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) { |
| return ent.newComponent = -1; |
| } |
| |
| int TDefaultIoResolverBase::resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) { return ent.newIndex = -1; } |
| |
| uint32_t TDefaultIoResolverBase::computeTypeLocationSize(const TType& type, EShLanguage stage) { |
| int typeLocationSize; |
| // Don’t take into account the outer-most array if the stage’s |
| // interface is automatically an array. |
| if (type.getQualifier().isArrayedIo(stage)) { |
| TType elementType(type, 0); |
| typeLocationSize = TIntermediate::computeTypeLocationSize(elementType, stage); |
| } else { |
| typeLocationSize = TIntermediate::computeTypeLocationSize(type, stage); |
| } |
| return typeLocationSize; |
| } |
| |
| //TDefaultGlslIoResolver |
| TResourceType TDefaultGlslIoResolver::getResourceType(const glslang::TType& type) { |
| if (isImageType(type)) { |
| return EResImage; |
| } |
| if (isTextureType(type)) { |
| return EResTexture; |
| } |
| if (isSsboType(type)) { |
| return EResSsbo; |
| } |
| if (isSamplerType(type)) { |
| return EResSampler; |
| } |
| if (isUboType(type)) { |
| return EResUbo; |
| } |
| return EResCount; |
| } |
| |
| TDefaultGlslIoResolver::TDefaultGlslIoResolver(const TIntermediate& intermediate) |
| : TDefaultIoResolverBase(intermediate) |
| , preStage(EShLangCount) |
| , currentStage(EShLangCount) |
| { } |
| |
| int TDefaultGlslIoResolver::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) { |
| const TType& type = ent.symbol->getType(); |
| const TString& name = IsAnonymous(ent.symbol->getName()) ? |
| ent.symbol->getType().getTypeName() |
| : |
| ent.symbol->getName(); |
| if (currentStage != stage) { |
| preStage = currentStage; |
| currentStage = stage; |
| } |
| // kick out of not doing this |
| if (! doAutoLocationMapping()) { |
| return ent.newLocation = -1; |
| } |
| // expand the location to each element if the symbol is a struct or array |
| if (type.getQualifier().hasLocation()) { |
| return ent.newLocation = type.getQualifier().layoutLocation; |
| } |
| // no locations added if already present, or a built-in variable |
| if (type.isBuiltIn()) { |
| return ent.newLocation = -1; |
| } |
| // no locations on blocks of built-in variables |
| if (type.isStruct()) { |
| if (type.getStruct()->size() < 1) { |
| return ent.newLocation = -1; |
| } |
| if ((*type.getStruct())[0].type->isBuiltIn()) { |
| return ent.newLocation = -1; |
| } |
| } |
| int typeLocationSize = computeTypeLocationSize(type, stage); |
| int location = type.getQualifier().layoutLocation; |
| bool hasLocation = false; |
| EShLanguage keyStage(EShLangCount); |
| TStorageQualifier storage; |
| storage = EvqInOut; |
| if (type.getQualifier().isPipeInput()) { |
| // If this symbol is a input, search pre stage's out |
| keyStage = preStage; |
| } |
| if (type.getQualifier().isPipeOutput()) { |
| // If this symbol is a output, search next stage's in |
| keyStage = currentStage; |
| } |
| // The in/out in current stage is not declared with location, but it is possible declared |
| // with explicit location in other stages, find the storageSlotMap firstly to check whether |
| // the in/out has location |
| int resourceKey = buildStorageKey(keyStage, storage); |
| if (! storageSlotMap[resourceKey].empty()) { |
| TVarSlotMap::iterator iter = storageSlotMap[resourceKey].find(name); |
| if (iter != storageSlotMap[resourceKey].end()) { |
| // If interface resource be found, set it has location and this symbol's new location |
| // equal the symbol's explicit location declarated in pre or next stage. |
| // |
| // vs: out vec4 a; |
| // fs: layout(..., location = 3,...) in vec4 a; |
| hasLocation = true; |
| location = iter->second; |
| // if we want deal like that: |
| // vs: layout(location=4) out vec4 a; |
| // out vec4 b; |
| // |
| // fs: in vec4 a; |
| // layout(location = 4) in vec4 b; |
| // we need retraverse the map. |
| } |
| if (! hasLocation) { |
| // If interface resource note found, It's mean the location in two stage are both implicit declarat. |
| // So we should find a new slot for this interface. |
| // |
| // vs: out vec4 a; |
| // fs: in vec4 a; |
| location = getFreeSlot(resourceKey, 0, typeLocationSize); |
| storageSlotMap[resourceKey][name] = location; |
| } |
| } else { |
| // the first interface declarated in a program. |
| TVarSlotMap varSlotMap; |
| location = getFreeSlot(resourceKey, 0, typeLocationSize); |
| varSlotMap[name] = location; |
| storageSlotMap[resourceKey] = varSlotMap; |
| } |
| //Update location |
| return ent.newLocation = location; |
| } |
| |
| int TDefaultGlslIoResolver::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) { |
| const TType& type = ent.symbol->getType(); |
| const TString& name = IsAnonymous(ent.symbol->getName()) ? |
| ent.symbol->getType().getTypeName() |
| : |
| ent.symbol->getName(); |
| // kick out of not doing this |
| if (! doAutoLocationMapping()) { |
| return ent.newLocation = -1; |
| } |
| // expand the location to each element if the symbol is a struct or array |
| if (type.getQualifier().hasLocation() && (type.isStruct() || type.isArray())) { |
| return ent.newLocation = type.getQualifier().layoutLocation; |
| } else { |
| // no locations added if already present, a built-in variable, a block, or an opaque |
| if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock || |
| type.isAtomic() || (type.containsOpaque() && intermediate.getSpv().openGl == 0)) { |
| return ent.newLocation = -1; |
| } |
| // no locations on blocks of built-in variables |
| if (type.isStruct()) { |
| if (type.getStruct()->size() < 1) { |
| return ent.newLocation = -1; |
| } |
| if ((*type.getStruct())[0].type->isBuiltIn()) { |
| return ent.newLocation = -1; |
| } |
| } |
| } |
| int location = intermediate.getUniformLocationOverride(name.c_str()); |
| if (location != -1) { |
| return ent.newLocation = location; |
| } |
| |
| int size = TIntermediate::computeTypeUniformLocationSize(type); |
| |
| // The uniform in current stage is not declared with location, but it is possible declared |
| // with explicit location in other stages, find the storageSlotMap firstly to check whether |
| // the uniform has location |
| bool hasLocation = false; |
| int resourceKey = buildStorageKey(EShLangCount, EvqUniform); |
| TVarSlotMap& slotMap = storageSlotMap[resourceKey]; |
| // Check dose shader program has uniform resource |
| if (! slotMap.empty()) { |
| // If uniform resource not empty, try find a same name uniform |
| TVarSlotMap::iterator iter = slotMap.find(name); |
| if (iter != slotMap.end()) { |
| // If uniform resource be found, set it has location and this symbol's new location |
| // equal the uniform's explicit location declarated in other stage. |
| // |
| // vs: uniform vec4 a; |
| // fs: layout(..., location = 3,...) uniform vec4 a; |
| hasLocation = true; |
| location = iter->second; |
| } |
| if (! hasLocation) { |
| // No explicit location declaraten in other stage. |
| // So we should find a new slot for this uniform. |
| // |
| // vs: uniform vec4 a; |
| // fs: uniform vec4 a; |
| location = getFreeSlot(resourceKey, 0, computeTypeLocationSize(type, currentStage)); |
| storageSlotMap[resourceKey][name] = location; |
| } |
| } else { |
| // the first uniform declarated in a program. |
| TVarSlotMap varSlotMap; |
| location = getFreeSlot(resourceKey, 0, size); |
| varSlotMap[name] = location; |
| storageSlotMap[resourceKey] = varSlotMap; |
| } |
| return ent.newLocation = location; |
| } |
| |
| int TDefaultGlslIoResolver::resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) { |
| const TType& type = ent.symbol->getType(); |
| const TString& name = IsAnonymous(ent.symbol->getName()) ? |
| ent.symbol->getType().getTypeName() |
| : |
| ent.symbol->getName(); |
| // On OpenGL arrays of opaque types take a seperate binding for each element |
| int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1; |
| TResourceType resource = getResourceType(type); |
| // don't need to handle uniform symbol, it will be handled in resolveUniformLocation |
| if (resource == EResUbo && type.getBasicType() != EbtBlock) { |
| return ent.newBinding = -1; |
| } |
| // There is no 'set' qualifier in OpenGL shading language, each resource has its own |
| // binding name space, so remap the 'set' to resource type which make each resource |
| // binding is valid from 0 to MAX_XXRESOURCE_BINDINGS |
| int set = resource; |
| if (resource < EResCount) { |
| if (type.getQualifier().hasBinding()) { |
| ent.newBinding = reserveSlot(set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding, numBindings); |
| return ent.newBinding; |
| } else if (ent.live && doAutoBindingMapping()) { |
| // The resource in current stage is not declared with binding, but it is possible declared |
| // with explicit binding in other stages, find the resourceSlotMap firstly to check whether |
| // the resource has binding, don't need to allocate if it already has a binding |
| bool hasBinding = false; |
| if (! resourceSlotMap[resource].empty()) { |
| TVarSlotMap::iterator iter = resourceSlotMap[resource].find(name); |
| if (iter != resourceSlotMap[resource].end()) { |
| hasBinding = true; |
| ent.newBinding = iter->second; |
| } |
| } |
| if (! hasBinding) { |
| TVarSlotMap varSlotMap; |
| // find free slot, the caller did make sure it passes all vars with binding |
| // first and now all are passed that do not have a binding and needs one |
| int binding = getFreeSlot(resource, getBaseBinding(resource, set), numBindings); |
| varSlotMap[name] = binding; |
| resourceSlotMap[resource] = varSlotMap; |
| ent.newBinding = binding; |
| } |
| return ent.newBinding; |
| } |
| } |
| return ent.newBinding = -1; |
| } |
| |
| void TDefaultGlslIoResolver::beginResolve(EShLanguage stage) { |
| // reset stage state |
| if (stage == EShLangCount) |
| preStage = currentStage = stage; |
| // update stage state |
| else if (currentStage != stage) { |
| preStage = currentStage; |
| currentStage = stage; |
| } |
| } |
| |
| void TDefaultGlslIoResolver::endResolve(EShLanguage /*stage*/) { |
| // TODO nothing |
| } |
| |
| void TDefaultGlslIoResolver::beginCollect(EShLanguage stage) { |
| // reset stage state |
| if (stage == EShLangCount) |
| preStage = currentStage = stage; |
| // update stage state |
| else if (currentStage != stage) { |
| preStage = currentStage; |
| currentStage = stage; |
| } |
| } |
| |
| void TDefaultGlslIoResolver::endCollect(EShLanguage /*stage*/) { |
| // TODO nothing |
| } |
| |
| void TDefaultGlslIoResolver::reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) { |
| const TType& type = ent.symbol->getType(); |
| const TString& name = IsAnonymous(ent.symbol->getName()) ? |
| ent.symbol->getType().getTypeName() |
| : |
| ent.symbol->getName(); |
| TStorageQualifier storage = type.getQualifier().storage; |
| EShLanguage stage(EShLangCount); |
| switch (storage) { |
| case EvqUniform: |
| if (type.getBasicType() != EbtBlock && type.getQualifier().hasLocation()) { |
| // |
| // Reserve the slots for the uniforms who has explicit location |
| int storageKey = buildStorageKey(EShLangCount, EvqUniform); |
| int location = type.getQualifier().layoutLocation; |
| TVarSlotMap& varSlotMap = storageSlotMap[storageKey]; |
| TVarSlotMap::iterator iter = varSlotMap.find(name); |
| if (iter == varSlotMap.end()) { |
| int numLocations = TIntermediate::computeTypeUniformLocationSize(type); |
| reserveSlot(storageKey, location, numLocations); |
| varSlotMap[name] = location; |
| } else { |
| // Allocate location by name for OpenGL driver, so the uniform in different |
| // stages should be declared with the same location |
| if (iter->second != location) { |
| TString errorMsg = "Invalid location: " + name; |
| infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); |
| hasError = true; |
| } |
| } |
| } |
| break; |
| case EvqVaryingIn: |
| case EvqVaryingOut: |
| // |
| // Reserve the slots for the inout who has explicit location |
| if (type.getQualifier().hasLocation()) { |
| stage = storage == EvqVaryingIn ? preStage : stage; |
| stage = storage == EvqVaryingOut ? currentStage : stage; |
| int storageKey = buildStorageKey(stage, EvqInOut); |
| int location = type.getQualifier().layoutLocation; |
| TVarSlotMap& varSlotMap = storageSlotMap[storageKey]; |
| TVarSlotMap::iterator iter = varSlotMap.find(name); |
| if (iter == varSlotMap.end()) { |
| int numLocations = TIntermediate::computeTypeUniformLocationSize(type); |
| reserveSlot(storageKey, location, numLocations); |
| varSlotMap[name] = location; |
| } else { |
| // Allocate location by name for OpenGL driver, so the uniform in different |
| // stages should be declared with the same location |
| if (iter->second != location) { |
| TString errorMsg = "Invalid location: " + name; |
| infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); |
| hasError = true; |
| } |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) { |
| const TType& type = ent.symbol->getType(); |
| const TString& name = IsAnonymous(ent.symbol->getName()) ? |
| ent.symbol->getType().getTypeName() |
| : |
| ent.symbol->getName(); |
| int resource = getResourceType(type); |
| if (type.getQualifier().hasBinding()) { |
| TVarSlotMap& varSlotMap = resourceSlotMap[resource]; |
| TVarSlotMap::iterator iter = varSlotMap.find(name); |
| int binding = type.getQualifier().layoutBinding; |
| if (iter == varSlotMap.end()) { |
| // Reserve the slots for the ubo, ssbo and opaques who has explicit binding |
| int numBindings = type.isSizedArray() ? type.getCumulativeArraySize() : 1; |
| varSlotMap[name] = binding; |
| reserveSlot(resource, binding, numBindings); |
| } else { |
| // Allocate binding by name for OpenGL driver, so the resource in different |
| // stages should be declared with the same binding |
| if (iter->second != binding) { |
| TString errorMsg = "Invalid binding: " + name; |
| infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); |
| hasError = true; |
| } |
| } |
| } |
| } |
| |
| //TDefaultGlslIoResolver end |
| |
| /* |
| * Basic implementation of glslang::TIoMapResolver that replaces the |
| * previous offset behavior. |
| * It does the same, uses the offsets for the corresponding uniform |
| * types. Also respects the EOptionAutoMapBindings flag and binds |
| * them if needed. |
| */ |
| /* |
| * Default resolver |
| */ |
| struct TDefaultIoResolver : public TDefaultIoResolverBase { |
| TDefaultIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { } |
| |
| bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; } |
| |
| TResourceType getResourceType(const glslang::TType& type) override { |
| if (isImageType(type)) { |
| return EResImage; |
| } |
| if (isTextureType(type)) { |
| return EResTexture; |
| } |
| if (isSsboType(type)) { |
| return EResSsbo; |
| } |
| if (isSamplerType(type)) { |
| return EResSampler; |
| } |
| if (isUboType(type)) { |
| return EResUbo; |
| } |
| return EResCount; |
| } |
| |
| int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override { |
| const TType& type = ent.symbol->getType(); |
| const int set = getLayoutSet(type); |
| // On OpenGL arrays of opaque types take a seperate binding for each element |
| int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1; |
| TResourceType resource = getResourceType(type); |
| if (resource < EResCount) { |
| if (type.getQualifier().hasBinding()) { |
| return ent.newBinding = reserveSlot( |
| set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding, numBindings); |
| } else if (ent.live && doAutoBindingMapping()) { |
| // find free slot, the caller did make sure it passes all vars with binding |
| // first and now all are passed that do not have a binding and needs one |
| return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set), numBindings); |
| } |
| } |
| return ent.newBinding = -1; |
| } |
| }; |
| |
| #ifdef ENABLE_HLSL |
| /******************************************************************************** |
| The following IO resolver maps types in HLSL register space, as follows: |
| |
| t - for shader resource views (SRV) |
| TEXTURE1D |
| TEXTURE1DARRAY |
| TEXTURE2D |
| TEXTURE2DARRAY |
| TEXTURE3D |
| TEXTURECUBE |
| TEXTURECUBEARRAY |
| TEXTURE2DMS |
| TEXTURE2DMSARRAY |
| STRUCTUREDBUFFER |
| BYTEADDRESSBUFFER |
| BUFFER |
| TBUFFER |
| |
| s - for samplers |
| SAMPLER |
| SAMPLER1D |
| SAMPLER2D |
| SAMPLER3D |
| SAMPLERCUBE |
| SAMPLERSTATE |
| SAMPLERCOMPARISONSTATE |
| |
| u - for unordered access views (UAV) |
| RWBYTEADDRESSBUFFER |
| RWSTRUCTUREDBUFFER |
| APPENDSTRUCTUREDBUFFER |
| CONSUMESTRUCTUREDBUFFER |
| RWBUFFER |
| RWTEXTURE1D |
| RWTEXTURE1DARRAY |
| RWTEXTURE2D |
| RWTEXTURE2DARRAY |
| RWTEXTURE3D |
| |
| b - for constant buffer views (CBV) |
| CBUFFER |
| CONSTANTBUFFER |
| ********************************************************************************/ |
| struct TDefaultHlslIoResolver : public TDefaultIoResolverBase { |
| TDefaultHlslIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { } |
| |
| bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; } |
| |
| TResourceType getResourceType(const glslang::TType& type) override { |
| if (isUavType(type)) { |
| return EResUav; |
| } |
| if (isSrvType(type)) { |
| return EResTexture; |
| } |
| if (isSamplerType(type)) { |
| return EResSampler; |
| } |
| if (isUboType(type)) { |
| return EResUbo; |
| } |
| return EResCount; |
| } |
| |
| int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override { |
| const TType& type = ent.symbol->getType(); |
| const int set = getLayoutSet(type); |
| TResourceType resource = getResourceType(type); |
| if (resource < EResCount) { |
| if (type.getQualifier().hasBinding()) { |
| return ent.newBinding = reserveSlot(set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding); |
| } else if (ent.live && doAutoBindingMapping()) { |
| // find free slot, the caller did make sure it passes all vars with binding |
| // first and now all are passed that do not have a binding and needs one |
| return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set)); |
| } |
| } |
| return ent.newBinding = -1; |
| } |
| }; |
| #endif |
| |
| // Map I/O variables to provided offsets, and make bindings for |
| // unbound but live variables. |
| // |
| // Returns false if the input is too malformed to do this. |
| bool TIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) { |
| bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() || |
| intermediate.getAutoMapLocations(); |
| // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce |
| // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true. |
| for (int res = 0; (res < EResCount && !somethingToDo); ++res) { |
| somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) || |
| intermediate.hasShiftBindingForSet(TResourceType(res)); |
| } |
| if (! somethingToDo && resolver == nullptr) |
| return true; |
| if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive()) |
| return false; |
| TIntermNode* root = intermediate.getTreeRoot(); |
| if (root == nullptr) |
| return false; |
| // if no resolver is provided, use the default resolver with the given shifts and auto map settings |
| TDefaultIoResolver defaultResolver(intermediate); |
| #ifdef ENABLE_HLSL |
| TDefaultHlslIoResolver defaultHlslResolver(intermediate); |
| if (resolver == nullptr) { |
| // TODO: use a passed in IO mapper for this |
| if (intermediate.usingHlslIoMapping()) |
| resolver = &defaultHlslResolver; |
| else |
| resolver = &defaultResolver; |
| } |
| resolver->addStage(stage); |
| #else |
| resolver = &defaultResolver; |
| #endif |
| |
| TVarLiveMap inVarMap, outVarMap, uniformVarMap; |
| TVarLiveVector inVector, outVector, uniformVector; |
| TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap); |
| TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap); |
| root->traverse(&iter_binding_all); |
| iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str()); |
| while (! iter_binding_live.functions.empty()) { |
| TIntermNode* function = iter_binding_live.functions.back(); |
| iter_binding_live.functions.pop_back(); |
| function->traverse(&iter_binding_live); |
| } |
| // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info. |
| std::for_each(inVarMap.begin(), inVarMap.end(), |
| [&inVector](TVarLivePair p) { inVector.push_back(p); }); |
| std::sort(inVector.begin(), inVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { |
| return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); |
| }); |
| std::for_each(outVarMap.begin(), outVarMap.end(), |
| [&outVector](TVarLivePair p) { outVector.push_back(p); }); |
| std::sort(outVector.begin(), outVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { |
| return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); |
| }); |
| std::for_each(uniformVarMap.begin(), uniformVarMap.end(), |
| [&uniformVector](TVarLivePair p) { uniformVector.push_back(p); }); |
| std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { |
| return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); |
| }); |
| bool hadError = false; |
| TNotifyInOutAdaptor inOutNotify(stage, *resolver); |
| TNotifyUniformAdaptor uniformNotify(stage, *resolver); |
| TResolverUniformAdaptor uniformResolve(stage, *resolver, infoSink, hadError); |
| TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError); |
| resolver->beginNotifications(stage); |
| std::for_each(inVector.begin(), inVector.end(), inOutNotify); |
| std::for_each(outVector.begin(), outVector.end(), inOutNotify); |
| std::for_each(uniformVector.begin(), uniformVector.end(), uniformNotify); |
| resolver->endNotifications(stage); |
| resolver->beginResolve(stage); |
| std::for_each(inVector.begin(), inVector.end(), inOutResolve); |
| std::for_each(inVector.begin(), inVector.end(), [&inVarMap](TVarLivePair p) { |
| auto at = inVarMap.find(p.second.symbol->getName()); |
| if (at != inVarMap.end()) |
| at->second = p.second; |
| }); |
| std::for_each(outVector.begin(), outVector.end(), inOutResolve); |
| std::for_each(outVector.begin(), outVector.end(), [&outVarMap](TVarLivePair p) { |
| auto at = outVarMap.find(p.second.symbol->getName()); |
| if (at != outVarMap.end()) |
| at->second = p.second; |
| }); |
| std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve); |
| std::for_each(uniformVector.begin(), uniformVector.end(), [&uniformVarMap](TVarLivePair p) { |
| auto at = uniformVarMap.find(p.second.symbol->getName()); |
| if (at != uniformVarMap.end()) |
| at->second = p.second; |
| }); |
| resolver->endResolve(stage); |
| if (!hadError) { |
| TVarSetTraverser iter_iomap(intermediate, inVarMap, outVarMap, uniformVarMap); |
| root->traverse(&iter_iomap); |
| } |
| return !hadError; |
| } |
| |
| // Map I/O variables to provided offsets, and make bindings for |
| // unbound but live variables. |
| // |
| // Returns false if the input is too malformed to do this. |
| bool TGlslIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) { |
| |
| bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() || |
| intermediate.getAutoMapLocations(); |
| // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce |
| // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true. |
| for (int res = 0; (res < EResCount && !somethingToDo); ++res) { |
| somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) || |
| intermediate.hasShiftBindingForSet(TResourceType(res)); |
| } |
| if (! somethingToDo && resolver == nullptr) { |
| return true; |
| } |
| if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive()) { |
| return false; |
| } |
| TIntermNode* root = intermediate.getTreeRoot(); |
| if (root == nullptr) { |
| return false; |
| } |
| // if no resolver is provided, use the default resolver with the given shifts and auto map settings |
| TDefaultGlslIoResolver defaultResolver(intermediate); |
| if (resolver == nullptr) { |
| resolver = &defaultResolver; |
| } |
| resolver->addStage(stage); |
| inVarMaps[stage] = new TVarLiveMap(); outVarMaps[stage] = new TVarLiveMap(); uniformVarMap[stage] = new TVarLiveMap(); |
| TVarGatherTraverser iter_binding_all(intermediate, true, *inVarMaps[stage], *outVarMaps[stage], |
| *uniformVarMap[stage]); |
| TVarGatherTraverser iter_binding_live(intermediate, false, *inVarMaps[stage], *outVarMaps[stage], |
| *uniformVarMap[stage]); |
| root->traverse(&iter_binding_all); |
| iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str()); |
| while (! iter_binding_live.functions.empty()) { |
| TIntermNode* function = iter_binding_live.functions.back(); |
| iter_binding_live.functions.pop_back(); |
| function->traverse(&iter_binding_live); |
| } |
| TNotifyInOutAdaptor inOutNotify(stage, *resolver); |
| TNotifyUniformAdaptor uniformNotify(stage, *resolver); |
| // Resolve current stage input symbol location with previous stage output here, |
| // uniform symbol, ubo, ssbo and opaque symbols are per-program resource, |
| // will resolve uniform symbol location and ubo/ssbo/opaque binding in doMap() |
| resolver->beginNotifications(stage); |
| std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), inOutNotify); |
| std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), inOutNotify); |
| std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), uniformNotify); |
| resolver->endNotifications(stage); |
| TSlotCollector slotCollector(*resolver, infoSink); |
| resolver->beginCollect(stage); |
| std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), slotCollector); |
| std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), slotCollector); |
| std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), slotCollector); |
| resolver->endCollect(stage); |
| intermediates[stage] = &intermediate; |
| return !hadError; |
| } |
| |
| bool TGlslIoMapper::doMap(TIoMapResolver* resolver, TInfoSink& infoSink) { |
| resolver->endResolve(EShLangCount); |
| if (!hadError) { |
| //Resolve uniform location, ubo/ssbo/opaque bindings across stages |
| TResolverUniformAdaptor uniformResolve(EShLangCount, *resolver, infoSink, hadError); |
| TResolverInOutAdaptor inOutResolve(EShLangCount, *resolver, infoSink, hadError); |
| TSymbolValidater symbolValidater(*resolver, infoSink, inVarMaps, outVarMaps, uniformVarMap, hadError); |
| TVarLiveVector uniformVector; |
| resolver->beginResolve(EShLangCount); |
| for (int stage = EShLangVertex; stage < EShLangCount; stage++) { |
| if (inVarMaps[stage] != nullptr) { |
| inOutResolve.setStage(EShLanguage(stage)); |
| std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), symbolValidater); |
| std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), inOutResolve); |
| std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), symbolValidater); |
| std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), inOutResolve); |
| } |
| if (uniformVarMap[stage] != nullptr) { |
| uniformResolve.setStage(EShLanguage(stage)); |
| // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info. |
| std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), |
| [&uniformVector](TVarLivePair p) { uniformVector.push_back(p); }); |
| } |
| } |
| std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { |
| return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); |
| }); |
| std::for_each(uniformVector.begin(), uniformVector.end(), symbolValidater); |
| std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve); |
| std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { |
| return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); |
| }); |
| resolver->endResolve(EShLangCount); |
| for (size_t stage = 0; stage < EShLangCount; stage++) { |
| if (intermediates[stage] != nullptr) { |
| // traverse each stage, set new location to each input/output and unifom symbol, set new binding to |
| // ubo, ssbo and opaque symbols |
| TVarLiveMap** pUniformVarMap = uniformVarMap; |
| std::for_each(uniformVector.begin(), uniformVector.end(), [pUniformVarMap, stage](TVarLivePair p) { |
| auto at = pUniformVarMap[stage]->find(p.second.symbol->getName()); |
| if (at != pUniformVarMap[stage]->end()) |
| at->second = p.second; |
| }); |
| TVarSetTraverser iter_iomap(*intermediates[stage], *inVarMaps[stage], *outVarMaps[stage], |
| *uniformVarMap[stage]); |
| intermediates[stage]->getTreeRoot()->traverse(&iter_iomap); |
| } |
| } |
| return !hadError; |
| } else { |
| return false; |
| } |
| } |
| |
| } // end namespace glslang |
| |
| #endif // GLSLANG_WEB |