WIP: HLSL: hlsl register class iomapping

Adds --hlsl-iomap option to perform IO mapping in HLSL register space.

--shift-cbuffer-binding is now a synonym for --shift-ubo-binding.

The idea way to do this seems to be passing in a dedicated IO resolver, but
that would require more intrusive restructuring, so maybe best for its
own PR.

The TDefaultHlslIoResolver class and the former TDefaultIoResolver class
share quite a bit of mechanism in a common base class.

TODO: tbuffers are landing in the wrong register class, which needs some
investigation.  They're either wrong upstream, or the detection in the
resolver is wrong.
This commit is contained in:
steve-lunarg 2017-04-18 12:18:01 -06:00
parent ba5cc2fafa
commit be28355019
8 changed files with 335 additions and 96 deletions

View File

@ -87,6 +87,7 @@ enum TOptions {
EOptionNoStorageFormat = (1 << 21),
EOptionKeepUncalled = (1 << 22),
EOptionHlslOffsets = (1 << 23),
EOptionHlslIoMapping = (1 << 24),
};
//
@ -166,6 +167,7 @@ std::array<unsigned int, EShLangCount> baseTextureBinding;
std::array<unsigned int, EShLangCount> baseImageBinding;
std::array<unsigned int, EShLangCount> baseUboBinding;
std::array<unsigned int, EShLangCount> baseSsboBinding;
std::array<unsigned int, EShLangCount> baseUavBinding;
//
// Create the default name for saving a binary if -o is not provided.
@ -256,6 +258,7 @@ void ProcessArguments(std::vector<std::unique_ptr<glslang::TWorkItem>>& workItem
baseImageBinding.fill(0);
baseUboBinding.fill(0);
baseSsboBinding.fill(0);
baseUavBinding.fill(0);
ExecutableName = argv[0];
workItems.reserve(argc);
@ -285,12 +288,19 @@ void ProcessArguments(std::vector<std::unique_ptr<glslang::TWorkItem>>& workItem
ProcessBindingBase(argc, argv, baseImageBinding);
} else if (lowerword == "shift-ubo-bindings" || // synonyms
lowerword == "shift-ubo-binding" ||
lowerword == "sub") {
lowerword == "shift-cbuffer-bindings" ||
lowerword == "shift-cbuffer-binding" ||
lowerword == "sub" ||
lowerword == "scb") {
ProcessBindingBase(argc, argv, baseUboBinding);
} else if (lowerword == "shift-ssbo-bindings" || // synonyms
lowerword == "shift-ssbo-binding" ||
lowerword == "sbb") {
ProcessBindingBase(argc, argv, baseSsboBinding);
} else if (lowerword == "shift-uav-bindings" || // synonyms
lowerword == "shift-uav-binding" ||
lowerword == "suavb") {
ProcessBindingBase(argc, argv, baseUavBinding);
} else if (lowerword == "auto-map-bindings" || // synonyms
lowerword == "auto-map-binding" ||
lowerword == "amb") {
@ -326,6 +336,10 @@ void ProcessArguments(std::vector<std::unique_ptr<glslang::TWorkItem>>& workItem
Options |= EOptionKeepUncalled;
} else if (lowerword == "hlsl-offsets") {
Options |= EOptionHlslOffsets;
} else if (lowerword == "hlsl-iomap" ||
lowerword == "hlsl-iomapper" ||
lowerword == "hlsl-iomapping") {
Options |= EOptionHlslIoMapping;
} else {
usage();
}
@ -577,9 +591,13 @@ void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits)
shader->setShiftImageBinding(baseImageBinding[compUnit.stage]);
shader->setShiftUboBinding(baseUboBinding[compUnit.stage]);
shader->setShiftSsboBinding(baseSsboBinding[compUnit.stage]);
shader->setShiftUavBinding(baseUavBinding[compUnit.stage]);
shader->setFlattenUniformArrays((Options & EOptionFlattenUniformArrays) != 0);
shader->setNoStorageFormat((Options & EOptionNoStorageFormat) != 0);
if (Options & EOptionHlslIoMapping)
shader->setHlslIoMapping(true);
if (Options & EOptionAutoMapBindings)
shader->setAutoMapBindings(true);
@ -982,11 +1000,15 @@ void usage()
" --sib [stage] num synonym for --shift-image-binding\n"
"\n"
" --shift-UBO-binding [stage] num set base binding number for UBOs\n"
" --shift-cbuffer-binding [stage] num synonym for --shift-UBO-binding\n"
" --sub [stage] num synonym for --shift-UBO-binding\n"
"\n"
" --shift-ssbo-binding [stage] num set base binding number for SSBOs\n"
" --sbb [stage] num synonym for --shift-ssbo-binding\n"
"\n"
" --shift-uav-binding [stage] num set base binding number for UAVs\n"
" --suavb [stage] num synonym for --shift-uav-binding\n"
"\n"
" --auto-map-bindings automatically bind uniform variables without\n"
" explicit bindings.\n"
" --amb synonym for --auto-map-bindings\n"
@ -1009,6 +1031,8 @@ void usage()
"\n"
" --hlsl-offsets Allow block offsets to follow HLSL rules instead of GLSL rules.\n"
" Works independently of source language.\n"
"\n"
" --hlsl-iomap Perform IO mapping in HLSL register space.\n"
);
exit(EFailUsage);

View File

@ -0,0 +1,27 @@
hlsl.automap.frag
Uniform reflection:
t1: offset -1, type 8b5d, size 1, index -1, binding 11
t2: offset -1, type 8b5e, size 1, index -1, binding 12
t3: offset -1, type 8b5f, size 1, index -1, binding 13
t4.@data: offset 0, type 8b52, size 1, index 0, binding -1
t5.@data: offset 0, type 1405, size 0, index 1, binding -1
t6: offset -1, type 8dc2, size 1, index -1, binding 16
s1: offset -1, type 0, size 1, index -1, binding 31
s2: offset -1, type 0, size 1, index -1, binding 32
u1: offset -1, type 904c, size 1, index -1, binding 41
u2: offset -1, type 904d, size 1, index -1, binding 42
u3: offset -1, type 904e, size 1, index -1, binding 43
u4: offset -1, type 9051, size 1, index -1, binding 44
u5.@data: offset 0, type 1405, size 0, index 2, binding -1
u6.@data: offset 0, type 1406, size 1, index 3, binding -1
cb1: offset 0, type 1404, size 1, index 4, binding -1
Uniform block reflection:
t4: offset -1, type ffffffff, size 0, index -1, binding 14
t5: offset -1, type ffffffff, size 0, index -1, binding 15
u5: offset -1, type ffffffff, size 0, index -1, binding 45
u6: offset -1, type ffffffff, size 0, index -1, binding 46
cb: offset -1, type ffffffff, size 4, index -1, binding 51
Vertex attribute reflection:

57
Test/hlsl.automap.frag Normal file
View File

@ -0,0 +1,57 @@
// Test register class offsets for different resource types
SamplerState s1 : register(s1);
SamplerComparisonState s2 : register(s2);
Texture1D <float4> t1 : register(t1);
Texture2D <float4> t2 : register(t2);
Texture3D <float4> t3 : register(t3);
StructuredBuffer<float4> t4 : register(t4);
ByteAddressBuffer t5 : register(t5);
Buffer<float4> t6 : register(t6);
RWTexture1D <float4> u1 : register(u1);
RWTexture2D <float4> u2 : register(u2);
RWTexture3D <float4> u3 : register(u3);
RWBuffer <float> u4 : register(u4);
RWByteAddressBuffer u5 : register(u5);
RWStructuredBuffer<float> u6 : register(u6);
AppendStructuredBuffer<float> u7 : register(u7);
ConsumeStructuredBuffer<float> u8 : register(u8);
cbuffer cb : register(b1) {
int cb1;
};
// tbuffer tb : register(t7) {
// int tb1;
// };
float4 main() : SV_Target0
{
t1;
t2;
t3;
t4[0];
t5.Load(0);
t6;
s1;
s2;
u1;
u2;
u3;
u4[0];
u5.Load(0);
u6[0];
u7[0];
u8[0];
cb1;
// tb1; TODO: wrong type?
return 0;
}

View File

@ -35,7 +35,8 @@ $EXE -D -e flizv -l -q -C -V hlsl.reflection.vert > $TARGETDIR/hlsl.reflection.v
diff -b $BASEDIR/hlsl.reflection.vert.out $TARGETDIR/hlsl.reflection.vert.out || HASERROR=1
$EXE -D -e main -l -q -C -V hlsl.reflection.binding.frag > $TARGETDIR/hlsl.reflection.binding.frag.out
diff -b $BASEDIR/hlsl.reflection.binding.frag.out $TARGETDIR/hlsl.reflection.binding.frag.out || HASERROR=1
$EXE -D -e main -l -q --hlsl-iomap --auto-map-bindings --stb 10 --sbb 20 --ssb 30 --suavb 40 --scb 50 -D -V -e main hlsl.automap.frag > $TARGETDIR/hlsl.automap.frag.out
diff -b $BASEDIR/hlsl.automap.frag.out $TARGETDIR/hlsl.automap.frag.out || HASERROR=1
#
# multi-threaded test

View File

@ -1559,8 +1559,11 @@ void TShader::setShiftSamplerBinding(unsigned int base) { intermediate->setShift
void TShader::setShiftTextureBinding(unsigned int base) { intermediate->setShiftTextureBinding(base); }
void TShader::setShiftImageBinding(unsigned int base) { intermediate->setShiftImageBinding(base); }
void TShader::setShiftUboBinding(unsigned int base) { intermediate->setShiftUboBinding(base); }
void TShader::setShiftCbufferBinding(unsigned int base) { intermediate->setShiftUboBinding(base); }
void TShader::setShiftUavBinding(unsigned int base) { intermediate->setShiftUavBinding(base); }
void TShader::setShiftSsboBinding(unsigned int base) { intermediate->setShiftSsboBinding(base); }
void TShader::setAutoMapBindings(bool map) { intermediate->setAutoMapBindings(map); }
void TShader::setHlslIoMapping(bool hlslIoMap) { intermediate->setHlslIoMapping(hlslIoMap); }
void TShader::setFlattenUniformArrays(bool flatten) { intermediate->setFlattenUniformArrays(flatten); }
void TShader::setNoStorageFormat(bool useUnknownFormat) { intermediate->setNoStorageFormat(useUnknownFormat); }

View File

@ -1,5 +1,5 @@
//
// Copyright (C) 2016 LunarG, Inc.
// Copyright (C) 2016-2017 LunarG, Inc.
//
// All rights reserved.
//
@ -310,20 +310,15 @@ private:
TResolverInOutAdaptor& operator=(TResolverInOutAdaptor&);
};
/*
* 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.
*/
struct TDefaultIoResolver : public glslang::TIoMapResolver
// 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;
bool doAutoMapping;
typedef std::vector<int> TSlotSet;
typedef std::unordered_map<int, TSlotSet> TSlotSetMap;
@ -360,83 +355,9 @@ struct TDefaultIoResolver : public glslang::TIoMapResolver
return reserveSlot(set, base);
}
bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool /*is_live*/) override
{
if (type.getQualifier().hasBinding()) {
int set;
if (type.getQualifier().hasSet())
set = type.getQualifier().layoutSet;
else
set = 0;
virtual bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool /*is_live*/) override = 0;
if (type.getBasicType() == glslang::EbtSampler) {
const glslang::TSampler& sampler = type.getSampler();
if (sampler.isPureSampler())
return checkEmpty(set, baseSamplerBinding + type.getQualifier().layoutBinding);
if (sampler.isTexture())
return checkEmpty(set, baseTextureBinding + type.getQualifier().layoutBinding);
}
if (type.getQualifier().storage == EvqUniform)
return checkEmpty(set, baseUboBinding + type.getQualifier().layoutBinding);
if (type.getQualifier().storage == EvqBuffer)
return checkEmpty(set, baseSsboBinding + type.getQualifier().layoutBinding);
}
return true;
}
int resolveBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool is_live) override
{
int set;
if (type.getQualifier().hasSet())
set = type.getQualifier().layoutSet;
else
set = 0;
if (type.getQualifier().hasBinding()) {
if (type.getBasicType() == glslang::EbtSampler) {
const glslang::TSampler& sampler = type.getSampler();
if (sampler.isImage())
return reserveSlot(set, baseImageBinding + type.getQualifier().layoutBinding);
if (sampler.isPureSampler())
return reserveSlot(set, baseSamplerBinding + type.getQualifier().layoutBinding);
if (sampler.isTexture())
return reserveSlot(set, baseTextureBinding + type.getQualifier().layoutBinding);
}
if (type.getQualifier().storage == EvqUniform)
return reserveSlot(set, baseUboBinding + type.getQualifier().layoutBinding);
if (type.getQualifier().storage == EvqBuffer)
return reserveSlot(set, baseSsboBinding + type.getQualifier().layoutBinding);
} else if (is_live && doAutoMapping) {
// 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 (type.getBasicType() == glslang::EbtSampler) {
const glslang::TSampler& sampler = type.getSampler();
if (sampler.isImage())
return getFreeSlot(set, baseImageBinding);
if (sampler.isPureSampler())
return getFreeSlot(set, baseSamplerBinding);
if (sampler.isTexture())
return getFreeSlot(set, baseTextureBinding);
}
if (type.getQualifier().storage == EvqUniform)
return getFreeSlot(set, baseUboBinding);
if (type.getQualifier().storage == EvqBuffer)
return getFreeSlot(set, baseSsboBinding);
}
return -1;
}
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
{
@ -461,8 +382,191 @@ struct TDefaultIoResolver : public glslang::TIoMapResolver
{
return -1;
}
protected:
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();
}
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
{
bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool /*is_live*/) override
{
if (type.getQualifier().hasBinding()) {
const int set = getLayoutSet(type);
if (isImageType(type))
return checkEmpty(set, baseImageBinding + type.getQualifier().layoutBinding);
if (isTextureType(type))
return checkEmpty(set, baseTextureBinding + type.getQualifier().layoutBinding);
if (isSsboType(type))
return checkEmpty(set, baseSsboBinding + type.getQualifier().layoutBinding);
if (isSamplerType(type))
return checkEmpty(set, baseSamplerBinding + type.getQualifier().layoutBinding);
if (isUboType(type))
return checkEmpty(set, baseUboBinding + type.getQualifier().layoutBinding);
}
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 (isImageType(type))
return reserveSlot(set, baseImageBinding + type.getQualifier().layoutBinding);
if (isTextureType(type))
return reserveSlot(set, baseTextureBinding + type.getQualifier().layoutBinding);
if (isSsboType(type))
return reserveSlot(set, baseSsboBinding + type.getQualifier().layoutBinding);
if (isSamplerType(type))
return reserveSlot(set, baseSamplerBinding + type.getQualifier().layoutBinding);
if (isUboType(type))
return reserveSlot(set, baseUboBinding + type.getQualifier().layoutBinding);
} else if (is_live && doAutoMapping) {
// 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);
if (isTextureType(type))
return getFreeSlot(set, baseTextureBinding);
if (isSsboType(type))
return getFreeSlot(set, baseSsboBinding);
if (isSamplerType(type))
return getFreeSlot(set, baseSamplerBinding);
if (isUboType(type))
return getFreeSlot(set, baseUboBinding);
}
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;
}
};
struct TDefaultHlslIoResolver : public TDefaultIoResolverBase
{
bool validateBinding(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool /*is_live*/) override
{
if (type.getQualifier().hasBinding()) {
const int set = getLayoutSet(type);
// Use Uav binding if requested: else will pass through to old behavior
if (isUavType(type))
return checkEmpty(set, baseUavBinding + type.getQualifier().layoutBinding);
if (isSrvType(type))
return checkEmpty(set, baseTextureBinding + type.getQualifier().layoutBinding);
if (isSamplerType(type))
return checkEmpty(set, baseSamplerBinding + type.getQualifier().layoutBinding);
if (isUboType(type))
return checkEmpty(set, baseUboBinding + type.getQualifier().layoutBinding);
}
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, baseUavBinding + type.getQualifier().layoutBinding);
if (isSrvType(type))
return reserveSlot(set, baseTextureBinding + type.getQualifier().layoutBinding);
if (isSamplerType(type))
return reserveSlot(set, baseSamplerBinding + type.getQualifier().layoutBinding);
if (isUboType(type))
return reserveSlot(set, baseUboBinding + type.getQualifier().layoutBinding);
} else if (is_live && doAutoMapping) {
// 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);
if (isSrvType(type))
return getFreeSlot(set, baseTextureBinding);
if (isSamplerType(type))
return getFreeSlot(set, baseSamplerBinding);
if (isUboType(type))
return getFreeSlot(set, baseUboBinding);
}
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.
//
@ -475,6 +579,7 @@ bool TIoMapper::addStage(EShLanguage stage, TIntermediate &intermediate, TInfoSi
intermediate.getShiftImageBinding() == 0 &&
intermediate.getShiftUboBinding() == 0 &&
intermediate.getShiftSsboBinding() == 0 &&
intermediate.getShiftUavBinding() == 0 &&
intermediate.getAutoMapBindings() == false &&
resolver == nullptr)
return true;
@ -488,15 +593,26 @@ bool TIoMapper::addStage(EShLanguage stage, TIntermediate &intermediate, TInfoSi
// if no resolver is provided, use the default resolver with the given shifts and auto map settings
TDefaultIoResolver defaultResolver;
if (resolver == nullptr) {
defaultResolver.baseSamplerBinding = intermediate.getShiftSamplerBinding();
defaultResolver.baseTextureBinding = intermediate.getShiftTextureBinding();
defaultResolver.baseImageBinding = intermediate.getShiftImageBinding();
defaultResolver.baseUboBinding = intermediate.getShiftUboBinding();
defaultResolver.baseSsboBinding = intermediate.getShiftSsboBinding();
defaultResolver.doAutoMapping = intermediate.getAutoMapBindings();
TDefaultHlslIoResolver defaultHlslResolver;
resolver = &defaultResolver;
if (resolver == nullptr) {
TDefaultIoResolverBase* resolverBase;
// TODO: use a passed in IO mapper for this
if (intermediate.usingHlslIoMapping())
resolverBase = &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->doAutoMapping = intermediate.getAutoMapBindings();
resolver = resolverBase;
}
TVarLiveMap inVarMap, outVarMap, uniformVarMap;

View File

@ -175,11 +175,13 @@ public:
shiftImageBinding(0),
shiftUboBinding(0),
shiftSsboBinding(0),
shiftUavBinding(0),
autoMapBindings(false),
flattenUniformArrays(false),
useUnknownFormat(false),
hlslOffsets(false),
useStorageBuffer(false)
useStorageBuffer(false),
hlslIoMapping(false)
{
localSize[0] = 1;
localSize[1] = 1;
@ -212,6 +214,8 @@ public:
unsigned int getShiftUboBinding() const { return shiftUboBinding; }
void setShiftSsboBinding(unsigned int shift) { shiftSsboBinding = shift; }
unsigned int getShiftSsboBinding() const { return shiftSsboBinding; }
void setShiftUavBinding(unsigned int shift) { shiftUavBinding = shift; }
unsigned int getShiftUavBinding() const { return shiftUavBinding; }
void setAutoMapBindings(bool map) { autoMapBindings = map; }
bool getAutoMapBindings() const { return autoMapBindings; }
void setFlattenUniformArrays(bool flatten) { flattenUniformArrays = flatten; }
@ -222,6 +226,8 @@ public:
bool usingHlslOFfsets() const { return hlslOffsets; }
void setUseStorageBuffer() { useStorageBuffer = true; }
bool usingStorageBuffer() const { return useStorageBuffer; }
void setHlslIoMapping(bool b) { hlslIoMapping = b; }
bool usingHlslIoMapping() { return hlslIoMapping; }
void setVersion(int v) { version = v; }
int getVersion() const { return version; }
@ -505,11 +511,13 @@ protected:
unsigned int shiftImageBinding;
unsigned int shiftUboBinding;
unsigned int shiftSsboBinding;
unsigned int shiftUavBinding;
bool autoMapBindings;
bool flattenUniformArrays;
bool useUnknownFormat;
bool hlslOffsets;
bool useStorageBuffer;
bool hlslIoMapping;
typedef std::list<TCall> TGraph;
TGraph callGraph;

View File

@ -303,8 +303,11 @@ public:
void setShiftTextureBinding(unsigned int base);
void setShiftImageBinding(unsigned int base);
void setShiftUboBinding(unsigned int base);
void setShiftUavBinding(unsigned int base);
void setShiftCbufferBinding(unsigned int base); // synonym for setShiftUboBinding
void setShiftSsboBinding(unsigned int base);
void setAutoMapBindings(bool map);
void setHlslIoMapping(bool hlslIoMap);
void setFlattenUniformArrays(bool flatten);
void setNoStorageFormat(bool useUnknownFormat);