ES31: Support images in the compiler on D3D backend.

BUG=angleproject:1987
TEST=angle_end2end_tests

Change-Id: I83f5f9ffda7e676a8f98b963d1f1c50e9463faf4
Reviewed-on: https://chromium-review.googlesource.com/706247
Commit-Queue: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/compiler/translator/BaseTypes.h b/src/compiler/translator/BaseTypes.h
index eda024e..470b282 100644
--- a/src/compiler/translator/BaseTypes.h
+++ b/src/compiler/translator/BaseTypes.h
@@ -477,6 +477,106 @@
     return false;
 }
 
+inline bool IsImage2D(TBasicType type)
+{
+    switch (type)
+    {
+        case EbtImage2D:
+        case EbtIImage2D:
+        case EbtUImage2D:
+            return true;
+        case EbtImage3D:
+        case EbtIImage3D:
+        case EbtUImage3D:
+        case EbtImage2DArray:
+        case EbtIImage2DArray:
+        case EbtUImage2DArray:
+        case EbtImageCube:
+        case EbtIImageCube:
+        case EbtUImageCube:
+            return false;
+        default:
+            assert(!IsImage(type));
+    }
+
+    return false;
+}
+
+inline bool IsImage3D(TBasicType type)
+{
+    switch (type)
+    {
+        case EbtImage3D:
+        case EbtIImage3D:
+        case EbtUImage3D:
+            return true;
+        case EbtImage2D:
+        case EbtIImage2D:
+        case EbtUImage2D:
+        case EbtImage2DArray:
+        case EbtIImage2DArray:
+        case EbtUImage2DArray:
+        case EbtImageCube:
+        case EbtIImageCube:
+        case EbtUImageCube:
+            return false;
+        default:
+            assert(!IsImage(type));
+    }
+
+    return false;
+}
+
+inline bool IsImage2DArray(TBasicType type)
+{
+    switch (type)
+    {
+        case EbtImage2DArray:
+        case EbtIImage2DArray:
+        case EbtUImage2DArray:
+            return true;
+        case EbtImage2D:
+        case EbtIImage2D:
+        case EbtUImage2D:
+        case EbtImage3D:
+        case EbtIImage3D:
+        case EbtUImage3D:
+        case EbtImageCube:
+        case EbtIImageCube:
+        case EbtUImageCube:
+            return false;
+        default:
+            assert(!IsImage(type));
+    }
+
+    return false;
+}
+
+inline bool IsImageCube(TBasicType type)
+{
+    switch (type)
+    {
+        case EbtImageCube:
+        case EbtIImageCube:
+        case EbtUImageCube:
+            return true;
+        case EbtImage2D:
+        case EbtIImage2D:
+        case EbtUImage2D:
+        case EbtImage3D:
+        case EbtIImage3D:
+        case EbtUImage3D:
+        case EbtImage2DArray:
+        case EbtIImage2DArray:
+        case EbtUImage2DArray:
+            return false;
+        default:
+            assert(!IsImage(type));
+    }
+
+    return false;
+}
+
 inline bool IsInteger(TBasicType type)
 {
     return type == EbtInt || type == EbtUInt;
diff --git a/src/compiler/translator/ImageFunctionHLSL.cpp b/src/compiler/translator/ImageFunctionHLSL.cpp
new file mode 100644
index 0000000..40b5e1f
--- /dev/null
+++ b/src/compiler/translator/ImageFunctionHLSL.cpp
@@ -0,0 +1,304 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ImageFunctionHLSL: Class for writing implementations of ESSL image functions into HLSL output.
+//
+
+#include "compiler/translator/ImageFunctionHLSL.h"
+#include "compiler/translator/UtilsHLSL.h"
+
+namespace sh
+{
+
+// static
+void ImageFunctionHLSL::OutputImageFunctionArgumentList(
+    TInfoSinkBase &out,
+    const ImageFunctionHLSL::ImageFunction &imageFunction)
+{
+    if (imageFunction.readonly)
+    {
+        out << TextureString(imageFunction.image, imageFunction.imageInternalFormat) << " tex";
+    }
+    else
+    {
+        out << RWTextureString(imageFunction.image, imageFunction.imageInternalFormat) << " tex";
+    }
+
+    if (imageFunction.method == ImageFunctionHLSL::ImageFunction::Method::LOAD ||
+        imageFunction.method == ImageFunctionHLSL::ImageFunction::Method::STORE)
+    {
+        switch (imageFunction.image)
+        {
+            case EbtImage2D:
+            case EbtIImage2D:
+            case EbtUImage2D:
+                out << ", int2 p";
+                break;
+            case EbtImage3D:
+            case EbtIImage3D:
+            case EbtUImage3D:
+            case EbtImageCube:
+            case EbtIImageCube:
+            case EbtUImageCube:
+            case EbtImage2DArray:
+            case EbtIImage2DArray:
+            case EbtUImage2DArray:
+                out << ", int3 p";
+                break;
+            default:
+                UNREACHABLE();
+        }
+
+        if (imageFunction.method == ImageFunctionHLSL::ImageFunction::Method::STORE)
+        {
+            switch (imageFunction.image)
+            {
+                case EbtImage2D:
+                case EbtImage3D:
+                case EbtImageCube:
+                case EbtImage2DArray:
+                    out << ", float4 data";
+                    break;
+                case EbtIImage2D:
+                case EbtIImage3D:
+                case EbtIImageCube:
+                case EbtIImage2DArray:
+                    out << ", int4 data";
+                    break;
+                case EbtUImage2D:
+                case EbtUImage3D:
+                case EbtUImageCube:
+                case EbtUImage2DArray:
+                    out << ", uint4 data";
+                    break;
+                default:
+                    UNREACHABLE();
+            }
+        }
+    }
+}
+
+// static
+void ImageFunctionHLSL::OutputImageSizeFunctionBody(
+    TInfoSinkBase &out,
+    const ImageFunctionHLSL::ImageFunction &imageFunction,
+    const TString &imageReference)
+{
+    if (IsImage3D(imageFunction.image) || IsImage2DArray(imageFunction.image) ||
+        IsImageCube(imageFunction.image))
+    {
+        // "depth" stores either the number of layers in an array texture or 3D depth
+        out << "    uint width; uint height; uint depth;\n"
+            << "    " << imageReference << ".GetDimensions(width, height, depth);\n";
+    }
+    else if (IsImage2D(imageFunction.image))
+    {
+        out << "    uint width; uint height;\n"
+            << "    " << imageReference << ".GetDimensions(width, height);\n";
+    }
+    else
+        UNREACHABLE();
+
+    if (strcmp(imageFunction.getReturnType(), "int3") == 0)
+    {
+        out << "    return int3(width, height, depth);\n";
+    }
+    else
+    {
+        out << "    return int2(width, height);\n";
+    }
+}
+
+// static
+void ImageFunctionHLSL::OutputImageLoadFunctionBody(
+    TInfoSinkBase &out,
+    const ImageFunctionHLSL::ImageFunction &imageFunction,
+    const TString &imageReference)
+{
+    if (IsImage3D(imageFunction.image) || IsImage2DArray(imageFunction.image) ||
+        IsImageCube(imageFunction.image))
+    {
+        out << "    return " << imageReference << "[uint3(p.x, p.y, p.z)];\n";
+    }
+    else if (IsImage2D(imageFunction.image))
+    {
+        out << "    return " << imageReference << "[uint2(p.x, p.y)];\n";
+    }
+    else
+        UNREACHABLE();
+}
+
+// static
+void ImageFunctionHLSL::OutputImageStoreFunctionBody(
+    TInfoSinkBase &out,
+    const ImageFunctionHLSL::ImageFunction &imageFunction,
+    const TString &imageReference)
+{
+    if (IsImage3D(imageFunction.image) || IsImage2DArray(imageFunction.image) ||
+        IsImage2D(imageFunction.image) || IsImageCube(imageFunction.image))
+    {
+        out << "    " << imageReference << "[p] = data;\n";
+    }
+    else
+        UNREACHABLE();
+}
+
+TString ImageFunctionHLSL::ImageFunction::name() const
+{
+    TString name = "gl_image";
+    if (readonly)
+    {
+        name += TextureTypeSuffix(image, imageInternalFormat);
+    }
+    else
+    {
+        name += RWTextureTypeSuffix(image, imageInternalFormat);
+    }
+
+    switch (method)
+    {
+        case Method::SIZE:
+            name += "Size";
+            break;
+        case Method::LOAD:
+            name += "Load";
+            break;
+        case Method::STORE:
+            name += "Store";
+            break;
+        default:
+            UNREACHABLE();
+    }
+
+    return name;
+}
+
+const char *ImageFunctionHLSL::ImageFunction::getReturnType() const
+{
+    if (method == ImageFunction::Method::SIZE)
+    {
+        switch (image)
+        {
+            case EbtImage2D:
+            case EbtIImage2D:
+            case EbtUImage2D:
+            case EbtImageCube:
+            case EbtIImageCube:
+            case EbtUImageCube:
+                return "int2";
+            case EbtImage3D:
+            case EbtIImage3D:
+            case EbtUImage3D:
+            case EbtImage2DArray:
+            case EbtIImage2DArray:
+            case EbtUImage2DArray:
+                return "int3";
+            default:
+                UNREACHABLE();
+        }
+    }
+    else if (method == ImageFunction::Method::LOAD)
+    {
+        switch (image)
+        {
+            case EbtImage2D:
+            case EbtImage3D:
+            case EbtImageCube:
+            case EbtImage2DArray:
+                return "float4";
+            case EbtIImage2D:
+            case EbtIImage3D:
+            case EbtIImageCube:
+            case EbtIImage2DArray:
+                return "int4";
+            case EbtUImage2D:
+            case EbtUImage3D:
+            case EbtUImageCube:
+            case EbtUImage2DArray:
+                return "uint4";
+            default:
+                UNREACHABLE();
+        }
+    }
+    else if (method == ImageFunction::Method::STORE)
+    {
+        return "void";
+    }
+    else
+    {
+        UNREACHABLE();
+    }
+    return "";
+}
+
+bool ImageFunctionHLSL::ImageFunction::operator<(const ImageFunction &rhs) const
+{
+    return std::tie(image, imageInternalFormat, readonly, method) <
+           std::tie(rhs.image, rhs.imageInternalFormat, rhs.readonly, rhs.method);
+}
+
+TString ImageFunctionHLSL::useImageFunction(const TString &name,
+                                            const TBasicType &type,
+                                            TLayoutImageInternalFormat imageInternalFormat,
+                                            bool readonly)
+{
+    ASSERT(IsImage(type));
+    ImageFunction imageFunction;
+    imageFunction.image               = type;
+    imageFunction.imageInternalFormat = imageInternalFormat;
+    imageFunction.readonly            = readonly;
+
+    if (name == "imageSize")
+    {
+        imageFunction.method = ImageFunction::Method::SIZE;
+    }
+    else if (name == "imageLoad")
+    {
+        imageFunction.method = ImageFunction::Method::LOAD;
+    }
+    else if (name == "imageStore")
+    {
+        imageFunction.method = ImageFunction::Method::STORE;
+    }
+    else
+        UNREACHABLE();
+
+    mUsesImage.insert(imageFunction);
+    return imageFunction.name();
+}
+
+void ImageFunctionHLSL::imageFunctionHeader(TInfoSinkBase &out)
+{
+    for (const ImageFunction &imageFunction : mUsesImage)
+    {
+        // Function header
+        out << imageFunction.getReturnType() << " " << imageFunction.name() << "(";
+
+        OutputImageFunctionArgumentList(out, imageFunction);
+
+        out << ")\n"
+               "{\n";
+
+        TString imageReference("tex");
+
+        if (imageFunction.method == ImageFunction::Method::SIZE)
+        {
+            OutputImageSizeFunctionBody(out, imageFunction, imageReference);
+        }
+        else if (imageFunction.method == ImageFunction::Method::LOAD)
+        {
+            OutputImageLoadFunctionBody(out, imageFunction, imageReference);
+        }
+        else
+        {
+            OutputImageStoreFunctionBody(out, imageFunction, imageReference);
+        }
+
+        out << "}\n"
+               "\n";
+    }
+}
+
+}  // namespace sh
diff --git a/src/compiler/translator/ImageFunctionHLSL.h b/src/compiler/translator/ImageFunctionHLSL.h
new file mode 100644
index 0000000..9db17a6
--- /dev/null
+++ b/src/compiler/translator/ImageFunctionHLSL.h
@@ -0,0 +1,76 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ImageFunctionHLSL: Class for writing implementations of ESSL image functions into HLSL output.
+//
+
+#ifndef COMPILER_TRANSLATOR_IMAGEFUNCTIONHLSL_H_
+#define COMPILER_TRANSLATOR_IMAGEFUNCTIONHLSL_H_
+
+#include <set>
+
+#include "GLSLANG/ShaderLang.h"
+#include "compiler/translator/BaseTypes.h"
+#include "compiler/translator/Common.h"
+#include "compiler/translator/InfoSink.h"
+#include "compiler/translator/Types.h"
+
+namespace sh
+{
+
+class ImageFunctionHLSL final : angle::NonCopyable
+{
+  public:
+    // Returns the name of the image function implementation to caller.
+    // The name that's passed in is the name of the GLSL image function that it should implement.
+    TString useImageFunction(const TString &name,
+                             const TBasicType &type,
+                             TLayoutImageInternalFormat imageInternalFormat,
+                             bool readonly);
+
+    void imageFunctionHeader(TInfoSinkBase &out);
+
+  private:
+    struct ImageFunction
+    {
+        // See ESSL 3.10.4 section 8.12 for reference about what the different methods below do.
+        enum class Method
+        {
+            SIZE,
+            LOAD,
+            STORE
+        };
+
+        TString name() const;
+
+        bool operator<(const ImageFunction &rhs) const;
+
+        const char *getReturnType() const;
+
+        TBasicType image;
+        TLayoutImageInternalFormat imageInternalFormat;
+        bool readonly;
+        Method method;
+    };
+
+    static void OutputImageFunctionArgumentList(
+        TInfoSinkBase &out,
+        const ImageFunctionHLSL::ImageFunction &imageFunction);
+    static void OutputImageSizeFunctionBody(TInfoSinkBase &out,
+                                            const ImageFunctionHLSL::ImageFunction &imageFunction,
+                                            const TString &imageReference);
+    static void OutputImageLoadFunctionBody(TInfoSinkBase &out,
+                                            const ImageFunctionHLSL::ImageFunction &imageFunction,
+                                            const TString &imageReference);
+    static void OutputImageStoreFunctionBody(TInfoSinkBase &out,
+                                             const ImageFunctionHLSL::ImageFunction &imageFunction,
+                                             const TString &imageReference);
+    using ImageFunctionSet = std::set<ImageFunction>;
+    ImageFunctionSet mUsesImage;
+};
+
+}  // namespace sh
+
+#endif  // COMPILER_TRANSLATOR_IMAGEFUNCTIONHLSL_H_
diff --git a/src/compiler/translator/IntermNode.h b/src/compiler/translator/IntermNode.h
index 1cb29f0..603bd86 100644
--- a/src/compiler/translator/IntermNode.h
+++ b/src/compiler/translator/IntermNode.h
@@ -556,6 +556,11 @@
     void setId(const TSymbolUniqueId &functionId);
     const TSymbolUniqueId &getId() const;
 
+    bool isImageFunction() const
+    {
+        return getName() == "imageSize" || getName() == "imageLoad" || getName() == "imageStore";
+    }
+
   private:
     TName mName;
     TSymbolUniqueId *mId;
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index b00ab78..54427a9 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -16,6 +16,7 @@
 #include "compiler/translator/BuiltInFunctionEmulator.h"
 #include "compiler/translator/BuiltInFunctionEmulatorHLSL.h"
 #include "compiler/translator/FlagStd140Structs.h"
+#include "compiler/translator/ImageFunctionHLSL.h"
 #include "compiler/translator/InfoSink.h"
 #include "compiler/translator/NodeSearch.h"
 #include "compiler/translator/RemoveSwitchFallThrough.h"
@@ -165,8 +166,9 @@
     mExcessiveLoopIndex = nullptr;
 
     mStructureHLSL       = new StructureHLSL;
-    mUniformHLSL         = new UniformHLSL(mStructureHLSL, outputType, uniforms);
+    mUniformHLSL         = new UniformHLSL(shaderType, mStructureHLSL, outputType, uniforms);
     mTextureFunctionHLSL = new TextureFunctionHLSL;
+    mImageFunctionHLSL   = new ImageFunctionHLSL;
 
     if (mOutputType == SH_HLSL_3_0_OUTPUT)
     {
@@ -186,6 +188,7 @@
     SafeDelete(mStructureHLSL);
     SafeDelete(mUniformHLSL);
     SafeDelete(mTextureFunctionHLSL);
+    SafeDelete(mImageFunctionHLSL);
     for (auto &eqFunction : mStructEqualityFunctions)
     {
         SafeDelete(eqFunction);
@@ -731,6 +734,7 @@
     bool getDimensionsIgnoresBaseLevel =
         (mCompileOptions & SH_HLSL_GET_DIMENSIONS_IGNORES_BASE_LEVEL) != 0;
     mTextureFunctionHLSL->textureFunctionHeader(out, mOutputType, getDimensionsIgnoresBaseLevel);
+    mImageFunctionHLSL->imageFunctionHeader(out);
 
     if (mUsesFragCoord)
     {
@@ -1865,6 +1869,15 @@
                 // AST, such as precision emulation functions.
                 out << DecorateFunctionIfNeeded(node->getFunctionSymbolInfo()->getNameObj()) << "(";
             }
+            else if (node->getFunctionSymbolInfo()->isImageFunction())
+            {
+                TString name              = node->getFunctionSymbolInfo()->getName();
+                TType type                = (*arguments)[0]->getAsTyped()->getType();
+                TString imageFunctionName = mImageFunctionHLSL->useImageFunction(
+                    name, type.getBasicType(), type.getLayoutQualifier().imageInternalFormat,
+                    type.getMemoryQualifier().readonly);
+                out << imageFunctionName << "(";
+            }
             else
             {
                 const TString &name    = node->getFunctionSymbolInfo()->getName();
diff --git a/src/compiler/translator/OutputHLSL.h b/src/compiler/translator/OutputHLSL.h
index e0e8cb8..5509842 100644
--- a/src/compiler/translator/OutputHLSL.h
+++ b/src/compiler/translator/OutputHLSL.h
@@ -23,6 +23,7 @@
 class StructureHLSL;
 class TextureFunctionHLSL;
 class TSymbolTable;
+class ImageFunctionHLSL;
 class UnfoldShortCircuit;
 class UniformHLSL;
 
@@ -164,6 +165,7 @@
     StructureHLSL *mStructureHLSL;
     UniformHLSL *mUniformHLSL;
     TextureFunctionHLSL *mTextureFunctionHLSL;
+    ImageFunctionHLSL *mImageFunctionHLSL;
 
     // Parameters determining what goes in the header output
     bool mUsesFragColor;
diff --git a/src/compiler/translator/UniformHLSL.cpp b/src/compiler/translator/UniformHLSL.cpp
index f61fca2..311209d 100644
--- a/src/compiler/translator/UniformHLSL.cpp
+++ b/src/compiler/translator/UniformHLSL.cpp
@@ -92,12 +92,16 @@
 
 }  // anonymous namespace
 
-UniformHLSL::UniformHLSL(StructureHLSL *structureHLSL,
+UniformHLSL::UniformHLSL(sh::GLenum shaderType,
+                         StructureHLSL *structureHLSL,
                          ShShaderOutput outputType,
                          const std::vector<Uniform> &uniforms)
     : mUniformRegister(0),
       mUniformBlockRegister(0),
-      mSamplerRegister(0),
+      mTextureRegister(0),
+      mRWTextureRegister(0),
+      mSamplerCount(0),
+      mShaderType(shaderType),
       mStructureHLSL(structureHLSL),
       mOutputType(outputType),
       mUniforms(uniforms)
@@ -131,19 +135,36 @@
                                                 const TString &name,
                                                 unsigned int *outRegisterCount)
 {
-    unsigned int registerIndex =
-        (IsSampler(type.getBasicType()) ? mSamplerRegister : mUniformRegister);
-
+    unsigned int registerIndex;
     const Uniform *uniform = findUniformByName(name);
     ASSERT(uniform);
 
+    if (IsSampler(type.getBasicType()) ||
+        (IsImage(type.getBasicType()) && type.getMemoryQualifier().readonly))
+    {
+        registerIndex = mTextureRegister;
+    }
+    else if (IsImage(type.getBasicType()))
+    {
+        registerIndex = mRWTextureRegister;
+    }
+    else
+    {
+        registerIndex = mUniformRegister;
+    }
+
     mUniformRegisterMap[uniform->name] = registerIndex;
 
     unsigned int registerCount = HLSLVariableRegisterCount(*uniform, mOutputType);
 
-    if (gl::IsSamplerType(uniform->type))
+    if (IsSampler(type.getBasicType()) ||
+        (IsImage(type.getBasicType()) && type.getMemoryQualifier().readonly))
     {
-        mSamplerRegister += registerCount;
+        mTextureRegister += registerCount;
+    }
+    else if (IsImage(type.getBasicType()))
+    {
+        mRWTextureRegister += registerCount;
     }
     else
     {
@@ -162,10 +183,10 @@
 {
     // Sampler that is a field of a uniform structure.
     ASSERT(IsSampler(type.getBasicType()));
-    unsigned int registerIndex                     = mSamplerRegister;
+    unsigned int registerIndex                     = mTextureRegister;
     mUniformRegisterMap[std::string(name.c_str())] = registerIndex;
     unsigned int registerCount = type.isArray() ? type.getArraySizeProduct() : 1u;
-    mSamplerRegister += registerCount;
+    mTextureRegister += registerCount;
     if (outRegisterCount)
     {
         *outRegisterCount = registerCount;
@@ -175,7 +196,7 @@
 
 void UniformHLSL::outputHLSLSamplerUniformGroup(
     TInfoSinkBase &out,
-    const HLSLTextureSamplerGroup textureGroup,
+    const HLSLTextureGroup textureGroup,
     const TVector<const TIntermSymbol *> &group,
     const TMap<const TIntermSymbol *, TString> &samplerInStructSymbolsToAPINames,
     unsigned int *groupTextureRegisterIndex)
@@ -251,6 +272,46 @@
         << str(registerIndex) << ");\n";
 }
 
+void UniformHLSL::outputHLSL4_1_FL11Texture(TInfoSinkBase &out,
+                                            const TType &type,
+                                            const TName &name,
+                                            const unsigned int registerIndex)
+{
+    // TODO(xinghua.cao@intel.com): if image2D variable is bound on one layer of Texture3D or
+    // Texture2DArray. Translate this variable to HLSL Texture3D object or HLSL Texture2DArray
+    // object, or create a temporary Texture2D to save content of the layer and bind the
+    // temporary Texture2D to image2D variable.
+    out << "uniform "
+        << TextureString(type.getBasicType(), type.getLayoutQualifier().imageInternalFormat) << " "
+        << DecorateVariableIfNeeded(name) << ArrayString(type) << " : register(t"
+        << str(registerIndex) << ");\n";
+    return;
+}
+
+void UniformHLSL::outputHLSL4_1_FL11RWTexture(TInfoSinkBase &out,
+                                              const TType &type,
+                                              const TName &name,
+                                              const unsigned int registerIndex)
+{
+    // TODO(xinghua.cao@intel.com): if image2D variable is bound on one layer of Texture3D or
+    // Texture2DArray. Translate this variable to HLSL RWTexture3D object or HLSL RWTexture2DArray
+    // object, or create a temporary Texture2D to save content of the layer and bind the
+    // temporary Texture2D to image2D variable.
+    if (mShaderType == GL_COMPUTE_SHADER)
+    {
+        out << "uniform "
+            << RWTextureString(type.getBasicType(), type.getLayoutQualifier().imageInternalFormat)
+            << " " << DecorateVariableIfNeeded(name) << ArrayString(type) << " : register(u"
+            << str(registerIndex) << ");\n";
+    }
+    else
+    {
+        // TODO(xinghua.cao@intel.com): Support images in vertex shader and fragment shader,
+        // which are needed to sync binding value when linking program.
+    }
+    return;
+}
+
 void UniformHLSL::outputUniform(TInfoSinkBase &out,
                                 const TType &type,
                                 const TName &name,
@@ -290,6 +351,7 @@
     // HLSL sampler type, enumerated in HLSLTextureSamplerGroup.
     TVector<TVector<const TIntermSymbol *>> groupedSamplerUniforms(HLSL_TEXTURE_MAX + 1);
     TMap<const TIntermSymbol *, TString> samplerInStructSymbolsToAPINames;
+    TVector<const TIntermSymbol *> imageUniformsHLSL41Output;
     for (auto &uniformIt : referencedUniforms)
     {
         // Output regular uniforms. Group sampler uniforms by type.
@@ -299,7 +361,7 @@
 
         if (outputType == SH_HLSL_4_1_OUTPUT && IsSampler(type.getBasicType()))
         {
-            HLSLTextureSamplerGroup group = TextureGroup(type.getBasicType());
+            HLSLTextureGroup group = TextureGroup(type.getBasicType());
             groupedSamplerUniforms[group].push_back(&uniform);
         }
         else if (outputType == SH_HLSL_4_0_FL9_3_OUTPUT && IsSampler(type.getBasicType()))
@@ -307,6 +369,10 @@
             unsigned int registerIndex = assignUniformRegister(type, name.getString(), nullptr);
             outputHLSL4_0_FL9_3Sampler(out, type, name, registerIndex);
         }
+        else if (outputType == SH_HLSL_4_1_OUTPUT && IsImage(type.getBasicType()))
+        {
+            imageUniformsHLSL41Output.push_back(&uniform);
+        }
         else
         {
             if (type.isStructureContainingSamplers())
@@ -325,7 +391,7 @@
 
                     if (outputType == SH_HLSL_4_1_OUTPUT)
                     {
-                        HLSLTextureSamplerGroup group = TextureGroup(samplerType.getBasicType());
+                        HLSLTextureGroup group = TextureGroup(samplerType.getBasicType());
                         groupedSamplerUniforms[group].push_back(sampler);
                         samplerInStructSymbolsToAPINames[sampler] = symbolsToAPINames[sampler];
                     }
@@ -357,16 +423,32 @@
         for (int groupId = HLSL_TEXTURE_MIN; groupId < HLSL_TEXTURE_MAX; ++groupId)
         {
             outputHLSLSamplerUniformGroup(
-                out, HLSLTextureSamplerGroup(groupId), groupedSamplerUniforms[groupId],
+                out, HLSLTextureGroup(groupId), groupedSamplerUniforms[groupId],
                 samplerInStructSymbolsToAPINames, &groupTextureRegisterIndex);
         }
+        mSamplerCount = groupTextureRegisterIndex;
+
+        for (const TIntermSymbol *image : imageUniformsHLSL41Output)
+        {
+            const TType &type          = image->getType();
+            const TName &name          = image->getName();
+            unsigned int registerIndex = assignUniformRegister(type, name.getString(), nullptr);
+            if (type.getMemoryQualifier().readonly)
+            {
+                outputHLSL4_1_FL11Texture(out, type, name, registerIndex);
+            }
+            else
+            {
+                outputHLSL4_1_FL11RWTexture(out, type, name, registerIndex);
+            }
+        }
     }
 }
 
 void UniformHLSL::samplerMetadataUniforms(TInfoSinkBase &out, const char *reg)
 {
-    // If mSamplerRegister is 0 the shader doesn't use any textures.
-    if (mSamplerRegister > 0)
+    // If mSamplerCount is 0 the shader doesn't use any textures for samplers.
+    if (mSamplerCount > 0)
     {
         out << "    struct SamplerMetadata\n"
                "    {\n"
@@ -376,7 +458,7 @@
                "        int padding;\n"
                "    };\n"
                "    SamplerMetadata samplerMetadata["
-            << mSamplerRegister << "] : packoffset(" << reg << ");\n";
+            << mSamplerCount << "] : packoffset(" << reg << ");\n";
     }
 }
 
diff --git a/src/compiler/translator/UniformHLSL.h b/src/compiler/translator/UniformHLSL.h
index 9c54f90..8784e50 100644
--- a/src/compiler/translator/UniformHLSL.h
+++ b/src/compiler/translator/UniformHLSL.h
@@ -21,7 +21,8 @@
 class UniformHLSL : angle::NonCopyable
 {
   public:
-    UniformHLSL(StructureHLSL *structureHLSL,
+    UniformHLSL(sh::GLenum shaderType,
+                StructureHLSL *structureHLSL,
                 ShShaderOutput outputType,
                 const std::vector<Uniform> &uniforms);
 
@@ -63,7 +64,14 @@
                                     const TType &type,
                                     const TName &name,
                                     const unsigned int registerIndex);
-
+    void outputHLSL4_1_FL11Texture(TInfoSinkBase &out,
+                                   const TType &type,
+                                   const TName &name,
+                                   const unsigned int registerIndex);
+    void outputHLSL4_1_FL11RWTexture(TInfoSinkBase &out,
+                                     const TType &type,
+                                     const TName &name,
+                                     const unsigned int registerIndex);
     void outputUniform(TInfoSinkBase &out,
                        const TType &type,
                        const TName &name,
@@ -79,14 +87,17 @@
 
     void outputHLSLSamplerUniformGroup(
         TInfoSinkBase &out,
-        const HLSLTextureSamplerGroup textureGroup,
+        const HLSLTextureGroup textureGroup,
         const TVector<const TIntermSymbol *> &group,
         const TMap<const TIntermSymbol *, TString> &samplerInStructSymbolsToAPINames,
         unsigned int *groupTextureRegisterIndex);
 
     unsigned int mUniformRegister;
     unsigned int mUniformBlockRegister;
-    unsigned int mSamplerRegister;
+    unsigned int mTextureRegister;
+    unsigned int mRWTextureRegister;
+    unsigned int mSamplerCount;
+    sh::GLenum mShaderType;
     StructureHLSL *mStructureHLSL;
     ShShaderOutput mOutputType;
 
diff --git a/src/compiler/translator/UtilsHLSL.cpp b/src/compiler/translator/UtilsHLSL.cpp
index 150da5a..8a77f00 100644
--- a/src/compiler/translator/UtilsHLSL.cpp
+++ b/src/compiler/translator/UtilsHLSL.cpp
@@ -27,7 +27,7 @@
     }
 }
 
-TString SamplerString(HLSLTextureSamplerGroup type)
+TString SamplerString(HLSLTextureGroup type)
 {
     if (type >= HLSL_COMPARISON_SAMPLER_GROUP_BEGIN && type <= HLSL_COMPARISON_SAMPLER_GROUP_END)
     {
@@ -39,7 +39,8 @@
     }
 }
 
-HLSLTextureSamplerGroup TextureGroup(const TBasicType type)
+HLSLTextureGroup TextureGroup(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat)
+
 {
     switch (type)
     {
@@ -81,24 +82,170 @@
             return HLSL_TEXTURE_CUBE_COMPARISON;
         case EbtSampler2DArrayShadow:
             return HLSL_TEXTURE_2D_ARRAY_COMPARISON;
+        case EbtImage2D:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32F:
+                case EiifRGBA16F:
+                case EiifR32F:
+                    return HLSL_TEXTURE_2D;
+                case EiifRGBA8:
+                    return HLSL_TEXTURE_2D_UNORM;
+                case EiifRGBA8_SNORM:
+                    return HLSL_TEXTURE_2D_SNORM;
+                default:
+                    UNREACHABLE();
+            }
+        }
+        case EbtIImage2D:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32I:
+                case EiifRGBA16I:
+                case EiifRGBA8I:
+                case EiifR32I:
+                    return HLSL_TEXTURE_2D_INT4;
+                default:
+                    UNREACHABLE();
+            }
+        }
+        case EbtUImage2D:
+        {
+            switch (imageInternalFormat)
+            {
+
+                case EiifRGBA32UI:
+                case EiifRGBA16UI:
+                case EiifRGBA8UI:
+                case EiifR32UI:
+                    return HLSL_TEXTURE_2D_UINT4;
+                default:
+                    UNREACHABLE();
+            }
+        }
+        case EbtImage3D:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32F:
+                case EiifRGBA16F:
+                case EiifR32F:
+                    return HLSL_TEXTURE_3D;
+                case EiifRGBA8:
+                    return HLSL_TEXTURE_3D_UNORM;
+                case EiifRGBA8_SNORM:
+                    return HLSL_TEXTURE_3D_SNORM;
+                default:
+                    UNREACHABLE();
+            }
+        }
+        case EbtIImage3D:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32I:
+                case EiifRGBA16I:
+                case EiifRGBA8I:
+                case EiifR32I:
+                    return HLSL_TEXTURE_3D_INT4;
+                default:
+                    UNREACHABLE();
+            }
+        }
+        case EbtUImage3D:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32UI:
+                case EiifRGBA16UI:
+                case EiifRGBA8UI:
+                case EiifR32UI:
+                    return HLSL_TEXTURE_3D_UINT4;
+                default:
+                    UNREACHABLE();
+            }
+        }
+        case EbtImage2DArray:
+        case EbtImageCube:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32F:
+                case EiifRGBA16F:
+                case EiifR32F:
+                    return HLSL_TEXTURE_2D_ARRAY;
+                case EiifRGBA8:
+                    return HLSL_TEXTURE_2D_ARRAY_UNORN;
+                case EiifRGBA8_SNORM:
+                    return HLSL_TEXTURE_2D_ARRAY_SNORM;
+                default:
+                    UNREACHABLE();
+            }
+        }
+        case EbtIImage2DArray:
+        case EbtIImageCube:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32I:
+                case EiifRGBA16I:
+                case EiifRGBA8I:
+                case EiifR32I:
+                    return HLSL_TEXTURE_2D_ARRAY_INT4;
+                default:
+                    UNREACHABLE();
+            }
+        }
+        case EbtUImage2DArray:
+        case EbtUImageCube:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32UI:
+                case EiifRGBA16UI:
+                case EiifRGBA8UI:
+                case EiifR32UI:
+                    return HLSL_TEXTURE_2D_ARRAY_UINT4;
+                default:
+                    UNREACHABLE();
+            }
+        }
         default:
             UNREACHABLE();
     }
     return HLSL_TEXTURE_UNKNOWN;
 }
 
-TString TextureString(const HLSLTextureSamplerGroup type)
+TString TextureString(const HLSLTextureGroup textureGroup)
 {
-    switch (type)
+    switch (textureGroup)
     {
         case HLSL_TEXTURE_2D:
-            return "Texture2D";
+            return "Texture2D<float4>";
         case HLSL_TEXTURE_CUBE:
-            return "TextureCube";
+            return "TextureCube<float4>";
         case HLSL_TEXTURE_2D_ARRAY:
-            return "Texture2DArray";
+            return "Texture2DArray<float4>";
         case HLSL_TEXTURE_3D:
-            return "Texture3D";
+            return "Texture3D<float4>";
+        case HLSL_TEXTURE_2D_UNORM:
+            return "Texture2D<unorm float4>";
+        case HLSL_TEXTURE_CUBE_UNORM:
+            return "TextureCube<unorm float4>";
+        case HLSL_TEXTURE_2D_ARRAY_UNORN:
+            return "Texture2DArray<unorm float4>";
+        case HLSL_TEXTURE_3D_UNORM:
+            return "Texture3D<unorm float4>";
+        case HLSL_TEXTURE_2D_SNORM:
+            return "Texture2D<snorm float4>";
+        case HLSL_TEXTURE_CUBE_SNORM:
+            return "TextureCube<snorm float4>";
+        case HLSL_TEXTURE_2D_ARRAY_SNORM:
+            return "Texture2DArray<snorm float4>";
+        case HLSL_TEXTURE_3D_SNORM:
+            return "Texture3D<snorm float4>";
         case HLSL_TEXTURE_2D_MS:
             return "Texture2DMS<float4>";
         case HLSL_TEXTURE_2D_INT4:
@@ -127,15 +274,15 @@
             UNREACHABLE();
     }
 
-    return "<unknown texture type>";
+    return "<unknown read texture type>";
 }
 
-TString TextureString(const TBasicType type)
+TString TextureString(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat)
 {
-    return TextureString(TextureGroup(type));
+    return TextureString(TextureGroup(type, imageInternalFormat));
 }
 
-TString TextureGroupSuffix(const HLSLTextureSamplerGroup type)
+TString TextureGroupSuffix(const HLSLTextureGroup type)
 {
     switch (type)
     {
@@ -147,6 +294,22 @@
             return "2DArray";
         case HLSL_TEXTURE_3D:
             return "3D";
+        case HLSL_TEXTURE_2D_UNORM:
+            return "2D_unorm_float4_";
+        case HLSL_TEXTURE_CUBE_UNORM:
+            return "Cube_unorm_float4_";
+        case HLSL_TEXTURE_2D_ARRAY_UNORN:
+            return "2DArray_unorm_float4_";
+        case HLSL_TEXTURE_3D_UNORM:
+            return "3D_unorm_float4_";
+        case HLSL_TEXTURE_2D_SNORM:
+            return "2D_snorm_float4_";
+        case HLSL_TEXTURE_CUBE_SNORM:
+            return "Cube_snorm_float4_";
+        case HLSL_TEXTURE_2D_ARRAY_SNORM:
+            return "2DArray_snorm_float4_";
+        case HLSL_TEXTURE_3D_SNORM:
+            return "3D_snorm_float4_";
         case HLSL_TEXTURE_2D_MS:
             return "2DMS";
         case HLSL_TEXTURE_2D_INT4:
@@ -178,12 +341,12 @@
     return "<unknown texture type>";
 }
 
-TString TextureGroupSuffix(const TBasicType type)
+TString TextureGroupSuffix(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat)
 {
-    return TextureGroupSuffix(TextureGroup(type));
+    return TextureGroupSuffix(TextureGroup(type, imageInternalFormat));
 }
 
-TString TextureTypeSuffix(const TBasicType type)
+TString TextureTypeSuffix(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat)
 {
     switch (type)
     {
@@ -193,9 +356,337 @@
             return "Cube_uint4_";
         case EbtSamplerExternalOES:
             return "_External";
+        case EbtImageCube:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32F:
+                case EiifRGBA16F:
+                case EiifR32F:
+                    return "Cube_float4_";
+                case EiifRGBA8:
+                    return "Cube_unorm_float4_";
+                case EiifRGBA8_SNORM:
+                    return "Cube_snorm_float4_";
+                default:
+                    UNREACHABLE();
+            }
+        }
+        case EbtIImageCube:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32I:
+                case EiifRGBA16I:
+                case EiifRGBA8I:
+                case EiifR32I:
+                    return "Cube_int4_";
+                default:
+                    UNREACHABLE();
+            }
+        }
+        case EbtUImageCube:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32UI:
+                case EiifRGBA16UI:
+                case EiifRGBA8UI:
+                case EiifR32UI:
+                    return "Cube_uint4_";
+                default:
+                    UNREACHABLE();
+            }
+        }
         default:
             // All other types are identified by their group suffix
-            return TextureGroupSuffix(type);
+            return TextureGroupSuffix(type, imageInternalFormat);
+    }
+}
+
+HLSLRWTextureGroup RWTextureGroup(const TBasicType type,
+                                  TLayoutImageInternalFormat imageInternalFormat)
+
+{
+    switch (type)
+    {
+        case EbtImage2D:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32F:
+                case EiifRGBA16F:
+                case EiifR32F:
+                    return HLSL_RWTEXTURE_2D_FLOAT4;
+                case EiifRGBA8:
+                    return HLSL_RWTEXTURE_2D_UNORM;
+                case EiifRGBA8_SNORM:
+                    return HLSL_RWTEXTURE_2D_SNORM;
+                default:
+                    UNREACHABLE();
+            }
+        }
+        case EbtIImage2D:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32I:
+                case EiifRGBA16I:
+                case EiifRGBA8I:
+                case EiifR32I:
+                    return HLSL_RWTEXTURE_2D_INT4;
+                default:
+                    UNREACHABLE();
+            }
+        }
+        case EbtUImage2D:
+        {
+            switch (imageInternalFormat)
+            {
+
+                case EiifRGBA32UI:
+                case EiifRGBA16UI:
+                case EiifRGBA8UI:
+                case EiifR32UI:
+                    return HLSL_RWTEXTURE_2D_UINT4;
+                default:
+                    UNREACHABLE();
+            }
+        }
+        case EbtImage3D:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32F:
+                case EiifRGBA16F:
+                case EiifR32F:
+                    return HLSL_RWTEXTURE_3D_FLOAT4;
+                case EiifRGBA8:
+                    return HLSL_RWTEXTURE_3D_UNORM;
+                case EiifRGBA8_SNORM:
+                    return HLSL_RWTEXTURE_3D_SNORM;
+                default:
+                    UNREACHABLE();
+            }
+        }
+        case EbtIImage3D:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32I:
+                case EiifRGBA16I:
+                case EiifRGBA8I:
+                case EiifR32I:
+                    return HLSL_RWTEXTURE_3D_INT4;
+                default:
+                    UNREACHABLE();
+            }
+        }
+        case EbtUImage3D:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32UI:
+                case EiifRGBA16UI:
+                case EiifRGBA8UI:
+                case EiifR32UI:
+                    return HLSL_RWTEXTURE_3D_UINT4;
+                default:
+                    UNREACHABLE();
+            }
+        }
+        case EbtImage2DArray:
+        case EbtImageCube:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32F:
+                case EiifRGBA16F:
+                case EiifR32F:
+                    return HLSL_RWTEXTURE_2D_ARRAY_FLOAT4;
+                case EiifRGBA8:
+                    return HLSL_RWTEXTURE_2D_ARRAY_UNORN;
+                case EiifRGBA8_SNORM:
+                    return HLSL_RWTEXTURE_2D_ARRAY_SNORM;
+                default:
+                    UNREACHABLE();
+            }
+        }
+        case EbtIImage2DArray:
+        case EbtIImageCube:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32I:
+                case EiifRGBA16I:
+                case EiifRGBA8I:
+                case EiifR32I:
+                    return HLSL_RWTEXTURE_2D_ARRAY_INT4;
+                default:
+                    UNREACHABLE();
+            }
+        }
+        case EbtUImage2DArray:
+        case EbtUImageCube:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32UI:
+                case EiifRGBA16UI:
+                case EiifRGBA8UI:
+                case EiifR32UI:
+                    return HLSL_RWTEXTURE_2D_ARRAY_UINT4;
+                default:
+                    UNREACHABLE();
+            }
+        }
+        default:
+            UNREACHABLE();
+    }
+    return HLSL_RWTEXTURE_UNKNOWN;
+}
+
+TString RWTextureString(const HLSLRWTextureGroup RWTextureGroup)
+{
+    switch (RWTextureGroup)
+    {
+        case HLSL_RWTEXTURE_2D_FLOAT4:
+            return "RWTexture2D<float4>";
+        case HLSL_RWTEXTURE_2D_ARRAY_FLOAT4:
+            return "RWTexture2DArray<float4>";
+        case HLSL_RWTEXTURE_3D_FLOAT4:
+            return "RWTexture3D<float4>";
+        case HLSL_RWTEXTURE_2D_UNORM:
+            return "RWTexture2D<unorm float4>";
+        case HLSL_RWTEXTURE_2D_ARRAY_UNORN:
+            return "RWTexture2DArray<unorm float4>";
+        case HLSL_RWTEXTURE_3D_UNORM:
+            return "RWTexture3D<unorm float4>";
+        case HLSL_RWTEXTURE_2D_SNORM:
+            return "RWTexture2D<snorm float4>";
+        case HLSL_RWTEXTURE_2D_ARRAY_SNORM:
+            return "RWTexture2DArray<snorm float4>";
+        case HLSL_RWTEXTURE_3D_SNORM:
+            return "RWTexture3D<snorm float4>";
+        case HLSL_RWTEXTURE_2D_UINT4:
+            return "RWTexture2D<uint4>";
+        case HLSL_RWTEXTURE_2D_ARRAY_UINT4:
+            return "RWTexture2DArray<uint4>";
+        case HLSL_RWTEXTURE_3D_UINT4:
+            return "RWTexture3D<uint4>";
+        case HLSL_RWTEXTURE_2D_INT4:
+            return "RWTexture2D<int4>";
+        case HLSL_RWTEXTURE_2D_ARRAY_INT4:
+            return "RWTexture2DArray<int4>";
+        case HLSL_RWTEXTURE_3D_INT4:
+            return "RWTexture3D<int4>";
+        default:
+            UNREACHABLE();
+    }
+
+    return "<unknown read and write texture type>";
+}
+
+TString RWTextureString(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat)
+{
+    return RWTextureString(RWTextureGroup(type, imageInternalFormat));
+}
+
+TString RWTextureGroupSuffix(const HLSLRWTextureGroup type)
+{
+    switch (type)
+    {
+        case HLSL_RWTEXTURE_2D_FLOAT4:
+            return "RW2D_float4_";
+        case HLSL_RWTEXTURE_2D_ARRAY_FLOAT4:
+            return "RW2DArray_float4_";
+        case HLSL_RWTEXTURE_3D_FLOAT4:
+            return "RW3D_float4_";
+        case HLSL_RWTEXTURE_2D_UNORM:
+            return "RW2D_unorm_float4_";
+        case HLSL_RWTEXTURE_2D_ARRAY_UNORN:
+            return "RW2DArray_unorm_float4_";
+        case HLSL_RWTEXTURE_3D_UNORM:
+            return "RW3D_unorm_float4_";
+        case HLSL_RWTEXTURE_2D_SNORM:
+            return "RW2D_snorm_float4_";
+        case HLSL_RWTEXTURE_2D_ARRAY_SNORM:
+            return "RW2DArray_snorm_float4_";
+        case HLSL_RWTEXTURE_3D_SNORM:
+            return "RW3D_snorm_float4_";
+        case HLSL_RWTEXTURE_2D_UINT4:
+            return "RW2D_uint4_";
+        case HLSL_RWTEXTURE_2D_ARRAY_UINT4:
+            return "RW2DArray_uint4_";
+        case HLSL_RWTEXTURE_3D_UINT4:
+            return "RW3D_uint4_";
+        case HLSL_RWTEXTURE_2D_INT4:
+            return "RW2D_int4_";
+        case HLSL_RWTEXTURE_2D_ARRAY_INT4:
+            return "RW2DArray_int4_";
+        case HLSL_RWTEXTURE_3D_INT4:
+            return "RW3D_int4_";
+        default:
+            UNREACHABLE();
+    }
+
+    return "<unknown read and write resource>";
+}
+
+TString RWTextureGroupSuffix(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat)
+{
+    return RWTextureGroupSuffix(RWTextureGroup(type, imageInternalFormat));
+}
+
+TString RWTextureTypeSuffix(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat)
+{
+    switch (type)
+    {
+        case EbtImageCube:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32F:
+                case EiifRGBA16F:
+                case EiifR32F:
+                    return "RWCube_float4_";
+                case EiifRGBA8:
+                    return "RWCube_unorm_float4_";
+                case EiifRGBA8_SNORM:
+                    return "RWCube_unorm_float4_";
+                default:
+                    UNREACHABLE();
+            }
+        }
+        case EbtIImageCube:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32I:
+                case EiifRGBA16I:
+                case EiifRGBA8I:
+                case EiifR32I:
+                    return "RWCube_int4_";
+                default:
+                    UNREACHABLE();
+            }
+        }
+        case EbtUImageCube:
+        {
+            switch (imageInternalFormat)
+            {
+                case EiifRGBA32UI:
+                case EiifRGBA16UI:
+                case EiifRGBA8UI:
+                case EiifR32UI:
+                    return "RWCube_uint4_";
+                default:
+                    UNREACHABLE();
+            }
+        }
+        default:
+            // All other types are identified by their group suffix
+            return TextureGroupSuffix(type, imageInternalFormat);
     }
 }
 
diff --git a/src/compiler/translator/UtilsHLSL.h b/src/compiler/translator/UtilsHLSL.h
index 44d05db..daeec8d 100644
--- a/src/compiler/translator/UtilsHLSL.h
+++ b/src/compiler/translator/UtilsHLSL.h
@@ -21,16 +21,24 @@
 namespace sh
 {
 
-// Unique combinations of HLSL Texture type and HLSL Sampler type.
-enum HLSLTextureSamplerGroup
+// HLSL Texture type for GLSL sampler type and readonly image type.
+enum HLSLTextureGroup
 {
-    // Regular samplers
+    // read resources
     HLSL_TEXTURE_2D,
     HLSL_TEXTURE_MIN = HLSL_TEXTURE_2D,
 
     HLSL_TEXTURE_CUBE,
     HLSL_TEXTURE_2D_ARRAY,
     HLSL_TEXTURE_3D,
+    HLSL_TEXTURE_2D_UNORM,
+    HLSL_TEXTURE_CUBE_UNORM,
+    HLSL_TEXTURE_2D_ARRAY_UNORN,
+    HLSL_TEXTURE_3D_UNORM,
+    HLSL_TEXTURE_2D_SNORM,
+    HLSL_TEXTURE_CUBE_SNORM,
+    HLSL_TEXTURE_2D_ARRAY_SNORM,
+    HLSL_TEXTURE_3D_SNORM,
     HLSL_TEXTURE_2D_MS,
     HLSL_TEXTURE_2D_INT4,
     HLSL_TEXTURE_3D_INT4,
@@ -54,14 +62,52 @@
     HLSL_TEXTURE_MAX = HLSL_TEXTURE_UNKNOWN
 };
 
-HLSLTextureSamplerGroup TextureGroup(const TBasicType type);
-TString TextureString(const HLSLTextureSamplerGroup type);
-TString TextureString(const TBasicType type);
-TString TextureGroupSuffix(const HLSLTextureSamplerGroup type);
-TString TextureGroupSuffix(const TBasicType type);
-TString TextureTypeSuffix(const TBasicType type);
+// HLSL RWTexture type for GLSL read and write image type.
+enum HLSLRWTextureGroup
+{
+    // read/write resource
+    HLSL_RWTEXTURE_2D_FLOAT4,
+    HLSL_RWTEXTURE_MIN = HLSL_RWTEXTURE_2D_FLOAT4,
+    HLSL_RWTEXTURE_2D_ARRAY_FLOAT4,
+    HLSL_RWTEXTURE_3D_FLOAT4,
+    HLSL_RWTEXTURE_2D_UNORM,
+    HLSL_RWTEXTURE_2D_ARRAY_UNORN,
+    HLSL_RWTEXTURE_3D_UNORM,
+    HLSL_RWTEXTURE_2D_SNORM,
+    HLSL_RWTEXTURE_2D_ARRAY_SNORM,
+    HLSL_RWTEXTURE_3D_SNORM,
+    HLSL_RWTEXTURE_2D_UINT4,
+    HLSL_RWTEXTURE_2D_ARRAY_UINT4,
+    HLSL_RWTEXTURE_3D_UINT4,
+    HLSL_RWTEXTURE_2D_INT4,
+    HLSL_RWTEXTURE_2D_ARRAY_INT4,
+    HLSL_RWTEXTURE_3D_INT4,
+
+    HLSL_RWTEXTURE_UNKNOWN,
+    HLSL_RWTEXTURE_MAX = HLSL_RWTEXTURE_UNKNOWN
+};
+
+HLSLTextureGroup TextureGroup(const TBasicType type,
+                              TLayoutImageInternalFormat imageInternalFormat = EiifUnspecified);
+TString TextureString(const HLSLTextureGroup textureGroup);
+TString TextureString(const TBasicType type,
+                      TLayoutImageInternalFormat imageInternalFormat = EiifUnspecified);
+TString TextureGroupSuffix(const HLSLTextureGroup type);
+TString TextureGroupSuffix(const TBasicType type,
+                           TLayoutImageInternalFormat imageInternalFormat = EiifUnspecified);
+TString TextureTypeSuffix(const TBasicType type,
+                          TLayoutImageInternalFormat imageInternalFormat = EiifUnspecified);
+HLSLRWTextureGroup RWTextureGroup(const TBasicType type,
+                                  TLayoutImageInternalFormat imageInternalFormat);
+TString RWTextureString(const HLSLRWTextureGroup textureGroup);
+TString RWTextureString(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat);
+TString RWTextureGroupSuffix(const HLSLRWTextureGroup type);
+TString RWTextureGroupSuffix(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat);
+TString RWTextureTypeSuffix(const TBasicType type, TLayoutImageInternalFormat imageInternalFormat);
+
 TString SamplerString(const TBasicType type);
-TString SamplerString(HLSLTextureSamplerGroup type);
+TString SamplerString(HLSLTextureGroup type);
+
 // Adds a prefix to user-defined names to avoid naming clashes.
 TString Decorate(const TString &string);
 TString DecorateVariableIfNeeded(const TName &name);