diff --git a/glslang/Include/Types.h b/glslang/Include/Types.h index 4d3c4b90..7078d8fd 100644 --- a/glslang/Include/Types.h +++ b/glslang/Include/Types.h @@ -394,6 +394,7 @@ public: void clear() { + semanticName = nullptr; precision = EpqNone; invariant = false; noContraction = false; @@ -403,6 +404,7 @@ public: // drop qualifiers that don't belong in a temporary variable void makeTemporary() { + semanticName = nullptr; storage = EvqTemporary; builtIn = EbvNone; clearInterstage(); @@ -447,10 +449,17 @@ public: // If A, then nothing should change, if B, then everything should change, but this is half way. void makePartialTemporary() { + semanticName = nullptr; storage = EvqTemporary; specConstant = false; } + bool hasSemantic() const + { + return semanticName != nullptr; + } + + const char* semanticName; TStorageQualifier storage : 6; TBuiltInVariable builtIn : 8; TPrecisionQualifier precision : 3; diff --git a/glslang/MachineIndependent/iomapper.cpp b/glslang/MachineIndependent/iomapper.cpp index 07aa4762..a478a2ca 100644 --- a/glslang/MachineIndependent/iomapper.cpp +++ b/glslang/MachineIndependent/iomapper.cpp @@ -66,11 +66,14 @@ namespace glslang { struct TVarEntryInfo { - int id; - TIntermSymbol* symbol; - bool live; - int newBinding; - int newSet; + int id; + TIntermSymbol* symbol; + bool live; + int newBinding; + int newSet; + int newLocation; + int newComponent; + int newIndex; struct TOrderById { @@ -113,70 +116,110 @@ typedef std::vector TVarLiveMap; class TVarGatherTraverser : public TLiveTraverser { public: - TVarGatherTraverser(const TIntermediate& i, TVarLiveMap& vars, bool traverseDeadCode) + TVarGatherTraverser(const TIntermediate& i, bool traverseDeadCode, TVarLiveMap& inList, TVarLiveMap& outList, TVarLiveMap& uniformList) : TLiveTraverser(i, traverseDeadCode, true, true, false) - , varLiveList(vars) + , inputList(inList) + , outputList(outList) + , uniformList(uniformList) { } virtual void visitSymbol(TIntermSymbol* base) { - if (base->getType().getQualifier().isUniformOrBuffer()) { + TVarLiveMap* target; + if (base->getQualifier().storage == EvqVaryingIn) + target = &inputList; + else if (base->getQualifier().storage == EvqVaryingOut) + target = &outputList; + else if (base->getType().getQualifier().isUniformOrBuffer()) + target = &uniformList; + else + target = nullptr; + if (target) { TVarEntryInfo ent = { base->getId(), base, !traverseAll }; - TVarLiveMap::iterator at = std::lower_bound(varLiveList.begin(), varLiveList.end(), ent, TVarEntryInfo::TOrderById()); - if (at != varLiveList.end() && at->id == ent.id) + TVarLiveMap::iterator at = std::lower_bound(target->begin(), target->end(), ent, TVarEntryInfo::TOrderById()); + if (at != target->end() && at->id == ent.id) at->live = at->live || !traverseAll; // update live state else - varLiveList.insert(at, ent); + target->insert(at, ent); } } private: - TVarLiveMap& varLiveList; + TVarLiveMap& inputList; + TVarLiveMap& outputList; + TVarLiveMap& uniformList; }; class TVarSetTraverser : public TLiveTraverser { public: - TVarSetTraverser(const TIntermediate& i, const TVarLiveMap& vars) + TVarSetTraverser(const TIntermediate& i, const TVarLiveMap& inList, const TVarLiveMap& outList, const TVarLiveMap& uniformList) : TLiveTraverser(i, true, true, true, false) - , varLiveList(vars) + , inputList(inList) + , outputList(outList) + , uniformList(uniformList) { } virtual void visitSymbol(TIntermSymbol* base) { - TVarEntryInfo ent = { base->getId() }; - TVarLiveMap::const_iterator at = std::lower_bound(varLiveList.begin(), varLiveList.end(), ent, TVarEntryInfo::TOrderById()); - if (at == varLiveList.end()) + const TVarLiveMap* source; + if (base->getQualifier().storage == EvqVaryingIn) + source = &inputList; + else if (base->getQualifier().storage == EvqVaryingOut) + source = &outputList; + else if (base->getQualifier().storage == EvqUniform) + source = &uniformList; + else return; - if (!(at->id == ent.id)) + + TVarEntryInfo ent = { base->getId() }; + TVarLiveMap::const_iterator at = std::lower_bound(source->begin(), source->end(), ent, TVarEntryInfo::TOrderById()); + if (at == source->end()) + return; + + if (at->id != ent.id) return; if (at->newBinding != -1) base->getWritableType().getQualifier().layoutBinding = at->newBinding; if (at->newSet != -1) base->getWritableType().getQualifier().layoutSet = at->newSet; + if (at->newLocation != -1) + base->getWritableType().getQualifier().layoutLocation = at->newLocation; + if (at->newComponent != -1) + base->getWritableType().getQualifier().layoutComponent = at->newComponent; + if (at->newIndex != -1) + base->getWritableType().getQualifier().layoutIndex = at->newIndex; } private: - const TVarLiveMap& varLiveList; + const TVarLiveMap& inputList; + const TVarLiveMap& outputList; + const TVarLiveMap& uniformList; }; -struct TResolverAdaptor +struct TResolverUniformAdaptor { - TResolverAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e) - : stage(s) - , resolver(r) + TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e, TIntermediate& interm) + : resolver(r) + , stage(s) , infoSink(i) , error(e) + , intermediate(interm) { } inline void operator()(TVarEntryInfo& ent) { + ent.newLocation = -1; + ent.newComponent = -1; + ent.newBinding = -1; + ent.newSet = -1; + ent.newIndex = -1; const bool isValid = resolver.validateBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), ent.live); if (isValid) { ent.newBinding = resolver.resolveBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), ent.live); @@ -209,9 +252,63 @@ struct TResolverAdaptor TIoMapResolver& resolver; TInfoSink& infoSink; bool& error; + TIntermediate& intermediate; private: - TResolverAdaptor& operator=(TResolverAdaptor&); + TResolverUniformAdaptor& operator=(TResolverUniformAdaptor&); +}; + +struct TResolverInOutAdaptor +{ + TResolverInOutAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e, TIntermediate& interm) + : resolver(r) + , stage(s) + , infoSink(i) + , error(e) + , intermediate(interm) + { + } + + inline void operator()(TVarEntryInfo& ent) + { + ent.newLocation = -1; + ent.newComponent = -1; + ent.newBinding = -1; + ent.newSet = -1; + ent.newIndex = -1; + const bool isValid = resolver.validateInOut(stage, + ent.symbol->getName().c_str(), + ent.symbol->getType(), + ent.live); + if (isValid) { + ent.newLocation = resolver.resolveInOutLocation(stage, + ent.symbol->getName().c_str(), + ent.symbol->getType(), + ent.live); + ent.newComponent = resolver.resolveInOutComponent(stage, + ent.symbol->getName().c_str(), + ent.symbol->getType(), + ent.live); + ent.newIndex = resolver.resolveInOutIndex(stage, + ent.symbol->getName().c_str(), + ent.symbol->getType(), + ent.live); + } else { + TString errorMsg = "Invalid shader In/Out variable semantic: "; + errorMsg += ent.symbol->getType().getQualifier().semanticName; + infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); + error = true; + } + } + + EShLanguage stage; + TIoMapResolver& resolver; + TInfoSink& infoSink; + bool& error; + TIntermediate& intermediate; + +private: + TResolverInOutAdaptor& operator=(TResolverInOutAdaptor&); }; /* @@ -348,6 +445,23 @@ struct TDefaultIoResolver : public glslang::TIoMapResolver return type.getQualifier().layoutSet; return 0; } + + bool validateInOut(EShLanguage stage, const char* name, const TType& type, bool is_live) override + { + return true; + } + int resolveInOutLocation(EShLanguage stage, const char* name, const TType& type, bool is_live) override + { + return -1; + } + int resolveInOutComponent(EShLanguage stage, const char* name, const TType& type, bool is_live) override + { + return -1; + } + int resolveInOutIndex(EShLanguage stage, const char* name, const TType& type, bool is_live) override + { + return -1; + } }; // Map I/O variables to provided offsets, and make bindings for @@ -386,9 +500,9 @@ bool TIoMapper::addStage(EShLanguage stage, TIntermediate &intermediate, TInfoSi resolver = &defaultResolver; } - TVarLiveMap varMap; - TVarGatherTraverser iter_binding_all(intermediate, varMap, true); - TVarGatherTraverser iter_binding_live(intermediate, varMap, false); + TVarLiveMap inVarMap, outVarMap, uniformVarMap; + 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()); @@ -400,16 +514,19 @@ bool TIoMapper::addStage(EShLanguage stage, TIntermediate &intermediate, TInfoSi } // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info. - std::sort(varMap.begin(), varMap.end(), TVarEntryInfo::TOrderByPriority()); + std::sort(uniformVarMap.begin(), uniformVarMap.end(), TVarEntryInfo::TOrderByPriority()); bool hadError = false; - TResolverAdaptor doResolve(stage, *resolver, infoSink, hadError); - std::for_each(varMap.begin(), varMap.end(), doResolve); + TResolverUniformAdaptor uniformResolve(stage, *resolver, infoSink, hadError, intermediate); + TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError, intermediate); + std::for_each(inVarMap.begin(), inVarMap.end(), inOutResolve); + std::for_each(outVarMap.begin(), outVarMap.end(), inOutResolve); + std::for_each(uniformVarMap.begin(), uniformVarMap.end(), uniformResolve); if (!hadError) { // sort by id again, so we can use lower bound to find entries - std::sort(varMap.begin(), varMap.end(), TVarEntryInfo::TOrderById()); - TVarSetTraverser iter_iomap(intermediate, varMap); + std::sort(uniformVarMap.begin(), uniformVarMap.end(), TVarEntryInfo::TOrderById()); + TVarSetTraverser iter_iomap(intermediate, inVarMap, outVarMap, uniformVarMap); root->traverse(&iter_iomap); } diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h index 34940a69..e9ed67ac 100644 --- a/glslang/MachineIndependent/localintermediate.h +++ b/glslang/MachineIndependent/localintermediate.h @@ -423,6 +423,11 @@ public: bool getGeoPassthroughEXT() const { return geoPassthroughEXT; } #endif + const char* addSemanticName(const TString& name) + { + return semanticNameSet.insert(name).first->c_str(); + } + protected: TIntermSymbol* addSymbol(int Id, const TString&, const TType&, const TConstUnionArray&, TIntermTyped* subtree, const TSourceLoc&); void error(TInfoSink& infoSink, const char*); @@ -501,6 +506,7 @@ protected: std::vector usedAtomics; // sets of bindings used by atomic counters std::vector xfbBuffers; // all the data we need to track per xfb buffer std::unordered_set usedConstantId; // specialization constant ids used + std::set semanticNameSet; private: void operator=(TIntermediate&); // prevent assignments diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h index 8aa9baa3..8d8abd77 100644 --- a/glslang/Public/ShaderLang.h +++ b/glslang/Public/ShaderLang.h @@ -452,10 +452,12 @@ class TIoMapper; // and resolveSet are invoked to resolve the binding and descriptor // set index respectively. // Invocations happen in a particular order: -// 1) var with binding and set already defined -// 2) var with binding but no set defined -// 3) var with set but no binding defined -// 4) var with no binding and no set defined +// 1) all shader inputs +// 2) all shader outputs +// 3) all uniforms with binding and set already defined +// 4) all uniforms with binding but no set defined +// 5) all uniforms with set but no binding defined +// 6) all uniforms with no binding and no set defined // // NOTE: that still limit checks are applied to bindings and sets // and may result in an error. @@ -473,6 +475,18 @@ public: // Should return a value >= 0 if the current set should be overriden. // Return -1 if the current set (including no set) should be kept. virtual int resolveSet(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0; + // Should return true if the resuling/current setup would be ok. + // Basic idea is to do aliasing checks and reject invalid semantic names. + virtual bool validateInOut(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0; + // Should return a value >= 0 if the current location should be overriden. + // Return -1 if the current location (including no location) should be kept. + virtual int resolveInOutLocation(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0; + // Should return a value >= 0 if the current component index should be overriden. + // Return -1 if the current component index (including no index) should be kept. + virtual int resolveInOutComponent(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0; + // Should return a value >= 0 if the current color index should be overriden. + // Return -1 if the current color index (including no index) should be kept. + virtual int resolveInOutIndex(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0; }; // Make one TProgram per set of shaders that will get linked together. Add all diff --git a/hlsl/hlslParseHelper.cpp b/hlsl/hlslParseHelper.cpp index 3c88d47b..358f9c56 100755 --- a/hlsl/hlslParseHelper.cpp +++ b/hlsl/hlslParseHelper.cpp @@ -4161,6 +4161,7 @@ void HlslParseContext::handleSemantic(TSourceLoc loc, TQualifier& qualifier, TBu } qualifier.builtIn = builtIn; + qualifier.semanticName = intermediate.addSemanticName(semanticUpperCase); } //