mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-01-19 14:55:38 +00:00
1260 lines
50 KiB
C++
1260 lines
50 KiB
C++
//
|
||
// Copyright (C) 2016-2017 LunarG, Inc.
|
||
//
|
||
// All rights reserved.
|
||
//
|
||
// Redistribution and use in source and binary forms, with or without
|
||
// modification, are permitted provided that the following conditions
|
||
// are met:
|
||
//
|
||
// Redistributions of source code must retain the above copyright
|
||
// notice, this list of conditions and the following disclaimer.
|
||
//
|
||
// Redistributions in binary form must reproduce the above
|
||
// copyright notice, this list of conditions and the following
|
||
// disclaimer in the documentation and/or other materials provided
|
||
// with the distribution.
|
||
//
|
||
// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
|
||
// contributors may be used to endorse or promote products derived
|
||
// from this software without specific prior written permission.
|
||
//
|
||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
// POSSIBILITY OF SUCH DAMAGE.
|
||
//
|
||
|
||
#ifndef GLSLANG_WEB
|
||
|
||
#include "../Include/Common.h"
|
||
#include "../Include/InfoSink.h"
|
||
|
||
#include "gl_types.h"
|
||
#include "iomapper.h"
|
||
|
||
//
|
||
// Map IO bindings.
|
||
//
|
||
// High-level algorithm for one stage:
|
||
//
|
||
// 1. Traverse all code (live+dead) to find the explicitly provided bindings.
|
||
//
|
||
// 2. Traverse (just) the live code to determine which non-provided bindings
|
||
// require auto-numbering. We do not auto-number dead ones.
|
||
//
|
||
// 3. Traverse all the code to apply the bindings:
|
||
// a. explicitly given bindings are offset according to their type
|
||
// b. implicit live bindings are auto-numbered into the holes, using
|
||
// any open binding slot.
|
||
// c. implicit dead bindings are left un-bound.
|
||
//
|
||
|
||
namespace glslang {
|
||
|
||
class TVarGatherTraverser : public TLiveTraverser {
|
||
public:
|
||
TVarGatherTraverser(const TIntermediate& i, bool traverseDeadCode, TVarLiveMap& inList, TVarLiveMap& outList, TVarLiveMap& uniformList)
|
||
: TLiveTraverser(i, traverseDeadCode, true, true, false)
|
||
, inputList(inList)
|
||
, outputList(outList)
|
||
, uniformList(uniformList)
|
||
{
|
||
}
|
||
|
||
virtual void visitSymbol(TIntermSymbol* base)
|
||
{
|
||
TVarLiveMap* target = nullptr;
|
||
if (base->getQualifier().storage == EvqVaryingIn)
|
||
target = &inputList;
|
||
else if (base->getQualifier().storage == EvqVaryingOut)
|
||
target = &outputList;
|
||
else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().isPushConstant())
|
||
target = &uniformList;
|
||
if (target) {
|
||
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)[ent.symbol->getName()] = ent;
|
||
}
|
||
}
|
||
|
||
private:
|
||
TVarLiveMap& inputList;
|
||
TVarLiveMap& outputList;
|
||
TVarLiveMap& uniformList;
|
||
};
|
||
|
||
class TVarSetTraverser : public TLiveTraverser
|
||
{
|
||
public:
|
||
TVarSetTraverser(const TIntermediate& i, const TVarLiveMap& inList, const TVarLiveMap& outList, const TVarLiveMap& uniformList)
|
||
: TLiveTraverser(i, true, true, true, false)
|
||
, inputList(inList)
|
||
, outputList(outList)
|
||
, uniformList(uniformList)
|
||
{
|
||
}
|
||
|
||
virtual void visitSymbol(TIntermSymbol* base) {
|
||
const TVarLiveMap* source;
|
||
if (base->getQualifier().storage == EvqVaryingIn)
|
||
source = &inputList;
|
||
else if (base->getQualifier().storage == EvqVaryingOut)
|
||
source = &outputList;
|
||
else if (base->getQualifier().isUniformOrBuffer())
|
||
source = &uniformList;
|
||
else
|
||
return;
|
||
|
||
TVarEntryInfo ent = { base->getId() };
|
||
TVarLiveMap::const_iterator at = source->find(base->getName());
|
||
if (at == source->end())
|
||
return;
|
||
|
||
if (at->second.id != ent.id)
|
||
return;
|
||
|
||
if (at->second.newBinding != -1)
|
||
base->getWritableType().getQualifier().layoutBinding = at->second.newBinding;
|
||
if (at->second.newSet != -1)
|
||
base->getWritableType().getQualifier().layoutSet = at->second.newSet;
|
||
if (at->second.newLocation != -1)
|
||
base->getWritableType().getQualifier().layoutLocation = at->second.newLocation;
|
||
if (at->second.newComponent != -1)
|
||
base->getWritableType().getQualifier().layoutComponent = at->second.newComponent;
|
||
if (at->second.newIndex != -1)
|
||
base->getWritableType().getQualifier().layoutIndex = at->second.newIndex;
|
||
}
|
||
|
||
private:
|
||
const TVarLiveMap& inputList;
|
||
const TVarLiveMap& outputList;
|
||
const TVarLiveMap& uniformList;
|
||
};
|
||
|
||
struct TNotifyUniformAdaptor
|
||
{
|
||
EShLanguage stage;
|
||
TIoMapResolver& resolver;
|
||
inline TNotifyUniformAdaptor(EShLanguage s, TIoMapResolver& r)
|
||
: stage(s)
|
||
, resolver(r)
|
||
{
|
||
}
|
||
|
||
inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
|
||
{
|
||
resolver.notifyBinding(stage, entKey.second);
|
||
}
|
||
|
||
private:
|
||
TNotifyUniformAdaptor& operator=(TNotifyUniformAdaptor&) = delete;
|
||
};
|
||
|
||
struct TNotifyInOutAdaptor
|
||
{
|
||
EShLanguage stage;
|
||
TIoMapResolver& resolver;
|
||
inline TNotifyInOutAdaptor(EShLanguage s, TIoMapResolver& r)
|
||
: stage(s)
|
||
, resolver(r)
|
||
{
|
||
}
|
||
|
||
inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
|
||
{
|
||
resolver.notifyInOut(stage, entKey.second);
|
||
}
|
||
|
||
private:
|
||
TNotifyInOutAdaptor& operator=(TNotifyInOutAdaptor&) = delete;
|
||
};
|
||
|
||
struct TResolverUniformAdaptor {
|
||
TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e)
|
||
: stage(s)
|
||
, resolver(r)
|
||
, infoSink(i)
|
||
, error(e)
|
||
{
|
||
}
|
||
|
||
inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
|
||
TVarEntryInfo& ent = entKey.second;
|
||
ent.newLocation = -1;
|
||
ent.newComponent = -1;
|
||
ent.newBinding = -1;
|
||
ent.newSet = -1;
|
||
ent.newIndex = -1;
|
||
const bool isValid = resolver.validateBinding(stage, ent);
|
||
if (isValid) {
|
||
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: " + entKey.first;
|
||
|
||
infoSink.info.message(EPrefixInternalError, err.c_str());
|
||
error = true;
|
||
}
|
||
}
|
||
if (ent.newSet != -1) {
|
||
if (ent.newSet >= int(TQualifier::layoutSetEnd)) {
|
||
TString err = "mapped set out of range: " + entKey.first;
|
||
|
||
infoSink.info.message(EPrefixInternalError, err.c_str());
|
||
error = true;
|
||
}
|
||
}
|
||
} else {
|
||
TString errorMsg = "Invalid binding: " + entKey.first;
|
||
infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
|
||
error = true;
|
||
}
|
||
}
|
||
|
||
inline void setStage(EShLanguage s) { stage = s; }
|
||
|
||
EShLanguage stage;
|
||
TIoMapResolver& resolver;
|
||
TInfoSink& infoSink;
|
||
bool& error;
|
||
|
||
private:
|
||
TResolverUniformAdaptor& operator=(TResolverUniformAdaptor&) = delete;
|
||
};
|
||
|
||
struct TResolverInOutAdaptor {
|
||
TResolverInOutAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e)
|
||
: stage(s)
|
||
, resolver(r)
|
||
, infoSink(i)
|
||
, error(e)
|
||
{
|
||
}
|
||
|
||
inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
|
||
{
|
||
TVarEntryInfo& ent = entKey.second;
|
||
ent.newLocation = -1;
|
||
ent.newComponent = -1;
|
||
ent.newBinding = -1;
|
||
ent.newSet = -1;
|
||
ent.newIndex = -1;
|
||
const bool isValid = resolver.validateInOut(stage, ent);
|
||
if (isValid) {
|
||
resolver.resolveInOutLocation(stage, ent);
|
||
resolver.resolveInOutComponent(stage, ent);
|
||
resolver.resolveInOutIndex(stage, ent);
|
||
} else {
|
||
TString errorMsg;
|
||
if (ent.symbol->getType().getQualifier().semanticName != nullptr) {
|
||
errorMsg = "Invalid shader In/Out variable semantic: ";
|
||
errorMsg += ent.symbol->getType().getQualifier().semanticName;
|
||
} else {
|
||
errorMsg = "Invalid shader In/Out variable: ";
|
||
errorMsg += ent.symbol->getName();
|
||
}
|
||
infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
|
||
error = true;
|
||
}
|
||
}
|
||
|
||
inline void setStage(EShLanguage s) { stage = s; }
|
||
|
||
EShLanguage stage;
|
||
TIoMapResolver& resolver;
|
||
TInfoSink& infoSink;
|
||
bool& error;
|
||
|
||
private:
|
||
TResolverInOutAdaptor& operator=(TResolverInOutAdaptor&) = delete;
|
||
};
|
||
|
||
// The class is used for reserving explicit uniform locations and ubo/ssbo/opaque bindings
|
||
|
||
struct TSymbolValidater
|
||
{
|
||
TSymbolValidater(TIoMapResolver& r, TInfoSink& i, TVarLiveMap* in[EShLangCount], TVarLiveMap* out[EShLangCount],
|
||
TVarLiveMap* uniform[EShLangCount], bool& hadError)
|
||
: 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*)));
|
||
}
|
||
|
||
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().isPushConstant()) {
|
||
// validate uniform type;
|
||
for (int i = 0; i < EShLangCount; i++) {
|
||
if (i != currentStage && outVarMaps[i] != nullptr) {
|
||
auto ent2 = uniformVarMap[i]->find(name);
|
||
if (ent2 != uniformVarMap[i]->end()) {
|
||
ent2->second.symbol->getType().appendMangledName(mangleName2);
|
||
if (mangleName1 != mangleName2) {
|
||
TString err = "Invalid Uniform variable type : " + entKey.first;
|
||
infoSink.info.message(EPrefixInternalError, err.c_str());
|
||
hadError = true;
|
||
}
|
||
mangleName2.clear();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
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&) = delete;
|
||
};
|
||
|
||
struct TSlotCollector {
|
||
TSlotCollector(TIoMapResolver& r, TInfoSink& i) : resolver(r), infoSink(i) { }
|
||
|
||
inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
|
||
resolver.reserverStorageSlot(entKey.second, infoSink);
|
||
resolver.reserverResourceSlot(entKey.second, infoSink);
|
||
}
|
||
TIoMapResolver& resolver;
|
||
TInfoSink& infoSink;
|
||
|
||
private:
|
||
TSlotCollector& operator=(TSlotCollector&) = delete;
|
||
};
|
||
|
||
TDefaultIoResolverBase::TDefaultIoResolverBase(const TIntermediate& intermediate)
|
||
: intermediate(intermediate)
|
||
, nextUniformLocation(intermediate.getUniformLocationBase())
|
||
, nextInputLocation(0)
|
||
, nextOutputLocation(0)
|
||
{
|
||
memset(stageMask, false, sizeof(bool) * (EShLangCount + 1));
|
||
}
|
||
|
||
int TDefaultIoResolverBase::getBaseBinding(TResourceType res, unsigned int set) const {
|
||
return selectBaseBinding(intermediate.getShiftBinding(res), intermediate.getShiftBindingForSet(res, set));
|
||
}
|
||
|
||
const std::vector<std::string>& TDefaultIoResolverBase::getResourceSetBinding() const {
|
||
return intermediate.getResourceSetBinding();
|
||
}
|
||
|
||
bool TDefaultIoResolverBase::doAutoBindingMapping() const { return intermediate.getAutoMapBindings(); }
|
||
|
||
bool TDefaultIoResolverBase::doAutoLocationMapping() const { return intermediate.getAutoMapLocations(); }
|
||
|
||
TDefaultIoResolverBase::TSlotSet::iterator TDefaultIoResolverBase::findSlot(int set, int slot) {
|
||
return std::lower_bound(slots[set].begin(), slots[set].end(), slot);
|
||
}
|
||
|
||
bool TDefaultIoResolverBase::checkEmpty(int set, int slot) {
|
||
TSlotSet::iterator at = findSlot(set, slot);
|
||
return ! (at != slots[set].end() && *at == slot);
|
||
}
|
||
|
||
int TDefaultIoResolverBase::reserveSlot(int set, int slot, int size) {
|
||
TSlotSet::iterator at = findSlot(set, slot);
|
||
// tolerate aliasing, by not double-recording aliases
|
||
// (policy about appropriateness of the alias is higher up)
|
||
for (int i = 0; i < size; i++) {
|
||
if (at == slots[set].end() || *at != slot + i)
|
||
at = slots[set].insert(at, slot + i);
|
||
++at;
|
||
}
|
||
return slot;
|
||
}
|
||
|
||
int TDefaultIoResolverBase::getFreeSlot(int set, int base, int size) {
|
||
TSlotSet::iterator at = findSlot(set, base);
|
||
if (at == slots[set].end())
|
||
return reserveSlot(set, base, size);
|
||
// look for a big enough gap
|
||
for (; at != slots[set].end(); ++at) {
|
||
if (*at - base >= size)
|
||
break;
|
||
base = *at + 1;
|
||
}
|
||
return reserveSlot(set, base, size);
|
||
}
|
||
|
||
int TDefaultIoResolverBase::resolveSet(EShLanguage /*stage*/, TVarEntryInfo& ent) {
|
||
const TType& type = ent.symbol->getType();
|
||
if (type.getQualifier().hasSet()) {
|
||
return ent.newSet = type.getQualifier().layoutSet;
|
||
}
|
||
// If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN)
|
||
if (getResourceSetBinding().size() == 1) {
|
||
return ent.newSet = atoi(getResourceSetBinding()[0].c_str());
|
||
}
|
||
return ent.newSet = 0;
|
||
}
|
||
|
||
int TDefaultIoResolverBase::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
|
||
const TType& type = ent.symbol->getType();
|
||
const char* name = ent.symbol->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.isAtomic() || (type.containsOpaque() && intermediate.getSpv().openGl == 0)) {
|
||
return ent.newLocation = -1;
|
||
}
|
||
// no locations on blocks of built-in variables
|
||
if (type.isStruct()) {
|
||
if (type.getStruct()->size() < 1) {
|
||
return ent.newLocation = -1;
|
||
}
|
||
if ((*type.getStruct())[0].type->isBuiltIn()) {
|
||
return ent.newLocation = -1;
|
||
}
|
||
}
|
||
int location = intermediate.getUniformLocationOverride(name);
|
||
if (location != -1) {
|
||
return ent.newLocation = location;
|
||
}
|
||
location = nextUniformLocation;
|
||
nextUniformLocation += TIntermediate::computeTypeUniformLocationSize(type);
|
||
return ent.newLocation = location;
|
||
}
|
||
|
||
int TDefaultIoResolverBase::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
|
||
const TType& type = ent.symbol->getType();
|
||
// kick out of not doing this
|
||
if (! doAutoLocationMapping()) {
|
||
return ent.newLocation = -1;
|
||
}
|
||
|
||
// no locations added if already present, or a built-in variable
|
||
if (type.getQualifier().hasLocation() || type.isBuiltIn()) {
|
||
return ent.newLocation = -1;
|
||
}
|
||
|
||
// no locations on blocks of built-in variables
|
||
if (type.isStruct()) {
|
||
if (type.getStruct()->size() < 1) {
|
||
return ent.newLocation = -1;
|
||
}
|
||
if ((*type.getStruct())[0].type->isBuiltIn()) {
|
||
return ent.newLocation = -1;
|
||
}
|
||
}
|
||
// point to the right input or output location counter
|
||
int& nextLocation = type.getQualifier().isPipeInput() ? nextInputLocation : nextOutputLocation;
|
||
// Placeholder. This does not do proper cross-stage lining up, nor
|
||
// work with mixed location/no-location declarations.
|
||
int location = nextLocation;
|
||
int typeLocationSize;
|
||
// Don’t take into account the outer-most array if the stage’s
|
||
// interface is automatically an array.
|
||
typeLocationSize = computeTypeLocationSize(type, stage);
|
||
nextLocation += typeLocationSize;
|
||
return ent.newLocation = location;
|
||
}
|
||
|
||
int TDefaultIoResolverBase::resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) {
|
||
return ent.newComponent = -1;
|
||
}
|
||
|
||
int TDefaultIoResolverBase::resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) { return ent.newIndex = -1; }
|
||
|
||
uint32_t TDefaultIoResolverBase::computeTypeLocationSize(const TType& type, EShLanguage stage) {
|
||
int typeLocationSize;
|
||
// Don’t take into account the outer-most array if the stage’s
|
||
// interface is automatically an array.
|
||
if (type.getQualifier().isArrayedIo(stage)) {
|
||
TType elementType(type, 0);
|
||
typeLocationSize = TIntermediate::computeTypeLocationSize(elementType, stage);
|
||
} else {
|
||
typeLocationSize = TIntermediate::computeTypeLocationSize(type, stage);
|
||
}
|
||
return typeLocationSize;
|
||
}
|
||
|
||
//TDefaultGlslIoResolver
|
||
TResourceType TDefaultGlslIoResolver::getResourceType(const glslang::TType& type) {
|
||
if (isImageType(type)) {
|
||
return EResImage;
|
||
}
|
||
if (isTextureType(type)) {
|
||
return EResTexture;
|
||
}
|
||
if (isSsboType(type)) {
|
||
return EResSsbo;
|
||
}
|
||
if (isSamplerType(type)) {
|
||
return EResSampler;
|
||
}
|
||
if (isUboType(type)) {
|
||
return EResUbo;
|
||
}
|
||
return EResCount;
|
||
}
|
||
|
||
TDefaultGlslIoResolver::TDefaultGlslIoResolver(const TIntermediate& intermediate)
|
||
: TDefaultIoResolverBase(intermediate)
|
||
, preStage(EShLangCount)
|
||
, currentStage(EShLangCount)
|
||
{ }
|
||
|
||
int TDefaultGlslIoResolver::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
|
||
const TType& type = ent.symbol->getType();
|
||
const TString& name = getAccessName(ent.symbol);
|
||
if (currentStage != stage) {
|
||
preStage = currentStage;
|
||
currentStage = stage;
|
||
}
|
||
// kick out of not doing this
|
||
if (! doAutoLocationMapping()) {
|
||
return ent.newLocation = -1;
|
||
}
|
||
// expand the location to each element if the symbol is a struct or array
|
||
if (type.getQualifier().hasLocation()) {
|
||
return ent.newLocation = type.getQualifier().layoutLocation;
|
||
}
|
||
// no locations added if already present, or a built-in variable
|
||
if (type.isBuiltIn()) {
|
||
return ent.newLocation = -1;
|
||
}
|
||
// no locations on blocks of built-in variables
|
||
if (type.isStruct()) {
|
||
if (type.getStruct()->size() < 1) {
|
||
return ent.newLocation = -1;
|
||
}
|
||
if ((*type.getStruct())[0].type->isBuiltIn()) {
|
||
return ent.newLocation = -1;
|
||
}
|
||
}
|
||
int typeLocationSize = computeTypeLocationSize(type, stage);
|
||
int location = type.getQualifier().layoutLocation;
|
||
bool hasLocation = false;
|
||
EShLanguage keyStage(EShLangCount);
|
||
TStorageQualifier storage;
|
||
storage = EvqInOut;
|
||
if (type.getQualifier().isPipeInput()) {
|
||
// If this symbol is a input, search pre stage's out
|
||
keyStage = preStage;
|
||
}
|
||
if (type.getQualifier().isPipeOutput()) {
|
||
// If this symbol is a output, search next stage's in
|
||
keyStage = currentStage;
|
||
}
|
||
// The in/out in current stage is not declared with location, but it is possible declared
|
||
// with explicit location in other stages, find the storageSlotMap firstly to check whether
|
||
// the in/out has location
|
||
int resourceKey = buildStorageKey(keyStage, storage);
|
||
if (! storageSlotMap[resourceKey].empty()) {
|
||
TVarSlotMap::iterator iter = storageSlotMap[resourceKey].find(name);
|
||
if (iter != storageSlotMap[resourceKey].end()) {
|
||
// If interface resource be found, set it has location and this symbol's new location
|
||
// equal the symbol's explicit location declaration in pre or next stage.
|
||
//
|
||
// vs: out vec4 a;
|
||
// fs: layout(..., location = 3,...) in vec4 a;
|
||
hasLocation = true;
|
||
location = iter->second;
|
||
// if we want deal like that:
|
||
// vs: layout(location=4) out vec4 a;
|
||
// out vec4 b;
|
||
//
|
||
// fs: in vec4 a;
|
||
// layout(location = 4) in vec4 b;
|
||
// we need retraverse the map.
|
||
}
|
||
if (! hasLocation) {
|
||
// If interface resource note found, It's mean the location in two stage are both implicit declarat.
|
||
// So we should find a new slot for this interface.
|
||
//
|
||
// vs: out vec4 a;
|
||
// fs: in vec4 a;
|
||
location = getFreeSlot(resourceKey, 0, typeLocationSize);
|
||
storageSlotMap[resourceKey][name] = location;
|
||
}
|
||
} else {
|
||
// the first interface declarated in a program.
|
||
TVarSlotMap varSlotMap;
|
||
location = getFreeSlot(resourceKey, 0, typeLocationSize);
|
||
varSlotMap[name] = location;
|
||
storageSlotMap[resourceKey] = varSlotMap;
|
||
}
|
||
//Update location
|
||
return ent.newLocation = location;
|
||
}
|
||
|
||
int TDefaultGlslIoResolver::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
|
||
const TType& type = ent.symbol->getType();
|
||
const TString& name = getAccessName(ent.symbol);
|
||
// kick out of not doing this
|
||
if (! doAutoLocationMapping()) {
|
||
return ent.newLocation = -1;
|
||
}
|
||
// expand the location to each element if the symbol is a struct or array
|
||
if (type.getQualifier().hasLocation() && (type.isStruct() || type.isArray())) {
|
||
return ent.newLocation = type.getQualifier().layoutLocation;
|
||
} else {
|
||
// no locations added if already present, a built-in variable, a block, or an opaque
|
||
if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock ||
|
||
type.isAtomic() || (type.containsOpaque() && intermediate.getSpv().openGl == 0)) {
|
||
return ent.newLocation = -1;
|
||
}
|
||
// no locations on blocks of built-in variables
|
||
if (type.isStruct()) {
|
||
if (type.getStruct()->size() < 1) {
|
||
return ent.newLocation = -1;
|
||
}
|
||
if ((*type.getStruct())[0].type->isBuiltIn()) {
|
||
return ent.newLocation = -1;
|
||
}
|
||
}
|
||
}
|
||
int location = intermediate.getUniformLocationOverride(name.c_str());
|
||
if (location != -1) {
|
||
return ent.newLocation = location;
|
||
}
|
||
|
||
int size = TIntermediate::computeTypeUniformLocationSize(type);
|
||
|
||
// The uniform in current stage is not declared with location, but it is possible declared
|
||
// with explicit location in other stages, find the storageSlotMap firstly to check whether
|
||
// the uniform has location
|
||
bool hasLocation = false;
|
||
int resourceKey = buildStorageKey(EShLangCount, EvqUniform);
|
||
TVarSlotMap& slotMap = storageSlotMap[resourceKey];
|
||
// Check dose shader program has uniform resource
|
||
if (! slotMap.empty()) {
|
||
// If uniform resource not empty, try find a same name uniform
|
||
TVarSlotMap::iterator iter = slotMap.find(name);
|
||
if (iter != slotMap.end()) {
|
||
// If uniform resource be found, set it has location and this symbol's new location
|
||
// equal the uniform's explicit location declaration in other stage.
|
||
//
|
||
// vs: uniform vec4 a;
|
||
// fs: layout(..., location = 3,...) uniform vec4 a;
|
||
hasLocation = true;
|
||
location = iter->second;
|
||
}
|
||
if (! hasLocation) {
|
||
// No explicit location declaration in other stage.
|
||
// So we should find a new slot for this uniform.
|
||
//
|
||
// vs: uniform vec4 a;
|
||
// fs: uniform vec4 a;
|
||
location = getFreeSlot(resourceKey, 0, computeTypeLocationSize(type, currentStage));
|
||
storageSlotMap[resourceKey][name] = location;
|
||
}
|
||
} else {
|
||
// the first uniform declaration in a program.
|
||
TVarSlotMap varSlotMap;
|
||
location = getFreeSlot(resourceKey, 0, size);
|
||
varSlotMap[name] = location;
|
||
storageSlotMap[resourceKey] = varSlotMap;
|
||
}
|
||
return ent.newLocation = location;
|
||
}
|
||
|
||
int TDefaultGlslIoResolver::resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) {
|
||
const TType& type = ent.symbol->getType();
|
||
const TString& name = getAccessName(ent.symbol);
|
||
// On OpenGL arrays of opaque types take a separate binding for each element
|
||
int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
|
||
TResourceType resource = getResourceType(type);
|
||
// don't need to handle uniform symbol, it will be handled in resolveUniformLocation
|
||
if (resource == EResUbo && type.getBasicType() != EbtBlock) {
|
||
return ent.newBinding = -1;
|
||
}
|
||
// There is no 'set' qualifier in OpenGL shading language, each resource has its own
|
||
// binding name space, so remap the 'set' to resource type which make each resource
|
||
// binding is valid from 0 to MAX_XXRESOURCE_BINDINGS
|
||
int set = resource;
|
||
if (resource < EResCount) {
|
||
if (type.getQualifier().hasBinding()) {
|
||
ent.newBinding = reserveSlot(set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding, numBindings);
|
||
return ent.newBinding;
|
||
} else if (ent.live && doAutoBindingMapping()) {
|
||
// The resource in current stage is not declared with binding, but it is possible declared
|
||
// with explicit binding in other stages, find the resourceSlotMap firstly to check whether
|
||
// the resource has binding, don't need to allocate if it already has a binding
|
||
bool hasBinding = false;
|
||
if (! resourceSlotMap[resource].empty()) {
|
||
TVarSlotMap::iterator iter = resourceSlotMap[resource].find(name);
|
||
if (iter != resourceSlotMap[resource].end()) {
|
||
hasBinding = true;
|
||
ent.newBinding = iter->second;
|
||
}
|
||
}
|
||
if (! hasBinding) {
|
||
TVarSlotMap varSlotMap;
|
||
// find free slot, the caller did make sure it passes all vars with binding
|
||
// first and now all are passed that do not have a binding and needs one
|
||
int binding = getFreeSlot(resource, getBaseBinding(resource, set), numBindings);
|
||
varSlotMap[name] = binding;
|
||
resourceSlotMap[resource] = varSlotMap;
|
||
ent.newBinding = binding;
|
||
}
|
||
return ent.newBinding;
|
||
}
|
||
}
|
||
return ent.newBinding = -1;
|
||
}
|
||
|
||
void TDefaultGlslIoResolver::beginResolve(EShLanguage stage) {
|
||
// reset stage state
|
||
if (stage == EShLangCount)
|
||
preStage = currentStage = stage;
|
||
// update stage state
|
||
else if (currentStage != stage) {
|
||
preStage = currentStage;
|
||
currentStage = stage;
|
||
}
|
||
}
|
||
|
||
void TDefaultGlslIoResolver::endResolve(EShLanguage /*stage*/) {
|
||
// TODO nothing
|
||
}
|
||
|
||
void TDefaultGlslIoResolver::beginCollect(EShLanguage stage) {
|
||
// reset stage state
|
||
if (stage == EShLangCount)
|
||
preStage = currentStage = stage;
|
||
// update stage state
|
||
else if (currentStage != stage) {
|
||
preStage = currentStage;
|
||
currentStage = stage;
|
||
}
|
||
}
|
||
|
||
void TDefaultGlslIoResolver::endCollect(EShLanguage /*stage*/) {
|
||
// TODO nothing
|
||
}
|
||
|
||
void TDefaultGlslIoResolver::reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
|
||
const TType& type = ent.symbol->getType();
|
||
const TString& name = getAccessName(ent.symbol);
|
||
TStorageQualifier storage = type.getQualifier().storage;
|
||
EShLanguage stage(EShLangCount);
|
||
switch (storage) {
|
||
case EvqUniform:
|
||
if (type.getBasicType() != EbtBlock && type.getQualifier().hasLocation()) {
|
||
//
|
||
// Reserve the slots for the uniforms who has explicit location
|
||
int storageKey = buildStorageKey(EShLangCount, EvqUniform);
|
||
int location = type.getQualifier().layoutLocation;
|
||
TVarSlotMap& varSlotMap = storageSlotMap[storageKey];
|
||
TVarSlotMap::iterator iter = varSlotMap.find(name);
|
||
if (iter == varSlotMap.end()) {
|
||
int numLocations = TIntermediate::computeTypeUniformLocationSize(type);
|
||
reserveSlot(storageKey, location, numLocations);
|
||
varSlotMap[name] = location;
|
||
} else {
|
||
// Allocate location by name for OpenGL driver, so the uniform in different
|
||
// stages should be declared with the same location
|
||
if (iter->second != location) {
|
||
TString errorMsg = "Invalid location: " + name;
|
||
infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
|
||
hasError = true;
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
case EvqVaryingIn:
|
||
case EvqVaryingOut:
|
||
//
|
||
// Reserve the slots for the inout who has explicit location
|
||
if (type.getQualifier().hasLocation()) {
|
||
stage = storage == EvqVaryingIn ? preStage : stage;
|
||
stage = storage == EvqVaryingOut ? currentStage : stage;
|
||
int storageKey = buildStorageKey(stage, EvqInOut);
|
||
int location = type.getQualifier().layoutLocation;
|
||
TVarSlotMap& varSlotMap = storageSlotMap[storageKey];
|
||
TVarSlotMap::iterator iter = varSlotMap.find(name);
|
||
if (iter == varSlotMap.end()) {
|
||
int numLocations = TIntermediate::computeTypeUniformLocationSize(type);
|
||
reserveSlot(storageKey, location, numLocations);
|
||
varSlotMap[name] = location;
|
||
} else {
|
||
// Allocate location by name for OpenGL driver, so the uniform in different
|
||
// stages should be declared with the same location
|
||
if (iter->second != location) {
|
||
TString errorMsg = "Invalid location: " + name;
|
||
infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
|
||
hasError = true;
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
|
||
const TType& type = ent.symbol->getType();
|
||
const TString& name = getAccessName(ent.symbol);
|
||
int resource = getResourceType(type);
|
||
if (type.getQualifier().hasBinding()) {
|
||
TVarSlotMap& varSlotMap = resourceSlotMap[resource];
|
||
TVarSlotMap::iterator iter = varSlotMap.find(name);
|
||
int binding = type.getQualifier().layoutBinding;
|
||
if (iter == varSlotMap.end()) {
|
||
// Reserve the slots for the ubo, ssbo and opaques who has explicit binding
|
||
int numBindings = type.isSizedArray() ? type.getCumulativeArraySize() : 1;
|
||
varSlotMap[name] = binding;
|
||
reserveSlot(resource, binding, numBindings);
|
||
} else {
|
||
// Allocate binding by name for OpenGL driver, so the resource in different
|
||
// stages should be declared with the same binding
|
||
if (iter->second != binding) {
|
||
TString errorMsg = "Invalid binding: " + name;
|
||
infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
|
||
hasError = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
const TString& TDefaultGlslIoResolver::getAccessName(const TIntermSymbol* symbol)
|
||
{
|
||
return symbol->getBasicType() == EbtBlock ?
|
||
symbol->getType().getTypeName() :
|
||
symbol->getName();
|
||
}
|
||
|
||
//TDefaultGlslIoResolver end
|
||
|
||
/*
|
||
* Basic implementation of glslang::TIoMapResolver that replaces the
|
||
* previous offset behavior.
|
||
* It does the same, uses the offsets for the corresponding uniform
|
||
* types. Also respects the EOptionAutoMapBindings flag and binds
|
||
* them if needed.
|
||
*/
|
||
/*
|
||
* Default resolver
|
||
*/
|
||
struct TDefaultIoResolver : public TDefaultIoResolverBase {
|
||
TDefaultIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
|
||
|
||
bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
|
||
|
||
TResourceType getResourceType(const glslang::TType& type) override {
|
||
if (isImageType(type)) {
|
||
return EResImage;
|
||
}
|
||
if (isTextureType(type)) {
|
||
return EResTexture;
|
||
}
|
||
if (isSsboType(type)) {
|
||
return EResSsbo;
|
||
}
|
||
if (isSamplerType(type)) {
|
||
return EResSampler;
|
||
}
|
||
if (isUboType(type)) {
|
||
return EResUbo;
|
||
}
|
||
return EResCount;
|
||
}
|
||
|
||
int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override {
|
||
const TType& type = ent.symbol->getType();
|
||
const int set = getLayoutSet(type);
|
||
// On OpenGL arrays of opaque types take a seperate binding for each element
|
||
int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
|
||
TResourceType resource = getResourceType(type);
|
||
if (resource < EResCount) {
|
||
if (type.getQualifier().hasBinding()) {
|
||
return ent.newBinding = reserveSlot(
|
||
set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding, numBindings);
|
||
} else if (ent.live && doAutoBindingMapping()) {
|
||
// find free slot, the caller did make sure it passes all vars with binding
|
||
// first and now all are passed that do not have a binding and needs one
|
||
return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set), numBindings);
|
||
}
|
||
}
|
||
return ent.newBinding = -1;
|
||
}
|
||
};
|
||
|
||
#ifdef ENABLE_HLSL
|
||
/********************************************************************************
|
||
The following IO resolver maps types in HLSL register space, as follows:
|
||
|
||
t - for shader resource views (SRV)
|
||
TEXTURE1D
|
||
TEXTURE1DARRAY
|
||
TEXTURE2D
|
||
TEXTURE2DARRAY
|
||
TEXTURE3D
|
||
TEXTURECUBE
|
||
TEXTURECUBEARRAY
|
||
TEXTURE2DMS
|
||
TEXTURE2DMSARRAY
|
||
STRUCTUREDBUFFER
|
||
BYTEADDRESSBUFFER
|
||
BUFFER
|
||
TBUFFER
|
||
|
||
s - for samplers
|
||
SAMPLER
|
||
SAMPLER1D
|
||
SAMPLER2D
|
||
SAMPLER3D
|
||
SAMPLERCUBE
|
||
SAMPLERSTATE
|
||
SAMPLERCOMPARISONSTATE
|
||
|
||
u - for unordered access views (UAV)
|
||
RWBYTEADDRESSBUFFER
|
||
RWSTRUCTUREDBUFFER
|
||
APPENDSTRUCTUREDBUFFER
|
||
CONSUMESTRUCTUREDBUFFER
|
||
RWBUFFER
|
||
RWTEXTURE1D
|
||
RWTEXTURE1DARRAY
|
||
RWTEXTURE2D
|
||
RWTEXTURE2DARRAY
|
||
RWTEXTURE3D
|
||
|
||
b - for constant buffer views (CBV)
|
||
CBUFFER
|
||
CONSTANTBUFFER
|
||
********************************************************************************/
|
||
struct TDefaultHlslIoResolver : public TDefaultIoResolverBase {
|
||
TDefaultHlslIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
|
||
|
||
bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
|
||
|
||
TResourceType getResourceType(const glslang::TType& type) override {
|
||
if (isUavType(type)) {
|
||
return EResUav;
|
||
}
|
||
if (isSrvType(type)) {
|
||
return EResTexture;
|
||
}
|
||
if (isSamplerType(type)) {
|
||
return EResSampler;
|
||
}
|
||
if (isUboType(type)) {
|
||
return EResUbo;
|
||
}
|
||
return EResCount;
|
||
}
|
||
|
||
int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override {
|
||
const TType& type = ent.symbol->getType();
|
||
const int set = getLayoutSet(type);
|
||
TResourceType resource = getResourceType(type);
|
||
if (resource < EResCount) {
|
||
if (type.getQualifier().hasBinding()) {
|
||
return ent.newBinding = reserveSlot(set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding);
|
||
} else if (ent.live && doAutoBindingMapping()) {
|
||
// find free slot, the caller did make sure it passes all vars with binding
|
||
// first and now all are passed that do not have a binding and needs one
|
||
return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set));
|
||
}
|
||
}
|
||
return ent.newBinding = -1;
|
||
}
|
||
};
|
||
#endif
|
||
|
||
// Map I/O variables to provided offsets, and make bindings for
|
||
// unbound but live variables.
|
||
//
|
||
// Returns false if the input is too malformed to do this.
|
||
bool TIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) {
|
||
bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() ||
|
||
intermediate.getAutoMapLocations();
|
||
// Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce
|
||
// unnecessary or insignificant for-loop operation after 'somethingToDo' have been true.
|
||
for (int res = 0; (res < EResCount && !somethingToDo); ++res) {
|
||
somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
|
||
intermediate.hasShiftBindingForSet(TResourceType(res));
|
||
}
|
||
if (! somethingToDo && resolver == nullptr)
|
||
return true;
|
||
if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive())
|
||
return false;
|
||
TIntermNode* root = intermediate.getTreeRoot();
|
||
if (root == nullptr)
|
||
return false;
|
||
// if no resolver is provided, use the default resolver with the given shifts and auto map settings
|
||
TDefaultIoResolver defaultResolver(intermediate);
|
||
#ifdef ENABLE_HLSL
|
||
TDefaultHlslIoResolver defaultHlslResolver(intermediate);
|
||
if (resolver == nullptr) {
|
||
// TODO: use a passed in IO mapper for this
|
||
if (intermediate.usingHlslIoMapping())
|
||
resolver = &defaultHlslResolver;
|
||
else
|
||
resolver = &defaultResolver;
|
||
}
|
||
resolver->addStage(stage);
|
||
#else
|
||
resolver = &defaultResolver;
|
||
#endif
|
||
|
||
TVarLiveMap inVarMap, outVarMap, uniformVarMap;
|
||
TVarLiveVector inVector, outVector, uniformVector;
|
||
TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap);
|
||
TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap);
|
||
root->traverse(&iter_binding_all);
|
||
iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
|
||
while (! iter_binding_live.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::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);
|
||
TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError);
|
||
resolver->beginNotifications(stage);
|
||
std::for_each(inVector.begin(), inVector.end(), inOutNotify);
|
||
std::for_each(outVector.begin(), outVector.end(), inOutNotify);
|
||
std::for_each(uniformVector.begin(), uniformVector.end(), uniformNotify);
|
||
resolver->endNotifications(stage);
|
||
resolver->beginResolve(stage);
|
||
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) {
|
||
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();
|
||
// Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce
|
||
// unnecessary or insignificant for-loop operation after 'somethingToDo' have been true.
|
||
for (int res = 0; (res < EResCount && !somethingToDo); ++res) {
|
||
somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
|
||
intermediate.hasShiftBindingForSet(TResourceType(res));
|
||
}
|
||
if (! somethingToDo && resolver == nullptr) {
|
||
return true;
|
||
}
|
||
if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive()) {
|
||
return false;
|
||
}
|
||
TIntermNode* root = intermediate.getTreeRoot();
|
||
if (root == nullptr) {
|
||
return false;
|
||
}
|
||
// if no resolver is provided, use the default resolver with the given shifts and auto map settings
|
||
TDefaultGlslIoResolver defaultResolver(intermediate);
|
||
if (resolver == nullptr) {
|
||
resolver = &defaultResolver;
|
||
}
|
||
resolver->addStage(stage);
|
||
inVarMaps[stage] = new TVarLiveMap(); outVarMaps[stage] = new TVarLiveMap(); uniformVarMap[stage] = new TVarLiveMap();
|
||
TVarGatherTraverser iter_binding_all(intermediate, true, *inVarMaps[stage], *outVarMaps[stage],
|
||
*uniformVarMap[stage]);
|
||
TVarGatherTraverser iter_binding_live(intermediate, false, *inVarMaps[stage], *outVarMaps[stage],
|
||
*uniformVarMap[stage]);
|
||
root->traverse(&iter_binding_all);
|
||
iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
|
||
while (! iter_binding_live.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
|
||
|
||
#endif // GLSLANG_WEB
|