summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/glslang/glslang/MachineIndependent/iomapper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/glslang/glslang/MachineIndependent/iomapper.cpp')
-rw-r--r--src/3rdparty/glslang/glslang/MachineIndependent/iomapper.cpp818
1 files changed, 818 insertions, 0 deletions
diff --git a/src/3rdparty/glslang/glslang/MachineIndependent/iomapper.cpp b/src/3rdparty/glslang/glslang/MachineIndependent/iomapper.cpp
new file mode 100644
index 0000000..46c7558
--- /dev/null
+++ b/src/3rdparty/glslang/glslang/MachineIndependent/iomapper.cpp
@@ -0,0 +1,818 @@
+//
+// 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.
+//
+
+#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>
+
+//
+// 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 {
+
+struct TVarEntryInfo
+{
+ int id;
+ TIntermSymbol* symbol;
+ bool live;
+ int newBinding;
+ int newSet;
+ int newLocation;
+ int newComponent;
+ int newIndex;
+
+ struct TOrderById
+ {
+ inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r)
+ {
+ return l.id < r.id;
+ }
+ };
+
+ struct TOrderByPriority
+ {
+ // ordering:
+ // 1) has both binding and set
+ // 2) has binding but no set
+ // 3) has no binding but set
+ // 4) has no binding and no set
+ inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r)
+ {
+ const TQualifier& lq = l.symbol->getQualifier();
+ const TQualifier& rq = r.symbol->getQualifier();
+
+ // simple rules:
+ // has binding gives 2 points
+ // has set gives 1 point
+ // who has the most points is more important.
+ int lPoints = (lq.hasBinding() ? 2 : 0) + (lq.hasSet() ? 1 : 0);
+ int rPoints = (rq.hasBinding() ? 2 : 0) + (rq.hasSet() ? 1 : 0);
+
+ if (lPoints == rPoints)
+ return l.id < r.id;
+ return lPoints > rPoints;
+ }
+ };
+};
+
+
+
+typedef std::vector<TVarEntryInfo> TVarLiveMap;
+
+class TVarGatherTraverser : public TLiveTraverser
+{
+public:
+ 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().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
+ else
+ target->insert(at, 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 = std::lower_bound(source->begin(), source->end(), ent, TVarEntryInfo::TOrderById());
+ if (at == source->end())
+ return;
+
+ if (at->id != ent.id)
+ return;
+
+ if (at->newBinding != -1)
+ base->getWritableType().getQualifier().layoutBinding = at->newBinding;
+ if (at->newSet != -1)
+ base->getWritableType().getQualifier().layoutSet = at->newSet;
+ if (at->newLocation != -1)
+ base->getWritableType().getQualifier().layoutLocation = at->newLocation;
+ if (at->newComponent != -1)
+ base->getWritableType().getQualifier().layoutComponent = at->newComponent;
+ if (at->newIndex != -1)
+ base->getWritableType().getQualifier().layoutIndex = at->newIndex;
+ }
+
+ private:
+ const TVarLiveMap& 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()(TVarEntryInfo& ent)
+ {
+ resolver.notifyBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), ent.live);
+ }
+private:
+ TNotifyUniformAdaptor& operator=(TNotifyUniformAdaptor&);
+};
+
+struct TNotifyInOutAdaptor
+{
+ EShLanguage stage;
+ TIoMapResolver& resolver;
+ inline TNotifyInOutAdaptor(EShLanguage s, TIoMapResolver& r)
+ : stage(s)
+ , resolver(r)
+ {
+ }
+ inline void operator()(TVarEntryInfo& ent)
+ {
+ resolver.notifyInOut(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), ent.live);
+ }
+private:
+ TNotifyInOutAdaptor& operator=(TNotifyInOutAdaptor&);
+};
+
+struct TResolverUniformAdaptor
+{
+ TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e, TIntermediate& interm)
+ : stage(s)
+ , resolver(r)
+ , infoSink(i)
+ , error(e)
+ , intermediate(interm)
+ {
+ }
+
+ inline void operator()(TVarEntryInfo& ent)
+ {
+ ent.newLocation = -1;
+ ent.newComponent = -1;
+ ent.newBinding = -1;
+ ent.newSet = -1;
+ ent.newIndex = -1;
+ const bool isValid = resolver.validateBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(),
+ ent.live);
+ if (isValid) {
+ ent.newBinding = resolver.resolveBinding(stage, ent.symbol->getName().c_str(), ent.symbol->getType(),
+ ent.live);
+ ent.newSet = resolver.resolveSet(stage, ent.symbol->getName().c_str(), ent.symbol->getType(), ent.live);
+ ent.newLocation = resolver.resolveUniformLocation(stage, ent.symbol->getName().c_str(),
+ ent.symbol->getType(), ent.live);
+
+ if (ent.newBinding != -1) {
+ if (ent.newBinding >= int(TQualifier::layoutBindingEnd)) {
+ TString err = "mapped binding out of range: " + ent.symbol->getName();
+
+ 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: " + ent.symbol->getName();
+
+ infoSink.info.message(EPrefixInternalError, err.c_str());
+ error = true;
+ }
+ }
+ } else {
+ TString errorMsg = "Invalid binding: " + ent.symbol->getName();
+ infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
+ error = true;
+ }
+ }
+
+ 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)
+ : stage(s)
+ , resolver(r)
+ , infoSink(i)
+ , error(e)
+ , intermediate(interm)
+ {
+ }
+
+ inline void operator()(TVarEntryInfo& ent)
+ {
+ ent.newLocation = -1;
+ ent.newComponent = -1;
+ ent.newBinding = -1;
+ ent.newSet = -1;
+ ent.newIndex = -1;
+ const bool isValid = resolver.validateInOut(stage,
+ ent.symbol->getName().c_str(),
+ ent.symbol->getType(),
+ ent.live);
+ if (isValid) {
+ ent.newLocation = resolver.resolveInOutLocation(stage,
+ ent.symbol->getName().c_str(),
+ ent.symbol->getType(),
+ ent.live);
+ ent.newComponent = resolver.resolveInOutComponent(stage,
+ ent.symbol->getName().c_str(),
+ ent.symbol->getType(),
+ ent.live);
+ ent.newIndex = resolver.resolveInOutIndex(stage,
+ ent.symbol->getName().c_str(),
+ ent.symbol->getType(),
+ ent.live);
+ } else {
+ TString errorMsg;
+ 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;
+ }
+ }
+
+ 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)
+ { }
+
+ int getBaseBinding(TResourceType res, unsigned int set) const {
+ return selectBaseBinding(intermediate.getShiftBinding(res),
+ intermediate.getShiftBindingForSet(res, set));
+ }
+
+ const std::vector<std::string>& getResourceSetBinding() const { return intermediate.getResourceSetBinding(); }
+
+ bool doAutoBindingMapping() const { return intermediate.getAutoMapBindings(); }
+ bool doAutoLocationMapping() const { return intermediate.getAutoMapLocations(); }
+
+ typedef std::vector<int> TSlotSet;
+ typedef std::unordered_map<int, TSlotSet> TSlotSetMap;
+ TSlotSetMap slots;
+
+ TSlotSet::iterator findSlot(int set, int slot)
+ {
+ return std::lower_bound(slots[set].begin(), slots[set].end(), slot);
+ }
+
+ bool checkEmpty(int set, int slot)
+ {
+ TSlotSet::iterator at = findSlot(set, slot);
+ return !(at != slots[set].end() && *at == slot);
+ }
+
+ int reserveSlot(int set, int slot, int size = 1)
+ {
+ 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 getFreeSlot(int set, int base, int size = 1)
+ {
+ 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);
+ }
+
+ virtual bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool /*is_live*/) override = 0;
+
+ virtual int resolveBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool is_live) override = 0;
+
+ int resolveSet(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool /*is_live*/) override
+ {
+ if (type.getQualifier().hasSet())
+ return 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 atoi(getResourceSetBinding()[0].c_str());
+
+ return 0;
+ }
+ 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;
+
+ // 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;
+
+ // 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 location = intermediate.getUniformLocationOverride(name);
+ if (location != -1)
+ return location;
+
+ location = nextUniformLocation;
+
+ nextUniformLocation += TIntermediate::computeTypeUniformLocationSize(type);
+
+ return location;
+ }
+ bool validateInOut(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
+ {
+ return true;
+ }
+ int resolveInOutLocation(EShLanguage stage, const char* /*name*/, const TType& type, bool /*is_live*/) override
+ {
+ // kick out of not doing this
+ if (!doAutoLocationMapping())
+ return -1;
+
+ // no locations added if already present, or a built-in variable
+ if (type.getQualifier().hasLocation() || type.isBuiltIn())
+ return -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);
+ }
+ nextLocation += typeLocationSize;
+
+ return location;
+ }
+ int resolveInOutComponent(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
+ {
+ return -1;
+ }
+ int resolveInOutIndex(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
+ {
+ return -1;
+ }
+
+ 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 {}
+
+protected:
+ TDefaultIoResolverBase(TDefaultIoResolverBase&);
+ TDefaultIoResolverBase& operator=(TDefaultIoResolverBase&);
+
+ const TIntermediate &intermediate;
+ int nextUniformLocation;
+ int nextInputLocation;
+ int nextOutputLocation;
+
+ // 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;
+ }
+};
+
+/*
+ * 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*/, 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);
+ // 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);
+ }
+
+ 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;
+ }
+};
+
+/********************************************************************************
+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*/, const char* /*name*/, const glslang::TType& /*type*/, bool /*is_live*/) override
+ {
+ return true;
+ }
+
+ int resolveBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool is_live) override
+ {
+ const int set = getLayoutSet(type);
+
+ if (type.getQualifier().hasBinding()) {
+ if (isUavType(type))
+ return reserveSlot(set, getBaseBinding(EResUav, set) + type.getQualifier().layoutBinding);
+
+ if (isSrvType(type))
+ return reserveSlot(set, getBaseBinding(EResTexture, set) + type.getQualifier().layoutBinding);
+
+ if (isSamplerType(type))
+ return reserveSlot(set, getBaseBinding(EResSampler, set) + type.getQualifier().layoutBinding);
+
+ if (isUboType(type))
+ return reserveSlot(set, getBaseBinding(EResUbo, set) + type.getQualifier().layoutBinding);
+ } else if (is_live && doAutoBindingMapping()) {
+ // find free slot, the caller did make sure it passes all vars with binding
+ // first and now all are passed that do not have a binding and needs one
+
+ if (isUavType(type))
+ return getFreeSlot(set, getBaseBinding(EResUav, set));
+
+ if (isSrvType(type))
+ return getFreeSlot(set, getBaseBinding(EResTexture, set));
+
+ if (isSamplerType(type))
+ return getFreeSlot(set, getBaseBinding(EResSampler, set));
+
+ if (isUboType(type))
+ return getFreeSlot(set, getBaseBinding(EResUbo, set));
+ }
+
+ return -1;
+ }
+
+protected:
+ // Return true if this is a SRV (shader resource view) type:
+ static bool isSrvType(const glslang::TType& type) {
+ return isTextureType(type) || type.getQualifier().storage == EvqBuffer;
+ }
+
+ // Return true if this is a UAV (unordered access view) type:
+ static bool isUavType(const glslang::TType& type) {
+ if (type.getQualifier().readonly)
+ return false;
+
+ return (type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage()) ||
+ (type.getQualifier().storage == EvqBuffer);
+ }
+};
+
+
+// Map I/O variables to provided offsets, and make bindings for
+// 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();
+
+ 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
+ TDefaultIoResolver defaultResolver(intermediate);
+ TDefaultHlslIoResolver defaultHlslResolver(intermediate);
+
+ if (resolver == nullptr) {
+ // TODO: use a passed in IO mapper for this
+ if (intermediate.usingHlslIoMapping())
+ resolver = &defaultHlslResolver;
+ else
+ resolver = &defaultResolver;
+ }
+
+ TVarLiveMap inVarMap, outVarMap, uniformVarMap;
+ TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap);
+ TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap);
+
+ root->traverse(&iter_binding_all);
+ iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
+
+ 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());
+
+ 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);
+ 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);
+ 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);
+ 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;
+}
+
+} // end namespace glslang