Add per-descriptor-set IO mapping shift values.

This PR adds the ability to provide per-descriptor-set IO mapping shift
values.  If a particular binding does not land into a per-set value,
then it falls back to the prior behavior (global shifts per resource class).

Because there were already 6 copies of many different methods and internal
variables and functions, and this PR would have added 6 more, a new API is
introduced to cut down on replication and present a cleaner interface.
For the global (non-set-specific) API, the old entry points still exist
for backward compatibility, but are phrased internally in terms of the
following.

    // Resource type for IO resolver
    enum TResourceType {
        EResSampler,
        EResTexture,
        EResImage,
        EResUbo,
        EResSsbo,
        EResUav,
        EResCount
    };

Methods on TShader:

    void setShiftBinding(TResourceType res, unsigned int base);
    void setShiftBindingForSet(TResourceType res, unsigned int set, unsigned int base);

The first method replaces the 6 prior entry points of various spellings, which
exist now in depreciated form.  The second provides per-resource-set functionality.
Both accept an enum from the list above.

From the command line, the existing options can accept either a single shift value as
before, or a series of 1 or more [set offset] pairs.  Both can be provided, as in:

    ... --stb 20 --stb 2 25 3 30 ...

which will use the offset 20 for anything except descriptor set 2 (which uses 25) and
3 (which uses 30).
This commit is contained in:
LoopDawg 2017-10-17 19:27:14 -06:00
parent 7d67c6cbc2
commit 08a14422c1
9 changed files with 523 additions and 159 deletions

View file

@ -351,16 +351,21 @@ private:
// Base class for shared TIoMapResolver services, used by several derivations.
struct TDefaultIoResolverBase : public glslang::TIoMapResolver
{
int baseSamplerBinding;
int baseTextureBinding;
int baseImageBinding;
int baseUboBinding;
int baseSsboBinding;
int baseUavBinding;
std::vector<std::string> baseResourceSetBinding;
bool doAutoBindingMapping;
bool doAutoLocationMapping;
int nextUniformLocation;
TDefaultIoResolverBase(const TIntermediate &intermediate) :
intermediate(intermediate),
nextUniformLocation(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;
@ -411,15 +416,15 @@ struct TDefaultIoResolverBase : public glslang::TIoMapResolver
return type.getQualifier().layoutSet;
// If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN)
if (baseResourceSetBinding.size() == 1)
return atoi(baseResourceSetBinding[0].c_str());
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)
if (!doAutoLocationMapping())
return -1;
// no locations added if already present, a built-in variable, a block, or an opaque
@ -444,7 +449,7 @@ struct TDefaultIoResolverBase : public glslang::TIoMapResolver
int resolveInOutLocation(EShLanguage /*stage*/, const char* /*name*/, const TType& type, bool /*is_live*/) override
{
// kick out of not doing this
if (!doAutoLocationMapping)
if (!doAutoLocationMapping())
return -1;
// no locations added if already present, or a built-in variable
@ -485,6 +490,14 @@ struct TDefaultIoResolverBase : public glslang::TIoMapResolver
void endResolve(EShLanguage) override {}
protected:
const TIntermediate &intermediate;
int nextUniformLocation;
// 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;
@ -518,6 +531,8 @@ protected:
*/
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;
@ -529,37 +544,37 @@ struct TDefaultIoResolver : public TDefaultIoResolverBase
if (type.getQualifier().hasBinding()) {
if (isImageType(type))
return reserveSlot(set, baseImageBinding + type.getQualifier().layoutBinding);
return reserveSlot(set, getBaseBinding(EResImage, set) + type.getQualifier().layoutBinding);
if (isTextureType(type))
return reserveSlot(set, baseTextureBinding + type.getQualifier().layoutBinding);
return reserveSlot(set, getBaseBinding(EResTexture, set) + type.getQualifier().layoutBinding);
if (isSsboType(type))
return reserveSlot(set, baseSsboBinding + type.getQualifier().layoutBinding);
return reserveSlot(set, getBaseBinding(EResSsbo, set) + type.getQualifier().layoutBinding);
if (isSamplerType(type))
return reserveSlot(set, baseSamplerBinding + type.getQualifier().layoutBinding);
return reserveSlot(set, getBaseBinding(EResSampler, set) + type.getQualifier().layoutBinding);
if (isUboType(type))
return reserveSlot(set, baseUboBinding + type.getQualifier().layoutBinding);
} else if (is_live && doAutoBindingMapping) {
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 (isImageType(type))
return getFreeSlot(set, baseImageBinding);
return getFreeSlot(set, getBaseBinding(EResImage, set));
if (isTextureType(type))
return getFreeSlot(set, baseTextureBinding);
return getFreeSlot(set, getBaseBinding(EResTexture, set));
if (isSsboType(type))
return getFreeSlot(set, baseSsboBinding);
return getFreeSlot(set, getBaseBinding(EResSsbo, set));
if (isSamplerType(type))
return getFreeSlot(set, baseSamplerBinding);
return getFreeSlot(set, getBaseBinding(EResSampler, set));
if (isUboType(type))
return getFreeSlot(set, baseUboBinding);
return getFreeSlot(set, getBaseBinding(EResUbo, set));
}
return -1;
@ -620,6 +635,8 @@ b for constant buffer views (CBV)
********************************************************************************/
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;
@ -631,31 +648,31 @@ struct TDefaultHlslIoResolver : public TDefaultIoResolverBase
if (type.getQualifier().hasBinding()) {
if (isUavType(type))
return reserveSlot(set, baseUavBinding + type.getQualifier().layoutBinding);
return reserveSlot(set, getBaseBinding(EResUav, set) + type.getQualifier().layoutBinding);
if (isSrvType(type))
return reserveSlot(set, baseTextureBinding + type.getQualifier().layoutBinding);
return reserveSlot(set, getBaseBinding(EResTexture, set) + type.getQualifier().layoutBinding);
if (isSamplerType(type))
return reserveSlot(set, baseSamplerBinding + type.getQualifier().layoutBinding);
return reserveSlot(set, getBaseBinding(EResSampler, set) + type.getQualifier().layoutBinding);
if (isUboType(type))
return reserveSlot(set, baseUboBinding + type.getQualifier().layoutBinding);
} else if (is_live && doAutoBindingMapping) {
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, baseUavBinding);
return getFreeSlot(set, getBaseBinding(EResUav, set));
if (isSrvType(type))
return getFreeSlot(set, baseTextureBinding);
return getFreeSlot(set, getBaseBinding(EResTexture, set));
if (isSamplerType(type))
return getFreeSlot(set, baseSamplerBinding);
return getFreeSlot(set, getBaseBinding(EResSampler, set));
if (isUboType(type))
return getFreeSlot(set, baseUboBinding);
return getFreeSlot(set, getBaseBinding(EResUbo, set));
}
return -1;
@ -684,17 +701,17 @@ protected:
// Returns false if the input is too malformed to do this.
bool TIoMapper::addStage(EShLanguage stage, TIntermediate &intermediate, TInfoSink &infoSink, TIoMapResolver *resolver)
{
// Trivial return if there is nothing to do.
if (intermediate.getShiftSamplerBinding() == 0 &&
intermediate.getShiftTextureBinding() == 0 &&
intermediate.getShiftImageBinding() == 0 &&
intermediate.getShiftUboBinding() == 0 &&
intermediate.getShiftSsboBinding() == 0 &&
intermediate.getShiftUavBinding() == 0 &&
intermediate.getResourceSetBinding().empty() &&
intermediate.getAutoMapBindings() == false &&
intermediate.getAutoMapLocations() == false &&
resolver == nullptr)
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())
@ -705,30 +722,15 @@ bool TIoMapper::addStage(EShLanguage stage, TIntermediate &intermediate, TInfoSi
return false;
// if no resolver is provided, use the default resolver with the given shifts and auto map settings
TDefaultIoResolver defaultResolver;
TDefaultHlslIoResolver defaultHlslResolver;
TDefaultIoResolver defaultResolver(intermediate);
TDefaultHlslIoResolver defaultHlslResolver(intermediate);
if (resolver == nullptr) {
TDefaultIoResolverBase* resolverBase;
// TODO: use a passed in IO mapper for this
if (intermediate.usingHlslIoMapping())
resolverBase = &defaultHlslResolver;
resolver = &defaultHlslResolver;
else
resolverBase = &defaultResolver;
resolverBase->baseSamplerBinding = intermediate.getShiftSamplerBinding();
resolverBase->baseTextureBinding = intermediate.getShiftTextureBinding();
resolverBase->baseImageBinding = intermediate.getShiftImageBinding();
resolverBase->baseUboBinding = intermediate.getShiftUboBinding();
resolverBase->baseSsboBinding = intermediate.getShiftSsboBinding();
resolverBase->baseUavBinding = intermediate.getShiftUavBinding();
resolverBase->baseResourceSetBinding = intermediate.getResourceSetBinding();
resolverBase->doAutoBindingMapping = intermediate.getAutoMapBindings();
resolverBase->doAutoLocationMapping = intermediate.getAutoMapLocations();
resolverBase->nextUniformLocation = 0;
resolver = resolverBase;
resolver = &defaultResolver;
}
TVarLiveMap inVarMap, outVarMap, uniformVarMap;