Unverified Commit f04f1f93 by John Kessenich Committed by GitHub

Merge pull request #1857 from Roy-AMD/automapping-opengl-location

Automapping opengl location
parents 7fc86834 03a93ae1
...@@ -1852,7 +1852,7 @@ const char* TShader::getInfoDebugLog() ...@@ -1852,7 +1852,7 @@ const char* TShader::getInfoDebugLog()
return infoSink->debug.c_str(); return infoSink->debug.c_str();
} }
TProgram::TProgram() : reflection(0), ioMapper(nullptr), linked(false) TProgram::TProgram() : reflection(0), linked(false)
{ {
pool = new TPoolAllocator; pool = new TPoolAllocator;
infoSink = new TInfoSink; infoSink = new TInfoSink;
...@@ -1864,7 +1864,6 @@ TProgram::TProgram() : reflection(0), ioMapper(nullptr), linked(false) ...@@ -1864,7 +1864,6 @@ TProgram::TProgram() : reflection(0), ioMapper(nullptr), linked(false)
TProgram::~TProgram() TProgram::~TProgram()
{ {
delete ioMapper;
delete infoSink; delete infoSink;
delete reflection; delete reflection;
...@@ -2035,21 +2034,24 @@ void TProgram::dumpReflection() { if (reflection != nullptr) reflection->dump(); ...@@ -2035,21 +2034,24 @@ void TProgram::dumpReflection() { if (reflection != nullptr) reflection->dump();
// //
// I/O mapping implementation. // I/O mapping implementation.
// //
bool TProgram::mapIO(TIoMapResolver* resolver) bool TProgram::mapIO(TIoMapResolver* pResolver, TIoMapper* pIoMapper)
{ {
if (! linked || ioMapper) if (! linked)
return false; return false;
TIoMapper* ioMapper = nullptr;
ioMapper = new TIoMapper; TIoMapper defaultIOMapper;
if (pIoMapper == nullptr)
ioMapper = &defaultIOMapper;
else
ioMapper = pIoMapper;
for (int s = 0; s < EShLangCount; ++s) { for (int s = 0; s < EShLangCount; ++s) {
if (intermediate[s]) { if (intermediate[s]) {
if (! ioMapper->addStage((EShLanguage)s, *intermediate[s], *infoSink, resolver)) if (! ioMapper->addStage((EShLanguage)s, *intermediate[s], *infoSink, pResolver))
return false; return false;
} }
} }
return true; return ioMapper->doMap(pResolver, *infoSink);
} }
} // end namespace glslang } // end namespace glslang
...@@ -35,14 +35,9 @@ ...@@ -35,14 +35,9 @@
#include "../Include/Common.h" #include "../Include/Common.h"
#include "../Include/InfoSink.h" #include "../Include/InfoSink.h"
#include "iomapper.h"
#include "LiveTraverser.h"
#include "localintermediate.h"
#include "gl_types.h" #include "gl_types.h"
#include "iomapper.h"
#include <unordered_set>
#include <unordered_map>
// //
// Map IO bindings. // Map IO bindings.
...@@ -61,60 +56,9 @@ ...@@ -61,60 +56,9 @@
// c. implicit dead bindings are left un-bound. // c. implicit dead bindings are left un-bound.
// //
namespace glslang { namespace glslang {
struct TVarEntryInfo class TVarGatherTraverser : public TLiveTraverser {
{
int id;
TIntermSymbol* symbol;
bool live;
int newBinding;
int newSet;
int newLocation;
int newComponent;
int newIndex;
struct TOrderById
{
inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r)
{
return l.id < r.id;
}
};
struct TOrderByPriority
{
// ordering:
// 1) has both binding and set
// 2) has binding but no set
// 3) has no binding but set
// 4) has no binding and no set
inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r)
{
const TQualifier& lq = l.symbol->getQualifier();
const TQualifier& rq = r.symbol->getQualifier();
// simple rules:
// has binding gives 2 points
// has set gives 1 point
// who has the most points is more important.
int lPoints = (lq.hasBinding() ? 2 : 0) + (lq.hasSet() ? 1 : 0);
int rPoints = (rq.hasBinding() ? 2 : 0) + (rq.hasSet() ? 1 : 0);
if (lPoints == rPoints)
return l.id < r.id;
return lPoints > rPoints;
}
};
};
typedef std::vector<TVarEntryInfo> TVarLiveMap;
class TVarGatherTraverser : public TLiveTraverser
{
public: public:
TVarGatherTraverser(const TIntermediate& i, bool traverseDeadCode, TVarLiveMap& inList, TVarLiveMap& outList, TVarLiveMap& uniformList) TVarGatherTraverser(const TIntermediate& i, bool traverseDeadCode, TVarLiveMap& inList, TVarLiveMap& outList, TVarLiveMap& uniformList)
: TLiveTraverser(i, traverseDeadCode, true, true, false) : TLiveTraverser(i, traverseDeadCode, true, true, false)
...@@ -124,7 +68,6 @@ public: ...@@ -124,7 +68,6 @@ public:
{ {
} }
virtual void visitSymbol(TIntermSymbol* base) virtual void visitSymbol(TIntermSymbol* base)
{ {
TVarLiveMap* target = nullptr; TVarLiveMap* target = nullptr;
...@@ -134,14 +77,15 @@ public: ...@@ -134,14 +77,15 @@ public:
target = &outputList; target = &outputList;
else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().layoutPushConstant) else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().layoutPushConstant)
target = &uniformList; target = &uniformList;
if (target) { if (target) {
TVarEntryInfo ent = { base->getId(), base, !traverseAll }; TVarEntryInfo ent = {base->getId(), base, ! traverseAll};
TVarLiveMap::iterator at = std::lower_bound(target->begin(), target->end(), ent, TVarEntryInfo::TOrderById()); ent.stage = intermediate.getStage();
if (at != target->end() && at->id == ent.id) TVarLiveMap::iterator at = target->find(
at->live = at->live || !traverseAll; // update live state 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 else
target->insert(at, ent); (*target)[ent.symbol->getName()] = ent;
} }
} }
...@@ -162,9 +106,7 @@ public: ...@@ -162,9 +106,7 @@ public:
{ {
} }
virtual void visitSymbol(TIntermSymbol* base) {
virtual void visitSymbol(TIntermSymbol* base)
{
const TVarLiveMap* source; const TVarLiveMap* source;
if (base->getQualifier().storage == EvqVaryingIn) if (base->getQualifier().storage == EvqVaryingIn)
source = &inputList; source = &inputList;
...@@ -176,23 +118,23 @@ public: ...@@ -176,23 +118,23 @@ public:
return; return;
TVarEntryInfo ent = { base->getId() }; TVarEntryInfo ent = { base->getId() };
TVarLiveMap::const_iterator at = std::lower_bound(source->begin(), source->end(), ent, TVarEntryInfo::TOrderById()); TVarLiveMap::const_iterator at = source->find(base->getName());
if (at == source->end()) if (at == source->end())
return; return;
if (at->id != ent.id) if (at->second.id != ent.id)
return; return;
if (at->newBinding != -1) if (at->second.newBinding != -1)
base->getWritableType().getQualifier().layoutBinding = at->newBinding; base->getWritableType().getQualifier().layoutBinding = at->second.newBinding;
if (at->newSet != -1) if (at->second.newSet != -1)
base->getWritableType().getQualifier().layoutSet = at->newSet; base->getWritableType().getQualifier().layoutSet = at->second.newSet;
if (at->newLocation != -1) if (at->second.newLocation != -1)
base->getWritableType().getQualifier().layoutLocation = at->newLocation; base->getWritableType().getQualifier().layoutLocation = at->second.newLocation;
if (at->newComponent != -1) if (at->second.newComponent != -1)
base->getWritableType().getQualifier().layoutComponent = at->newComponent; base->getWritableType().getQualifier().layoutComponent = at->second.newComponent;
if (at->newIndex != -1) if (at->second.newIndex != -1)
base->getWritableType().getQualifier().layoutIndex = at->newIndex; base->getWritableType().getQualifier().layoutIndex = at->second.newIndex;
} }
private: private:
...@@ -210,10 +152,12 @@ struct TNotifyUniformAdaptor ...@@ -210,10 +152,12 @@ struct TNotifyUniformAdaptor
, resolver(r) , resolver(r)
{ {
} }
inline void operator()(TVarEntryInfo& ent)
inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
{ {
resolver.notifyBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), ent.live); resolver.notifyBinding(stage, entKey.second);
} }
private: private:
TNotifyUniformAdaptor& operator=(TNotifyUniformAdaptor&); TNotifyUniformAdaptor& operator=(TNotifyUniformAdaptor&);
}; };
...@@ -222,49 +166,46 @@ struct TNotifyInOutAdaptor ...@@ -222,49 +166,46 @@ struct TNotifyInOutAdaptor
{ {
EShLanguage stage; EShLanguage stage;
TIoMapResolver& resolver; TIoMapResolver& resolver;
inline TNotifyInOutAdaptor(EShLanguage s, TIoMapResolver& r) inline TNotifyInOutAdaptor(EShLanguage s, TIoMapResolver& r)
: stage(s) : stage(s)
, resolver(r) , resolver(r)
{ {
} }
inline void operator()(TVarEntryInfo& ent)
inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
{ {
resolver.notifyInOut(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), ent.live); resolver.notifyInOut(stage, entKey.second);
} }
private: private:
TNotifyInOutAdaptor& operator=(TNotifyInOutAdaptor&); TNotifyInOutAdaptor& operator=(TNotifyInOutAdaptor&);
}; };
struct TResolverUniformAdaptor struct TResolverUniformAdaptor {
{ TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e)
TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e, TIntermediate& interm)
: stage(s) : stage(s)
, resolver(r) , resolver(r)
, infoSink(i) , infoSink(i)
, error(e) , error(e)
, intermediate(interm)
{ {
} }
inline void operator()(TVarEntryInfo& ent) inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
{ TVarEntryInfo& ent = entKey.second;
ent.newLocation = -1; ent.newLocation = -1;
ent.newComponent = -1; ent.newComponent = -1;
ent.newBinding = -1; ent.newBinding = -1;
ent.newSet = -1; ent.newSet = -1;
ent.newIndex = -1; ent.newIndex = -1;
const bool isValid = resolver.validateBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), const bool isValid = resolver.validateBinding(stage, ent);
ent.live);
if (isValid) { if (isValid) {
ent.newBinding = resolver.resolveBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), resolver.resolveBinding(stage, ent);
ent.live); resolver.resolveSet(stage, ent);
ent.newSet = resolver.resolveSet(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), ent.live); resolver.resolveUniformLocation(stage, ent);
ent.newLocation = resolver.resolveUniformLocation(stage, ent.symbol->getName().c_str(),
ent.symbol->getType(), ent.live);
if (ent.newBinding != -1) { if (ent.newBinding != -1) {
if (ent.newBinding >= int(TQualifier::layoutBindingEnd)) { if (ent.newBinding >= int(TQualifier::layoutBindingEnd)) {
TString err = "mapped binding out of range: " + ent.symbol->getName(); TString err = "mapped binding out of range: " + entKey.first;
infoSink.info.message(EPrefixInternalError, err.c_str()); infoSink.info.message(EPrefixInternalError, err.c_str());
error = true; error = true;
...@@ -272,64 +213,52 @@ struct TResolverUniformAdaptor ...@@ -272,64 +213,52 @@ struct TResolverUniformAdaptor
} }
if (ent.newSet != -1) { if (ent.newSet != -1) {
if (ent.newSet >= int(TQualifier::layoutSetEnd)) { if (ent.newSet >= int(TQualifier::layoutSetEnd)) {
TString err = "mapped set out of range: " + ent.symbol->getName(); TString err = "mapped set out of range: " + entKey.first;
infoSink.info.message(EPrefixInternalError, err.c_str()); infoSink.info.message(EPrefixInternalError, err.c_str());
error = true; error = true;
} }
} }
} else { } else {
TString errorMsg = "Invalid binding: " + ent.symbol->getName(); TString errorMsg = "Invalid binding: " + entKey.first;
infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
error = true; error = true;
} }
} }
inline void setStage(EShLanguage s) { stage = s; }
EShLanguage stage; EShLanguage stage;
TIoMapResolver& resolver; TIoMapResolver& resolver;
TInfoSink& infoSink; TInfoSink& infoSink;
bool& error; bool& error;
TIntermediate& intermediate;
private: private:
TResolverUniformAdaptor& operator=(TResolverUniformAdaptor&); TResolverUniformAdaptor& operator=(TResolverUniformAdaptor&);
}; };
struct TResolverInOutAdaptor struct TResolverInOutAdaptor {
{ TResolverInOutAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e)
TResolverInOutAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e, TIntermediate& interm)
: stage(s) : stage(s)
, resolver(r) , resolver(r)
, infoSink(i) , infoSink(i)
, error(e) , error(e)
, intermediate(interm)
{ {
} }
inline void operator()(TVarEntryInfo& ent) inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
{ {
TVarEntryInfo& ent = entKey.second;
ent.newLocation = -1; ent.newLocation = -1;
ent.newComponent = -1; ent.newComponent = -1;
ent.newBinding = -1; ent.newBinding = -1;
ent.newSet = -1; ent.newSet = -1;
ent.newIndex = -1; ent.newIndex = -1;
const bool isValid = resolver.validateInOut(stage, const bool isValid = resolver.validateInOut(stage, ent);
ent.symbol->getName().c_str(),
ent.symbol->getType(),
ent.live);
if (isValid) { if (isValid) {
ent.newLocation = resolver.resolveInOutLocation(stage, resolver.resolveInOutLocation(stage, ent);
ent.symbol->getName().c_str(), resolver.resolveInOutComponent(stage, ent);
ent.symbol->getType(), resolver.resolveInOutIndex(stage, ent);
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 { } else {
TString errorMsg; TString errorMsg;
if (ent.symbol->getType().getQualifier().semanticName != nullptr) { if (ent.symbol->getType().getQualifier().semanticName != nullptr) {
...@@ -344,218 +273,621 @@ struct TResolverInOutAdaptor ...@@ -344,218 +273,621 @@ struct TResolverInOutAdaptor
} }
} }
inline void setStage(EShLanguage s) { stage = s; }
EShLanguage stage; EShLanguage stage;
TIoMapResolver& resolver; TIoMapResolver& resolver;
TInfoSink& infoSink; TInfoSink& infoSink;
bool& error; bool& error;
TIntermediate& intermediate;
private: private:
TResolverInOutAdaptor& operator=(TResolverInOutAdaptor&); TResolverInOutAdaptor& operator=(TResolverInOutAdaptor&);
}; };
// Base class for shared TIoMapResolver services, used by several derivations. // The class is used for reserving explicit uniform locations and ubo/ssbo/opaque bindings
struct TDefaultIoResolverBase : public glslang::TIoMapResolver
{
TDefaultIoResolverBase(const TIntermediate &intermediate) :
intermediate(intermediate),
nextUniformLocation(intermediate.getUniformLocationBase()),
nextInputLocation(0),
nextOutputLocation(0)
{ }
int getBaseBinding(TResourceType res, unsigned int set) const { struct TSymbolValidater
return selectBaseBinding(intermediate.getShiftBinding(res), {
intermediate.getShiftBindingForSet(res, set)); 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*)));
} }
const std::vector<std::string>& getResourceSetBinding() const { return intermediate.getResourceSetBinding(); } inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
TVarEntryInfo& ent1 = entKey.second;
bool doAutoBindingMapping() const { return intermediate.getAutoMapBindings(); } TIntermSymbol* base = ent1.symbol;
bool doAutoLocationMapping() const { return intermediate.getAutoMapLocations(); } 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().layoutPushConstant) {
// 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;
typedef std::vector<int> TSlotSet; private:
typedef std::unordered_map<int, TSlotSet> TSlotSetMap; TSymbolValidater& operator=(TSymbolValidater&);
TSlotSetMap slots; };
TSlotSet::iterator findSlot(int set, int slot) struct TSlotCollector {
{ TSlotCollector(TIoMapResolver& r, TInfoSink& i) : resolver(r), infoSink(i) { }
return std::lower_bound(slots[set].begin(), slots[set].end(), slot);
}
bool checkEmpty(int set, int slot) inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
{ resolver.reserverStorageSlot(entKey.second, infoSink);
TSlotSet::iterator at = findSlot(set, slot); resolver.reserverResourceSlot(entKey.second, infoSink);
return !(at != slots[set].end() && *at == slot);
} }
TIoMapResolver& resolver;
TInfoSink& infoSink;
int reserveSlot(int set, int slot, int size = 1) private:
{ TSlotCollector& operator=(TSlotCollector&);
TSlotSet::iterator at = findSlot(set, slot); };
// tolerate aliasing, by not double-recording aliases TDefaultIoResolverBase::TDefaultIoResolverBase(const TIntermediate& intermediate)
// (policy about appropriateness of the alias is higher up) : intermediate(intermediate)
for (int i = 0; i < size; i++) { , nextUniformLocation(intermediate.getUniformLocationBase())
if (at == slots[set].end() || *at != slot + i) , nextInputLocation(0)
at = slots[set].insert(at, slot + i); , nextOutputLocation(0)
++at; {
} memset(stageMask, false, sizeof(bool) * (EShLangCount + 1));
}
return slot; int TDefaultIoResolverBase::getBaseBinding(TResourceType res, unsigned int set) const {
} return selectBaseBinding(intermediate.getShiftBinding(res), intermediate.getShiftBindingForSet(res, set));
}
int getFreeSlot(int set, int base, int size = 1) const std::vector<std::string>& TDefaultIoResolverBase::getResourceSetBinding() const {
{ return intermediate.getResourceSetBinding();
TSlotSet::iterator at = findSlot(set, base); }
if (at == slots[set].end())
return reserveSlot(set, base, size);
// look for a big enough gap bool TDefaultIoResolverBase::doAutoBindingMapping() const { return intermediate.getAutoMapBindings(); }
for (; at != slots[set].end(); ++at) {
if (*at - base >= size)
break;
base = *at + 1;
}
return reserveSlot(set, base, size);
}
virtual bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool /*is_live*/) override = 0; bool TDefaultIoResolverBase::doAutoLocationMapping() const { return intermediate.getAutoMapLocations(); }
virtual int resolveBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool is_live) override = 0; TDefaultIoResolverBase::TSlotSet::iterator TDefaultIoResolverBase::findSlot(int set, int slot) {
return std::lower_bound(slots[set].begin(), slots[set].end(), slot);
}
int resolveSet(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool /*is_live*/) override bool TDefaultIoResolverBase::checkEmpty(int set, int slot) {
{ TSlotSet::iterator at = findSlot(set, slot);
if (type.getQualifier().hasSet()) return ! (at != slots[set].end() && *at == slot);
return type.getQualifier().layoutSet; }
// If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN) int TDefaultIoResolverBase::reserveSlot(int set, int slot, int size) {
if (getResourceSetBinding().size() == 1) TSlotSet::iterator at = findSlot(set, slot);
return atoi(getResourceSetBinding()[0].c_str()); // 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;
}
return 0; 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;
} }
int resolveUniformLocation(EShLanguage /*stage*/, const char* name, const glslang::TType& type, bool /*is_live*/) override return reserveSlot(set, base, size);
{ }
// kick out of not doing this
if (!doAutoLocationMapping())
return -1;
// no locations added if already present, a built-in variable, a block, or an opaque int TDefaultIoResolverBase::resolveSet(EShLanguage /*stage*/, TVarEntryInfo& ent) {
if (type.getQualifier().hasLocation() || type.isBuiltIn() || const TType& type = ent.symbol->getType();
type.getBasicType() == EbtBlock || if (type.getQualifier().hasSet()) {
type.getBasicType() == EbtAtomicUint || return ent.newSet = type.getQualifier().layoutSet;
(type.containsOpaque() && intermediate.getSpv().openGl == 0)) }
return -1; // 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;
}
// no locations on blocks of built-in variables int TDefaultIoResolverBase::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
if (type.isStruct()) { const TType& type = ent.symbol->getType();
if (type.getStruct()->size() < 1) const char* name = ent.symbol->getName().c_str();
return -1; // kick out of not doing this
if ((*type.getStruct())[0].type->isBuiltIn()) if (! doAutoLocationMapping()) {
return -1; 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.getBasicType() == EbtAtomicUint || (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 location = intermediate.getUniformLocationOverride(name); int TDefaultIoResolverBase::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
if (location != -1) const TType& type = ent.symbol->getType();
return location; // kick out of not doing this
if (! doAutoLocationMapping()) {
return ent.newLocation = -1;
}
location = nextUniformLocation; // 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;
}
nextUniformLocation += TIntermediate::computeTypeUniformLocationSize(type); int TDefaultIoResolverBase::resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) {
return ent.newComponent = -1;
}
return location; 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);
} }
bool validateInOut(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override return typeLocationSize;
{ }
return true;
//TDefaultGlslIoResolver
TResourceType TDefaultGlslIoResolver::getResourceType(const glslang::TType& type) {
if (isImageType(type)) {
return EResImage;
} }
int resolveInOutLocation(EShLanguage stage, const char* /*name*/, const TType& type, bool /*is_live*/) override if (isTextureType(type)) {
{ return EResTexture;
// kick out of not doing this }
if (!doAutoLocationMapping()) if (isSsboType(type)) {
return -1; return EResSsbo;
}
if (isSamplerType(type)) {
return EResSampler;
}
if (isUboType(type)) {
return EResUbo;
}
return EResCount;
}
// no locations added if already present, or a built-in variable TDefaultGlslIoResolver::TDefaultGlslIoResolver(const TIntermediate& intermediate)
if (type.getQualifier().hasLocation() || type.isBuiltIn()) : TDefaultIoResolverBase(intermediate)
return -1; , preStage(EShLangCount)
, currentStage(EShLangCount)
{ }
int TDefaultGlslIoResolver::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
const TType& type = ent.symbol->getType();
const TString& name = 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 = 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.getBasicType() == EbtAtomicUint || (type.containsOpaque() && intermediate.getSpv().openGl == 0)) {
return ent.newLocation = -1;
}
// no locations on blocks of built-in variables // no locations on blocks of built-in variables
if (type.isStruct()) { if (type.isStruct()) {
if (type.getStruct()->size() < 1) if (type.getStruct()->size() < 1) {
return -1; return ent.newLocation = -1;
if ((*type.getStruct())[0].type->isBuiltIn()) }
return -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.
if (type.getQualifier().isArrayedIo(stage)) {
TType elementType(type, 0);
typeLocationSize = TIntermediate::computeTypeLocationSize(elementType, stage);
} else {
typeLocationSize = TIntermediate::computeTypeLocationSize(type, stage);
} }
nextLocation += typeLocationSize;
return location;
} }
int resolveInOutComponent(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override int location = intermediate.getUniformLocationOverride(name.c_str());
{ if (location != -1) {
return -1; return ent.newLocation = location;
} }
int resolveInOutIndex(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
{ int size = TIntermediate::computeTypeUniformLocationSize(type);
return -1;
// 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;
}
void notifyBinding(EShLanguage, const char* /*name*/, const TType&, bool /*is_live*/) override {} int TDefaultGlslIoResolver::resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) {
void notifyInOut(EShLanguage, const char* /*name*/, const TType&, bool /*is_live*/) override {} const TType& type = ent.symbol->getType();
void endNotifications(EShLanguage) override {} const TString& name = ent.symbol->getName();
void beginNotifications(EShLanguage) override {} // On OpenGL arrays of opaque types take a seperate binding for each element
void beginResolve(EShLanguage) override {} int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
void endResolve(EShLanguage) override {} 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;
}
protected: void TDefaultGlslIoResolver::beginResolve(EShLanguage stage) {
TDefaultIoResolverBase(TDefaultIoResolverBase&); // reset stage state
TDefaultIoResolverBase& operator=(TDefaultIoResolverBase&); if (stage == EShLangCount)
preStage = currentStage = stage;
// update stage state
else if (currentStage != stage) {
preStage = currentStage;
currentStage = stage;
}
}
const TIntermediate &intermediate; void TDefaultGlslIoResolver::endResolve(EShLanguage /*stage*/) {
int nextUniformLocation; // TODO nothing
int nextInputLocation; }
int nextOutputLocation;
// Return descriptor set specific base if there is one, and the generic base otherwise. void TDefaultGlslIoResolver::beginCollect(EShLanguage stage) {
int selectBaseBinding(int base, int descriptorSetBase) const { // reset stage state
return descriptorSetBase != -1 ? descriptorSetBase : base; if (stage == EShLangCount)
preStage = currentStage = stage;
// update stage state
else if (currentStage != stage) {
preStage = currentStage;
currentStage = stage;
} }
}
static int getLayoutSet(const glslang::TType& type) { void TDefaultGlslIoResolver::endCollect(EShLanguage /*stage*/) {
if (type.getQualifier().hasSet()) // TODO nothing
return type.getQualifier().layoutSet; }
else
return 0;
}
static bool isSamplerType(const glslang::TType& type) { void TDefaultGlslIoResolver::reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
return type.getBasicType() == glslang::EbtSampler && type.getSampler().isPureSampler(); const TType& type = ent.symbol->getType();
const TString& name = 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());
}
}
}
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());
}
}
}
break;
default:
break;
} }
}
static bool isTextureType(const glslang::TType& type) { void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
return (type.getBasicType() == glslang::EbtSampler && const TType& type = ent.symbol->getType();
(type.getSampler().isTexture() || type.getSampler().isSubpass())); const TString& name = 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());
}
}
} }
}
static bool isUboType(const glslang::TType& type) { //TDefaultGlslIoResolver end
return type.getQualifier().storage == EvqUniform;
}
};
/* /*
* Basic implementation of glslang::TIoMapResolver that replaces the * Basic implementation of glslang::TIoMapResolver that replaces the
...@@ -567,66 +899,47 @@ protected: ...@@ -567,66 +899,47 @@ protected:
/* /*
* Default resolver * Default resolver
*/ */
struct TDefaultIoResolver : public TDefaultIoResolverBase struct TDefaultIoResolver : public TDefaultIoResolverBase {
{ TDefaultIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
TDefaultIoResolver(const TIntermediate &intermediate) : TDefaultIoResolverBase(intermediate) { }
bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& /*type*/, bool /*is_live*/) override bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
{
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*/, const char* /*name*/, const glslang::TType& type, bool is_live) override int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override {
{ const TType& type = ent.symbol->getType();
const int set = getLayoutSet(type); const int set = getLayoutSet(type);
// On OpenGL arrays of opaque types take a seperate binding for each element // On OpenGL arrays of opaque types take a seperate binding for each element
int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1; int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
TResourceType resource = getResourceType(type);
if (type.getQualifier().hasBinding()) { if (resource < EResCount) {
if (isImageType(type)) if (type.getQualifier().hasBinding()) {
return reserveSlot(set, getBaseBinding(EResImage, set) + type.getQualifier().layoutBinding, numBindings); return ent.newBinding = reserveSlot(
set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding, numBindings);
if (isTextureType(type)) } else if (ent.live && doAutoBindingMapping()) {
return reserveSlot(set, getBaseBinding(EResTexture, set) + type.getQualifier().layoutBinding, numBindings); // 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
if (isSsboType(type)) return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set), numBindings);
return reserveSlot(set, getBaseBinding(EResSsbo, set) + type.getQualifier().layoutBinding, numBindings); }
if (isSamplerType(type))
return reserveSlot(set, getBaseBinding(EResSampler, set) + type.getQualifier().layoutBinding, numBindings);
if (isUboType(type))
return reserveSlot(set, getBaseBinding(EResUbo, set) + type.getQualifier().layoutBinding, numBindings);
} else if (is_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
if (isImageType(type))
return getFreeSlot(set, getBaseBinding(EResImage, set), numBindings);
if (isTextureType(type))
return getFreeSlot(set, getBaseBinding(EResTexture, set), numBindings);
if (isSsboType(type))
return getFreeSlot(set, getBaseBinding(EResSsbo, set), numBindings);
if (isSamplerType(type))
return getFreeSlot(set, getBaseBinding(EResSampler, set), numBindings);
if (isUboType(type))
return getFreeSlot(set, getBaseBinding(EResUbo, set), numBindings);
} }
return ent.newBinding = -1;
return -1;
}
protected:
static bool isImageType(const glslang::TType& type) {
return type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage();
}
static bool isSsboType(const glslang::TType& type) {
return type.getQualifier().storage == EvqBuffer;
} }
}; };
...@@ -647,7 +960,7 @@ t - for shader resource views (SRV) ...@@ -647,7 +960,7 @@ t - for shader resource views (SRV)
BYTEADDRESSBUFFER BYTEADDRESSBUFFER
BUFFER BUFFER
TBUFFER TBUFFER
s - for samplers s - for samplers
SAMPLER SAMPLER
SAMPLER1D SAMPLER1D
...@@ -673,98 +986,65 @@ b - for constant buffer views (CBV) ...@@ -673,98 +986,65 @@ b - for constant buffer views (CBV)
CBUFFER CBUFFER
CONSTANTBUFFER CONSTANTBUFFER
********************************************************************************/ ********************************************************************************/
struct TDefaultHlslIoResolver : public TDefaultIoResolverBase struct TDefaultHlslIoResolver : public TDefaultIoResolverBase {
{ TDefaultHlslIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
TDefaultHlslIoResolver(const TIntermediate &intermediate) : TDefaultIoResolverBase(intermediate) { }
bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& /*type*/, bool /*is_live*/) override
{
return true;
}
int resolveBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool is_live) override
{
const int set = getLayoutSet(type);
if (type.getQualifier().hasBinding()) {
if (isUavType(type))
return reserveSlot(set, getBaseBinding(EResUav, set) + type.getQualifier().layoutBinding);
if (isSrvType(type))
return reserveSlot(set, getBaseBinding(EResTexture, set) + type.getQualifier().layoutBinding);
if (isSamplerType(type))
return reserveSlot(set, getBaseBinding(EResSampler, set) + type.getQualifier().layoutBinding);
if (isUboType(type))
return reserveSlot(set, getBaseBinding(EResUbo, set) + type.getQualifier().layoutBinding);
} else if (is_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
if (isUavType(type)) bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
return getFreeSlot(set, getBaseBinding(EResUav, set));
if (isSrvType(type)) TResourceType getResourceType(const glslang::TType& type) override {
return getFreeSlot(set, getBaseBinding(EResTexture, set)); if (isUavType(type)) {
return EResUav;
if (isSamplerType(type))
return getFreeSlot(set, getBaseBinding(EResSampler, set));
if (isUboType(type))
return getFreeSlot(set, getBaseBinding(EResUbo, set));
} }
if (isSrvType(type)) {
return -1; return EResTexture;
} }
if (isSamplerType(type)) {
protected: return EResSampler;
// Return true if this is a SRV (shader resource view) type: }
static bool isSrvType(const glslang::TType& type) { if (isUboType(type)) {
return isTextureType(type) || type.getQualifier().storage == EvqBuffer; return EResUbo;
}
return EResCount;
} }
// Return true if this is a UAV (unordered access view) type: int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override {
static bool isUavType(const glslang::TType& type) { const TType& type = ent.symbol->getType();
if (type.getQualifier().readonly) const int set = getLayoutSet(type);
return false; TResourceType resource = getResourceType(type);
if (resource < EResCount) {
return (type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage()) || if (type.getQualifier().hasBinding()) {
(type.getQualifier().storage == EvqBuffer); 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;
} }
}; };
// Map I/O variables to provided offsets, and make bindings for // Map I/O variables to provided offsets, and make bindings for
// unbound but live variables. // unbound but live variables.
// //
// Returns false if the input is too malformed to do this. // Returns false if the input is too malformed to do this.
bool TIoMapper::addStage(EShLanguage stage, TIntermediate &intermediate, TInfoSink &infoSink, TIoMapResolver *resolver) bool TIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) {
{ bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() ||
bool somethingToDo = !intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapLocations();
intermediate.getAutoMapBindings() ||
intermediate.getAutoMapLocations();
for (int res = 0; res < EResCount; ++res) { for (int res = 0; res < EResCount; ++res) {
somethingToDo = somethingToDo || somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
(intermediate.getShiftBinding(TResourceType(res)) != 0) || intermediate.hasShiftBindingForSet(TResourceType(res));
intermediate.hasShiftBindingForSet(TResourceType(res));
} }
if (! somethingToDo && resolver == nullptr)
if (!somethingToDo && resolver == nullptr)
return true; return true;
if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive()) if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive())
return false; return false;
TIntermNode* root = intermediate.getTreeRoot(); TIntermNode* root = intermediate.getTreeRoot();
if (root == nullptr) if (root == nullptr)
return false; return false;
// if no resolver is provided, use the default resolver with the given shifts and auto map settings // if no resolver is provided, use the default resolver with the given shifts and auto map settings
TDefaultIoResolver defaultResolver(intermediate); TDefaultIoResolver defaultResolver(intermediate);
TDefaultHlslIoResolver defaultHlslResolver(intermediate); TDefaultHlslIoResolver defaultHlslResolver(intermediate);
if (resolver == nullptr) { if (resolver == nullptr) {
// TODO: use a passed in IO mapper for this // TODO: use a passed in IO mapper for this
if (intermediate.usingHlslIoMapping()) if (intermediate.usingHlslIoMapping())
...@@ -772,47 +1052,183 @@ bool TIoMapper::addStage(EShLanguage stage, TIntermediate &intermediate, TInfoSi ...@@ -772,47 +1052,183 @@ bool TIoMapper::addStage(EShLanguage stage, TIntermediate &intermediate, TInfoSi
else else
resolver = &defaultResolver; resolver = &defaultResolver;
} }
resolver->addStage(stage);
TVarLiveMap inVarMap, outVarMap, uniformVarMap; TVarLiveMap inVarMap, outVarMap, uniformVarMap;
TVarLiveVector inVector, outVector, uniformVector;
TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap); TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap);
TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap); TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap);
root->traverse(&iter_binding_all); root->traverse(&iter_binding_all);
iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str()); iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
while (! iter_binding_live.functions.empty()) {
while (!iter_binding_live.functions.empty()) {
TIntermNode* function = iter_binding_live.functions.back(); TIntermNode* function = iter_binding_live.functions.back();
iter_binding_live.functions.pop_back(); iter_binding_live.functions.pop_back();
function->traverse(&iter_binding_live); function->traverse(&iter_binding_live);
} }
// sort entries by priority. see TVarEntryInfo::TOrderByPriority for info. // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info.
std::sort(uniformVarMap.begin(), uniformVarMap.end(), TVarEntryInfo::TOrderByPriority()); 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; bool hadError = false;
TNotifyInOutAdaptor inOutNotify(stage, *resolver); TNotifyInOutAdaptor inOutNotify(stage, *resolver);
TNotifyUniformAdaptor uniformNotify(stage, *resolver); TNotifyUniformAdaptor uniformNotify(stage, *resolver);
TResolverUniformAdaptor uniformResolve(stage, *resolver, infoSink, hadError, intermediate); TResolverUniformAdaptor uniformResolve(stage, *resolver, infoSink, hadError);
TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError, intermediate); TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError);
resolver->beginNotifications(stage); resolver->beginNotifications(stage);
std::for_each(inVarMap.begin(), inVarMap.end(), inOutNotify); std::for_each(inVector.begin(), inVector.end(), inOutNotify);
std::for_each(outVarMap.begin(), outVarMap.end(), inOutNotify); std::for_each(outVector.begin(), outVector.end(), inOutNotify);
std::for_each(uniformVarMap.begin(), uniformVarMap.end(), uniformNotify); std::for_each(uniformVector.begin(), uniformVector.end(), uniformNotify);
resolver->endNotifications(stage); resolver->endNotifications(stage);
resolver->beginResolve(stage); resolver->beginResolve(stage);
std::for_each(inVarMap.begin(), inVarMap.end(), inOutResolve); std::for_each(inVector.begin(), inVector.end(), inOutResolve);
std::for_each(outVarMap.begin(), outVarMap.end(), inOutResolve); std::for_each(inVector.begin(), inVector.end(), [&inVarMap](TVarLivePair p) {
std::for_each(uniformVarMap.begin(), uniformVarMap.end(), uniformResolve); 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); resolver->endResolve(stage);
if (!hadError) { if (!hadError) {
// sort by id again, so we can use lower bound to find entries
std::sort(uniformVarMap.begin(), uniformVarMap.end(), TVarEntryInfo::TOrderById());
TVarSetTraverser iter_iomap(intermediate, inVarMap, outVarMap, uniformVarMap); TVarSetTraverser iter_iomap(intermediate, inVarMap, outVarMap, uniformVarMap);
root->traverse(&iter_iomap); 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();
for (int res = 0; res < EResCount; ++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; 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 } // end namespace glslang
...@@ -36,8 +36,9 @@ ...@@ -36,8 +36,9 @@
#ifndef _IOMAPPER_INCLUDED #ifndef _IOMAPPER_INCLUDED
#define _IOMAPPER_INCLUDED #define _IOMAPPER_INCLUDED
#include "../Public/ShaderLang.h" #include "LiveTraverser.h"
#include <unordered_map>
#include <unordered_set>
// //
// A reflection database and its interface, consistent with the OpenGL API reflection queries. // A reflection database and its interface, consistent with the OpenGL API reflection queries.
// //
...@@ -47,15 +48,245 @@ class TInfoSink; ...@@ -47,15 +48,245 @@ class TInfoSink;
namespace glslang { namespace glslang {
class TIntermediate; class TIntermediate;
struct TVarEntryInfo {
int id;
TIntermSymbol* symbol;
bool live;
int newBinding;
int newSet;
int newLocation;
int newComponent;
int newIndex;
EShLanguage stage;
struct TOrderById {
inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r) { return l.id < r.id; }
};
struct TOrderByPriority {
// ordering:
// 1) has both binding and set
// 2) has binding but no set
// 3) has no binding but set
// 4) has no binding and no set
inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r) {
const TQualifier& lq = l.symbol->getQualifier();
const TQualifier& rq = r.symbol->getQualifier();
// simple rules:
// has binding gives 2 points
// has set gives 1 point
// who has the most points is more important.
int lPoints = (lq.hasBinding() ? 2 : 0) + (lq.hasSet() ? 1 : 0);
int rPoints = (rq.hasBinding() ? 2 : 0) + (rq.hasSet() ? 1 : 0);
if (lPoints == rPoints)
return l.id < r.id;
return lPoints > rPoints;
}
};
};
// Base class for shared TIoMapResolver services, used by several derivations.
struct TDefaultIoResolverBase : public glslang::TIoMapResolver {
public:
TDefaultIoResolverBase(const TIntermediate& intermediate);
typedef std::vector<int> TSlotSet;
typedef std::unordered_map<int, TSlotSet> TSlotSetMap;
// grow the reflection stage by stage
void notifyBinding(EShLanguage, TVarEntryInfo& /*ent*/) override {}
void notifyInOut(EShLanguage, TVarEntryInfo& /*ent*/) override {}
void beginNotifications(EShLanguage) override {}
void endNotifications(EShLanguage) override {}
void beginResolve(EShLanguage) override {}
void endResolve(EShLanguage) override {}
void beginCollect(EShLanguage) override {}
void endCollect(EShLanguage) override {}
void reserverResourceSlot(TVarEntryInfo& /*ent*/, TInfoSink& /*infoSink*/) override {}
void reserverStorageSlot(TVarEntryInfo& /*ent*/, TInfoSink& /*infoSink*/) override {}
int getBaseBinding(TResourceType res, unsigned int set) const;
const std::vector<std::string>& getResourceSetBinding() const;
virtual TResourceType getResourceType(const glslang::TType& type) = 0;
bool doAutoBindingMapping() const;
bool doAutoLocationMapping() const;
TSlotSet::iterator findSlot(int set, int slot);
bool checkEmpty(int set, int slot);
bool validateInOut(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; };
int reserveSlot(int set, int slot, int size = 1);
int getFreeSlot(int set, int base, int size = 1);
int resolveSet(EShLanguage /*stage*/, TVarEntryInfo& ent) override;
int resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) override;
int resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) override;
int resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) override;
int resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) override;
void addStage(EShLanguage stage) override {
if (stage < EShLangCount)
stageMask[stage] = true;
};
uint32_t computeTypeLocationSize(const TType& type, EShLanguage stage);
TSlotSetMap slots;
protected:
TDefaultIoResolverBase(TDefaultIoResolverBase&);
TDefaultIoResolverBase& operator=(TDefaultIoResolverBase&);
const TIntermediate& intermediate;
int nextUniformLocation;
int nextInputLocation;
int nextOutputLocation;
bool stageMask[EShLangCount + 1];
// Return descriptor set specific base if there is one, and the generic base otherwise.
int selectBaseBinding(int base, int descriptorSetBase) const {
return descriptorSetBase != -1 ? descriptorSetBase : base;
}
static int getLayoutSet(const glslang::TType& type) {
if (type.getQualifier().hasSet())
return type.getQualifier().layoutSet;
else
return 0;
}
static bool isSamplerType(const glslang::TType& type) {
return type.getBasicType() == glslang::EbtSampler && type.getSampler().isPureSampler();
}
static bool isTextureType(const glslang::TType& type) {
return (type.getBasicType() == glslang::EbtSampler &&
(type.getSampler().isTexture() || type.getSampler().isSubpass()));
}
static bool isUboType(const glslang::TType& type) {
return type.getQualifier().storage == EvqUniform;
}
static bool isImageType(const glslang::TType& type) {
return type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage();
}
static bool isSsboType(const glslang::TType& type) {
return type.getQualifier().storage == EvqBuffer;
}
// Return true if this is a SRV (shader resource view) type:
static bool isSrvType(const glslang::TType& type) {
return isTextureType(type) || type.getQualifier().storage == EvqBuffer;
}
// Return true if this is a UAV (unordered access view) type:
static bool isUavType(const glslang::TType& type) {
if (type.getQualifier().readonly)
return false;
return (type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage()) ||
(type.getQualifier().storage == EvqBuffer);
}
};
// Defaulf I/O resolver for OpenGL
struct TDefaultGlslIoResolver : public TDefaultIoResolverBase {
public:
typedef std::map<TString, int> TVarSlotMap; // <resourceName, location/binding>
typedef std::map<int, TVarSlotMap> TSlotMap; // <resourceKey, TVarSlotMap>
TDefaultGlslIoResolver(const TIntermediate& intermediate);
bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; };
TResourceType getResourceType(const glslang::TType& type) override;
int resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) override;
int resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) override;
int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override;
void beginResolve(EShLanguage /*stage*/) override;
void endResolve(EShLanguage stage) override;
void beginCollect(EShLanguage) override;
void endCollect(EShLanguage) override;
void reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) override;
void reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) override;
// in/out symbol and uniform symbol are stored in the same resourceSlotMap, the storage key is used to identify each type of symbol.
// We use stage and storage qualifier to construct a storage key. it can help us identify the same storage resource used in different stage.
// if a resource is a program resource and we don't need know it usage stage, we can use same stage to build storage key.
// Note: both stage and type must less then 0xffff.
int buildStorageKey(EShLanguage stage, TStorageQualifier type) {
assert(static_cast<uint32_t>(stage) <= 0x0000ffff && static_cast<uint32_t>(type) <= 0x0000ffff);
return (stage << 16) | type;
};
protected:
// Use for mark pre stage, to get more interface symbol information.
EShLanguage preStage;
// Use for mark current shader stage for resolver
EShLanguage currentStage;
// Slot map for storage resource(location of uniform and interface symbol) It's a program share slot
TSlotMap resourceSlotMap;
// Slot map for other resource(image, ubo, ssbo), It's a program share slot.
TSlotMap storageSlotMap;
};
typedef std::map<TString, TVarEntryInfo> TVarLiveMap;
// override function "operator=", if a vector<const _Kty, _Ty> being sort,
// when use vc++, the sort function will call :
// pair& operator=(const pair<_Other1, _Other2>& _Right)
// {
// first = _Right.first;
// second = _Right.second;
// return (*this);
// }
// that will make a const type handing on left.
// override this function can avoid a compiler error.
// In the future, if the vc++ compiler can handle such a situation,
// this part of the code will be removed.
struct TVarLivePair : std::pair<const TString, TVarEntryInfo> {
TVarLivePair(std::pair<const TString, TVarEntryInfo>& _Right) : pair(_Right.first, _Right.second) {}
TVarLivePair& operator=(const TVarLivePair& _Right) {
const_cast<TString&>(first) = _Right.first;
second = _Right.second;
return (*this);
};
};
typedef std::vector<TVarLivePair> TVarLiveVector;
// I/O mapper // I/O mapper
class TIoMapper { class TIoMapper {
public: public:
TIoMapper() {} TIoMapper() {}
virtual ~TIoMapper() {} virtual ~TIoMapper() {}
// grow the reflection stage by stage
bool virtual addStage(EShLanguage, TIntermediate&, TInfoSink&, TIoMapResolver*);
bool virtual doMap(TIoMapResolver*, TInfoSink&) { return true; };
};
// I/O mapper for OpenGL
class TGlslIoMapper : public TIoMapper {
public:
TGlslIoMapper() {
memset(inVarMaps, 0, sizeof(TVarLiveMap*) * (EShLangCount + 1));
memset(outVarMaps, 0, sizeof(TVarLiveMap*) * (EShLangCount + 1));
memset(uniformVarMap, 0, sizeof(TVarLiveMap*) * (EShLangCount + 1));
memset(intermediates, 0, sizeof(TIntermediate*) * (EShLangCount + 1));
}
virtual ~TGlslIoMapper() {
for (size_t stage = 0; stage < EShLangCount; stage++) {
if (inVarMaps[stage] != nullptr) {
delete inVarMaps[stage];
inVarMaps[stage] = nullptr;
}
if (outVarMaps[stage] != nullptr) {
delete outVarMaps[stage];
outVarMaps[stage] = nullptr;
}
if (uniformVarMap[stage] != nullptr) {
delete uniformVarMap[stage];
uniformVarMap[stage] = nullptr;
}
if (intermediates[stage] != nullptr)
intermediates[stage] = nullptr;
}
}
// grow the reflection stage by stage // grow the reflection stage by stage
bool addStage(EShLanguage, TIntermediate&, TInfoSink&, TIoMapResolver*); bool addStage(EShLanguage, TIntermediate&, TInfoSink&, TIoMapResolver*) override;
bool doMap(TIoMapResolver*, TInfoSink&) override;
TVarLiveMap *inVarMaps[EShLangCount], *outVarMaps[EShLangCount],
*uniformVarMap[EShLangCount];
TIntermediate* intermediates[EShLangCount];
bool hadError = false;
}; };
} // end namespace glslang } // end namespace glslang
......
...@@ -646,8 +646,9 @@ protected: ...@@ -646,8 +646,9 @@ protected:
const TType* type; const TType* type;
}; };
class TReflection; class TReflection;
class TIoMapper; class TIoMapper;
struct TVarEntryInfo;
// Allows to customize the binding layout after linking. // Allows to customize the binding layout after linking.
// All used uniform variables will invoke at least validateBinding. // All used uniform variables will invoke at least validateBinding.
...@@ -668,51 +669,61 @@ class TIoMapper; ...@@ -668,51 +669,61 @@ class TIoMapper;
// notifiy callbacks, this phase ends with a call to endNotifications. // notifiy callbacks, this phase ends with a call to endNotifications.
// Phase two starts directly after the call to endNotifications // Phase two starts directly after the call to endNotifications
// and calls all other callbacks to validate and to get the // and calls all other callbacks to validate and to get the
// bindings, sets, locations, component and color indices. // bindings, sets, locations, component and color indices.
// //
// NOTE: that still limit checks are applied to bindings and sets // NOTE: that still limit checks are applied to bindings and sets
// and may result in an error. // and may result in an error.
class TIoMapResolver class TIoMapResolver
{ {
public: public:
virtual ~TIoMapResolver() {} virtual ~TIoMapResolver() {}
// Should return true if the resulting/current binding would be okay. // Should return true if the resulting/current binding would be okay.
// Basic idea is to do aliasing binding checks with this. // Basic idea is to do aliasing binding checks with this.
virtual bool validateBinding(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0; virtual bool validateBinding(EShLanguage stage, TVarEntryInfo& ent) = 0;
// Should return a value >= 0 if the current binding should be overridden. // Should return a value >= 0 if the current binding should be overridden.
// Return -1 if the current binding (including no binding) should be kept. // Return -1 if the current binding (including no binding) should be kept.
virtual int resolveBinding(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0; virtual int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) = 0;
// Should return a value >= 0 if the current set should be overridden. // Should return a value >= 0 if the current set should be overridden.
// Return -1 if the current set (including no set) should be kept. // 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; virtual int resolveSet(EShLanguage stage, TVarEntryInfo& ent) = 0;
// Should return a value >= 0 if the current location should be overridden. // Should return a value >= 0 if the current location should be overridden.
// Return -1 if the current location (including no location) should be kept. // Return -1 if the current location (including no location) should be kept.
virtual int resolveUniformLocation(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0; virtual int resolveUniformLocation(EShLanguage stage, TVarEntryInfo& ent) = 0;
// Should return true if the resulting/current setup would be okay. // Should return true if the resulting/current setup would be okay.
// Basic idea is to do aliasing checks and reject invalid semantic names. // 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; virtual bool validateInOut(EShLanguage stage, TVarEntryInfo& ent) = 0;
// Should return a value >= 0 if the current location should be overridden. // Should return a value >= 0 if the current location should be overridden.
// Return -1 if the current location (including no location) should be kept. // 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; virtual int resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) = 0;
// Should return a value >= 0 if the current component index should be overridden. // Should return a value >= 0 if the current component index should be overridden.
// Return -1 if the current component index (including no index) should be kept. // 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; virtual int resolveInOutComponent(EShLanguage stage, TVarEntryInfo& ent) = 0;
// Should return a value >= 0 if the current color index should be overridden. // Should return a value >= 0 if the current color index should be overridden.
// Return -1 if the current color index (including no index) should be kept. // 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; virtual int resolveInOutIndex(EShLanguage stage, TVarEntryInfo& ent) = 0;
// Notification of a uniform variable // Notification of a uniform variable
virtual void notifyBinding(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0; virtual void notifyBinding(EShLanguage stage, TVarEntryInfo& ent) = 0;
// Notification of a in or out variable // Notification of a in or out variable
virtual void notifyInOut(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0; virtual void notifyInOut(EShLanguage stage, TVarEntryInfo& ent) = 0;
// Called by mapIO when it has finished the notify pass // Called by mapIO when it starts its notify pass for the given stage
virtual void endNotifications(EShLanguage stage) = 0; virtual void beginNotifications(EShLanguage stage) = 0;
// Called by mapIO when it starts its notify pass for the given stage // Called by mapIO when it has finished the notify pass
virtual void beginNotifications(EShLanguage stage) = 0; virtual void endNotifications(EShLanguage stage) = 0;
// Called by mipIO when it starts its resolve pass for the given stage // Called by mipIO when it starts its resolve pass for the given stage
virtual void beginResolve(EShLanguage stage) = 0; virtual void beginResolve(EShLanguage stage) = 0;
// Called by mapIO when it has finished the resolve pass // Called by mapIO when it has finished the resolve pass
virtual void endResolve(EShLanguage stage) = 0; virtual void endResolve(EShLanguage stage) = 0;
// Called by mapIO when it starts its symbol collect for teh given stage
virtual void beginCollect(EShLanguage stage) = 0;
// Called by mapIO when it has finished the symbol collect
virtual void endCollect(EShLanguage stage) = 0;
// Called by TSlotCollector to resolve storage locations or bindings
virtual void reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) = 0;
// Called by TSlotCollector to resolve resource locations or bindings
virtual void reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) = 0;
// Called by mapIO.addStage to set shader stage mask to mark a stage be added to this pipeline
virtual void addStage(EShLanguage stage) = 0;
}; };
// Make one TProgram per set of shaders that will get linked together. Add all // Make one TProgram per set of shaders that will get linked together. Add all
...@@ -824,7 +835,7 @@ public: ...@@ -824,7 +835,7 @@ public:
// I/O mapping: apply base offsets and map live unbound variables // I/O mapping: apply base offsets and map live unbound variables
// If resolver is not provided it uses the previous approach // If resolver is not provided it uses the previous approach
// and respects auto assignment and offsets. // and respects auto assignment and offsets.
bool mapIO(TIoMapResolver* resolver = NULL); bool mapIO(TIoMapResolver* pResolver = nullptr, TIoMapper* pIoMapper = nullptr);
protected: protected:
bool linkStage(EShLanguage, EShMessages); bool linkStage(EShLanguage, EShMessages);
...@@ -835,7 +846,6 @@ protected: ...@@ -835,7 +846,6 @@ protected:
bool newedIntermediate[EShLangCount]; // track which intermediate were "new" versus reusing a singleton unit in a stage bool newedIntermediate[EShLangCount]; // track which intermediate were "new" versus reusing a singleton unit in a stage
TInfoSink* infoSink; TInfoSink* infoSink;
TReflection* reflection; TReflection* reflection;
TIoMapper* ioMapper;
bool linked; bool linked;
private: private:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment