From be2835501915861c316697eb41fbfa077faa6768 Mon Sep 17 00:00:00 2001
From: steve-lunarg <steve_gh@khasekhemwy.net>
Date: Tue, 18 Apr 2017 12:18:01 -0600
Subject: [PATCH] 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.
---
 StandAlone/StandAlone.cpp                     |  26 +-
 Test/baseResults/hlsl.automap.frag.out        |  27 ++
 Test/hlsl.automap.frag                        |  57 ++++
 Test/runtests                                 |   3 +-
 glslang/MachineIndependent/ShaderLang.cpp     |   3 +
 glslang/MachineIndependent/iomapper.cpp       | 270 +++++++++++++-----
 .../MachineIndependent/localintermediate.h    |  10 +-
 glslang/Public/ShaderLang.h                   |   3 +
 8 files changed, 319 insertions(+), 80 deletions(-)
 create mode 100644 Test/baseResults/hlsl.automap.frag.out
 create mode 100644 Test/hlsl.automap.frag

diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp
index 60dbc4dae..3faadecfc 100644
--- a/StandAlone/StandAlone.cpp
+++ b/StandAlone/StandAlone.cpp
@@ -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);
diff --git a/Test/baseResults/hlsl.automap.frag.out b/Test/baseResults/hlsl.automap.frag.out
new file mode 100644
index 000000000..ca7d285be
--- /dev/null
+++ b/Test/baseResults/hlsl.automap.frag.out
@@ -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:
+
diff --git a/Test/hlsl.automap.frag b/Test/hlsl.automap.frag
new file mode 100644
index 000000000..fbb454518
--- /dev/null
+++ b/Test/hlsl.automap.frag
@@ -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;
+}
diff --git a/Test/runtests b/Test/runtests
index efc449d9b..176e73ff6 100755
--- a/Test/runtests
+++ b/Test/runtests
@@ -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
diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp
index 95c16e802..992038338 100644
--- a/glslang/MachineIndependent/ShaderLang.cpp
+++ b/glslang/MachineIndependent/ShaderLang.cpp
@@ -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); }
 
diff --git a/glslang/MachineIndependent/iomapper.cpp b/glslang/MachineIndependent/iomapper.cpp
index 9a6613e24..355cc428e 100644
--- a/glslang/MachineIndependent/iomapper.cpp
+++ b/glslang/MachineIndependent/iomapper.cpp
@@ -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,109 +355,218 @@ struct TDefaultIoResolver : public glslang::TIoMapResolver
         return reserveSlot(set, base);
     }
 
+    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;
+        return 0;
+    }
+
+    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
+    {
+        return -1;
+    }
+    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;
+    }
+
+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()) {
-            int set;
-            if (type.getQualifier().hasSet())
-                set = type.getQualifier().layoutSet;
-            else
-                set = 0;
+            const int set = getLayoutSet(type);
 
-            if (type.getBasicType() == glslang::EbtSampler) {
-                const glslang::TSampler& sampler = type.getSampler();
-                if (sampler.isPureSampler())
-                    return checkEmpty(set, baseSamplerBinding + type.getQualifier().layoutBinding);
+            if (isImageType(type))
+                return checkEmpty(set, baseImageBinding + 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 (isTextureType(type))
+                return checkEmpty(set, baseTextureBinding + type.getQualifier().layoutBinding);
 
-            if (type.getQualifier().storage == EvqBuffer)
+            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
     {
-        int set;
-        if (type.getQualifier().hasSet())
-            set = type.getQualifier().layoutSet;
-        else
-            set = 0;
+        const int set = getLayoutSet(type);
 
         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 (isImageType(type))
+                return reserveSlot(set, baseImageBinding + type.getQualifier().layoutBinding);
+                
+            if (isTextureType(type))
+                return reserveSlot(set, baseTextureBinding + type.getQualifier().layoutBinding);
 
-                if (sampler.isPureSampler())
-                    return reserveSlot(set, baseSamplerBinding + type.getQualifier().layoutBinding);
+            if (isSsboType(type))
+                return reserveSlot(set, baseSsboBinding + type.getQualifier().layoutBinding);
 
-                if (sampler.isTexture())
-                    return reserveSlot(set, baseTextureBinding + type.getQualifier().layoutBinding);
-            }
+            if (isSamplerType(type))
+                return reserveSlot(set, baseSamplerBinding + type.getQualifier().layoutBinding);
 
-            if (type.getQualifier().storage == EvqUniform)
+            if (isUboType(type))
                 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 (isImageType(type))
+                return getFreeSlot(set, baseImageBinding);
 
-            if (type.getQualifier().storage == EvqUniform)
-                return getFreeSlot(set, baseUboBinding);
+            if (isTextureType(type))
+                return getFreeSlot(set, baseTextureBinding);
 
-            if (type.getQualifier().storage == EvqBuffer)
+            if (isSsboType(type))
                 return getFreeSlot(set, baseSsboBinding);
+
+            if (isSamplerType(type))
+                return getFreeSlot(set, baseSamplerBinding);
+
+            if (isUboType(type))
+                return getFreeSlot(set, baseUboBinding);
         }
 
         return -1;
     }
 
-    int resolveSet(EShLanguage /*stage*/, const char* /*name*/, const glslang::TType& type, bool /*is_live*/) override
-    {
-        if (type.getQualifier().hasSet())
-            return type.getQualifier().layoutSet;
-        return 0;
+protected:
+    static bool isImageType(const glslang::TType& type) {
+        return type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage();
     }
 
-    bool validateInOut(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
+    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 resolveInOutLocation(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
+
+    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;
     }
-    int resolveInOutComponent(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
-    {
-        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;
     }
-    int resolveInOutIndex(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
-    {
-        return -1;
+
+    // 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;
+    TDefaultHlslIoResolver defaultHlslResolver;
+
     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();
-
-        resolver = &defaultResolver;
+        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;
diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h
index 0bd9d161f..2fd2e9fd3 100644
--- a/glslang/MachineIndependent/localintermediate.h
+++ b/glslang/MachineIndependent/localintermediate.h
@@ -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;
diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h
index e5e50508e..432329ce0 100644
--- a/glslang/Public/ShaderLang.h
+++ b/glslang/Public/ShaderLang.h
@@ -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);
 
-- 
GitLab