ES31: Support bindImageTexture on Texture3D/Texture2DArray/TextureCube
for compute shaders on D3D

BUG=angleproject:1987
TEST=angle_end2end_tests.ComputeShaderTest.*

Change-Id: I075296ac3b6796a334929699c16f2399d7915e51
Reviewed-on: https://chromium-review.googlesource.com/844063
Commit-Queue: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp b/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
index e68a513..90e0ba9 100644
--- a/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
@@ -1316,8 +1316,7 @@
     srvDesc.ViewDimension             = D3D11_SRV_DIMENSION_TEXTURE2D;
     srvDesc.Texture2D.MostDetailedMip = mTopLevel + level;
     srvDesc.Texture2D.MipLevels       = 1;
-    const TextureHelper11 *srvTexture = &texture;
-    ANGLE_TRY(mRenderer->allocateResource(srvDesc, srvTexture->get(), outSRV));
+    ANGLE_TRY(mRenderer->allocateResource(srvDesc, texture.get(), outSRV));
     outSRV->setDebugName("TexStorage2D.SRVForImage");
     return gl::NoError();
 }
@@ -1333,8 +1332,7 @@
     uavDesc.Format                    = format;
     uavDesc.ViewDimension             = D3D11_UAV_DIMENSION_TEXTURE2D;
     uavDesc.Texture2D.MipSlice        = mTopLevel + level;
-    const TextureHelper11 *uavTexture = &texture;
-    ANGLE_TRY(mRenderer->allocateResource(uavDesc, uavTexture->get(), outUAV));
+    ANGLE_TRY(mRenderer->allocateResource(uavDesc, texture.get(), outUAV));
     outUAV->setDebugName("TexStorage2D.UAVForImage");
     return gl::NoError();
 }
@@ -2357,8 +2355,17 @@
                                                    const TextureHelper11 &texture,
                                                    d3d11::SharedSRV *outSRV)
 {
-    UNIMPLEMENTED();
-    return gl::InternalError();
+    ASSERT(outSRV);
+    D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
+    srvDesc.Format                         = format;
+    srvDesc.ViewDimension                  = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
+    srvDesc.Texture2DArray.MostDetailedMip = mTopLevel + level;
+    srvDesc.Texture2DArray.MipLevels       = 1;
+    srvDesc.Texture2DArray.FirstArraySlice = 0;
+    srvDesc.Texture2DArray.ArraySize       = gl::CUBE_FACE_COUNT;
+    ANGLE_TRY(mRenderer->allocateResource(srvDesc, texture.get(), outSRV));
+    outSRV->setDebugName("TexStorageCube.SRVForImage");
+    return gl::NoError();
 }
 
 gl::Error TextureStorage11_Cube::createUAVForImage(const gl::Context *context,
@@ -2367,8 +2374,16 @@
                                                    const TextureHelper11 &texture,
                                                    d3d11::SharedUAV *outUAV)
 {
-    UNIMPLEMENTED();
-    return gl::InternalError();
+    ASSERT(outUAV);
+    D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
+    uavDesc.Format                         = format;
+    uavDesc.ViewDimension                  = D3D11_UAV_DIMENSION_TEXTURE2DARRAY;
+    uavDesc.Texture2DArray.MipSlice        = mTopLevel + level;
+    uavDesc.Texture2DArray.FirstArraySlice = 0;
+    uavDesc.Texture2DArray.ArraySize       = gl::CUBE_FACE_COUNT;
+    ANGLE_TRY(mRenderer->allocateResource(uavDesc, texture.get(), outUAV));
+    outUAV->setDebugName("TexStorageCube.UAVForImage");
+    return gl::NoError();
 }
 
 gl::Error TextureStorage11_Cube::getSwizzleTexture(const TextureHelper11 **outTexture)
@@ -2651,8 +2666,15 @@
                                                  const TextureHelper11 &texture,
                                                  d3d11::SharedSRV *outSRV)
 {
-    UNIMPLEMENTED();
-    return gl::InternalError();
+    ASSERT(outSRV);
+    D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
+    srvDesc.Format                    = format;
+    srvDesc.ViewDimension             = D3D11_SRV_DIMENSION_TEXTURE3D;
+    srvDesc.Texture3D.MostDetailedMip = mTopLevel + level;
+    srvDesc.Texture3D.MipLevels       = 1;
+    ANGLE_TRY(mRenderer->allocateResource(srvDesc, texture.get(), outSRV));
+    outSRV->setDebugName("TexStorage3D.SRVForImage");
+    return gl::NoError();
 }
 
 gl::Error TextureStorage11_3D::createUAVForImage(const gl::Context *context,
@@ -2661,8 +2683,16 @@
                                                  const TextureHelper11 &texture,
                                                  d3d11::SharedUAV *outUAV)
 {
-    UNIMPLEMENTED();
-    return gl::InternalError();
+    ASSERT(outUAV);
+    D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
+    uavDesc.Format                = format;
+    uavDesc.ViewDimension         = D3D11_UAV_DIMENSION_TEXTURE3D;
+    uavDesc.Texture3D.MipSlice    = mTopLevel + level;
+    uavDesc.Texture3D.FirstWSlice = 0;
+    uavDesc.Texture3D.WSize       = mTextureDepth;
+    ANGLE_TRY(mRenderer->allocateResource(uavDesc, texture.get(), outUAV));
+    outUAV->setDebugName("TexStorage3D.UAVForImage");
+    return gl::NoError();
 }
 
 gl::Error TextureStorage11_3D::getRenderTarget(const gl::Context *context,
@@ -2978,8 +3008,17 @@
                                                       const TextureHelper11 &texture,
                                                       d3d11::SharedSRV *outSRV)
 {
-    UNIMPLEMENTED();
-    return gl::InternalError();
+    ASSERT(outSRV);
+    D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
+    srvDesc.Format                         = format;
+    srvDesc.ViewDimension                  = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
+    srvDesc.Texture2DArray.MostDetailedMip = mTopLevel + level;
+    srvDesc.Texture2DArray.MipLevels       = 1;
+    srvDesc.Texture2DArray.FirstArraySlice = 0;
+    srvDesc.Texture2DArray.ArraySize       = mTextureDepth;
+    ANGLE_TRY(mRenderer->allocateResource(srvDesc, texture.get(), outSRV));
+    outSRV->setDebugName("TexStorage2DArray.SRVForImage");
+    return gl::NoError();
 }
 
 gl::Error TextureStorage11_2DArray::createUAVForImage(const gl::Context *context,
@@ -2988,8 +3027,16 @@
                                                       const TextureHelper11 &texture,
                                                       d3d11::SharedUAV *outUAV)
 {
-    UNIMPLEMENTED();
-    return gl::InternalError();
+    ASSERT(outUAV);
+    D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
+    uavDesc.Format                         = format;
+    uavDesc.ViewDimension                  = D3D11_UAV_DIMENSION_TEXTURE2DARRAY;
+    uavDesc.Texture2DArray.MipSlice        = mTopLevel + level;
+    uavDesc.Texture2DArray.FirstArraySlice = 0;
+    uavDesc.Texture2DArray.ArraySize       = mTextureDepth;
+    ANGLE_TRY(mRenderer->allocateResource(uavDesc, texture.get(), outUAV));
+    outUAV->setDebugName("TexStorage2DArray.UAVForImage");
+    return gl::NoError();
 }
 
 gl::Error TextureStorage11_2DArray::createRenderTargetSRV(const TextureHelper11 &texture,
diff --git a/src/tests/gl_tests/ComputeShaderTest.cpp b/src/tests/gl_tests/ComputeShaderTest.cpp
index 1c5d7e2..c24f831 100644
--- a/src/tests/gl_tests/ComputeShaderTest.cpp
+++ b/src/tests/gl_tests/ComputeShaderTest.cpp
@@ -6,9 +6,9 @@
 // ComputeShaderTest:
 //   Compute shader specific tests.
 
+#include <vector>
 #include "test_utils/ANGLETest.h"
 #include "test_utils/gl_raii.h"
-#include <vector>
 
 using namespace angle;
 
@@ -526,7 +526,7 @@
     EXPECT_GL_NO_ERROR();
 }
 
-// Use image uniform to read and write textures in compute shader, and verify the contents.
+// Use image uniform to read and write Texture2D in compute shader, and verify the contents.
 TEST_P(ComputeShaderTest, BindImageTextureWithTexture2D)
 {
     GLTexture texture[2];
@@ -608,6 +608,326 @@
     }
 }
 
+// Use image uniform to read and write Texture2DArray in compute shader, and verify the contents.
+TEST_P(ComputeShaderTest, BindImageTextureWithTexture2DArray)
+{
+    GLTexture texture[2];
+    GLFramebuffer framebuffer;
+    const std::string csSource =
+        R"(#version 310 es
+        layout(local_size_x=2, local_size_y=2, local_size_z=2) in;
+        layout(r32ui, binding = 0) readonly uniform highp uimage2DArray uImage_1;
+        layout(r32ui, binding = 1) writeonly uniform highp uimage2DArray uImage_2;
+        void main()
+        {
+            uvec4 value = imageLoad(uImage_1, ivec3(gl_LocalInvocationID.xyz));
+            imageStore(uImage_2, ivec3(gl_LocalInvocationID.xyz), value);
+        })";
+
+    constexpr int kWidth = 2, kHeight = 2, kDepth = 2;
+    constexpr GLuint kInputValues[2][8] = {{200, 200, 200, 200, 200, 200, 200, 200},
+                                           {100, 100, 100, 100, 100, 100, 100, 100}};
+
+    glBindTexture(GL_TEXTURE_2D_ARRAY, texture[0]);
+    glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_R32UI, kWidth, kHeight, kDepth);
+    glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, kWidth, kHeight, kDepth, GL_RED_INTEGER,
+                    GL_UNSIGNED_INT, kInputValues[0]);
+    EXPECT_GL_NO_ERROR();
+
+    glBindTexture(GL_TEXTURE_2D_ARRAY, texture[1]);
+    glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_R32UI, kWidth, kHeight, kDepth);
+    glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, kWidth, kHeight, kDepth, GL_RED_INTEGER,
+                    GL_UNSIGNED_INT, kInputValues[1]);
+    EXPECT_GL_NO_ERROR();
+
+    glUseProgram(0);
+    GLuint outputValues[4];
+    constexpr GLuint expectedValue_1 = 200;
+    constexpr GLuint expectedValue_2 = 100;
+    glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
+
+    glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture[0], 0, 0);
+    glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, texture[0], 0, 1);
+    EXPECT_GL_NO_ERROR();
+
+    glReadBuffer(GL_COLOR_ATTACHMENT0);
+    glReadPixels(0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT, outputValues);
+    for (int i = 0; i < kWidth * kHeight; i++)
+    {
+        EXPECT_EQ(expectedValue_1, outputValues[i]);
+    }
+
+    glReadBuffer(GL_COLOR_ATTACHMENT1);
+    glReadPixels(0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT, outputValues);
+
+    for (int i = 0; i < kWidth * kHeight; i++)
+    {
+        EXPECT_EQ(expectedValue_1, outputValues[i]);
+    }
+
+    glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture[1], 0, 0);
+    glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, texture[1], 0, 1);
+    EXPECT_GL_NO_ERROR();
+
+    glReadBuffer(GL_COLOR_ATTACHMENT0);
+    glReadPixels(0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT, outputValues);
+    for (int i = 0; i < kWidth * kHeight; i++)
+    {
+        EXPECT_EQ(expectedValue_2, outputValues[i]);
+    }
+
+    glReadBuffer(GL_COLOR_ATTACHMENT1);
+    glReadPixels(0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT, outputValues);
+    for (int i = 0; i < kWidth * kHeight; i++)
+    {
+        EXPECT_EQ(expectedValue_2, outputValues[i]);
+    }
+
+    ANGLE_GL_COMPUTE_PROGRAM(program, csSource);
+    glUseProgram(program.get());
+
+    glBindImageTexture(0, texture[0], 0, GL_TRUE, 0, GL_READ_ONLY, GL_R32UI);
+    EXPECT_GL_NO_ERROR();
+
+    glBindImageTexture(1, texture[1], 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_R32UI);
+    EXPECT_GL_NO_ERROR();
+
+    glDispatchCompute(1, 1, 1);
+    EXPECT_GL_NO_ERROR();
+
+    glUseProgram(0);
+    glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
+
+    glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture[1], 0, 0);
+    glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, texture[1], 0, 1);
+    EXPECT_GL_NO_ERROR();
+    glReadBuffer(GL_COLOR_ATTACHMENT0);
+    glReadPixels(0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT, outputValues);
+    EXPECT_GL_NO_ERROR();
+    for (int i = 0; i < kWidth * kHeight; i++)
+    {
+        EXPECT_EQ(expectedValue_1, outputValues[i]);
+    }
+    glReadBuffer(GL_COLOR_ATTACHMENT1);
+    glReadPixels(0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT, outputValues);
+    EXPECT_GL_NO_ERROR();
+    for (int i = 0; i < kWidth * kHeight; i++)
+    {
+        EXPECT_EQ(expectedValue_1, outputValues[i]);
+    }
+}
+
+// Use image uniform to read and write Texture3D in compute shader, and verify the contents.
+TEST_P(ComputeShaderTest, BindImageTextureWithTexture3D)
+{
+    GLTexture texture[2];
+    GLFramebuffer framebuffer;
+    const std::string csSource =
+        R"(#version 310 es
+        layout(local_size_x=2, local_size_y=2, local_size_z=2) in;
+        layout(r32ui, binding = 0) readonly uniform highp uimage3D uImage_1;
+        layout(r32ui, binding = 1) writeonly uniform highp uimage3D uImage_2;
+        void main()
+        {
+            uvec4 value = imageLoad(uImage_1, ivec3(gl_LocalInvocationID.xyz));
+            imageStore(uImage_2, ivec3(gl_LocalInvocationID.xyz), value);
+        })";
+
+    constexpr int kWidth = 2, kHeight = 2, kDepth = 2;
+    constexpr GLuint kInputValues[2][8] = {{200, 200, 200, 200, 200, 200, 200, 200},
+                                           {100, 100, 100, 100, 100, 100, 100, 100}};
+
+    glBindTexture(GL_TEXTURE_3D, texture[0]);
+    glTexStorage3D(GL_TEXTURE_3D, 1, GL_R32UI, kWidth, kHeight, kDepth);
+    glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, kWidth, kHeight, kDepth, GL_RED_INTEGER,
+                    GL_UNSIGNED_INT, kInputValues[0]);
+    EXPECT_GL_NO_ERROR();
+
+    glBindTexture(GL_TEXTURE_3D, texture[1]);
+    glTexStorage3D(GL_TEXTURE_3D, 1, GL_R32UI, kWidth, kHeight, kDepth);
+    glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, kWidth, kHeight, kDepth, GL_RED_INTEGER,
+                    GL_UNSIGNED_INT, kInputValues[1]);
+    EXPECT_GL_NO_ERROR();
+
+    glUseProgram(0);
+    GLuint outputValues[4];
+    constexpr GLuint expectedValue_1 = 200;
+    constexpr GLuint expectedValue_2 = 100;
+    glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
+
+    glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture[0], 0, 0);
+    glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, texture[0], 0, 1);
+    EXPECT_GL_NO_ERROR();
+
+    glReadBuffer(GL_COLOR_ATTACHMENT0);
+    glReadPixels(0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT, outputValues);
+    for (int i = 0; i < kWidth * kHeight; i++)
+    {
+        EXPECT_EQ(expectedValue_1, outputValues[i]);
+    }
+
+    glReadBuffer(GL_COLOR_ATTACHMENT1);
+    glReadPixels(0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT, outputValues);
+
+    for (int i = 0; i < kWidth * kHeight; i++)
+    {
+        EXPECT_EQ(expectedValue_1, outputValues[i]);
+    }
+
+    glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture[1], 0, 0);
+    glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, texture[1], 0, 1);
+    EXPECT_GL_NO_ERROR();
+
+    glReadBuffer(GL_COLOR_ATTACHMENT0);
+    glReadPixels(0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT, outputValues);
+    for (int i = 0; i < kWidth * kHeight; i++)
+    {
+        EXPECT_EQ(expectedValue_2, outputValues[i]);
+    }
+
+    glReadBuffer(GL_COLOR_ATTACHMENT1);
+    glReadPixels(0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT, outputValues);
+    for (int i = 0; i < kWidth * kHeight; i++)
+    {
+        EXPECT_EQ(expectedValue_2, outputValues[i]);
+    }
+
+    ANGLE_GL_COMPUTE_PROGRAM(program, csSource);
+    glUseProgram(program.get());
+
+    glBindImageTexture(0, texture[0], 0, GL_TRUE, 0, GL_READ_ONLY, GL_R32UI);
+    EXPECT_GL_NO_ERROR();
+
+    glBindImageTexture(1, texture[1], 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_R32UI);
+    EXPECT_GL_NO_ERROR();
+
+    glDispatchCompute(1, 1, 1);
+    EXPECT_GL_NO_ERROR();
+
+    glUseProgram(0);
+    glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
+
+    glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture[1], 0, 0);
+    glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, texture[1], 0, 1);
+    EXPECT_GL_NO_ERROR();
+    glReadBuffer(GL_COLOR_ATTACHMENT0);
+    glReadPixels(0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT, outputValues);
+    EXPECT_GL_NO_ERROR();
+    for (int i = 0; i < kWidth * kHeight; i++)
+    {
+        EXPECT_EQ(expectedValue_1, outputValues[i]);
+    }
+    glReadBuffer(GL_COLOR_ATTACHMENT1);
+    glReadPixels(0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT, outputValues);
+    EXPECT_GL_NO_ERROR();
+    for (int i = 0; i < kWidth * kHeight; i++)
+    {
+        EXPECT_EQ(expectedValue_1, outputValues[i]);
+    }
+}
+
+// Use image uniform to read and write TextureCube in compute shader, and verify the contents.
+TEST_P(ComputeShaderTest, BindImageTextureWithTextureCube)
+{
+    GLTexture texture[2];
+    GLFramebuffer framebuffer;
+    const std::string csSource =
+        R"(#version 310 es
+        layout(local_size_x=2, local_size_y=2, local_size_z=1) in;
+        layout(r32ui, binding = 0) readonly uniform highp uimageCube uImage_1;
+        layout(r32ui, binding = 1) writeonly uniform highp uimageCube uImage_2;
+        void main()
+        {
+            for (int i = 0; i < 6; i++)
+            {
+                uvec4 value = imageLoad(uImage_1, ivec3(gl_LocalInvocationID.xy, i));
+                imageStore(uImage_2, ivec3(gl_LocalInvocationID.xy, i), value);
+            }
+        })";
+
+    constexpr int kWidth = 2, kHeight = 2;
+    constexpr GLuint kInputValues[2][4] = {{200, 200, 200, 200}, {100, 100, 100, 100}};
+
+    glBindTexture(GL_TEXTURE_CUBE_MAP, texture[0]);
+    glTexStorage2D(GL_TEXTURE_CUBE_MAP, 1, GL_R32UI, kWidth, kHeight);
+    for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
+         face++)
+    {
+        glTexSubImage2D(face, 0, 0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT,
+                        kInputValues[0]);
+    }
+    EXPECT_GL_NO_ERROR();
+
+    glBindTexture(GL_TEXTURE_CUBE_MAP, texture[1]);
+    glTexStorage2D(GL_TEXTURE_CUBE_MAP, 1, GL_R32UI, kWidth, kHeight);
+    for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
+         face++)
+    {
+        glTexSubImage2D(face, 0, 0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT,
+                        kInputValues[1]);
+    }
+    EXPECT_GL_NO_ERROR();
+
+    glUseProgram(0);
+    GLuint outputValues[4];
+    constexpr GLuint expectedValue_1 = 200;
+    constexpr GLuint expectedValue_2 = 100;
+    glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
+    for (GLenum face = 0; face < 6; face++)
+    {
+        glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                               GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, texture[0], 0);
+        EXPECT_GL_NO_ERROR();
+        glReadPixels(0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT, outputValues);
+        EXPECT_GL_NO_ERROR();
+
+        for (int i = 0; i < kWidth * kHeight; i++)
+        {
+            EXPECT_EQ(expectedValue_1, outputValues[i]);
+        }
+
+        glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                               GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, texture[1], 0);
+        EXPECT_GL_NO_ERROR();
+        glReadPixels(0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT, outputValues);
+        EXPECT_GL_NO_ERROR();
+
+        for (int i = 0; i < kWidth * kHeight; i++)
+        {
+            EXPECT_EQ(expectedValue_2, outputValues[i]);
+        }
+    }
+
+    ANGLE_GL_COMPUTE_PROGRAM(program, csSource);
+    glUseProgram(program.get());
+
+    glBindImageTexture(0, texture[0], 0, GL_TRUE, 0, GL_READ_ONLY, GL_R32UI);
+    EXPECT_GL_NO_ERROR();
+
+    glBindImageTexture(1, texture[1], 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_R32UI);
+    EXPECT_GL_NO_ERROR();
+
+    glDispatchCompute(1, 1, 1);
+    EXPECT_GL_NO_ERROR();
+
+    glUseProgram(0);
+    glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
+
+    for (GLenum face = 0; face < 6; face++)
+    {
+        glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                               GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, texture[1], 0);
+        EXPECT_GL_NO_ERROR();
+        glReadPixels(0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT, outputValues);
+        EXPECT_GL_NO_ERROR();
+
+        for (int i = 0; i < kWidth * kHeight; i++)
+        {
+            EXPECT_EQ(expectedValue_1, outputValues[i]);
+        }
+    }
+}
+
 // Check that it is not possible to create a compute shader when the context does not support ES
 // 3.10
 TEST_P(ComputeShaderTestES3, NotSupported)