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:
parent
7d67c6cbc2
commit
08a14422c1
9 changed files with 523 additions and 159 deletions
|
|
@ -3222,4 +3222,20 @@ void TIntermediate::performTextureUpgradeAndSamplerRemovalTransformation(TInterm
|
|||
root->traverse(&transform);
|
||||
}
|
||||
|
||||
const char* TIntermediate::getResourceName(TResourceType res)
|
||||
{
|
||||
switch (res) {
|
||||
case EResSampler: return "shift-sampler-binding";
|
||||
case EResTexture: return "shift-texture-binding";
|
||||
case EResImage: return "shift-image-binding";
|
||||
case EResUbo: return "shift-UBO-binding";
|
||||
case EResSsbo: return "shift-ssbo-binding";
|
||||
case EResUav: return "shift-uav-binding";
|
||||
default:
|
||||
assert(0); // internal error: should only be called with valid resource types.
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // end namespace glslang
|
||||
|
|
|
|||
|
|
@ -1661,20 +1661,30 @@ void TShader::addProcesses(const std::vector<std::string>& p)
|
|||
intermediate->addProcesses(p);
|
||||
}
|
||||
|
||||
// Set binding base for given resource type
|
||||
void TShader::setShiftBinding(TResourceType res, unsigned int base) {
|
||||
intermediate->setShiftBinding(res, base);
|
||||
}
|
||||
|
||||
// Set binding base for given resource type for a given binding set.
|
||||
void TShader::setShiftBindingForSet(TResourceType res, unsigned int set, unsigned int base) {
|
||||
intermediate->setShiftBindingForSet(res, set, base);
|
||||
}
|
||||
|
||||
// Set binding base for sampler types
|
||||
void TShader::setShiftSamplerBinding(unsigned int base) { intermediate->setShiftSamplerBinding(base); }
|
||||
void TShader::setShiftSamplerBinding(unsigned int base) { setShiftBinding(EResSampler, base); }
|
||||
// Set binding base for texture types (SRV)
|
||||
void TShader::setShiftTextureBinding(unsigned int base) { intermediate->setShiftTextureBinding(base); }
|
||||
void TShader::setShiftTextureBinding(unsigned int base) { setShiftBinding(EResTexture, base); }
|
||||
// Set binding base for image types
|
||||
void TShader::setShiftImageBinding(unsigned int base) { intermediate->setShiftImageBinding(base); }
|
||||
void TShader::setShiftImageBinding(unsigned int base) { setShiftBinding(EResImage, base); }
|
||||
// Set binding base for uniform buffer objects (CBV)
|
||||
void TShader::setShiftUboBinding(unsigned int base) { intermediate->setShiftUboBinding(base); }
|
||||
void TShader::setShiftUboBinding(unsigned int base) { setShiftBinding(EResUbo, base); }
|
||||
// Synonym for setShiftUboBinding, to match HLSL language.
|
||||
void TShader::setShiftCbufferBinding(unsigned int base) { intermediate->setShiftUboBinding(base); }
|
||||
void TShader::setShiftCbufferBinding(unsigned int base) { setShiftBinding(EResUbo, base); }
|
||||
// Set binding base for UAV (unordered access view)
|
||||
void TShader::setShiftUavBinding(unsigned int base) { intermediate->setShiftUavBinding(base); }
|
||||
void TShader::setShiftUavBinding(unsigned int base) { setShiftBinding(EResUav, base); }
|
||||
// Set binding base for SSBOs
|
||||
void TShader::setShiftSsboBinding(unsigned int base) { intermediate->setShiftSsboBinding(base); }
|
||||
void TShader::setShiftSsboBinding(unsigned int base) { setShiftBinding(EResSsbo, base); }
|
||||
// Enables binding automapping using TIoMapper
|
||||
void TShader::setAutoMapBindings(bool map) { intermediate->setAutoMapBindings(map); }
|
||||
// Fragile: currently within one stage: simple auto-assignment of location
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <array>
|
||||
|
||||
class TInfoSink;
|
||||
|
||||
|
|
@ -221,12 +222,6 @@ public:
|
|||
layoutOverrideCoverage(false),
|
||||
geoPassthroughEXT(false),
|
||||
#endif
|
||||
shiftSamplerBinding(0),
|
||||
shiftTextureBinding(0),
|
||||
shiftImageBinding(0),
|
||||
shiftUboBinding(0),
|
||||
shiftSsboBinding(0),
|
||||
shiftUavBinding(0),
|
||||
autoMapBindings(false),
|
||||
autoMapLocations(false),
|
||||
flattenUniformArrays(false),
|
||||
|
|
@ -244,6 +239,8 @@ public:
|
|||
localSizeSpecId[1] = TQualifier::layoutNotSet;
|
||||
localSizeSpecId[2] = TQualifier::layoutNotSet;
|
||||
xfbBuffers.resize(TQualifier::layoutXfbBufferEnd);
|
||||
|
||||
shiftBinding.fill(0);
|
||||
}
|
||||
void setLimits(const TBuiltInResource& r) { resources = r; }
|
||||
|
||||
|
|
@ -263,42 +260,39 @@ public:
|
|||
const std::string& getEntryPointName() const { return entryPointName; }
|
||||
const std::string& getEntryPointMangledName() const { return entryPointMangledName; }
|
||||
|
||||
void setShiftSamplerBinding(unsigned int shift)
|
||||
void setShiftBinding(TResourceType res, unsigned int shift)
|
||||
{
|
||||
shiftSamplerBinding = shift;
|
||||
processes.addIfNonZero("shift-sampler-binding", shift);
|
||||
shiftBinding[res] = shift;
|
||||
|
||||
const char* name = getResourceName(res);
|
||||
if (name != nullptr)
|
||||
processes.addIfNonZero(name, shift);
|
||||
}
|
||||
unsigned int getShiftSamplerBinding() const { return shiftSamplerBinding; }
|
||||
void setShiftTextureBinding(unsigned int shift)
|
||||
|
||||
unsigned int getShiftBinding(TResourceType res) const { return shiftBinding[res]; }
|
||||
|
||||
void setShiftBindingForSet(TResourceType res, unsigned int set, unsigned int shift)
|
||||
{
|
||||
shiftTextureBinding = shift;
|
||||
processes.addIfNonZero("shift-texture-binding", shift);
|
||||
if (shift == 0) // ignore if there's no shift: it's a no-op.
|
||||
return;
|
||||
|
||||
shiftBindingForSet[res][set] = shift;
|
||||
|
||||
const char* name = getResourceName(res);
|
||||
if (name != nullptr) {
|
||||
processes.addProcess(name);
|
||||
processes.addArgument(set);
|
||||
processes.addArgument(shift);
|
||||
}
|
||||
}
|
||||
unsigned int getShiftTextureBinding() const { return shiftTextureBinding; }
|
||||
void setShiftImageBinding(unsigned int shift)
|
||||
|
||||
int getShiftBindingForSet(TResourceType res, unsigned int set) const
|
||||
{
|
||||
shiftImageBinding = shift;
|
||||
processes.addIfNonZero("shift-image-binding", shift);
|
||||
const auto shift = shiftBindingForSet[res].find(set);
|
||||
return shift == shiftBindingForSet[res].end() ? -1 : shift->second;
|
||||
}
|
||||
unsigned int getShiftImageBinding() const { return shiftImageBinding; }
|
||||
void setShiftUboBinding(unsigned int shift)
|
||||
{
|
||||
shiftUboBinding = shift;
|
||||
processes.addIfNonZero("shift-UBO-binding", shift);
|
||||
}
|
||||
unsigned int getShiftUboBinding() const { return shiftUboBinding; }
|
||||
void setShiftSsboBinding(unsigned int shift)
|
||||
{
|
||||
shiftSsboBinding = shift;
|
||||
processes.addIfNonZero("shift-ssbo-binding", shift);
|
||||
}
|
||||
unsigned int getShiftSsboBinding() const { return shiftSsboBinding; }
|
||||
void setShiftUavBinding(unsigned int shift)
|
||||
{
|
||||
shiftUavBinding = shift;
|
||||
processes.addIfNonZero("shift-uav-binding", shift);
|
||||
}
|
||||
unsigned int getShiftUavBinding() const { return shiftUavBinding; }
|
||||
bool hasShiftBindingForSet(TResourceType res) const { return !shiftBindingForSet[res].empty(); }
|
||||
|
||||
void setResourceSetBinding(const std::vector<std::string>& shift)
|
||||
{
|
||||
resourceSetBinding = shift;
|
||||
|
|
@ -638,6 +632,7 @@ protected:
|
|||
void pushSelector(TIntermSequence&, const TMatrixSelector&, const TSourceLoc&);
|
||||
bool specConstantPropagates(const TIntermTyped&, const TIntermTyped&);
|
||||
void performTextureUpgradeAndSamplerRemovalTransformation(TIntermNode* root);
|
||||
static const char* getResourceName(TResourceType);
|
||||
|
||||
const EShLanguage language; // stage, known at construction time
|
||||
EShSource source; // source language, known a bit later
|
||||
|
|
@ -678,12 +673,13 @@ protected:
|
|||
bool geoPassthroughEXT;
|
||||
#endif
|
||||
|
||||
unsigned int shiftSamplerBinding;
|
||||
unsigned int shiftTextureBinding;
|
||||
unsigned int shiftImageBinding;
|
||||
unsigned int shiftUboBinding;
|
||||
unsigned int shiftSsboBinding;
|
||||
unsigned int shiftUavBinding;
|
||||
// Base shift values
|
||||
std::array<unsigned int, EResCount> shiftBinding;
|
||||
|
||||
// Per-descriptor-set shift values
|
||||
typedef std::map<int, int> TDescriptorSetShift;
|
||||
TDescriptorSetShift shiftBindingForSet[EResCount];
|
||||
|
||||
std::vector<std::string> resourceSetBinding;
|
||||
bool autoMapBindings;
|
||||
bool autoMapLocations;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue