ES31: Support compute shader shared variables in HLSL

This patch implements 'shared' variables in compute shader on D3D11
back-ends. GLSL shared variables are translated into 'groupshared'
ones in HLSL.

Note that although HLSL allows initializing the variables with
'groupshared' qualifier, currently we do not initialize them because:
1. It is very slow to for d3d11 drivers to compile the compute shader if
   we add the code to initialize a shared variable with large array size.
2. It seems unnecessary to do so and in GLSL it is not allowed to
   initialize a shared variable in the declaration. (ESSL 3.1, Chapter
   4.3.8)

BUG=angleproject:2682
TEST=angle_end2end_tests

Change-Id: Ica8247e1b98059968612a36e369718ef113a598c
Reviewed-on: https://chromium-review.googlesource.com/1109587
Reviewed-by: Jiajia Qin <jiajia.qin@intel.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
diff --git a/src/tests/gl_tests/ComputeShaderTest.cpp b/src/tests/gl_tests/ComputeShaderTest.cpp
index 44d52ea..bade1f5 100644
--- a/src/tests/gl_tests/ComputeShaderTest.cpp
+++ b/src/tests/gl_tests/ComputeShaderTest.cpp
@@ -19,6 +19,57 @@
 {
   protected:
     ComputeShaderTest() {}
+
+    template <GLint kWidth, GLint kHeight>
+    void runSharedMemoryTest(const char *csSource,
+                             const std::array<GLuint, kWidth * kHeight> &inputData,
+                             const std::array<GLuint, kWidth * kHeight> &expectedValues)
+    {
+        GLTexture texture[2];
+        GLFramebuffer framebuffer;
+
+        glBindTexture(GL_TEXTURE_2D, texture[0]);
+        glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, kWidth, kHeight);
+        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT,
+                        inputData.data());
+        EXPECT_GL_NO_ERROR();
+
+        constexpr GLuint initData[kWidth * kHeight] = {};
+        glBindTexture(GL_TEXTURE_2D, texture[1]);
+        glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, kWidth, kHeight);
+        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT,
+                        initData);
+        EXPECT_GL_NO_ERROR();
+
+        ANGLE_GL_COMPUTE_PROGRAM(program, csSource);
+        glUseProgram(program.get());
+
+        glBindImageTexture(0, texture[0], 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32UI);
+        EXPECT_GL_NO_ERROR();
+
+        glBindImageTexture(1, texture[1], 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32UI);
+        EXPECT_GL_NO_ERROR();
+
+        glDispatchCompute(1, 1, 1);
+        EXPECT_GL_NO_ERROR();
+
+        glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT);
+
+        GLuint outputValues[kWidth * kHeight];
+        glUseProgram(0);
+        glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
+
+        glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 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(expectedValues[i], outputValues[i]);
+        }
+    }
 };
 
 class ComputeShaderTestES3 : public ANGLETest
@@ -1305,6 +1356,91 @@
     EXPECT_GL_NO_ERROR();
 }
 
+// Verify shared non-array variables can work correctly.
+TEST_P(ComputeShaderTest, NonArraySharedVariable)
+{
+    const char kCSShader[] =
+        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 uimage2D srcImage;
+        layout (r32ui, binding = 1) writeonly uniform highp uimage2D dstImage;
+        shared uint temp;
+        void main()
+        {
+            if (gl_LocalInvocationID == uvec3(0, 0, 0))
+            {
+                temp = imageLoad(srcImage, ivec2(gl_LocalInvocationID.xy)).x;
+            }
+            groupMemoryBarrier();
+            barrier();
+            if (gl_LocalInvocationID == uvec3(1, 1, 0))
+            {
+                imageStore(dstImage, ivec2(gl_LocalInvocationID.xy), uvec4(temp));
+            }
+            else
+            {
+                uint inputValue = imageLoad(srcImage, ivec2(gl_LocalInvocationID.xy)).x;
+                imageStore(dstImage, ivec2(gl_LocalInvocationID.xy), uvec4(inputValue));
+            }
+        })";
+
+    const std::array<GLuint, 4> inputData      = {{250, 200, 150, 100}};
+    const std::array<GLuint, 4> expectedValues = {{250, 200, 150, 250}};
+    runSharedMemoryTest<2, 2>(kCSShader, inputData, expectedValues);
+}
+
+// Verify shared non-struct array variables can work correctly.
+TEST_P(ComputeShaderTest, NonStructArrayAsSharedVariable)
+{
+    const char kCSShader[] =
+        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 uimage2D srcImage;
+        layout (r32ui, binding = 1) writeonly uniform highp uimage2D dstImage;
+        shared uint sharedData[2][2];
+        void main()
+        {
+            uint inputData = imageLoad(srcImage, ivec2(gl_LocalInvocationID.xy)).x;
+            sharedData[gl_LocalInvocationID.x][gl_LocalInvocationID.y] = inputData;
+            groupMemoryBarrier();
+            barrier();
+            imageStore(dstImage, ivec2(gl_LocalInvocationID.xy),
+                       uvec4(sharedData[gl_LocalInvocationID.y][gl_LocalInvocationID.x]));
+        })";
+
+    const std::array<GLuint, 4> inputData      = {{250, 200, 150, 100}};
+    const std::array<GLuint, 4> expectedValues = {{250, 150, 200, 100}};
+    runSharedMemoryTest<2, 2>(kCSShader, inputData, expectedValues);
+}
+
+// Verify shared struct array variables work correctly.
+TEST_P(ComputeShaderTest, StructArrayAsSharedVariable)
+{
+    const char kCSShader[] =
+        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 uimage2D srcImage;
+        layout (r32ui, binding = 1) writeonly uniform highp uimage2D dstImage;
+        struct SharedStruct
+        {
+            uint data;
+        };
+        shared SharedStruct sharedData[2][2];
+        void main()
+        {
+            uint inputData = imageLoad(srcImage, ivec2(gl_LocalInvocationID.xy)).x;
+            sharedData[gl_LocalInvocationID.x][gl_LocalInvocationID.y].data = inputData;
+            groupMemoryBarrier();
+            barrier();
+            imageStore(dstImage, ivec2(gl_LocalInvocationID.xy),
+                       uvec4(sharedData[gl_LocalInvocationID.y][gl_LocalInvocationID.x].data));
+        })";
+
+    const std::array<GLuint, 4> inputData      = {{250, 200, 150, 100}};
+    const std::array<GLuint, 4> expectedValues = {{250, 150, 200, 100}};
+    runSharedMemoryTest<2, 2>(kCSShader, inputData, expectedValues);
+}
+
 // Check that it is not possible to create a compute shader when the context does not support ES
 // 3.10
 TEST_P(ComputeShaderTestES3, NotSupported)