Commit 302fe97e by John Kessenich

Revert "Merge pull request #1792 from Roy-AMD/automapping-opengl-location"

This reverts commit 50ada66c, reversing changes made to 3464b6f5.
parent 50ada66c
...@@ -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), linked(false) TProgram::TProgram() : reflection(0), ioMapper(nullptr), linked(false)
{ {
pool = new TPoolAllocator; pool = new TPoolAllocator;
infoSink = new TInfoSink; infoSink = new TInfoSink;
...@@ -1864,6 +1864,7 @@ TProgram::TProgram() : reflection(0), linked(false) ...@@ -1864,6 +1864,7 @@ TProgram::TProgram() : reflection(0), linked(false)
TProgram::~TProgram() TProgram::~TProgram()
{ {
delete ioMapper;
delete infoSink; delete infoSink;
delete reflection; delete reflection;
...@@ -2034,24 +2035,21 @@ void TProgram::dumpReflection() { if (reflection != nullptr) reflection->dump(); ...@@ -2034,24 +2035,21 @@ void TProgram::dumpReflection() { if (reflection != nullptr) reflection->dump();
// //
// I/O mapping implementation. // I/O mapping implementation.
// //
bool TProgram::mapIO(TIoMapResolver* pResolver, TIoMapper* pIoMapper) bool TProgram::mapIO(TIoMapResolver* resolver)
{ {
if (! linked) if (! linked || ioMapper)
return false; return false;
TIoMapper* ioMapper = nullptr;
TIoMapper defaultIOMapper; ioMapper = new TIoMapper;
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, pResolver)) if (! ioMapper->addStage((EShLanguage)s, *intermediate[s], *infoSink, resolver))
return false; return false;
} }
} }
return ioMapper->doMap(pResolver, *infoSink); return true;
} }
} // end namespace glslang } // end namespace glslang
...@@ -35,9 +35,14 @@ ...@@ -35,9 +35,14 @@
#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.
...@@ -56,9 +61,60 @@ ...@@ -56,9 +61,60 @@
// c. implicit dead bindings are left un-bound. // c. implicit dead bindings are left un-bound.
// //
namespace glslang { namespace glslang {
class TVarGatherTraverser : public TLiveTraverser { struct TVarEntryInfo
{
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)
...@@ -68,6 +124,7 @@ public: ...@@ -68,6 +124,7 @@ public:
{ {
} }
virtual void visitSymbol(TIntermSymbol* base) virtual void visitSymbol(TIntermSymbol* base)
{ {
TVarLiveMap* target = nullptr; TVarLiveMap* target = nullptr;
...@@ -77,15 +134,14 @@ public: ...@@ -77,15 +134,14 @@ 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 };
ent.stage = intermediate.getStage(); TVarLiveMap::iterator at = std::lower_bound(target->begin(), target->end(), ent, TVarEntryInfo::TOrderById());
TVarLiveMap::iterator at = target->find( if (at != target->end() && at->id == ent.id)
ent.symbol->getName()); // std::lower_bound(target->begin(), target->end(), ent, TVarEntryInfo::TOrderById()); at->live = at->live || !traverseAll; // update live state
if (at != target->end() && at->second.id == ent.id)
at->second.live = at->second.live || ! traverseAll; // update live state
else else
(*target)[ent.symbol->getName()] = ent; target->insert(at, ent);
} }
} }
...@@ -106,7 +162,9 @@ public: ...@@ -106,7 +162,9 @@ 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;
...@@ -118,23 +176,23 @@ public: ...@@ -118,23 +176,23 @@ public:
return; return;
TVarEntryInfo ent = { base->getId() }; TVarEntryInfo ent = { base->getId() };
TVarLiveMap::const_iterator at = source->find(base->getName()); TVarLiveMap::const_iterator at = std::lower_bound(source->begin(), source->end(), ent, TVarEntryInfo::TOrderById());
if (at == source->end()) if (at == source->end())
return; return;
if (at->second.id != ent.id) if (at->id != ent.id)
return; return;
if (at->second.newBinding != -1) if (at->newBinding != -1)
base->getWritableType().getQualifier().layoutBinding = at->second.newBinding; base->getWritableType().getQualifier().layoutBinding = at->newBinding;
if (at->second.newSet != -1) if (at->newSet != -1)
base->getWritableType().getQualifier().layoutSet = at->second.newSet; base->getWritableType().getQualifier().layoutSet = at->newSet;
if (at->second.newLocation != -1) if (at->newLocation != -1)
base->getWritableType().getQualifier().layoutLocation = at->second.newLocation; base->getWritableType().getQualifier().layoutLocation = at->newLocation;
if (at->second.newComponent != -1) if (at->newComponent != -1)
base->getWritableType().getQualifier().layoutComponent = at->second.newComponent; base->getWritableType().getQualifier().layoutComponent = at->newComponent;
if (at->second.newIndex != -1) if (at->newIndex != -1)
base->getWritableType().getQualifier().layoutIndex = at->second.newIndex; base->getWritableType().getQualifier().layoutIndex = at->newIndex;
} }
private: private:
...@@ -152,12 +210,10 @@ struct TNotifyUniformAdaptor ...@@ -152,12 +210,10 @@ struct TNotifyUniformAdaptor
, resolver(r) , resolver(r)
{ {
} }
inline void operator()(TVarEntryInfo& ent)
inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
{ {
resolver.notifyBinding(stage, entKey.second); resolver.notifyBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), ent.live);
} }
private: private:
TNotifyUniformAdaptor& operator=(TNotifyUniformAdaptor&); TNotifyUniformAdaptor& operator=(TNotifyUniformAdaptor&);
}; };
...@@ -166,46 +222,49 @@ struct TNotifyInOutAdaptor ...@@ -166,46 +222,49 @@ 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, entKey.second); resolver.notifyInOut(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), ent.live);
} }
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()(std::pair<const TString, TVarEntryInfo>& entKey) { inline void operator()(TVarEntryInfo& ent)
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); const bool isValid = resolver.validateBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(),
ent.live);
if (isValid) { if (isValid) {
resolver.resolveBinding(stage, ent); ent.newBinding = resolver.resolveBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(),
resolver.resolveSet(stage, ent); ent.live);
resolver.resolveUniformLocation(stage, ent); ent.newSet = resolver.resolveSet(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), ent.live);
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: " + entKey.first; TString err = "mapped binding out of range: " + ent.symbol->getName();
infoSink.info.message(EPrefixInternalError, err.c_str()); infoSink.info.message(EPrefixInternalError, err.c_str());
error = true; error = true;
...@@ -213,52 +272,64 @@ struct TResolverUniformAdaptor { ...@@ -213,52 +272,64 @@ 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: " + entKey.first; TString err = "mapped set out of range: " + ent.symbol->getName();
infoSink.info.message(EPrefixInternalError, err.c_str()); infoSink.info.message(EPrefixInternalError, err.c_str());
error = true; error = true;
} }
} }
} else { } else {
TString errorMsg = "Invalid binding: " + entKey.first; TString errorMsg = "Invalid binding: " + ent.symbol->getName();
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()(std::pair<const TString, TVarEntryInfo>& entKey) inline void operator()(TVarEntryInfo& ent)
{ {
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, ent); const bool isValid = resolver.validateInOut(stage,
ent.symbol->getName().c_str(),
ent.symbol->getType(),
ent.live);
if (isValid) { if (isValid) {
resolver.resolveInOutLocation(stage, ent); ent.newLocation = resolver.resolveInOutLocation(stage,
resolver.resolveInOutComponent(stage, ent); ent.symbol->getName().c_str(),
resolver.resolveInOutIndex(stage, ent); 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 { } else {
TString errorMsg; TString errorMsg;
if (ent.symbol->getType().getQualifier().semanticName != nullptr) { if (ent.symbol->getType().getQualifier().semanticName != nullptr) {
...@@ -273,621 +344,218 @@ struct TResolverInOutAdaptor { ...@@ -273,621 +344,218 @@ 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&);
}; };
// The class is used for reserving explicit uniform locations and ubo/ssbo/opaque bindings // Base class for shared TIoMapResolver services, used by several derivations.
struct TDefaultIoResolverBase : public glslang::TIoMapResolver
struct TSymbolValidater
{ {
TSymbolValidater(TIoMapResolver& r, TInfoSink& i, TVarLiveMap* in[EShLangCount], TVarLiveMap* out[EShLangCount], TDefaultIoResolverBase(const TIntermediate &intermediate) :
TVarLiveMap* uniform[EShLangCount], bool& hadError) intermediate(intermediate),
: resolver(r) nextUniformLocation(intermediate.getUniformLocationBase()),
, infoSink(i) nextInputLocation(0),
, currentStage(EShLangCount) nextOutputLocation(0)
, preStage(EShLangCount) { }
, nextStage(EShLangCount)
, hadError(hadError)
{
memcpy(inVarMaps, in, EShLangCount * (sizeof(TVarLiveMap*)));
memcpy(outVarMaps, out, EShLangCount * (sizeof(TVarLiveMap*)));
memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*)));
}
inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
TVarEntryInfo& ent1 = entKey.second;
TIntermSymbol* base = ent1.symbol;
const TType& type = ent1.symbol->getType();
const TString& name = entKey.first;
TString mangleName1, mangleName2;
type.appendMangledName(mangleName1);
EShLanguage stage = ent1.stage;
if (currentStage != stage) {
preStage = currentStage;
currentStage = stage;
nextStage = EShLangCount;
for (int i = currentStage + 1; i < EShLangCount; i++) {
if (inVarMaps[i] != nullptr)
nextStage = static_cast<EShLanguage>(i);
}
}
if (base->getQualifier().storage == EvqVaryingIn) {
// validate stage in;
if (preStage == EShLangCount)
return;
if (outVarMaps[preStage] != nullptr) {
auto ent2 = outVarMaps[preStage]->find(name);
if (ent2 != outVarMaps[preStage]->end()) {
ent2->second.symbol->getType().appendMangledName(mangleName2);
if (mangleName1 == mangleName2)
return;
else {
TString err = "Invalid In/Out variable type : " + entKey.first;
infoSink.info.message(EPrefixInternalError, err.c_str());
hadError = true;
}
}
return;
}
} else if (base->getQualifier().storage == EvqVaryingOut) {
// validate stage out;
if (nextStage == EShLangCount)
return;
if (outVarMaps[nextStage] != nullptr) {
auto ent2 = inVarMaps[nextStage]->find(name);
if (ent2 != inVarMaps[nextStage]->end()) {
ent2->second.symbol->getType().appendMangledName(mangleName2);
if (mangleName1 == mangleName2)
return;
else {
TString err = "Invalid In/Out variable type : " + entKey.first;
infoSink.info.message(EPrefixInternalError, err.c_str());
hadError = true;
}
}
return;
}
} else if (base->getQualifier().isUniformOrBuffer() && ! base->getQualifier().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;
private:
TSymbolValidater& operator=(TSymbolValidater&);
};
struct TSlotCollector { int getBaseBinding(TResourceType res, unsigned int set) const {
TSlotCollector(TIoMapResolver& r, TInfoSink& i) : resolver(r), infoSink(i) { } return selectBaseBinding(intermediate.getShiftBinding(res),
intermediate.getShiftBindingForSet(res, set));
inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
resolver.reserverStorageSlot(entKey.second, infoSink);
resolver.reserverResourceSlot(entKey.second, infoSink);
} }
TIoMapResolver& resolver;
TInfoSink& infoSink;
private: const std::vector<std::string>& getResourceSetBinding() const { return intermediate.getResourceSetBinding(); }
TSlotCollector& operator=(TSlotCollector&);
};
TDefaultIoResolverBase::TDefaultIoResolverBase(const TIntermediate& intermediate) bool doAutoBindingMapping() const { return intermediate.getAutoMapBindings(); }
: intermediate(intermediate) bool doAutoLocationMapping() const { return intermediate.getAutoMapLocations(); }
, nextUniformLocation(intermediate.getUniformLocationBase())
, nextInputLocation(0)
, nextOutputLocation(0)
{
memset(stageMask, false, sizeof(bool) * (EShLangCount + 1));
}
int TDefaultIoResolverBase::getBaseBinding(TResourceType res, unsigned int set) const {
return selectBaseBinding(intermediate.getShiftBinding(res), intermediate.getShiftBindingForSet(res, set));
}
const std::vector<std::string>& TDefaultIoResolverBase::getResourceSetBinding() const {
return intermediate.getResourceSetBinding();
}
bool TDefaultIoResolverBase::doAutoBindingMapping() const { return intermediate.getAutoMapBindings(); }
bool TDefaultIoResolverBase::doAutoLocationMapping() const { return intermediate.getAutoMapLocations(); }
TDefaultIoResolverBase::TSlotSet::iterator TDefaultIoResolverBase::findSlot(int set, int slot) { typedef std::vector<int> TSlotSet;
return std::lower_bound(slots[set].begin(), slots[set].end(), slot); typedef std::unordered_map<int, TSlotSet> TSlotSetMap;
} TSlotSetMap slots;
bool TDefaultIoResolverBase::checkEmpty(int set, int slot) {
TSlotSet::iterator at = findSlot(set, slot);
return ! (at != slots[set].end() && *at == slot);
}
int TDefaultIoResolverBase::reserveSlot(int set, int slot, int size) { TSlotSet::iterator findSlot(int set, int slot)
TSlotSet::iterator at = findSlot(set, slot); {
// tolerate aliasing, by not double-recording aliases return std::lower_bound(slots[set].begin(), slots[set].end(), slot);
// (policy about appropriateness of the alias is higher up)
for (int i = 0; i < size; i++) {
if (at == slots[set].end() || *at != slot + i)
at = slots[set].insert(at, slot + i);
++at;
} }
return slot;
}
int TDefaultIoResolverBase::getFreeSlot(int set, int base, int size) { bool checkEmpty(int set, int slot)
TSlotSet::iterator at = findSlot(set, base); {
if (at == slots[set].end()) TSlotSet::iterator at = findSlot(set, slot);
return reserveSlot(set, base, size); return !(at != slots[set].end() && *at == slot);
// look for a big enough gap
for (; at != slots[set].end(); ++at) {
if (*at - base >= size)
break;
base = *at + 1;
} }
return reserveSlot(set, base, size);
}
int TDefaultIoResolverBase::resolveSet(EShLanguage /*stage*/, TVarEntryInfo& ent) { int reserveSlot(int set, int slot, int size = 1)
const TType& type = ent.symbol->getType(); {
if (type.getQualifier().hasSet()) { TSlotSet::iterator at = findSlot(set, slot);
return ent.newSet = type.getQualifier().layoutSet;
}
// If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN)
if (getResourceSetBinding().size() == 1) {
return ent.newSet = atoi(getResourceSetBinding()[0].c_str());
}
return ent.newSet = 0;
}
int TDefaultIoResolverBase::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) { // tolerate aliasing, by not double-recording aliases
const TType& type = ent.symbol->getType(); // (policy about appropriateness of the alias is higher up)
const char* name = ent.symbol->getName().c_str(); for (int i = 0; i < size; i++) {
// kick out of not doing this if (at == slots[set].end() || *at != slot + i)
if (! doAutoLocationMapping()) { at = slots[set].insert(at, slot + i);
return ent.newLocation = -1; ++at;
}
// 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 TDefaultIoResolverBase::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) { return slot;
const TType& type = ent.symbol->getType();
// kick out of not doing this
if (! doAutoLocationMapping()) {
return ent.newLocation = -1;
} }
// no locations added if already present, or a built-in variable int getFreeSlot(int set, int base, int size = 1)
if (type.getQualifier().hasLocation() || type.isBuiltIn()) { {
return ent.newLocation = -1; TSlotSet::iterator at = findSlot(set, base);
} if (at == slots[set].end())
return reserveSlot(set, base, size);
// no locations on blocks of built-in variables // look for a big enough gap
if (type.isStruct()) { for (; at != slots[set].end(); ++at) {
if (type.getStruct()->size() < 1) { if (*at - base >= size)
return ent.newLocation = -1; break;
} base = *at + 1;
if ((*type.getStruct())[0].type->isBuiltIn()) {
return ent.newLocation = -1;
} }
return reserveSlot(set, base, size);
} }
// point to the right input or output location counter
int& nextLocation = type.getQualifier().isPipeInput() ? nextInputLocation : nextOutputLocation;
// Placeholder. This does not do proper cross-stage lining up, nor
// work with mixed location/no-location declarations.
int location = nextLocation;
int typeLocationSize;
// Don’t take into account the outer-most array if the stage’s
// interface is automatically an array.
typeLocationSize = computeTypeLocationSize(type, stage);
nextLocation += typeLocationSize;
return ent.newLocation = location;
}
int TDefaultIoResolverBase::resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) { virtual bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool /*is_live*/) override = 0;
return ent.newComponent = -1;
}
int TDefaultIoResolverBase::resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) { return ent.newIndex = -1; } virtual int resolveBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool is_live) override = 0;
uint32_t TDefaultIoResolverBase::computeTypeLocationSize(const TType& type, EShLanguage stage) {
int typeLocationSize;
// Don’t take into account the outer-most array if the stage’s
// interface is automatically an array.
if (type.getQualifier().isArrayedIo(stage)) {
TType elementType(type, 0);
typeLocationSize = TIntermediate::computeTypeLocationSize(elementType, stage);
} else {
typeLocationSize = TIntermediate::computeTypeLocationSize(type, stage);
}
return typeLocationSize;
}
//TDefaultGlslIoResolver int resolveSet(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool /*is_live*/) override
TResourceType TDefaultGlslIoResolver::getResourceType(const glslang::TType& type) { {
if (isImageType(type)) { if (type.getQualifier().hasSet())
return EResImage; return type.getQualifier().layoutSet;
}
if (isTextureType(type)) {
return EResTexture;
}
if (isSsboType(type)) {
return EResSsbo;
}
if (isSamplerType(type)) {
return EResSampler;
}
if (isUboType(type)) {
return EResUbo;
}
return EResCount;
}
TDefaultGlslIoResolver::TDefaultGlslIoResolver(const TIntermediate& intermediate) // If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN)
: TDefaultIoResolverBase(intermediate) if (getResourceSetBinding().size() == 1)
, preStage(EShLangCount) return atoi(getResourceSetBinding()[0].c_str());
, 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) { return 0;
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 int resolveUniformLocation(EShLanguage /*stage*/, const char* name, const glslang::TType& type, bool /*is_live*/) override
if (type.getQualifier().hasLocation() && (type.isStruct() || type.isArray())) { {
return ent.newLocation = type.getQualifier().layoutLocation; // kick out of not doing this
} else { if (!doAutoLocationMapping())
return -1;
// no locations added if already present, a built-in variable, a block, or an opaque // no locations added if already present, a built-in variable, a block, or an opaque
if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock || if (type.getQualifier().hasLocation() || type.isBuiltIn() ||
type.getBasicType() == EbtAtomicUint || (type.containsOpaque() && intermediate.getSpv().openGl == 0)) { type.getBasicType() == EbtBlock ||
return ent.newLocation = -1; type.getBasicType() == EbtAtomicUint ||
} (type.containsOpaque() && intermediate.getSpv().openGl == 0))
return -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 ent.newLocation = -1; return -1;
} if ((*type.getStruct())[0].type->isBuiltIn())
if ((*type.getStruct())[0].type->isBuiltIn()) { return -1;
return ent.newLocation = -1;
}
} }
int location = intermediate.getUniformLocationOverride(name);
if (location != -1)
return location;
location = nextUniformLocation;
nextUniformLocation += TIntermediate::computeTypeUniformLocationSize(type);
return location;
} }
int location = intermediate.getUniformLocationOverride(name.c_str()); bool validateInOut(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
if (location != -1) { {
return ent.newLocation = location; return true;
} }
int resolveInOutLocation(EShLanguage stage, const char* /*name*/, const TType& type, bool /*is_live*/) override
{
// kick out of not doing this
if (!doAutoLocationMapping())
return -1;
int size = TIntermediate::computeTypeUniformLocationSize(type); // no locations added if already present, or a built-in variable
if (type.getQualifier().hasLocation() || type.isBuiltIn())
// The uniform in current stage is not declared with location, but it is possible declared return -1;
// with explicit location in other stages, find the storageSlotMap firstly to check whether
// the uniform has location // no locations on blocks of built-in variables
bool hasLocation = false; if (type.isStruct()) {
int resourceKey = buildStorageKey(EShLangCount, EvqUniform); if (type.getStruct()->size() < 1)
TVarSlotMap& slotMap = storageSlotMap[resourceKey]; return -1;
// Check dose shader program has uniform resource if ((*type.getStruct())[0].type->isBuiltIn())
if (! slotMap.empty()) { return -1;
// If uniform resource not empty, try find a same name uniform }
TVarSlotMap::iterator iter = slotMap.find(name);
if (iter != slotMap.end()) { // point to the right input or output location counter
// If uniform resource be found, set it has location and this symbol's new location int& nextLocation = type.getQualifier().isPipeInput() ? nextInputLocation : nextOutputLocation;
// equal the uniform's explicit location declarated in other stage.
// // Placeholder. This does not do proper cross-stage lining up, nor
// vs: uniform vec4 a; // work with mixed location/no-location declarations.
// fs: layout(..., location = 3,...) uniform vec4 a; int location = nextLocation;
hasLocation = true; int typeLocationSize;
location = iter->second; // Don’t take into account the outer-most array if the stage’s
} // interface is automatically an array.
if (! hasLocation) { if (type.getQualifier().isArrayedIo(stage)) {
// No explicit location declaraten in other stage. TType elementType(type, 0);
// So we should find a new slot for this uniform. typeLocationSize = TIntermediate::computeTypeLocationSize(elementType, stage);
// } else {
// vs: uniform vec4 a; typeLocationSize = TIntermediate::computeTypeLocationSize(type, stage);
// fs: uniform vec4 a;
location = getFreeSlot(resourceKey, 0, computeTypeLocationSize(type, currentStage));
storageSlotMap[resourceKey][name] = location;
} }
} else { nextLocation += typeLocationSize;
// the first uniform declarated in a program.
TVarSlotMap varSlotMap;
location = getFreeSlot(resourceKey, 0, size);
varSlotMap[name] = location;
storageSlotMap[resourceKey] = varSlotMap;
}
return ent.newLocation = location;
}
int TDefaultGlslIoResolver::resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) { return location;
const TType& type = ent.symbol->getType();
const TString& name = ent.symbol->getName();
// On OpenGL arrays of opaque types take a seperate binding for each element
int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
TResourceType resource = getResourceType(type);
// don't need to handle uniform symbol, it will be handled in resolveUniformLocation
if (resource == EResUbo && type.getBasicType() != EbtBlock) {
return ent.newBinding = -1;
} }
// There is no 'set' qualifier in OpenGL shading language, each resource has its own int resolveInOutComponent(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
// binding name space, so remap the 'set' to resource type which make each resource {
// binding is valid from 0 to MAX_XXRESOURCE_BINDINGS return -1;
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; int resolveInOutIndex(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
} {
return -1;
void TDefaultGlslIoResolver::beginResolve(EShLanguage stage) {
// reset stage state
if (stage == EShLangCount)
preStage = currentStage = stage;
// update stage state
else if (currentStage != stage) {
preStage = currentStage;
currentStage = stage;
} }
}
void TDefaultGlslIoResolver::endResolve(EShLanguage /*stage*/) { void notifyBinding(EShLanguage, const char* /*name*/, const TType&, bool /*is_live*/) override {}
// TODO nothing void notifyInOut(EShLanguage, const char* /*name*/, const TType&, bool /*is_live*/) override {}
} void endNotifications(EShLanguage) override {}
void beginNotifications(EShLanguage) override {}
void beginResolve(EShLanguage) override {}
void endResolve(EShLanguage) override {}
protected:
TDefaultIoResolverBase(TDefaultIoResolverBase&);
TDefaultIoResolverBase& operator=(TDefaultIoResolverBase&);
void TDefaultGlslIoResolver::beginCollect(EShLanguage stage) { const TIntermediate &intermediate;
// reset stage state int nextUniformLocation;
if (stage == EShLangCount) int nextInputLocation;
preStage = currentStage = stage; int nextOutputLocation;
// update stage state
else if (currentStage != stage) { // Return descriptor set specific base if there is one, and the generic base otherwise.
preStage = currentStage; int selectBaseBinding(int base, int descriptorSetBase) const {
currentStage = stage; return descriptorSetBase != -1 ? descriptorSetBase : base;
} }
}
void TDefaultGlslIoResolver::endCollect(EShLanguage /*stage*/) { static int getLayoutSet(const glslang::TType& type) {
// TODO nothing if (type.getQualifier().hasSet())
} return type.getQualifier().layoutSet;
else
return 0;
}
void TDefaultGlslIoResolver::reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) { static bool isSamplerType(const glslang::TType& type) {
const TType& type = ent.symbol->getType(); return type.getBasicType() == glslang::EbtSampler && type.getSampler().isPureSampler();
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;
} }
}
void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) { static bool isTextureType(const glslang::TType& type) {
const TType& type = ent.symbol->getType(); return (type.getBasicType() == glslang::EbtSampler &&
const TString& name = ent.symbol->getName(); (type.getSampler().isTexture() || type.getSampler().isSubpass()));
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());
}
}
} }
}
//TDefaultGlslIoResolver end static bool isUboType(const glslang::TType& type) {
return type.getQualifier().storage == EvqUniform;
}
};
/* /*
* Basic implementation of glslang::TIoMapResolver that replaces the * Basic implementation of glslang::TIoMapResolver that replaces the
...@@ -899,47 +567,66 @@ void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& ...@@ -899,47 +567,66 @@ void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink&
/* /*
* 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*/, TVarEntryInfo& /*ent*/) override { return true; }
TResourceType getResourceType(const glslang::TType& type) override { bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& /*type*/, bool /*is_live*/) override
if (isImageType(type)) { {
return EResImage; return true;
}
if (isTextureType(type)) {
return EResTexture;
}
if (isSsboType(type)) {
return EResSsbo;
}
if (isSamplerType(type)) {
return EResSampler;
}
if (isUboType(type)) {
return EResUbo;
}
return EResCount;
} }
int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override { int resolveBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool is_live) 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 (resource < EResCount) { if (type.getQualifier().hasBinding()) {
if (type.getQualifier().hasBinding()) { if (isImageType(type))
return ent.newBinding = reserveSlot( return reserveSlot(set, getBaseBinding(EResImage, set) + type.getQualifier().layoutBinding, numBindings);
set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding, numBindings);
} else if (ent.live && doAutoBindingMapping()) { if (isTextureType(type))
// find free slot, the caller did make sure it passes all vars with binding return reserveSlot(set, getBaseBinding(EResTexture, set) + type.getQualifier().layoutBinding, numBindings);
// first and now all are passed that do not have a binding and needs one
return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set), numBindings); if (isSsboType(type))
} 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;
} }
}; };
...@@ -960,7 +647,7 @@ t - for shader resource views (SRV) ...@@ -960,7 +647,7 @@ t - for shader resource views (SRV)
BYTEADDRESSBUFFER BYTEADDRESSBUFFER
BUFFER BUFFER
TBUFFER TBUFFER
s - for samplers s - for samplers
SAMPLER SAMPLER
SAMPLER1D SAMPLER1D
...@@ -986,65 +673,98 @@ b - for constant buffer views (CBV) ...@@ -986,65 +673,98 @@ 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*/, TVarEntryInfo& /*ent*/) override { return true; }
TResourceType getResourceType(const glslang::TType& type) override { bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& /*type*/, bool /*is_live*/) override
if (isUavType(type)) { {
return EResUav; return true;
}
if (isSrvType(type)) {
return EResTexture;
}
if (isSamplerType(type)) {
return EResSampler;
}
if (isUboType(type)) {
return EResUbo;
}
return EResCount;
} }
int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override { int resolveBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool is_live) override
const TType& type = ent.symbol->getType(); {
const int set = getLayoutSet(type); const int set = getLayoutSet(type);
TResourceType resource = getResourceType(type);
if (resource < EResCount) { if (type.getQualifier().hasBinding()) {
if (type.getQualifier().hasBinding()) { if (isUavType(type))
return ent.newBinding = reserveSlot(set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding); return reserveSlot(set, getBaseBinding(EResUav, set) + type.getQualifier().layoutBinding);
} else if (ent.live && doAutoBindingMapping()) {
// find free slot, the caller did make sure it passes all vars with binding if (isSrvType(type))
// first and now all are passed that do not have a binding and needs one return reserveSlot(set, getBaseBinding(EResTexture, set) + type.getQualifier().layoutBinding);
return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set));
} 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))
return getFreeSlot(set, getBaseBinding(EResUav, set));
if (isSrvType(type))
return getFreeSlot(set, getBaseBinding(EResTexture, set));
if (isSamplerType(type))
return getFreeSlot(set, getBaseBinding(EResSampler, set));
if (isUboType(type))
return getFreeSlot(set, getBaseBinding(EResUbo, set));
} }
return ent.newBinding = -1;
return -1;
}
protected:
// 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);
} }
}; };
// 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() || {
intermediate.getAutoMapLocations(); bool somethingToDo = !intermediate.getResourceSetBinding().empty() ||
intermediate.getAutoMapBindings() ||
intermediate.getAutoMapLocations();
for (int res = 0; res < EResCount; ++res) { for (int res = 0; res < EResCount; ++res) {
somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) || somethingToDo = somethingToDo ||
intermediate.hasShiftBindingForSet(TResourceType(res)); (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
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())
...@@ -1052,163 +772,47 @@ bool TIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSi ...@@ -1052,163 +772,47 @@ 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 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::for_each(uniformVarMap.begin(), uniformVarMap.end(), std::sort(uniformVarMap.begin(), uniformVarMap.end(), TVarEntryInfo::TOrderByPriority());
[&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); TResolverUniformAdaptor uniformResolve(stage, *resolver, infoSink, hadError, intermediate);
TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError); TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError, intermediate);
resolver->beginNotifications(stage); resolver->beginNotifications(stage);
std::for_each(inVarMap.begin(), inVarMap.end(), inOutNotify); std::for_each(inVarMap.begin(), inVarMap.end(), inOutNotify);
std::for_each(outVarMap.begin(), outVarMap.end(), inOutNotify); std::for_each(outVarMap.begin(), outVarMap.end(), inOutNotify);
std::for_each(uniformVector.begin(), uniformVector.end(), uniformNotify); std::for_each(uniformVarMap.begin(), uniformVarMap.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(inVarMap.begin(), inVarMap.end(), inOutResolve);
std::for_each(outVarMap.begin(), outVarMap.end(), inOutResolve); std::for_each(outVarMap.begin(), outVarMap.end(), inOutResolve);
std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve); std::for_each(uniformVarMap.begin(), uniformVarMap.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,9 +36,8 @@ ...@@ -36,9 +36,8 @@
#ifndef _IOMAPPER_INCLUDED #ifndef _IOMAPPER_INCLUDED
#define _IOMAPPER_INCLUDED #define _IOMAPPER_INCLUDED
#include "LiveTraverser.h" #include "../Public/ShaderLang.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.
// //
...@@ -48,245 +47,15 @@ class TInfoSink; ...@@ -48,245 +47,15 @@ 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*/) { 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);
void beginResolve(EShLanguage /*stage*/);
void endResolve(EShLanguage stage);
void beginCollect(EShLanguage) override;
void endCollect(EShLanguage) override;
void reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink);
void reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink);
// 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(stage <= 0xffff && type <= 0xffff);
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*) override; bool addStage(EShLanguage, TIntermediate&, TInfoSink&, TIoMapResolver*);
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,9 +646,8 @@ protected: ...@@ -646,9 +646,8 @@ 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.
...@@ -669,61 +668,51 @@ struct TVarEntryInfo; ...@@ -669,61 +668,51 @@ struct TVarEntryInfo;
// 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, TVarEntryInfo& ent) = 0; virtual bool validateBinding(EShLanguage stage, const char* name, const TType& type, bool is_live) = 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, TVarEntryInfo& ent) = 0; virtual int resolveBinding(EShLanguage stage, const char* name, const TType& type, bool is_live) = 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, TVarEntryInfo& ent) = 0; virtual int resolveSet(EShLanguage stage, const char* name, const TType& type, bool is_live) = 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, TVarEntryInfo& ent) = 0; virtual int resolveUniformLocation(EShLanguage stage, const char* name, const TType& type, bool is_live) = 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, TVarEntryInfo& ent) = 0; 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 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, TVarEntryInfo& ent) = 0; 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 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, TVarEntryInfo& ent) = 0; 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 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, TVarEntryInfo& ent) = 0; virtual int resolveInOutIndex(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0;
// Notification of a uniform variable // Notification of a uniform variable
virtual void notifyBinding(EShLanguage stage, TVarEntryInfo& ent) = 0; virtual void notifyBinding(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0;
// Notification of a in or out variable // Notification of a in or out variable
virtual void notifyInOut(EShLanguage stage, TVarEntryInfo& ent) = 0; virtual void notifyInOut(EShLanguage stage, const char* name, const TType& type, bool is_live) = 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 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 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
...@@ -835,7 +824,7 @@ public: ...@@ -835,7 +824,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* pResolver = nullptr, TIoMapper* pIoMapper = nullptr); bool mapIO(TIoMapResolver* resolver = NULL);
protected: protected:
bool linkStage(EShLanguage, EShMessages); bool linkStage(EShLanguage, EShMessages);
...@@ -846,6 +835,7 @@ protected: ...@@ -846,6 +835,7 @@ 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