| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601 |
- //
- // 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.
- //
- #if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
- #include "../Include/Common.h"
- #include "../Include/InfoSink.h"
- #include "../Include/Types.h"
- #include "gl_types.h"
- #include "iomapper.h"
- #include "SymbolTable.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 a global is being visited, then we should also traverse it incase it's evaluation
- // ends up visiting inputs we want to tag as live
- else if (base->getQualifier().storage == EvqGlobal)
- addGlobalReference(base->getAccessName());
- if (target) {
- TVarEntryInfo ent = {base->getId(), base, ! traverseAll};
- ent.stage = intermediate.getStage();
- TVarLiveMap::iterator at = target->find(
- ent.symbol->getAccessName()); // 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->getAccessName()] = 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() };
- // Fix a defect, when block has no instance name, we need to find its block name
- TVarLiveMap::const_iterator at = source->find(base->getAccessName());
- 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(entKey.second.stage, entKey.second);
- }
- private:
- TNotifyInOutAdaptor& operator=(TNotifyInOutAdaptor&) = delete;
- };
- struct TResolverUniformAdaptor {
- TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TVarLiveMap* uniform[EShLangCount], TInfoSink& i, bool& e)
- : stage(s)
- , resolver(r)
- , infoSink(i)
- , error(e)
- {
- memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*)));
- }
- 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(ent.stage, ent);
- resolver.resolveSet(ent.stage, ent);
- resolver.resolveUniformLocation(ent.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.symbol->getQualifier().hasBinding()) {
- for (uint32_t idx = EShLangVertex; idx < EShLangCount; ++idx) {
- if (idx == ent.stage || uniformVarMap[idx] == nullptr)
- continue;
- auto entKey2 = uniformVarMap[idx]->find(entKey.first);
- if (entKey2 != uniformVarMap[idx]->end()) {
- entKey2->second.newBinding = ent.newBinding;
- }
- }
- }
- }
- 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;
- }
- if (ent.symbol->getQualifier().hasSet()) {
- for (uint32_t idx = EShLangVertex; idx < EShLangCount; ++idx) {
- if ((idx == stage) || (uniformVarMap[idx] == nullptr))
- continue;
- auto entKey2 = uniformVarMap[idx]->find(entKey.first);
- if (entKey2 != uniformVarMap[idx]->end()) {
- entKey2->second.newSet = ent.newSet;
- }
- }
- }
- }
- } 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;
- TVarLiveMap* uniformVarMap[EShLangCount];
- 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(ent.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, EProfile profile, int version)
- : preStage(EShLangCount)
- , currentStage(EShLangCount)
- , nextStage(EShLangCount)
- , resolver(r)
- , infoSink(i)
- , hadError(hadError)
- , profile(profile)
- , version(version)
- {
- memcpy(inVarMaps, in, EShLangCount * (sizeof(TVarLiveMap*)));
- memcpy(outVarMaps, out, EShLangCount * (sizeof(TVarLiveMap*)));
- memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*)));
- std::map<TString, TString> anonymousMemberMap;
- std::vector<TRange> usedUniformLocation;
- std::vector<TString> usedUniformName;
- usedUniformLocation.clear();
- usedUniformName.clear();
- for (int i = 0; i < EShLangCount; i++) {
- if (uniformVarMap[i]) {
- for (auto uniformVar : *uniformVarMap[i])
- {
- TIntermSymbol* pSymbol = uniformVar.second.symbol;
- TQualifier qualifier = uniformVar.second.symbol->getQualifier();
- TString symbolName = pSymbol->getAccessName();
- // All the uniform needs multi-stage location check (block/default)
- int uniformLocation = qualifier.layoutLocation;
- if (uniformLocation != TQualifier::layoutLocationEnd) {
- // Total size of current uniform, could be block, struct or other types.
- int size = TIntermediate::computeTypeUniformLocationSize(pSymbol->getType());
- TRange locationRange(uniformLocation, uniformLocation + size - 1);
- // Combine location and component ranges
- int overlapLocation = -1;
- bool diffLocation = false;
- // Check for collisions, except for vertex inputs on desktop targeting OpenGL
- overlapLocation = checkLocationOverlap(locationRange, usedUniformLocation, symbolName, usedUniformName, diffLocation);
- // Overlap locations of uniforms, regardless of components (multi stages)
- if (overlapLocation == -1) {
- usedUniformLocation.push_back(locationRange);
- usedUniformName.push_back(symbolName);
- }
- else if (overlapLocation >= 0) {
- if (diffLocation == true) {
- TString err = ("Uniform location should be equal for same uniforms: " +std::to_string(overlapLocation)).c_str();
- infoSink.info.message(EPrefixInternalError, err.c_str());
- hadError = true;
- break;
- }
- else {
- TString err = ("Uniform location overlaps across stages: " + std::to_string(overlapLocation)).c_str();
- infoSink.info.message(EPrefixInternalError, err.c_str());
- hadError = true;
- break;
- }
- }
- }
- if ((uniformVar.second.symbol->getBasicType() == EbtBlock) &&
- IsAnonymous(uniformVar.second.symbol->getName()))
- {
- auto blockType = uniformVar.second.symbol->getType().getStruct();
- for (size_t memberIdx = 0; memberIdx < blockType->size(); ++memberIdx) {
- auto memberName = (*blockType)[memberIdx].type->getFieldName();
- if (anonymousMemberMap.find(memberName) != anonymousMemberMap.end())
- {
- if (anonymousMemberMap[memberName] != uniformVar.second.symbol->getType().getTypeName())
- {
- TString err = "Invalid block member name: " + memberName;
- infoSink.info.message(EPrefixInternalError, err.c_str());
- hadError = true;
- break;
- }
- }
- else
- {
- anonymousMemberMap[memberName] = uniformVar.second.symbol->getType().getTypeName();
- }
- }
- }
- if (hadError)
- break;
- }
- }
- }
- }
- // In case we need to new an intermediate, which costs too much
- int checkLocationOverlap(const TRange& locationRange, std::vector<TRange>& usedUniformLocation, const TString symbolName, std::vector<TString>& usedUniformName, bool& diffLocation)
- {
- for (size_t r = 0; r < usedUniformLocation.size(); ++r) {
- if (usedUniformName[r] == symbolName) {
- diffLocation = true;
- return (usedUniformLocation[r].start == locationRange.start &&
- usedUniformLocation[r].last == locationRange.last)
- ? -2 : std::max(locationRange.start, usedUniformLocation[r].start);
- }
- if (locationRange.overlap(usedUniformLocation[r])) {
- // there is a collision; pick one
- return std::max(locationRange.start, usedUniformLocation[r].start);
- }
- }
- return -1; // no collision
- }
- 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;
- EShLanguage stage = ent1.stage;
- TString mangleName1, mangleName2;
- 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);
- break;
- }
- }
- }
- if (type.getQualifier().isArrayedIo(stage)) {
- TType subType(type, 0);
- subType.appendMangledName(mangleName1);
- } else {
- type.appendMangledName(mangleName1);
- }
- if (base->getQualifier().storage == EvqVaryingIn) {
- // validate stage in;
- if (preStage == EShLangCount)
- return;
- if (TSymbolTable::isBuiltInSymbol(base->getId()))
- return;
- if (outVarMaps[preStage] != nullptr) {
- auto ent2 = outVarMaps[preStage]->find(name);
- uint32_t location = base->getType().getQualifier().layoutLocation;
- if (ent2 == outVarMaps[preStage]->end() &&
- location != glslang::TQualifier::layoutLocationEnd) {
- for (auto var = outVarMaps[preStage]->begin(); var != ent2; var++) {
- if (var->second.symbol->getType().getQualifier().layoutLocation == location) {
- ent2 = var;
- break;
- }
- }
- }
- if (ent2 != outVarMaps[preStage]->end()) {
- auto& type1 = base->getType();
- auto& type2 = ent2->second.symbol->getType();
- hadError = hadError || typeCheck(&type1, &type2, name.c_str(), false);
- if (ent2->second.symbol->getType().getQualifier().isArrayedIo(preStage)) {
- TType subType(ent2->second.symbol->getType(), 0);
- subType.appendMangledName(mangleName2);
- }
- else {
- ent2->second.symbol->getType().appendMangledName(mangleName2);
- }
- if (mangleName1 == mangleName2) {
- // For ES 3.0 only, other versions have no such restrictions
- // According to ES 3.0 spec: The type and presence of the interpolation qualifiers and
- // storage qualifiers of variables with the same name declared in all linked shaders must
- // match, otherwise the link command will fail.
- if (profile == EEsProfile && version == 300) {
- // Don't need to check smooth qualifier, as it uses the default interpolation mode
- if (ent1.stage == EShLangFragment && type1.isBuiltIn() == false) {
- if (type1.getQualifier().flat != type2.getQualifier().flat ||
- type1.getQualifier().nopersp != type2.getQualifier().nopersp) {
- TString err = "Interpolation qualifier mismatch : " + entKey.first;
- infoSink.info.message(EPrefixInternalError, err.c_str());
- hadError = true;
- }
- }
- }
- return;
- }
- else {
- TString err = "Invalid In/Out variable type : " + entKey.first;
- infoSink.info.message(EPrefixInternalError, err.c_str());
- hadError = true;
- }
- }
- else if (!base->getType().isBuiltIn()) {
- // According to spec: A link error is generated if any statically referenced input variable
- // or block does not have a matching output
- if (profile == EEsProfile && ent1.live) {
- hadError = true;
- TString errorStr = name + ": not been declare as a output variable in pre shader stage.";
- infoSink.info.message(EPrefixError, errorStr.c_str());
- }
- }
- return;
- }
- } else if (base->getQualifier().storage == EvqVaryingOut) {
- // validate stage out;
- if (nextStage == EShLangCount)
- return;
- if (TSymbolTable::isBuiltInSymbol(base->getId()))
- return;
- if (inVarMaps[nextStage] != nullptr) {
- auto ent2 = inVarMaps[nextStage]->find(name);
- if (ent2 != inVarMaps[nextStage]->end()) {
- if (ent2->second.symbol->getType().getQualifier().isArrayedIo(nextStage)) {
- TType subType(ent2->second.symbol->getType(), 0);
- subType.appendMangledName(mangleName2);
- }
- else {
- 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();
- // validate instance name of blocks
- if (hadError == false &&
- base->getType().getBasicType() == EbtBlock &&
- IsAnonymous(base->getName()) != IsAnonymous(ent2->second.symbol->getName())) {
- TString err = "Matched uniform block names must also either all be lacking "
- "an instance name or all having an instance name: " + entKey.first;
- infoSink.info.message(EPrefixInternalError, err.c_str());
- hadError = true;
- }
- // validate uniform block member qualifier and member names
- auto& type1 = base->getType();
- auto& type2 = ent2->second.symbol->getType();
- if (hadError == false && base->getType().getBasicType() == EbtBlock) {
- hadError = hadError || typeCheck(&type1, &type2, name.c_str(), true);
- }
- else {
- hadError = hadError || typeCheck(&type1, &type2, name.c_str(), false);
- }
- }
- else if (base->getBasicType() == EbtBlock)
- {
- if (IsAnonymous(base->getName()))
- {
- // The name of anonymous block member can't same with default uniform variable.
- auto blockType1 = base->getType().getStruct();
- for (size_t memberIdx = 0; memberIdx < blockType1->size(); ++memberIdx) {
- auto memberName = (*blockType1)[memberIdx].type->getFieldName();
- if (uniformVarMap[i]->find(memberName) != uniformVarMap[i]->end())
- {
- TString err = "Invalid Uniform variable name : " + memberName;
- infoSink.info.message(EPrefixInternalError, err.c_str());
- hadError = true;
- break;
- }
- }
- }
- }
- }
- }
- }
- }
- 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;
- EProfile profile;
- int version;
- private:
- TSymbolValidater& operator=(TSymbolValidater&) = delete;
- bool qualifierCheck(const TType* const type1, const TType* const type2, const std::string& name, bool isBlock)
- {
- bool hasError = false;
- const TQualifier& qualifier1 = type1->getQualifier();
- const TQualifier& qualifier2 = type2->getQualifier();
- if (((isBlock == false) &&
- (type1->getQualifier().storage == EvqUniform && type2->getQualifier().storage == EvqUniform)) ||
- (type1->getQualifier().storage == EvqGlobal && type2->getQualifier().storage == EvqGlobal)) {
- if (qualifier1.precision != qualifier2.precision) {
- hasError = true;
- std::string errorStr = name + ": have precision conflict cross stage.";
- infoSink.info.message(EPrefixError, errorStr.c_str());
- }
- if (qualifier1.hasFormat() && qualifier2.hasFormat()) {
- if (qualifier1.layoutFormat != qualifier2.layoutFormat) {
- hasError = true;
- std::string errorStr = name + ": have layout format conflict cross stage.";
- infoSink.info.message(EPrefixError, errorStr.c_str());
- }
- }
- }
- if (isBlock == true) {
- if (qualifier1.layoutPacking != qualifier2.layoutPacking) {
- hasError = true;
- std::string errorStr = name + ": have layoutPacking conflict cross stage.";
- infoSink.info.message(EPrefixError, errorStr.c_str());
- }
- if (qualifier1.layoutMatrix != qualifier2.layoutMatrix) {
- hasError = true;
- std::string errorStr = name + ": have layoutMatrix conflict cross stage.";
- infoSink.info.message(EPrefixError, errorStr.c_str());
- }
- if (qualifier1.layoutOffset != qualifier2.layoutOffset) {
- hasError = true;
- std::string errorStr = name + ": have layoutOffset conflict cross stage.";
- infoSink.info.message(EPrefixError, errorStr.c_str());
- }
- if (qualifier1.layoutAlign != qualifier2.layoutAlign) {
- hasError = true;
- std::string errorStr = name + ": have layoutAlign conflict cross stage.";
- infoSink.info.message(EPrefixError, errorStr.c_str());
- }
- }
- return hasError;
- }
- bool typeCheck(const TType* const type1, const TType* const type2, const std::string& name, bool isBlock)
- {
- bool hasError = false;
- if (!(type1->isStruct() && type2->isStruct())) {
- hasError = hasError || qualifierCheck(type1, type2, name, isBlock);
- }
- else {
- if (type1->getBasicType() == EbtBlock && type2->getBasicType() == EbtBlock)
- isBlock = true;
- const TTypeList* typeList1 = type1->getStruct();
- const TTypeList* typeList2 = type2->getStruct();
- std::string newName = name;
- size_t memberCount = typeList1->size();
- size_t index2 = 0;
- for (size_t index = 0; index < memberCount; index++, index2++) {
- // Skip inactive member
- if (typeList1->at(index).type->getBasicType() == EbtVoid)
- continue;
- while (index2 < typeList2->size() && typeList2->at(index2).type->getBasicType() == EbtVoid) {
- ++index2;
- }
- // TypeList1 has more members in list
- if (index2 == typeList2->size()) {
- std::string errorStr = name + ": struct mismatch.";
- infoSink.info.message(EPrefixError, errorStr.c_str());
- hasError = true;
- break;
- }
- if (typeList1->at(index).type->getFieldName() != typeList2->at(index2).type->getFieldName()) {
- std::string errorStr = name + ": member name mismatch.";
- infoSink.info.message(EPrefixError, errorStr.c_str());
- hasError = true;
- }
- else {
- newName = typeList1->at(index).type->getFieldName().c_str();
- }
- hasError = hasError || typeCheck(typeList1->at(index).type, typeList2->at(index2).type, newName, isBlock);
- }
- while (index2 < typeList2->size())
- {
- // TypeList2 has more members
- if (typeList2->at(index2).type->getBasicType() != EbtVoid) {
- std::string errorStr = name + ": struct mismatch.";
- infoSink.info.message(EPrefixError, errorStr.c_str());
- hasError = true;
- break;
- }
- ++index2;
- }
- }
- return hasError;
- }
- };
- 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->getAccessName().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 = ent.symbol->getAccessName();
- 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 declaration 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 = ent.symbol->getAccessName();
- // 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 declaration in other stage.
- //
- // vs: uniform vec4 a;
- // fs: layout(..., location = 3,...) uniform vec4 a;
- hasLocation = true;
- location = iter->second;
- }
- if (! hasLocation) {
- // No explicit location declaration 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 declaration 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 = ent.symbol->getAccessName();
- // On OpenGL arrays of opaque types take a separate 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 = ent.symbol->getAccessName();
- 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 = ent.symbol->getAccessName();
- 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.destinations.empty()) {
- TIntermNode* destination = iter_binding_live.destinations.back();
- iter_binding_live.destinations.pop_back();
- destination->traverse(&iter_binding_live);
- }
- // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info.
- for (auto& var : inVarMap) { inVector.push_back(var); }
- std::sort(inVector.begin(), inVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
- return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
- });
- for (auto& var : outVarMap) { outVector.push_back(var); }
- std::sort(outVector.begin(), outVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
- return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
- });
- for (auto& var : uniformVarMap) { uniformVector.push_back(var); }
- std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
- return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
- });
- bool hadError = false;
- TVarLiveMap* dummyUniformVarMap[EShLangCount] = {};
- TNotifyInOutAdaptor inOutNotify(stage, *resolver);
- TNotifyUniformAdaptor uniformNotify(stage, *resolver);
- TResolverUniformAdaptor uniformResolve(stage, *resolver, dummyUniformVarMap, 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);
- for (auto& var : inVector) { inOutResolve(var); }
- std::for_each(inVector.begin(), inVector.end(), [&inVarMap](TVarLivePair p) {
- auto at = inVarMap.find(p.second.symbol->getAccessName());
- if (at != inVarMap.end() && p.second.id == at->second.id)
- at->second = p.second;
- });
- for (auto& var : outVector) { inOutResolve(var); }
- std::for_each(outVector.begin(), outVector.end(), [&outVarMap](TVarLivePair p) {
- auto at = outVarMap.find(p.second.symbol->getAccessName());
- if (at != outVarMap.end() && p.second.id == at->second.id)
- 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->getAccessName());
- if (at != uniformVarMap.end() && p.second.id == at->second.id)
- 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();
- // Profile and version are use for symbol validate.
- profile = intermediate.getProfile();
- version = intermediate.getVersion();
- // 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.destinations.empty()) {
- TIntermNode* destination = iter_binding_live.destinations.back();
- iter_binding_live.destinations.pop_back();
- destination->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, uniformVarMap, infoSink, hadError);
- TResolverInOutAdaptor inOutResolve(EShLangCount, *resolver, infoSink, hadError);
- TSymbolValidater symbolValidater(*resolver, infoSink, inVarMaps,
- outVarMaps, uniformVarMap, hadError, profile, version);
- TVarLiveVector uniformVector;
- resolver->beginResolve(EShLangCount);
- for (int stage = EShLangVertex; stage < EShLangCount; stage++) {
- if (inVarMaps[stage] != nullptr) {
- inOutResolve.setStage(EShLanguage(stage));
- for (auto& var : *(inVarMaps[stage])) { symbolValidater(var); }
- for (auto& var : *(inVarMaps[stage])) { inOutResolve(var); }
- for (auto& var : *(outVarMaps[stage])) { symbolValidater(var); }
- for (auto& var : *(outVarMaps[stage])) { inOutResolve(var); }
- }
- if (uniformVarMap[stage] != nullptr) {
- uniformResolve.setStage(EShLanguage(stage));
- for (auto& var : *(uniformVarMap[stage])) { uniformVector.push_back(var); }
- }
- }
- std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
- return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
- });
- for (auto& var : uniformVector) { symbolValidater(var); }
- for (auto& var : uniformVector) { uniformResolve(var); }
- 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 = uniformResolve.uniformVarMap;
- std::for_each(uniformVector.begin(), uniformVector.end(), [pUniformVarMap, stage](TVarLivePair p) {
- auto at = pUniformVarMap[stage]->find(p.second.symbol->getAccessName());
- if (at != pUniformVarMap[stage]->end() && at->second.id == p.second.id){
- int resolvedBinding = at->second.newBinding;
- at->second = p.second;
- if (resolvedBinding > 0)
- at->second.newBinding = resolvedBinding;
- }
- });
- TVarSetTraverser iter_iomap(*intermediates[stage], *inVarMaps[stage], *outVarMaps[stage],
- *uniformResolve.uniformVarMap[stage]);
- intermediates[stage]->getTreeRoot()->traverse(&iter_iomap);
- }
- }
- return !hadError;
- } else {
- return false;
- }
- }
- } // end namespace glslang
- #endif // !GLSLANG_WEB && !GLSLANG_ANGLE
|