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()
return infoSink->debug.c_str();
}
TProgram::TProgram() : reflection(0), ioMapper(nullptr), linked(false)
TProgram::TProgram() : reflection(0), linked(false)
{
pool = new TPoolAllocator;
infoSink = new TInfoSink;
......@@ -1864,7 +1864,6 @@ TProgram::TProgram() : reflection(0), ioMapper(nullptr), linked(false)
TProgram::~TProgram()
{
delete ioMapper;
delete infoSink;
delete reflection;
......@@ -2035,21 +2034,24 @@ void TProgram::dumpReflection() { if (reflection != nullptr) reflection->dump();
//
// I/O mapping implementation.
//
bool TProgram::mapIO(TIoMapResolver* resolver)
bool TProgram::mapIO(TIoMapResolver* pResolver, TIoMapper* pIoMapper)
{
if (! linked || ioMapper)
if (! linked)
return false;
ioMapper = new TIoMapper;
TIoMapper* ioMapper = nullptr;
TIoMapper defaultIOMapper;
if (pIoMapper == nullptr)
ioMapper = &defaultIOMapper;
else
ioMapper = pIoMapper;
for (int s = 0; s < EShLangCount; ++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 true;
return ioMapper->doMap(pResolver, *infoSink);
}
} // end namespace glslang
......@@ -35,14 +35,9 @@
#include "../Include/Common.h"
#include "../Include/InfoSink.h"
#include "iomapper.h"
#include "LiveTraverser.h"
#include "localintermediate.h"
#include "gl_types.h"
#include <unordered_set>
#include <unordered_map>
#include "iomapper.h"
//
// Map IO bindings.
......@@ -61,60 +56,9 @@
// c. implicit dead bindings are left un-bound.
//
namespace glslang {
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
{
class TVarGatherTraverser : public TLiveTraverser {
public:
TVarGatherTraverser(const TIntermediate& i, bool traverseDeadCode, TVarLiveMap& inList, TVarLiveMap& outList, TVarLiveMap& uniformList)
: TLiveTraverser(i, traverseDeadCode, true, true, false)
......@@ -124,7 +68,6 @@ public:
{
}
virtual void visitSymbol(TIntermSymbol* base)
{
TVarLiveMap* target = nullptr;
......@@ -134,14 +77,15 @@ public:
target = &outputList;
else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().layoutPushConstant)
target = &uniformList;
if (target) {
TVarEntryInfo ent = { base->getId(), base, !traverseAll };
TVarLiveMap::iterator at = std::lower_bound(target->begin(), target->end(), ent, TVarEntryInfo::TOrderById());
if (at != target->end() && at->id == ent.id)
at->live = at->live || !traverseAll; // update live state
TVarEntryInfo ent = {base->getId(), base, ! traverseAll};
ent.stage = intermediate.getStage();
TVarLiveMap::iterator at = target->find(
ent.symbol->getName()); // std::lower_bound(target->begin(), target->end(), ent, TVarEntryInfo::TOrderById());
if (at != target->end() && at->second.id == ent.id)
at->second.live = at->second.live || ! traverseAll; // update live state
else
target->insert(at, ent);
(*target)[ent.symbol->getName()] = ent;
}
}
......@@ -162,9 +106,7 @@ public:
{
}
virtual void visitSymbol(TIntermSymbol* base)
{
virtual void visitSymbol(TIntermSymbol* base) {
const TVarLiveMap* source;
if (base->getQualifier().storage == EvqVaryingIn)
source = &inputList;
......@@ -176,23 +118,23 @@ public:
return;
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())
return;
if (at->id != ent.id)
if (at->second.id != ent.id)
return;
if (at->newBinding != -1)
base->getWritableType().getQualifier().layoutBinding = at->newBinding;
if (at->newSet != -1)
base->getWritableType().getQualifier().layoutSet = at->newSet;
if (at->newLocation != -1)
base->getWritableType().getQualifier().layoutLocation = at->newLocation;
if (at->newComponent != -1)
base->getWritableType().getQualifier().layoutComponent = at->newComponent;
if (at->newIndex != -1)
base->getWritableType().getQualifier().layoutIndex = at->newIndex;
if (at->second.newBinding != -1)
base->getWritableType().getQualifier().layoutBinding = at->second.newBinding;
if (at->second.newSet != -1)
base->getWritableType().getQualifier().layoutSet = at->second.newSet;
if (at->second.newLocation != -1)
base->getWritableType().getQualifier().layoutLocation = at->second.newLocation;
if (at->second.newComponent != -1)
base->getWritableType().getQualifier().layoutComponent = at->second.newComponent;
if (at->second.newIndex != -1)
base->getWritableType().getQualifier().layoutIndex = at->second.newIndex;
}
private:
......@@ -210,10 +152,12 @@ struct TNotifyUniformAdaptor
, 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:
TNotifyUniformAdaptor& operator=(TNotifyUniformAdaptor&);
};
......@@ -222,49 +166,46 @@ struct TNotifyInOutAdaptor
{
EShLanguage stage;
TIoMapResolver& resolver;
inline TNotifyInOutAdaptor(EShLanguage s, TIoMapResolver& r)
inline TNotifyInOutAdaptor(EShLanguage s, TIoMapResolver& r)
: stage(s)
, 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:
TNotifyInOutAdaptor& operator=(TNotifyInOutAdaptor&);
};
struct TResolverUniformAdaptor
{
TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e, TIntermediate& interm)
struct TResolverUniformAdaptor {
TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e)
: stage(s)
, resolver(r)
, infoSink(i)
, 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.newComponent = -1;
ent.newBinding = -1;
ent.newSet = -1;
ent.newIndex = -1;
const bool isValid = resolver.validateBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(),
ent.live);
const bool isValid = resolver.validateBinding(stage, ent);
if (isValid) {
ent.newBinding = resolver.resolveBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(),
ent.live);
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);
resolver.resolveBinding(stage, ent);
resolver.resolveSet(stage, ent);
resolver.resolveUniformLocation(stage, ent);
if (ent.newBinding != -1) {
if (ent.newBinding >= int(TQualifier::layoutBindingEnd)) {
TString err = "mapped binding out of range: " + ent.symbol->getName();
TString err = "mapped binding out of range: " + entKey.first;
infoSink.info.message(EPrefixInternalError, err.c_str());
error = true;
......@@ -272,64 +213,52 @@ struct TResolverUniformAdaptor
}
if (ent.newSet != -1) {
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());
error = true;
}
}
} else {
TString errorMsg = "Invalid binding: " + ent.symbol->getName();
TString errorMsg = "Invalid binding: " + entKey.first;
infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
error = true;
}
}
inline void setStage(EShLanguage s) { stage = s; }
EShLanguage stage;
TIoMapResolver& resolver;
TInfoSink& infoSink;
bool& error;
TIntermediate& intermediate;
private:
TResolverUniformAdaptor& operator=(TResolverUniformAdaptor&);
};
struct TResolverInOutAdaptor
{
TResolverInOutAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e, TIntermediate& interm)
struct TResolverInOutAdaptor {
TResolverInOutAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e)
: stage(s)
, resolver(r)
, infoSink(i)
, 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.newComponent = -1;
ent.newBinding = -1;
ent.newSet = -1;
ent.newIndex = -1;
const bool isValid = resolver.validateInOut(stage,
ent.symbol->getName().c_str(),
ent.symbol->getType(),
ent.live);
const bool isValid = resolver.validateInOut(stage, ent);
if (isValid) {
ent.newLocation = resolver.resolveInOutLocation(stage,
ent.symbol->getName().c_str(),
ent.symbol->getType(),
ent.live);
ent.newComponent = resolver.resolveInOutComponent(stage,
ent.symbol->getName().c_str(),
ent.symbol->getType(),
ent.live);
ent.newIndex = resolver.resolveInOutIndex(stage,
ent.symbol->getName().c_str(),
ent.symbol->getType(),
ent.live);
resolver.resolveInOutLocation(stage, ent);
resolver.resolveInOutComponent(stage, ent);
resolver.resolveInOutIndex(stage, ent);
} else {
TString errorMsg;
if (ent.symbol->getType().getQualifier().semanticName != nullptr) {
......@@ -344,218 +273,621 @@ struct TResolverInOutAdaptor
}
}
inline void setStage(EShLanguage s) { stage = s; }
EShLanguage stage;
TIoMapResolver& resolver;
TInfoSink& infoSink;
bool& error;
TIntermediate& intermediate;
private:
TResolverInOutAdaptor& operator=(TResolverInOutAdaptor&);
};
// Base class for shared TIoMapResolver services, used by several derivations.
struct TDefaultIoResolverBase : public glslang::TIoMapResolver
{
TDefaultIoResolverBase(const TIntermediate &intermediate) :
intermediate(intermediate),
nextUniformLocation(intermediate.getUniformLocationBase()),
nextInputLocation(0),
nextOutputLocation(0)
{ }
// The class is used for reserving explicit uniform locations and ubo/ssbo/opaque bindings
int getBaseBinding(TResourceType res, unsigned int set) const {
return selectBaseBinding(intermediate.getShiftBinding(res),
intermediate.getShiftBindingForSet(res, set));
struct TSymbolValidater
{
TSymbolValidater(TIoMapResolver& r, TInfoSink& i, TVarLiveMap* in[EShLangCount], TVarLiveMap* out[EShLangCount],
TVarLiveMap* uniform[EShLangCount], bool& hadError)
: preStage(EShLangCount)
, currentStage(EShLangCount)
, nextStage(EShLangCount)
, resolver(r)
, infoSink(i)
, hadError(hadError)
{
memcpy(inVarMaps, in, EShLangCount * (sizeof(TVarLiveMap*)));
memcpy(outVarMaps, out, EShLangCount * (sizeof(TVarLiveMap*)));
memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*)));
}
const std::vector<std::string>& getResourceSetBinding() const { return intermediate.getResourceSetBinding(); }
bool doAutoBindingMapping() const { return intermediate.getAutoMapBindings(); }
bool doAutoLocationMapping() const { return intermediate.getAutoMapLocations(); }
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;
typedef std::vector<int> TSlotSet;
typedef std::unordered_map<int, TSlotSet> TSlotSetMap;
TSlotSetMap slots;
private:
TSymbolValidater& operator=(TSymbolValidater&);
};
TSlotSet::iterator findSlot(int set, int slot)
{
return std::lower_bound(slots[set].begin(), slots[set].end(), slot);
}
struct TSlotCollector {
TSlotCollector(TIoMapResolver& r, TInfoSink& i) : resolver(r), infoSink(i) { }
bool checkEmpty(int set, int slot)
{
TSlotSet::iterator at = findSlot(set, slot);
return !(at != slots[set].end() && *at == slot);
inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
resolver.reserverStorageSlot(entKey.second, infoSink);
resolver.reserverResourceSlot(entKey.second, infoSink);
}
TIoMapResolver& resolver;
TInfoSink& infoSink;
int reserveSlot(int set, int slot, int size = 1)
{
TSlotSet::iterator at = findSlot(set, slot);
private:
TSlotCollector& operator=(TSlotCollector&);
};
// 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;
}
TDefaultIoResolverBase::TDefaultIoResolverBase(const TIntermediate& intermediate)
: intermediate(intermediate)
, nextUniformLocation(intermediate.getUniformLocationBase())
, nextInputLocation(0)
, nextOutputLocation(0)
{
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)
{
TSlotSet::iterator at = findSlot(set, base);
if (at == slots[set].end())
return reserveSlot(set, base, size);
const std::vector<std::string>& TDefaultIoResolverBase::getResourceSetBinding() const {
return intermediate.getResourceSetBinding();
}
// 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);
}
bool TDefaultIoResolverBase::doAutoBindingMapping() const { return intermediate.getAutoMapBindings(); }
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
{
if (type.getQualifier().hasSet())
return type.getQualifier().layoutSet;
bool TDefaultIoResolverBase::checkEmpty(int set, int slot) {
TSlotSet::iterator at = findSlot(set, slot);
return ! (at != slots[set].end() && *at == slot);
}
// If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN)
if (getResourceSetBinding().size() == 1)
return atoi(getResourceSetBinding()[0].c_str());
int TDefaultIoResolverBase::reserveSlot(int set, int slot, int size) {
TSlotSet::iterator at = findSlot(set, slot);
// tolerate aliasing, by not double-recording aliases
// (policy about appropriateness of the alias is higher up)
for (int i = 0; i < size; i++) {
if (at == slots[set].end() || *at != slot + i)
at = slots[set].insert(at, slot + i);
++at;
}
return slot;
}
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
{
// kick out of not doing this
if (!doAutoLocationMapping())
return -1;
return reserveSlot(set, base, size);
}
// 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 -1;
int TDefaultIoResolverBase::resolveSet(EShLanguage /*stage*/, TVarEntryInfo& ent) {
const TType& type = ent.symbol->getType();
if (type.getQualifier().hasSet()) {
return ent.newSet = type.getQualifier().layoutSet;
}
// If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN)
if (getResourceSetBinding().size() == 1) {
return ent.newSet = atoi(getResourceSetBinding()[0].c_str());
}
return ent.newSet = 0;
}
// no locations on blocks of built-in variables
if (type.isStruct()) {
if (type.getStruct()->size() < 1)
return -1;
if ((*type.getStruct())[0].type->isBuiltIn())
return -1;
int TDefaultIoResolverBase::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
const TType& type = ent.symbol->getType();
const char* name = ent.symbol->getName().c_str();
// kick out of not doing this
if (! doAutoLocationMapping()) {
return ent.newLocation = -1;
}
// no locations added if already present, a built-in variable, a block, or an opaque
if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock ||
type.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);
if (location != -1)
return location;
int TDefaultIoResolverBase::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
const TType& type = ent.symbol->getType();
// kick out of not doing this
if (! doAutoLocationMapping()) {
return ent.newLocation = -1;
}
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 true;
return typeLocationSize;
}
//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
{
// kick out of not doing this
if (!doAutoLocationMapping())
return -1;
if (isTextureType(type)) {
return EResTexture;
}
if (isSsboType(type)) {
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
if (type.getQualifier().hasLocation() || type.isBuiltIn())
return -1;
TDefaultGlslIoResolver::TDefaultGlslIoResolver(const TIntermediate& intermediate)
: TDefaultIoResolverBase(intermediate)
, preStage(EShLangCount)
, currentStage(EShLangCount)
{ }
int TDefaultGlslIoResolver::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
const TType& type = ent.symbol->getType();
const TString& name = ent.symbol->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
if (type.isStruct()) {
if (type.getStruct()->size() < 1)
return -1;
if ((*type.getStruct())[0].type->isBuiltIn())
return -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);
if (type.getStruct()->size() < 1) {
return ent.newLocation = -1;
}
if ((*type.getStruct())[0].type->isBuiltIn()) {
return ent.newLocation = -1;
}
}
nextLocation += typeLocationSize;
return location;
}
int resolveInOutComponent(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
{
return -1;
int location = intermediate.getUniformLocationOverride(name.c_str());
if (location != -1) {
return ent.newLocation = location;
}
int resolveInOutIndex(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
{
return -1;
int size = TIntermediate::computeTypeUniformLocationSize(type);
// The uniform in current stage is not declared with location, but it is possible declared
// with explicit location in other stages, find the storageSlotMap firstly to check whether
// the uniform has location
bool hasLocation = false;
int resourceKey = buildStorageKey(EShLangCount, EvqUniform);
TVarSlotMap& slotMap = storageSlotMap[resourceKey];
// Check dose shader program has uniform resource
if (! slotMap.empty()) {
// If uniform resource not empty, try find a same name uniform
TVarSlotMap::iterator iter = slotMap.find(name);
if (iter != slotMap.end()) {
// If uniform resource be found, set it has location and this symbol's new location
// equal the uniform's explicit location declarated in other stage.
//
// vs: uniform vec4 a;
// fs: layout(..., location = 3,...) uniform vec4 a;
hasLocation = true;
location = iter->second;
}
if (! hasLocation) {
// No explicit location declaraten in other stage.
// So we should find a new slot for this uniform.
//
// vs: uniform vec4 a;
// fs: uniform vec4 a;
location = getFreeSlot(resourceKey, 0, computeTypeLocationSize(type, currentStage));
storageSlotMap[resourceKey][name] = location;
}
} else {
// the first uniform declarated in a program.
TVarSlotMap varSlotMap;
location = getFreeSlot(resourceKey, 0, size);
varSlotMap[name] = location;
storageSlotMap[resourceKey] = varSlotMap;
}
return ent.newLocation = location;
}
void notifyBinding(EShLanguage, const char* /*name*/, const TType&, bool /*is_live*/) override {}
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 {}
int TDefaultGlslIoResolver::resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) {
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
// 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:
TDefaultIoResolverBase(TDefaultIoResolverBase&);
TDefaultIoResolverBase& operator=(TDefaultIoResolverBase&);
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;
}
}
const TIntermediate &intermediate;
int nextUniformLocation;
int nextInputLocation;
int nextOutputLocation;
void TDefaultGlslIoResolver::endResolve(EShLanguage /*stage*/) {
// TODO nothing
}
// 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;
void TDefaultGlslIoResolver::beginCollect(EShLanguage stage) {
// reset stage state
if (stage == EShLangCount)
preStage = currentStage = stage;
// update stage state
else if (currentStage != stage) {
preStage = currentStage;
currentStage = stage;
}
}
static int getLayoutSet(const glslang::TType& type) {
if (type.getQualifier().hasSet())
return type.getQualifier().layoutSet;
else
return 0;
}
void TDefaultGlslIoResolver::endCollect(EShLanguage /*stage*/) {
// TODO nothing
}
static bool isSamplerType(const glslang::TType& type) {
return type.getBasicType() == glslang::EbtSampler && type.getSampler().isPureSampler();
void TDefaultGlslIoResolver::reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
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) {
return (type.getBasicType() == glslang::EbtSampler &&
(type.getSampler().isTexture() || type.getSampler().isSubpass()));
void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
const TType& type = ent.symbol->getType();
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) {
return type.getQualifier().storage == EvqUniform;
}
};
//TDefaultGlslIoResolver end
/*
* Basic implementation of glslang::TIoMapResolver that replaces the
......@@ -567,66 +899,47 @@ protected:
/*
* Default resolver
*/
struct TDefaultIoResolver : public TDefaultIoResolverBase
{
TDefaultIoResolver(const TIntermediate &intermediate) : TDefaultIoResolverBase(intermediate) { }
struct TDefaultIoResolver : public TDefaultIoResolverBase {
TDefaultIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& /*type*/, bool /*is_live*/) override
{
return true;
bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
TResourceType getResourceType(const glslang::TType& type) override {
if (isImageType(type)) {
return EResImage;
}
if (isTextureType(type)) {
return EResTexture;
}
if (isSsboType(type)) {
return EResSsbo;
}
if (isSamplerType(type)) {
return EResSampler;
}
if (isUboType(type)) {
return EResUbo;
}
return EResCount;
}
int resolveBinding(EShLanguage /*stage*/, 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);
// On OpenGL arrays of opaque types take a seperate binding for each element
int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
if (type.getQualifier().hasBinding()) {
if (isImageType(type))
return reserveSlot(set, getBaseBinding(EResImage, set) + type.getQualifier().layoutBinding, numBindings);
if (isTextureType(type))
return reserveSlot(set, getBaseBinding(EResTexture, set) + type.getQualifier().layoutBinding, 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);
TResourceType resource = getResourceType(type);
if (resource < EResCount) {
if (type.getQualifier().hasBinding()) {
return ent.newBinding = reserveSlot(
set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding, numBindings);
} else if (ent.live && doAutoBindingMapping()) {
// find free slot, the caller did make sure it passes all vars with binding
// first and now all are passed that do not have a binding and needs one
return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set), numBindings);
}
}
return -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;
return ent.newBinding = -1;
}
};
......@@ -647,7 +960,7 @@ t - for shader resource views (SRV)
BYTEADDRESSBUFFER
BUFFER
TBUFFER
s - for samplers
SAMPLER
SAMPLER1D
......@@ -673,98 +986,65 @@ b - for constant buffer views (CBV)
CBUFFER
CONSTANTBUFFER
********************************************************************************/
struct TDefaultHlslIoResolver : public TDefaultIoResolverBase
{
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
struct TDefaultHlslIoResolver : public TDefaultIoResolverBase {
TDefaultHlslIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
if (isUavType(type))
return getFreeSlot(set, getBaseBinding(EResUav, set));
bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
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));
TResourceType getResourceType(const glslang::TType& type) override {
if (isUavType(type)) {
return EResUav;
}
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;
if (isSrvType(type)) {
return EResTexture;
}
if (isSamplerType(type)) {
return EResSampler;
}
if (isUboType(type)) {
return EResUbo;
}
return EResCount;
}
// 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);
int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override {
const TType& type = ent.symbol->getType();
const int set = getLayoutSet(type);
TResourceType resource = getResourceType(type);
if (resource < EResCount) {
if (type.getQualifier().hasBinding()) {
return ent.newBinding = reserveSlot(set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding);
} else if (ent.live && doAutoBindingMapping()) {
// find free slot, the caller did make sure it passes all vars with binding
// first and now all are passed that do not have a binding and needs one
return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set));
}
}
return ent.newBinding = -1;
}
};
// Map I/O variables to provided offsets, and make bindings for
// unbound but live variables.
//
// Returns false if the input is too malformed to do this.
bool TIoMapper::addStage(EShLanguage stage, TIntermediate &intermediate, TInfoSink &infoSink, TIoMapResolver *resolver)
{
bool somethingToDo = !intermediate.getResourceSetBinding().empty() ||
intermediate.getAutoMapBindings() ||
intermediate.getAutoMapLocations();
bool TIoMapper::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));
somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
intermediate.hasShiftBindingForSet(TResourceType(res));
}
if (!somethingToDo && resolver == nullptr)
if (! somethingToDo && resolver == nullptr)
return true;
if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive())
return false;
TIntermNode* root = intermediate.getTreeRoot();
if (root == nullptr)
return false;
// if no resolver is provided, use the default resolver with the given shifts and auto map settings
TDefaultIoResolver defaultResolver(intermediate);
TDefaultHlslIoResolver defaultHlslResolver(intermediate);
if (resolver == nullptr) {
// TODO: use a passed in IO mapper for this
if (intermediate.usingHlslIoMapping())
......@@ -772,47 +1052,183 @@ bool TIoMapper::addStage(EShLanguage stage, TIntermediate &intermediate, TInfoSi
else
resolver = &defaultResolver;
}
resolver->addStage(stage);
TVarLiveMap inVarMap, outVarMap, uniformVarMap;
TVarLiveVector inVector, outVector, uniformVector;
TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap);
TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap);
root->traverse(&iter_binding_all);
iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
while (!iter_binding_live.functions.empty()) {
while (! iter_binding_live.functions.empty()) {
TIntermNode* function = iter_binding_live.functions.back();
iter_binding_live.functions.pop_back();
function->traverse(&iter_binding_live);
}
// sort entries by priority. see TVarEntryInfo::TOrderByPriority for info.
std::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;
TNotifyInOutAdaptor inOutNotify(stage, *resolver);
TNotifyUniformAdaptor uniformNotify(stage, *resolver);
TResolverUniformAdaptor uniformResolve(stage, *resolver, infoSink, hadError, intermediate);
TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError, intermediate);
TResolverUniformAdaptor uniformResolve(stage, *resolver, infoSink, hadError);
TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError);
resolver->beginNotifications(stage);
std::for_each(inVarMap.begin(), inVarMap.end(), inOutNotify);
std::for_each(outVarMap.begin(), outVarMap.end(), inOutNotify);
std::for_each(uniformVarMap.begin(), uniformVarMap.end(), uniformNotify);
std::for_each(inVector.begin(), inVector.end(), inOutNotify);
std::for_each(outVector.begin(), outVector.end(), inOutNotify);
std::for_each(uniformVector.begin(), uniformVector.end(), uniformNotify);
resolver->endNotifications(stage);
resolver->beginResolve(stage);
std::for_each(inVarMap.begin(), inVarMap.end(), inOutResolve);
std::for_each(outVarMap.begin(), outVarMap.end(), inOutResolve);
std::for_each(uniformVarMap.begin(), uniformVarMap.end(), uniformResolve);
std::for_each(inVector.begin(), inVector.end(), inOutResolve);
std::for_each(inVector.begin(), inVector.end(), [&inVarMap](TVarLivePair p) {
auto at = inVarMap.find(p.second.symbol->getName());
if (at != inVarMap.end())
at->second = p.second;
});
std::for_each(outVector.begin(), outVector.end(), inOutResolve);
std::for_each(outVector.begin(), outVector.end(), [&outVarMap](TVarLivePair p) {
auto at = outVarMap.find(p.second.symbol->getName());
if (at != outVarMap.end())
at->second = p.second;
});
std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve);
std::for_each(uniformVector.begin(), uniformVector.end(), [&uniformVarMap](TVarLivePair p) {
auto at = uniformVarMap.find(p.second.symbol->getName());
if (at != uniformVarMap.end())
at->second = p.second;
});
resolver->endResolve(stage);
if (!hadError) {
// 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);
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;
}
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
......@@ -36,8 +36,9 @@
#ifndef _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.
//
......@@ -47,15 +48,245 @@ class TInfoSink;
namespace glslang {
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
class TIoMapper {
public:
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
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
......
......@@ -646,8 +646,9 @@ protected:
const TType* type;
};
class TReflection;
class TIoMapper;
class TReflection;
class TIoMapper;
struct TVarEntryInfo;
// Allows to customize the binding layout after linking.
// All used uniform variables will invoke at least validateBinding.
......@@ -668,51 +669,61 @@ class TIoMapper;
// notifiy callbacks, this phase ends with a call to endNotifications.
// Phase two starts directly after the call to endNotifications
// 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
// and may result in an error.
class TIoMapResolver
{
public:
virtual ~TIoMapResolver() {}
// Should return true if the resulting/current binding would be okay.
// 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;
// Should return a value >= 0 if the current binding should be overridden.
// 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;
// Should return a value >= 0 if the current set should be overridden.
// Return -1 if the current set (including no set) should be kept.
virtual int resolveSet(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0;
// Should return a value >= 0 if the current location should be overridden.
// 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;
// Should return true if the resulting/current setup would be okay.
// Basic idea is to do aliasing checks and reject invalid semantic names.
virtual bool validateInOut(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0;
// Should return a value >= 0 if the current location should be overridden.
// Return -1 if the current location (including no location) should be kept.
virtual int resolveInOutLocation(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0;
// Should return a value >= 0 if the current component index should be overridden.
// Return -1 if the current component index (including no index) should be kept.
virtual int resolveInOutComponent(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0;
// Should return a value >= 0 if the current color index should be overridden.
// 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;
// Notification of a uniform variable
virtual void notifyBinding(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0;
// Notification of a in or out variable
virtual void notifyInOut(EShLanguage stage, const char* name, const TType& type, bool is_live) = 0;
// Called by mapIO when it has finished the notify pass
virtual void endNotifications(EShLanguage stage) = 0;
// Called by mapIO when it starts its notify pass for the given stage
virtual void beginNotifications(EShLanguage stage) = 0;
// Called by mipIO when it starts its resolve pass for the given stage
virtual void beginResolve(EShLanguage stage) = 0;
// Called by mapIO when it has finished the resolve pass
virtual void endResolve(EShLanguage stage) = 0;
virtual ~TIoMapResolver() {}
// Should return true if the resulting/current binding would be okay.
// Basic idea is to do aliasing binding checks with this.
virtual bool validateBinding(EShLanguage stage, TVarEntryInfo& ent) = 0;
// Should return a value >= 0 if the current binding should be overridden.
// Return -1 if the current binding (including no binding) should be kept.
virtual int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) = 0;
// Should return a value >= 0 if the current set should be overridden.
// Return -1 if the current set (including no set) should be kept.
virtual int resolveSet(EShLanguage stage, TVarEntryInfo& ent) = 0;
// Should return a value >= 0 if the current location should be overridden.
// Return -1 if the current location (including no location) should be kept.
virtual int resolveUniformLocation(EShLanguage stage, TVarEntryInfo& ent) = 0;
// Should return true if the resulting/current setup would be okay.
// Basic idea is to do aliasing checks and reject invalid semantic names.
virtual bool validateInOut(EShLanguage stage, TVarEntryInfo& ent) = 0;
// Should return a value >= 0 if the current location should be overridden.
// Return -1 if the current location (including no location) should be kept.
virtual int resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) = 0;
// 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.
virtual int resolveInOutComponent(EShLanguage stage, TVarEntryInfo& ent) = 0;
// 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.
virtual int resolveInOutIndex(EShLanguage stage, TVarEntryInfo& ent) = 0;
// Notification of a uniform variable
virtual void notifyBinding(EShLanguage stage, TVarEntryInfo& ent) = 0;
// Notification of a in or out variable
virtual void notifyInOut(EShLanguage stage, TVarEntryInfo& ent) = 0;
// Called by mapIO when it starts its notify pass for the given stage
virtual void beginNotifications(EShLanguage stage) = 0;
// Called by mapIO when it has finished the notify pass
virtual void endNotifications(EShLanguage stage) = 0;
// Called by mipIO when it starts its resolve pass for the given stage
virtual void beginResolve(EShLanguage stage) = 0;
// Called by mapIO when it has finished the resolve pass
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
......@@ -824,7 +835,7 @@ public:
// I/O mapping: apply base offsets and map live unbound variables
// If resolver is not provided it uses the previous approach
// and respects auto assignment and offsets.
bool mapIO(TIoMapResolver* resolver = NULL);
bool mapIO(TIoMapResolver* pResolver = nullptr, TIoMapper* pIoMapper = nullptr);
protected:
bool linkStage(EShLanguage, EShMessages);
......@@ -835,7 +846,6 @@ protected:
bool newedIntermediate[EShLangCount]; // track which intermediate were "new" versus reusing a singleton unit in a stage
TInfoSink* infoSink;
TReflection* reflection;
TIoMapper* ioMapper;
bool linked;
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