Edit

kc3-lang/angle/src/tests/gl_tests/ShaderStorageBufferTest.cpp

Branch :

  • Show log

    Commit

  • Author : Shahbaz Youssefi
    Date : 2019-12-06 22:31:08
    Hash : 44722daa
    Message : Vulkan: Enable inactive SSBO with unsized array tests The change introduced in https://chromium-review.googlesource.com/c/angle/angle/+/1951523 removes inactive shader interface declarations. That automatically resolves an issue where glslang wrapper doesn't handle inactive SSBO declarations with unsized arrays, by removing those declarations at translation time altogether. Bug: angleproject:3714 Change-Id: I710d59546d716bfb5bc0112b5152fed20a810a52 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1954615 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Courtney Goeltzenleuchter <courtneygo@google.com> Reviewed-by: Jamie Madill <jmadill@chromium.org>

  • src/tests/gl_tests/ShaderStorageBufferTest.cpp
  • //
    // Copyright 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.
    //
    // ShaderStorageBufferTest:
    //   Various tests related for shader storage buffers.
    //
    
    #include "test_utils/ANGLETest.h"
    #include "test_utils/gl_raii.h"
    
    using namespace angle;
    
    namespace
    {
    
    struct MatrixCase
    {
        MatrixCase(unsigned cols,
                   unsigned rows,
                   unsigned matrixStride,
                   const char *computeShaderSource,
                   const float *inputData)
            : mColumns(cols),
              mRows(rows),
              mMatrixStride(matrixStride),
              mComputeShaderSource(computeShaderSource),
              mInputdata(inputData)
        {}
        unsigned int mColumns;
        unsigned int mRows;
        unsigned int mMatrixStride;
        const char *mComputeShaderSource;
        const float *mInputdata;
        const unsigned int kBytesPerComponent = sizeof(float);
    };
    
    struct VectorCase
    {
        VectorCase(unsigned components,
                   const char *computeShaderSource,
                   const GLuint *inputData,
                   const GLuint *expectedData)
            : mComponents(components),
              mComputeShaderSource(computeShaderSource),
              mInputdata(inputData),
              mExpectedData(expectedData)
        {}
        unsigned int mComponents;
        const char *mComputeShaderSource;
        const GLuint *mInputdata;
        const GLuint *mExpectedData;
        const unsigned int kBytesPerComponent = sizeof(GLuint);
    };
    
    class ShaderStorageBufferTest31 : public ANGLETest
    {
      protected:
        ShaderStorageBufferTest31()
        {
            setWindowWidth(128);
            setWindowHeight(128);
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
    
            // Test flakiness was noticed when reusing displays.
            forceNewDisplay();
        }
    
        void runMatrixTest(const MatrixCase &matrixCase)
        {
            ANGLE_GL_COMPUTE_PROGRAM(program, matrixCase.mComputeShaderSource);
            glUseProgram(program);
    
            // Create shader storage buffer
            GLBuffer shaderStorageBuffer[2];
            glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]);
            glBufferData(GL_SHADER_STORAGE_BUFFER, matrixCase.mRows * matrixCase.mMatrixStride,
                         matrixCase.mInputdata, GL_STATIC_DRAW);
            glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
            glBufferData(GL_SHADER_STORAGE_BUFFER, matrixCase.mRows * matrixCase.mMatrixStride, nullptr,
                         GL_STATIC_DRAW);
    
            // Bind shader storage buffer
            glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]);
            glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]);
    
            glDispatchCompute(1, 1, 1);
            glFinish();
    
            // Read back shader storage buffer
            glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
            const GLfloat *ptr = reinterpret_cast<const GLfloat *>(
                glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0,
                                 matrixCase.mRows * matrixCase.mMatrixStride, GL_MAP_READ_BIT));
    
            for (unsigned int row = 0; row < matrixCase.mRows; row++)
            {
                for (unsigned int col = 0; col < matrixCase.mColumns; col++)
                {
                    GLfloat expected = matrixCase.mInputdata[row * (matrixCase.mMatrixStride /
                                                                    matrixCase.kBytesPerComponent) +
                                                             col];
                    GLfloat actual =
                        *(ptr + row * (matrixCase.mMatrixStride / matrixCase.kBytesPerComponent) + col);
    
                    EXPECT_EQ(expected, actual) << " at row " << row << " and column " << col;
                }
            }
    
            EXPECT_GL_NO_ERROR();
        }
    
        void runVectorTest(const VectorCase &vectorCase)
        {
            ANGLE_GL_COMPUTE_PROGRAM(program, vectorCase.mComputeShaderSource);
            glUseProgram(program);
    
            // Create shader storage buffer
            GLBuffer shaderStorageBuffer[2];
            glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]);
            glBufferData(GL_SHADER_STORAGE_BUFFER,
                         vectorCase.mComponents * vectorCase.kBytesPerComponent, vectorCase.mInputdata,
                         GL_STATIC_DRAW);
            glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
            glBufferData(GL_SHADER_STORAGE_BUFFER,
                         vectorCase.mComponents * vectorCase.kBytesPerComponent, nullptr,
                         GL_STATIC_DRAW);
    
            // Bind shader storage buffer
            glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]);
            glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]);
    
            glDispatchCompute(1, 1, 1);
            glFinish();
    
            // Read back shader storage buffer
            glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
            const GLuint *ptr = reinterpret_cast<const GLuint *>(glMapBufferRange(
                GL_SHADER_STORAGE_BUFFER, 0, vectorCase.mComponents * vectorCase.kBytesPerComponent,
                GL_MAP_READ_BIT));
            for (unsigned int idx = 0; idx < vectorCase.mComponents; idx++)
            {
                EXPECT_EQ(vectorCase.mExpectedData[idx], *(ptr + idx));
            }
    
            EXPECT_GL_NO_ERROR();
        }
    };
    
    // Matched block names within a shader interface must match in terms of having the same number of
    // declarations with the same sequence of types.
    TEST_P(ShaderStorageBufferTest31, MatchedBlockNameWithDifferentMemberType)
    {
        constexpr char kVS[] =
            "#version 310 es\n"
            "buffer blockName {\n"
            "    float data;\n"
            "};\n"
            "void main()\n"
            "{\n"
            "}\n";
        constexpr char kFS[] =
            "#version 310 es\n"
            "buffer blockName {\n"
            "    uint data;\n"
            "};\n"
            "void main()\n"
            "{\n"
            "}\n";
    
        GLuint program = CompileProgram(kVS, kFS);
        EXPECT_EQ(0u, program);
    }
    
    // Linking should fail if blocks in vertex shader exceed GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS.
    TEST_P(ShaderStorageBufferTest31, ExceedMaxVertexShaderStorageBlocks)
    {
        std::ostringstream instanceCount;
        GLint maxVertexShaderStorageBlocks = 0;
        glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &maxVertexShaderStorageBlocks);
        EXPECT_GL_NO_ERROR();
        instanceCount << maxVertexShaderStorageBlocks;
    
        const std::string &vertexShaderSource =
            "#version 310 es\n"
            "layout(shared) buffer blockName {\n"
            "    uint data;\n"
            "} instance[" +
            instanceCount.str() +
            " + 1];\n"
            "void main()\n"
            "{\n"
            "}\n";
        constexpr char kFS[] =
            "#version 310 es\n"
            "void main()\n"
            "{\n"
            "}\n";
    
        GLuint program = CompileProgram(vertexShaderSource.c_str(), kFS);
        EXPECT_EQ(0u, program);
    }
    
    // Linking should fail if the sum of the number of active shader storage blocks exceeds
    // MAX_COMBINED_SHADER_STORAGE_BLOCKS.
    TEST_P(ShaderStorageBufferTest31, ExceedMaxCombinedShaderStorageBlocks)
    {
        std::ostringstream vertexInstanceCount;
        GLint maxVertexShaderStorageBlocks = 0;
        glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &maxVertexShaderStorageBlocks);
        vertexInstanceCount << maxVertexShaderStorageBlocks;
    
        GLint maxFragmentShaderStorageBlocks = 0;
        glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &maxFragmentShaderStorageBlocks);
    
        GLint maxCombinedShaderStorageBlocks = 0;
        glGetIntegerv(GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS, &maxCombinedShaderStorageBlocks);
        EXPECT_GL_NO_ERROR();
    
        ASSERT_GE(maxCombinedShaderStorageBlocks, maxVertexShaderStorageBlocks);
        ASSERT_GE(maxCombinedShaderStorageBlocks, maxFragmentShaderStorageBlocks);
    
        // As SPEC allows MAX_VERTEX_SHADER_STORAGE_BLOCKS and MAX_FRAGMENT_SHADER_STORAGE_BLOCKS to be
        // 0, in this situation we should skip this test to prevent these unexpected compile errors.
        ANGLE_SKIP_TEST_IF(maxVertexShaderStorageBlocks == 0 || maxFragmentShaderStorageBlocks == 0);
    
        GLint fragmentShaderStorageBlocks =
            maxCombinedShaderStorageBlocks - maxVertexShaderStorageBlocks + 1;
        ANGLE_SKIP_TEST_IF(fragmentShaderStorageBlocks > maxFragmentShaderStorageBlocks);
    
        std::ostringstream fragmentInstanceCount;
        fragmentInstanceCount << fragmentShaderStorageBlocks;
    
        const std::string &vertexShaderSource =
            "#version 310 es\n"
            "layout(shared) buffer blockName0 {\n"
            "    uint data;\n"
            "} instance0[" +
            vertexInstanceCount.str() +
            "];\n"
            "void main()\n"
            "{\n"
            "}\n";
        const std::string &fragmentShaderSource =
            "#version 310 es\n"
            "layout(shared) buffer blockName1 {\n"
            "    uint data;\n"
            "} instance1[" +
            fragmentInstanceCount.str() +
            "];\n"
            "void main()\n"
            "{\n"
            "}\n";
    
        GLuint program = CompileProgram(vertexShaderSource.c_str(), fragmentShaderSource.c_str());
        EXPECT_EQ(0u, program);
    }
    
    // Test shader storage buffer read write.
    TEST_P(ShaderStorageBufferTest31, ShaderStorageBufferReadWrite)
    {
        constexpr char kCS[] =
            "#version 310 es\n"
            "layout(local_size_x=1, local_size_y=1, local_size_z=1) in;\n"
            "layout(std140, binding = 1) buffer blockName {\n"
            "    uint data[2];\n"
            "} instanceName;\n"
            "void main()\n"
            "{\n"
            "    instanceName.data[0] = 3u;\n"
            "    instanceName.data[1] = 4u;\n"
            "}\n";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kCS);
    
        glUseProgram(program.get());
    
        constexpr unsigned int kElementCount = 2;
        // The array stride are rounded up to the base alignment of a vec4 for std140 layout.
        constexpr unsigned int kArrayStride = 16;
        // Create shader storage buffer
        GLBuffer shaderStorageBuffer;
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kElementCount * kArrayStride, nullptr, GL_STATIC_DRAW);
    
        // Bind shader storage buffer
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer);
    
        // Dispath compute
        glDispatchCompute(1, 1, 1);
    
        glFinish();
    
        // Read back shader storage buffer
        constexpr unsigned int kExpectedValues[2] = {3u, 4u};
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer);
        void *ptr = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kElementCount * kArrayStride,
                                     GL_MAP_READ_BIT);
        for (unsigned int idx = 0; idx < kElementCount; idx++)
        {
            EXPECT_EQ(kExpectedValues[idx],
                      *(reinterpret_cast<const GLuint *>(reinterpret_cast<const GLbyte *>(ptr) +
                                                         idx * kArrayStride)));
        }
        glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Tests modifying an existing shader storage buffer
    TEST_P(ShaderStorageBufferTest31, ShaderStorageBufferReadWriteSame)
    {
        // Missing PBO support in Vulkan.  http://anglebug.com/3210
        ANGLE_SKIP_TEST_IF(IsVulkan());
    
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
    layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
    layout(std140, binding = 0) buffer block {
        uint data;
    } instance;
    void main()
    {
        uint temp = instance.data;
        instance.data = temp + 1u;
    }
    )";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
    
        glUseProgram(program);
    
        constexpr unsigned int kBytesPerComponent = sizeof(GLuint);
        constexpr unsigned int kInitialData       = 123u;
    
        // Create shader storage buffer
        GLBuffer shaderStorageBuffer;
    
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kBytesPerComponent, &kInitialData, GL_STATIC_DRAW);
    
        // Bind shader storage buffer
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer);
    
        glDispatchCompute(1, 1, 1);
    
        glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT | GL_SHADER_STORAGE_BARRIER_BIT);
    
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer);
        const void *bufferData =
            glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kBytesPerComponent, GL_MAP_READ_BIT);
    
        constexpr unsigned int kExpectedData = 124u;
        EXPECT_EQ(kExpectedData, *static_cast<const GLuint *>(bufferData));
        glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
    
        // Running shader twice to make sure that the buffer gets updated correctly 123->124->125
        glDispatchCompute(1, 1, 1);
    
        glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT | GL_PIXEL_BUFFER_BARRIER_BIT);
    
        bufferData = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kBytesPerComponent, GL_MAP_READ_BIT);
    
        constexpr unsigned int kExpectedData2 = 125u;
        EXPECT_EQ(kExpectedData2, *static_cast<const GLuint *>(bufferData));
    
        // Verify re-using the SSBO buffer with a PBO contains expected data.
        // This will read-back from FBO using a PBO into the same SSBO buffer.
    
        glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
    
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, getWindowWidth(), getWindowHeight());
    
        GLFramebuffer framebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
    
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
    
        glBindBuffer(GL_PIXEL_PACK_BUFFER, shaderStorageBuffer);
        glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 0);
        EXPECT_GL_NO_ERROR();
    
        void *mappedPtr =
            glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, kBytesPerComponent, GL_MAP_READ_BIT);
        GLColor *dataColor = static_cast<GLColor *>(mappedPtr);
        EXPECT_GL_NO_ERROR();
    
        EXPECT_EQ(GLColor::red, dataColor[0]);
        glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
        EXPECT_GL_NO_ERROR();
    
        // Verify that binding the buffer back to the SSBO keeps the expected data.
    
        glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
    
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer);
        const GLColor *ptr = reinterpret_cast<GLColor *>(
            glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kBytesPerComponent, GL_MAP_READ_BIT));
        EXPECT_EQ(GLColor::red, *ptr);
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Tests reading and writing to a shader storage buffer bound at an offset.
    TEST_P(ShaderStorageBufferTest31, ShaderStorageBufferReadWriteOffset)
    {
        // http://anglebug.com/4092
        ANGLE_SKIP_TEST_IF(isSwiftshader());
        constexpr char kCS[] = R"(#version 310 es
    layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
    
    layout(std140, binding = 0) buffer block0 {
        uint data[2];
    } instance0;
    
    void main()
    {
        instance0.data[0] = 3u;
        instance0.data[1] = 4u;
    }
    )";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kCS);
    
        glUseProgram(program);
    
        constexpr unsigned int kElementCount = 2;
        // The array stride are rounded up to the base alignment of a vec4 for std140 layout.
        constexpr unsigned int kArrayStride = 16;
        // Create shader storage buffer
        GLBuffer shaderStorageBuffer;
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer);
    
        int bufferAlignOffset;
        glGetIntegerv(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, &bufferAlignOffset);
    
        constexpr int kBufferSize = kElementCount * kArrayStride;
        const int kBufferOffset   = kBufferSize + (kBufferSize % bufferAlignOffset);
    
        glBufferData(GL_SHADER_STORAGE_BUFFER, kBufferOffset + kBufferSize, nullptr, GL_STATIC_DRAW);
    
        // Bind shader storage buffer at an offset
        glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer, kBufferOffset, kBufferSize);
        EXPECT_GL_NO_ERROR();
    
        glDispatchCompute(1, 1, 1);
        glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT | GL_SHADER_STORAGE_BARRIER_BIT);
    
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer);
    
        // Bind the buffer at a separate location
        glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer, 0, kBufferSize);
        EXPECT_GL_NO_ERROR();
    
        glDispatchCompute(1, 1, 1);
        glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
    
        // Read back shader storage buffer
        constexpr unsigned int kExpectedValues[2] = {3u, 4u};
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer);
        void *ptr = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kBufferSize, GL_MAP_READ_BIT);
        for (unsigned int idx = 0; idx < kElementCount; idx++)
        {
            EXPECT_EQ(kExpectedValues[idx],
                      *(reinterpret_cast<const GLuint *>(reinterpret_cast<const GLbyte *>(ptr) +
                                                         idx * kArrayStride)));
        }
        glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
    
        ptr = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, kBufferOffset, kBufferSize, GL_MAP_READ_BIT);
        for (unsigned int idx = 0; idx < kElementCount; idx++)
        {
            EXPECT_EQ(kExpectedValues[idx],
                      *(reinterpret_cast<const GLuint *>(reinterpret_cast<const GLbyte *>(ptr) +
                                                         idx * kArrayStride)));
        }
        glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Test that access/write to vector data in shader storage buffer.
    TEST_P(ShaderStorageBufferTest31, ShaderStorageBufferVector)
    {
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
     layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
     layout(std140, binding = 0) buffer blockIn {
         uvec2 data;
     } instanceIn;
     layout(std140, binding = 1) buffer blockOut {
         uvec2 data;
     } instanceOut;
     void main()
     {
         instanceOut.data[0] = instanceIn.data[0];
         instanceOut.data[1] = instanceIn.data[1];
     }
     )";
    
        constexpr unsigned int kComponentCount         = 2;
        constexpr GLuint kInputValues[kComponentCount] = {3u, 4u};
    
        VectorCase vectorCase(kComponentCount, kComputeShaderSource, kInputValues, kInputValues);
        runVectorTest(vectorCase);
    }
    
    // Test that the shader works well with an active SSBO but not statically used.
    TEST_P(ShaderStorageBufferTest31, ActiveSSBOButNotStaticallyUsed)
    {
        // http://anglebug.com/3725
        ANGLE_SKIP_TEST_IF(IsAndroid() && IsPixel2() && IsVulkan());
    
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
     layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
     layout(std140, binding = 0) buffer blockIn {
         uvec2 data;
     } instanceIn;
     layout(std140, binding = 1) buffer blockOut {
         uvec2 data;
     } instanceOut;
     layout(std140, binding = 2) buffer blockC {
         uvec2 data;
     } instanceC;
     void main()
     {
         instanceOut.data[0] = instanceIn.data[0];
         instanceOut.data[1] = instanceIn.data[1];
     }
     )";
    
        GLBuffer shaderStorageBufferC;
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBufferC);
        glBufferData(GL_SHADER_STORAGE_BUFFER, 32, nullptr, GL_STATIC_DRAW);
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, shaderStorageBufferC);
    
        constexpr unsigned int kComponentCount         = 2;
        constexpr GLuint kInputValues[kComponentCount] = {3u, 4u};
    
        VectorCase vectorCase(kComponentCount, kComputeShaderSource, kInputValues, kInputValues);
        runVectorTest(vectorCase);
    }
    
    // Test that access/write to swizzle scalar data in shader storage block.
    TEST_P(ShaderStorageBufferTest31, ScalarSwizzleTest)
    {
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
     layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
     layout(std140, binding = 0) buffer blockIn {
         uvec2 data;
     } instanceIn;
     layout(std140, binding = 1) buffer blockOut {
         uvec2 data;
     } instanceOut;
     void main()
     {
         instanceOut.data.x = instanceIn.data.y;
         instanceOut.data.y = instanceIn.data.x;
     }
     )";
    
        constexpr unsigned int kComponentCount            = 2;
        constexpr GLuint kInputValues[kComponentCount]    = {3u, 4u};
        constexpr GLuint kExpectedValues[kComponentCount] = {4u, 3u};
    
        VectorCase vectorCase(kComponentCount, kComputeShaderSource, kInputValues, kExpectedValues);
        runVectorTest(vectorCase);
    }
    
    // Test that access/write to swizzle vector data in shader storage block.
    TEST_P(ShaderStorageBufferTest31, VectorSwizzleTest)
    {
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
     layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
     layout(std140, binding = 0) buffer blockIn {
         uvec2 data;
     } instanceIn;
     layout(std140, binding = 1) buffer blockOut {
         uvec2 data;
     } instanceOut;
     void main()
     {
         instanceOut.data.yx = instanceIn.data.xy;
     }
     )";
    
        constexpr unsigned int kComponentCount            = 2;
        constexpr GLuint kInputValues[kComponentCount]    = {3u, 4u};
        constexpr GLuint kExpectedValues[kComponentCount] = {4u, 3u};
    
        VectorCase vectorCase(kComponentCount, kComputeShaderSource, kInputValues, kExpectedValues);
        runVectorTest(vectorCase);
    }
    
    // Test that access/write to swizzle vector data in column_major matrix in shader storage block.
    TEST_P(ShaderStorageBufferTest31, VectorSwizzleInColumnMajorMatrixTest)
    {
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
     layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
     layout(std140, binding = 0) buffer blockIn {
         layout(column_major) mat2x3 data;
     } instanceIn;
     layout(std140, binding = 1) buffer blockOut {
         layout(column_major) mat2x3 data;
     } instanceOut;
     void main()
     {
         instanceOut.data[0].xyz = instanceIn.data[0].xyz;
         instanceOut.data[1].xyz = instanceIn.data[1].xyz;
     }
     )";
    
        constexpr unsigned int kColumns                                             = 2;
        constexpr unsigned int kRows                                                = 3;
        constexpr unsigned int kBytesPerComponent                                   = sizeof(float);
        constexpr unsigned int kMatrixStride                                        = 16;
        constexpr float kInputDada[kColumns * (kMatrixStride / kBytesPerComponent)] = {
            0.1, 0.2, 0.3, 0.0, 0.4, 0.5, 0.6, 0.0};
        MatrixCase matrixCase(kRows, kColumns, kMatrixStride, kComputeShaderSource, kInputDada);
        runMatrixTest(matrixCase);
    }
    
    // Test that access/write to swizzle vector data in row_major matrix in shader storage block.
    TEST_P(ShaderStorageBufferTest31, VectorSwizzleInRowMajorMatrixTest)
    {
        ANGLE_SKIP_TEST_IF(IsAndroid());
    
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
     layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
     layout(std140, binding = 0) buffer blockIn {
         layout(row_major) mat2x3 data;
     } instanceIn;
     layout(std140, binding = 1) buffer blockOut {
         layout(row_major) mat2x3 data;
     } instanceOut;
     void main()
     {
         instanceOut.data[0].xyz = instanceIn.data[0].xyz;
         instanceOut.data[1].xyz = instanceIn.data[1].xyz;
     }
     )";
    
        constexpr unsigned int kColumns           = 2;
        constexpr unsigned int kRows              = 3;
        constexpr unsigned int kBytesPerComponent = sizeof(float);
        // std140 layout requires that base alignment and stride of arrays of scalars and vectors are
        // rounded up a multiple of the base alignment of a vec4.
        constexpr unsigned int kMatrixStride                                     = 16;
        constexpr float kInputDada[kRows * (kMatrixStride / kBytesPerComponent)] = {
            0.1, 0.2, 0.0, 0.0, 0.3, 0.4, 0.0, 0.0, 0.5, 0.6, 0.0, 0.0};
        MatrixCase matrixCase(kColumns, kRows, kMatrixStride, kComputeShaderSource, kInputDada);
        runMatrixTest(matrixCase);
    }
    
    // Test that access/write to scalar data in matrix in shader storage block with row major.
    TEST_P(ShaderStorageBufferTest31, ScalarDataInMatrixInSSBOWithRowMajorQualifier)
    {
        // TODO(jiajia.qin@intel.com): Figure out why it fails on Intel Linux platform.
        // http://anglebug.com/1951
        ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux());
        ANGLE_SKIP_TEST_IF(IsAndroid());
    
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
    layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
    layout(std140, binding = 0) buffer blockIn {
        layout(row_major) mat2x3 data;
    } instanceIn;
    layout(std140, binding = 1) buffer blockOut {
        layout(row_major) mat2x3 data;
    } instanceOut;
    void main()
    {
        instanceOut.data[0][0] = instanceIn.data[0][0];
        instanceOut.data[0][1] = instanceIn.data[0][1];
        instanceOut.data[0][2] = instanceIn.data[0][2];
        instanceOut.data[1][0] = instanceIn.data[1][0];
        instanceOut.data[1][1] = instanceIn.data[1][1];
        instanceOut.data[1][2] = instanceIn.data[1][2];
    }
    )";
    
        constexpr unsigned int kColumns           = 2;
        constexpr unsigned int kRows              = 3;
        constexpr unsigned int kBytesPerComponent = sizeof(float);
        // std140 layout requires that base alignment and stride of arrays of scalars and vectors are
        // rounded up a multiple of the base alignment of a vec4.
        constexpr unsigned int kMatrixStride                                     = 16;
        constexpr float kInputDada[kRows * (kMatrixStride / kBytesPerComponent)] = {
            0.1, 0.2, 0.0, 0.0, 0.3, 0.4, 0.0, 0.0, 0.5, 0.6, 0.0, 0.0};
        MatrixCase matrixCase(kColumns, kRows, kMatrixStride, kComputeShaderSource, kInputDada);
        runMatrixTest(matrixCase);
    }
    
    TEST_P(ShaderStorageBufferTest31, VectorDataInMatrixInSSBOWithRowMajorQualifier)
    {
        ANGLE_SKIP_TEST_IF(IsAndroid());
    
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
    layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
    layout(std140, binding = 0) buffer blockIn {
        layout(row_major) mat2x3 data;
    } instanceIn;
    layout(std140, binding = 1) buffer blockOut {
        layout(row_major) mat2x3 data;
    } instanceOut;
    void main()
    {
        instanceOut.data[0] = instanceIn.data[0];
        instanceOut.data[1] = instanceIn.data[1];
    }
    )";
    
        constexpr unsigned int kColumns           = 2;
        constexpr unsigned int kRows              = 3;
        constexpr unsigned int kBytesPerComponent = sizeof(float);
        // std140 layout requires that base alignment and stride of arrays of scalars and vectors are
        // rounded up a multiple of the base alignment of a vec4.
        constexpr unsigned int kMatrixStride                                     = 16;
        constexpr float kInputDada[kRows * (kMatrixStride / kBytesPerComponent)] = {
            0.1, 0.2, 0.0, 0.0, 0.3, 0.4, 0.0, 0.0, 0.5, 0.6, 0.0, 0.0};
        MatrixCase matrixCase(kColumns, kRows, kMatrixStride, kComputeShaderSource, kInputDada);
        runMatrixTest(matrixCase);
    }
    
    TEST_P(ShaderStorageBufferTest31, MatrixDataInSSBOWithRowMajorQualifier)
    {
        ANGLE_SKIP_TEST_IF(IsAMD() && IsWindows() && IsOpenGL());
    
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
    layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
    layout(std140, binding = 0) buffer blockIn {
        layout(row_major) mat2x3 data;
    } instanceIn;
    layout(std140, binding = 1) buffer blockOut {
        layout(row_major) mat2x3 data;
    } instanceOut;
    void main()
    {
        instanceOut.data = instanceIn.data;
    }
    )";
    
        constexpr unsigned int kColumns           = 2;
        constexpr unsigned int kRows              = 3;
        constexpr unsigned int kBytesPerComponent = sizeof(float);
        // std140 layout requires that base alignment and stride of arrays of scalars and vectors are
        // rounded up a multiple of the base alignment of a vec4.
        constexpr unsigned int kMatrixStride                                     = 16;
        constexpr float kInputDada[kRows * (kMatrixStride / kBytesPerComponent)] = {
            0.1, 0.2, 0.0, 0.0, 0.3, 0.4, 0.0, 0.0, 0.5, 0.6, 0.0, 0.0};
        MatrixCase matrixCase(kColumns, kRows, kMatrixStride, kComputeShaderSource, kInputDada);
        runMatrixTest(matrixCase);
    }
    
    // Test that access/write to scalar data in structure matrix in shader storage block with row major.
    TEST_P(ShaderStorageBufferTest31, ScalarDataInMatrixInStructureInSSBOWithRowMajorQualifier)
    {
        // TODO(jiajia.qin@intel.com): Figure out why it fails on Intel Linux platform.
        // http://anglebug.com/1951
        ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux());
        ANGLE_SKIP_TEST_IF(IsAndroid());
    
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
    layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
    struct S
    {
        mat2x3 data;
    };
    layout(std140, binding = 0) buffer blockIn {
        layout(row_major) S s;
    } instanceIn;
    layout(std140, binding = 1) buffer blockOut {
        layout(row_major) S s;
    } instanceOut;
    void main()
    {
        instanceOut.s.data[0][0] = instanceIn.s.data[0][0];
        instanceOut.s.data[0][1] = instanceIn.s.data[0][1];
        instanceOut.s.data[0][2] = instanceIn.s.data[0][2];
        instanceOut.s.data[1][0] = instanceIn.s.data[1][0];
        instanceOut.s.data[1][1] = instanceIn.s.data[1][1];
        instanceOut.s.data[1][2] = instanceIn.s.data[1][2];
    }
    )";
    
        constexpr unsigned int kColumns           = 2;
        constexpr unsigned int kRows              = 3;
        constexpr unsigned int kBytesPerComponent = sizeof(float);
        // std140 layout requires that base alignment and stride of arrays of scalars and vectors are
        // rounded up a multiple of the base alignment of a vec4.
        constexpr unsigned int kMatrixStride                                     = 16;
        constexpr float kInputDada[kRows * (kMatrixStride / kBytesPerComponent)] = {
            0.1, 0.2, 0.0, 0.0, 0.3, 0.4, 0.0, 0.0, 0.5, 0.6, 0.0, 0.0};
        MatrixCase matrixCase(kColumns, kRows, kMatrixStride, kComputeShaderSource, kInputDada);
        runMatrixTest(matrixCase);
    }
    
    // Test that access/write to column major matrix data in shader storage buffer.
    TEST_P(ShaderStorageBufferTest31, ScalarDataInMatrixInSSBO)
    {
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
    layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
    layout(std140, binding = 0) buffer blockIn {
        mat2x3 data;
    } instanceIn;
    layout(std140, binding = 1) buffer blockOut {
        mat2x3 data;
    } instanceOut;
    void main()
    {
        instanceOut.data[0][0] = instanceIn.data[0][0];
        instanceOut.data[0][1] = instanceIn.data[0][1];
        instanceOut.data[0][2] = instanceIn.data[0][2];
        instanceOut.data[1][0] = instanceIn.data[1][0];
        instanceOut.data[1][1] = instanceIn.data[1][1];
        instanceOut.data[1][2] = instanceIn.data[1][2];
    }
    )";
    
        constexpr unsigned int kColumns                                             = 2;
        constexpr unsigned int kRows                                                = 3;
        constexpr unsigned int kBytesPerComponent                                   = sizeof(float);
        constexpr unsigned int kMatrixStride                                        = 16;
        constexpr float kInputDada[kColumns * (kMatrixStride / kBytesPerComponent)] = {
            0.1, 0.2, 0.3, 0.0, 0.4, 0.5, 0.6, 0.0};
        MatrixCase matrixCase(kRows, kColumns, kMatrixStride, kComputeShaderSource, kInputDada);
        runMatrixTest(matrixCase);
    }
    
    TEST_P(ShaderStorageBufferTest31, VectorDataInMatrixInSSBOWithColumnMajorQualifier)
    {
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
    layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
    layout(std140, binding = 0) buffer blockIn {
        layout(column_major) mat2x3 data;
    } instanceIn;
    layout(std140, binding = 1) buffer blockOut {
        layout(column_major) mat2x3 data;
    } instanceOut;
    void main()
    {
        instanceOut.data[0] = instanceIn.data[0];
        instanceOut.data[1] = instanceIn.data[1];
    }
    )";
    
        constexpr unsigned int kColumns                                             = 2;
        constexpr unsigned int kRows                                                = 3;
        constexpr unsigned int kBytesPerComponent                                   = sizeof(float);
        constexpr unsigned int kMatrixStride                                        = 16;
        constexpr float kInputDada[kColumns * (kMatrixStride / kBytesPerComponent)] = {
            0.1, 0.2, 0.3, 0.0, 0.4, 0.5, 0.6, 0.0};
        MatrixCase matrixCase(kRows, kColumns, kMatrixStride, kComputeShaderSource, kInputDada);
        runMatrixTest(matrixCase);
    }
    
    TEST_P(ShaderStorageBufferTest31, MatrixDataInSSBOWithColumnMajorQualifier)
    {
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
    layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
    layout(std140, binding = 0) buffer blockIn {
        layout(column_major) mat2x3 data;
    } instanceIn;
    layout(std140, binding = 1) buffer blockOut {
        layout(column_major) mat2x3 data;
    } instanceOut;
    void main()
    {
        instanceOut.data = instanceIn.data;
    }
    )";
    
        constexpr unsigned int kColumns                                             = 2;
        constexpr unsigned int kRows                                                = 3;
        constexpr unsigned int kBytesPerComponent                                   = sizeof(float);
        constexpr unsigned int kMatrixStride                                        = 16;
        constexpr float kInputDada[kColumns * (kMatrixStride / kBytesPerComponent)] = {
            0.1, 0.2, 0.3, 0.0, 0.4, 0.5, 0.6, 0.0};
        MatrixCase matrixCase(kRows, kColumns, kMatrixStride, kComputeShaderSource, kInputDada);
        runMatrixTest(matrixCase);
    }
    
    // Test that access/write to structure data in shader storage buffer.
    TEST_P(ShaderStorageBufferTest31, ShaderStorageBufferStructureArray)
    {
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
    layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
    struct S
    {
        uvec2 uvData;
        uint uiData[2];
    };
    layout(std140, binding = 0) buffer blockIn {
        S s[2];
        uint lastData;
    } instanceIn;
    layout(std140, binding = 1) buffer blockOut {
        S s[2];
        uint lastData;
    } instanceOut;
    void main()
    {
        instanceOut.s[0].uvData = instanceIn.s[0].uvData;
        instanceOut.s[0].uiData[0] = instanceIn.s[0].uiData[0];
        instanceOut.s[0].uiData[1] = instanceIn.s[0].uiData[1];
        instanceOut.s[1].uvData = instanceIn.s[1].uvData;
        instanceOut.s[1].uiData[0] = instanceIn.s[1].uiData[0];
        instanceOut.s[1].uiData[1] = instanceIn.s[1].uiData[1];
        instanceOut.lastData = instanceIn.lastData;
    }
    )";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
    
        glUseProgram(program);
    
        std::array<GLuint, 4> kUVData = {{
            1u,
            2u,
            0u,
            0u,
        }};
        std::array<GLuint, 8> kUIData = {{
            3u,
            0u,
            0u,
            0u,
            4u,
            0u,
            0u,
            0u,
        }};
        GLuint kLastData              = 5u;
    
        constexpr unsigned int kBytesPerComponent = sizeof(GLuint);
        constexpr unsigned int kStructureStride   = 48;
        constexpr unsigned int totalSize          = kStructureStride * 2 + sizeof(kLastData);
    
        // Create shader storage buffer
        GLBuffer shaderStorageBuffer[2];
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, totalSize, nullptr, GL_STATIC_DRAW);
        GLint offset = 0;
        // upload data to instanceIn.s[0]
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUVData.size() * kBytesPerComponent,
                        kUVData.data());
        offset += (kUVData.size() * kBytesPerComponent);
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUIData.size() * kBytesPerComponent,
                        kUIData.data());
        offset += (kUIData.size() * kBytesPerComponent);
        // upload data to instanceIn.s[1]
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUVData.size() * kBytesPerComponent,
                        kUVData.data());
        offset += (kUVData.size() * kBytesPerComponent);
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUIData.size() * kBytesPerComponent,
                        kUIData.data());
        offset += (kUIData.size() * kBytesPerComponent);
        // upload data to instanceIn.lastData
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, sizeof(kLastData), &kLastData);
    
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, totalSize, nullptr, GL_STATIC_DRAW);
    
        // Bind shader storage buffer
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]);
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]);
    
        glDispatchCompute(1, 1, 1);
        glFinish();
    
        // Read back shader storage buffer
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        constexpr float kExpectedValues[5] = {1u, 2u, 3u, 4u, 5u};
        const GLuint *ptr                  = reinterpret_cast<const GLuint *>(
            glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, totalSize, GL_MAP_READ_BIT));
        // instanceOut.s[0]
        EXPECT_EQ(kExpectedValues[0], *ptr);
        EXPECT_EQ(kExpectedValues[1], *(ptr + 1));
        EXPECT_EQ(kExpectedValues[2], *(ptr + 4));
        EXPECT_EQ(kExpectedValues[3], *(ptr + 8));
        // instanceOut.s[1]
        ptr += kStructureStride / kBytesPerComponent;
        EXPECT_EQ(kExpectedValues[0], *ptr);
        EXPECT_EQ(kExpectedValues[1], *(ptr + 1));
        EXPECT_EQ(kExpectedValues[2], *(ptr + 4));
        EXPECT_EQ(kExpectedValues[3], *(ptr + 8));
        // instanceOut.lastData
        ptr += kStructureStride / kBytesPerComponent;
        EXPECT_EQ(kExpectedValues[4], *(ptr));
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Test that access/write to array of array structure data in shader storage buffer.
    TEST_P(ShaderStorageBufferTest31, ShaderStorageBufferStructureArrayOfArray)
    {
        constexpr char kComputeShaderSource[] = R"(#version 310 es
    layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
    struct S
    {
        uvec2 uvData;
        uint uiData[2];
    };
    layout(std140, binding = 0) buffer blockIn {
        S s[3][2];
        uint lastData;
    } instanceIn;
    layout(std140, binding = 1) buffer blockOut {
        S s[3][2];
        uint lastData;
    } instanceOut;
    void main()
    {
        instanceOut.s[1][0].uvData = instanceIn.s[1][0].uvData;
        instanceOut.s[1][0].uiData[0] = instanceIn.s[1][0].uiData[0];
        instanceOut.s[1][0].uiData[1] = instanceIn.s[1][0].uiData[1];
        instanceOut.s[1][1].uvData = instanceIn.s[1][1].uvData;
        instanceOut.s[1][1].uiData[0] = instanceIn.s[1][1].uiData[0];
        instanceOut.s[1][1].uiData[1] = instanceIn.s[1][1].uiData[1];
    
        instanceOut.lastData = instanceIn.lastData;
    }
    )";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
    
        glUseProgram(program);
    
        std::array<GLuint, 4> kUVData = {{
            1u,
            2u,
            0u,
            0u,
        }};
        std::array<GLuint, 8> kUIData = {{
            3u,
            0u,
            0u,
            0u,
            4u,
            0u,
            0u,
            0u,
        }};
        GLuint kLastData              = 5u;
    
        constexpr unsigned int kBytesPerComponent        = sizeof(GLuint);
        constexpr unsigned int kStructureStride          = 48;
        constexpr unsigned int kStructureArrayDimension0 = 3;
        constexpr unsigned int kStructureArrayDimension1 = 2;
        constexpr unsigned int kLastDataOffset =
            kStructureStride * kStructureArrayDimension0 * kStructureArrayDimension1;
        constexpr unsigned int totalSize = kLastDataOffset + sizeof(kLastData);
    
        GLBuffer shaderStorageBuffer[2];
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, totalSize, nullptr, GL_STATIC_DRAW);
        // offset of instanceIn.s[1][0]
        GLint offset      = kStructureStride * (kStructureArrayDimension1 * 1 + 0);
        GLuint uintOffset = offset / kBytesPerComponent;
        // upload data to instanceIn.s[1][0]
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUVData.size() * kBytesPerComponent,
                        kUVData.data());
        offset += (kUVData.size() * kBytesPerComponent);
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUIData.size() * kBytesPerComponent,
                        kUIData.data());
        offset += (kUIData.size() * kBytesPerComponent);
        // upload data to instanceIn.s[1][1]
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUVData.size() * kBytesPerComponent,
                        kUVData.data());
        offset += (kUVData.size() * kBytesPerComponent);
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUIData.size() * kBytesPerComponent,
                        kUIData.data());
        // upload data to instanceIn.lastData
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, kLastDataOffset, sizeof(kLastData), &kLastData);
    
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, totalSize, nullptr, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]);
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]);
    
        glDispatchCompute(1, 1, 1);
        glFinish();
    
        // Read back shader storage buffer
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        constexpr float kExpectedValues[5] = {1u, 2u, 3u, 4u, 5u};
        const GLuint *ptr                  = reinterpret_cast<const GLuint *>(
            glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, totalSize, GL_MAP_READ_BIT));
    
        // instanceOut.s[0][0]
        EXPECT_EQ(kExpectedValues[0], *(ptr + uintOffset));
        EXPECT_EQ(kExpectedValues[1], *(ptr + uintOffset + 1));
        EXPECT_EQ(kExpectedValues[2], *(ptr + uintOffset + 4));
        EXPECT_EQ(kExpectedValues[3], *(ptr + uintOffset + 8));
    
        // instanceOut.s[0][1]
        EXPECT_EQ(kExpectedValues[0], *(ptr + uintOffset + 12));
        EXPECT_EQ(kExpectedValues[1], *(ptr + uintOffset + 13));
        EXPECT_EQ(kExpectedValues[2], *(ptr + uintOffset + 16));
        EXPECT_EQ(kExpectedValues[3], *(ptr + uintOffset + 20));
    
        // instanceOut.lastData
        EXPECT_EQ(kExpectedValues[4], *(ptr + (kLastDataOffset / kBytesPerComponent)));
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Test that access/write to vector data in std430 shader storage block.
    TEST_P(ShaderStorageBufferTest31, VectorArrayInSSBOWithStd430Qualifier)
    {
        constexpr char kComputeShaderSource[] = R"(#version 310 es
    layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
    layout(std430, binding = 0) buffer blockIn {
        uvec2 data[2];
    } instanceIn;
    layout(std430, binding = 1) buffer blockOut {
        uvec2 data[2];
    } instanceOut;
    void main()
    {
        instanceOut.data[0] = instanceIn.data[0];
        instanceOut.data[1] = instanceIn.data[1];
    }
    )";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
    
        glUseProgram(program);
    
        constexpr unsigned int kElementCount      = 2;
        constexpr unsigned int kBytesPerComponent = sizeof(unsigned int);
        constexpr unsigned int kArrayStride       = 8;
        constexpr unsigned int kComponentCount    = kArrayStride / kBytesPerComponent;
        constexpr unsigned int kExpectedValues[kElementCount][kComponentCount] = {{1u, 2u}, {3u, 4u}};
        // Create shader storage buffer
        GLBuffer shaderStorageBuffer[2];
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kElementCount * kArrayStride, kExpectedValues,
                     GL_STATIC_DRAW);
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kElementCount * kArrayStride, nullptr, GL_STATIC_DRAW);
    
        // Bind shader storage buffer
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]);
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]);
    
        glDispatchCompute(1, 1, 1);
    
        glFinish();
    
        // Read back shader storage buffer
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        const GLuint *ptr = reinterpret_cast<const GLuint *>(glMapBufferRange(
            GL_SHADER_STORAGE_BUFFER, 0, kElementCount * kArrayStride, GL_MAP_READ_BIT));
        for (unsigned int idx = 0; idx < kElementCount; idx++)
        {
            for (unsigned int idy = 0; idy < kComponentCount; idy++)
            {
                EXPECT_EQ(kExpectedValues[idx][idy], *(ptr + idx * kComponentCount + idy));
            }
        }
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Test that access/write to matrix data in std430 shader storage block.
    TEST_P(ShaderStorageBufferTest31, MatrixInSSBOWithStd430Qualifier)
    {
        constexpr char kComputeShaderSource[] = R"(#version 310 es
    layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
    layout(std430, binding = 0) buffer blockIn {
        mat2 data;
    } instanceIn;
    layout(std430, binding = 1) buffer blockOut {
        mat2 data;
    } instanceOut;
    void main()
    {
        instanceOut.data = instanceIn.data;
    }
    )";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
    
        glUseProgram(program);
    
        constexpr unsigned int kColumns              = 2;
        constexpr unsigned int kRows                 = 2;
        constexpr unsigned int kBytesPerComponent    = sizeof(float);
        constexpr unsigned int kMatrixStride         = kRows * kBytesPerComponent;
        constexpr float kInputDada[kColumns * kRows] = {0.1, 0.2, 0.4, 0.5};
        MatrixCase matrixCase(kRows, kColumns, kMatrixStride, kComputeShaderSource, kInputDada);
        runMatrixTest(matrixCase);
    }
    
    // Test that access/write to structure data in std430 shader storage block.
    TEST_P(ShaderStorageBufferTest31, StructureInSSBOWithStd430Qualifier)
    {
        constexpr char kComputeShaderSource[] = R"(#version 310 es
    layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
    struct S
    {
        uvec2 u;
    };
    layout(std430, binding = 0) buffer blockIn {
        uint i1;
        S s;
        uint i2;
    } instanceIn;
    layout(std430, binding = 1) buffer blockOut {
        uint i1;
        S s;
        uint i2;
    } instanceOut;
    void main()
    {
        instanceOut.i1 = instanceIn.i1;
        instanceOut.s.u = instanceIn.s.u;
        instanceOut.i2 = instanceIn.i2;
    }
    )";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
        glUseProgram(program);
    
        GLuint kI1Data               = 1u;
        std::array<GLuint, 2> kUData = {{
            2u,
            3u,
        }};
        GLuint kI2Data               = 4u;
    
        constexpr unsigned int kBytesPerComponent    = sizeof(GLuint);
        constexpr unsigned int kStructureStartOffset = 8;
        constexpr unsigned int kStructureSize        = 8;
        constexpr unsigned int kTotalSize = kStructureStartOffset + kStructureSize + kBytesPerComponent;
    
        // Create shader storage buffer
        GLBuffer shaderStorageBuffer[2];
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kTotalSize, nullptr, GL_STATIC_DRAW);
        // upload data to instanceIn.i1
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, kBytesPerComponent, &kI1Data);
        // upload data to instanceIn.s.u
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, kStructureStartOffset, kStructureSize, kUData.data());
        // upload data to instanceIn.i2
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, kStructureStartOffset + kStructureSize,
                        kBytesPerComponent, &kI2Data);
    
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kTotalSize, nullptr, GL_STATIC_DRAW);
    
        // Bind shader storage buffer
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]);
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]);
    
        glDispatchCompute(1, 1, 1);
        glFinish();
    
        // Read back shader storage buffer
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        GLuint kExpectedValues[4] = {1u, 2u, 3u, 4u};
        const GLuint *ptr         = reinterpret_cast<const GLuint *>(
            glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kTotalSize, GL_MAP_READ_BIT));
        EXPECT_EQ(kExpectedValues[0], *ptr);
        ptr += (kStructureStartOffset / kBytesPerComponent);
        EXPECT_EQ(kExpectedValues[1], *ptr);
        EXPECT_EQ(kExpectedValues[2], *(ptr + 1));
        ptr += (kStructureSize / kBytesPerComponent);
        EXPECT_EQ(kExpectedValues[3], *ptr);
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Test that access/write to structure of structure data in std430 shader storage block.
    TEST_P(ShaderStorageBufferTest31, StructureOfStructureInSSBOWithStd430Qualifier)
    {
        constexpr char kComputeShaderSource[] = R"(#version 310 es
    layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
    struct S2
    {
        uvec3 u2;
    };
    struct S1
    {
        uvec2 u1;
        S2 s2;
    };
    layout(std430, binding = 0) buffer blockIn {
        uint i1;
        S1 s1;
        uint i2;
    } instanceIn;
    layout(std430, binding = 1) buffer blockOut {
        uint i1;
        S1 s1;
        uint i2;
    } instanceOut;
    void main()
    {
        instanceOut.i1 = instanceIn.i1;
        instanceOut.s1.u1 = instanceIn.s1.u1;
        instanceOut.s1.s2.u2 = instanceIn.s1.s2.u2;
        instanceOut.i2 = instanceIn.i2;
    }
    )";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
        glUseProgram(program);
    
        constexpr unsigned int kBytesPerComponent      = sizeof(GLuint);
        constexpr unsigned int kStructureS1StartOffset = 16;
        constexpr unsigned int kStructureS2StartOffset = 32;
        constexpr unsigned int kStructureS1Size        = 32;
        constexpr unsigned int kTotalSize =
            kStructureS1StartOffset + kStructureS1Size + kBytesPerComponent;
    
        GLuint kI1Data                = 1u;
        std::array<GLuint, 2> kU1Data = {{2u, 3u}};
        std::array<GLuint, 3> kU2Data = {{4u, 5u, 6u}};
        GLuint kI2Data                = 7u;
    
        // Create shader storage buffer
        GLBuffer shaderStorageBuffer[2];
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kTotalSize, nullptr, GL_STATIC_DRAW);
        // upload data to instanceIn.i1
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, kBytesPerComponent, &kI1Data);
        // upload data to instanceIn.s1.u1
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, kStructureS1StartOffset,
                        kU1Data.size() * kBytesPerComponent, kU1Data.data());
        // upload data to instanceIn.s1.s2.u2
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, kStructureS2StartOffset,
                        kU2Data.size() * kBytesPerComponent, kU2Data.data());
        // upload data to instanceIn.i2
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, kStructureS1StartOffset + kStructureS1Size,
                        kBytesPerComponent, &kI2Data);
    
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kTotalSize, nullptr, GL_STATIC_DRAW);
    
        // Bind shader storage buffer
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]);
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]);
    
        glDispatchCompute(1, 1, 1);
        glFinish();
    
        // Read back shader storage buffer
        GLuint kExpectedValues[7] = {1u, 2u, 3u, 4u, 5u, 6u, 7u};
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        const GLuint *ptr = reinterpret_cast<const GLuint *>(
            glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kTotalSize, GL_MAP_READ_BIT));
        EXPECT_EQ(kExpectedValues[0], *ptr);
        ptr += (kStructureS1StartOffset / kBytesPerComponent);
        EXPECT_EQ(kExpectedValues[1], *ptr);
        EXPECT_EQ(kExpectedValues[2], *(ptr + 1));
        ptr += ((kStructureS2StartOffset - kStructureS1StartOffset) / kBytesPerComponent);
        EXPECT_EQ(kExpectedValues[3], *ptr);
        EXPECT_EQ(kExpectedValues[4], *(ptr + 1));
        EXPECT_EQ(kExpectedValues[5], *(ptr + 2));
        ptr += ((kStructureS1Size - kStructureS2StartOffset) / kBytesPerComponent);
        EXPECT_EQ(kExpectedValues[6], *(ptr + 4));
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Test atomic memory functions.
    TEST_P(ShaderStorageBufferTest31, AtomicMemoryFunctions)
    {
        constexpr char kCS[] = R"(#version 310 es
    layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
    layout(std140, binding = 1) buffer blockName {
        uint data[2];
    } instanceName;
    
    void main()
    {
        instanceName.data[0] = 0u;
        instanceName.data[1] = 0u;
        atomicAdd(instanceName.data[0], 5u);
        atomicMax(instanceName.data[1], 7u);
    })";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kCS);
    
        glUseProgram(program.get());
    
        constexpr unsigned int kElementCount = 2;
        // The array stride are rounded up to the base alignment of a vec4 for std140 layout.
        constexpr unsigned int kArrayStride = 16;
        // Create shader storage buffer
        GLBuffer shaderStorageBuffer;
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kElementCount * kArrayStride, nullptr, GL_STATIC_DRAW);
    
        // Bind shader storage buffer
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer);
    
        // Dispath compute
        glDispatchCompute(1, 1, 1);
    
        glFinish();
    
        // Read back shader storage buffer
        constexpr unsigned int kExpectedValues[2] = {5u, 7u};
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer);
        void *ptr = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kElementCount * kArrayStride,
                                     GL_MAP_READ_BIT);
        for (unsigned int idx = 0; idx < kElementCount; idx++)
        {
            EXPECT_EQ(kExpectedValues[idx],
                      *(reinterpret_cast<const GLuint *>(reinterpret_cast<const GLbyte *>(ptr) +
                                                         idx * kArrayStride)));
        }
        glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Test multiple storage buffers work correctly when program switching. In angle, storage buffer
    // bindings are updated accord to current program. If switch program, need to update storage buffer
    // bindings again.
    TEST_P(ShaderStorageBufferTest31, MultiStorageBuffersForMultiPrograms)
    {
        constexpr char kCS1[] = R"(#version 310 es
    layout(local_size_x=3, local_size_y=1, local_size_z=1) in;
    layout(binding = 1) buffer Output {
        uint result1[];
    } sb_out1;
    void main()
    {
        highp uint offset = gl_LocalInvocationID.x;
        sb_out1.result1[gl_LocalInvocationIndex] = gl_LocalInvocationIndex + 1u;
    })";
    
        constexpr char kCS2[] = R"(#version 310 es
    layout(local_size_x=3, local_size_y=1, local_size_z=1) in;
    layout(binding = 2) buffer Output {
        uint result2[];
    } sb_out2;
    void main()
    {
        highp uint offset = gl_LocalInvocationID.x;
        sb_out2.result2[gl_LocalInvocationIndex] = gl_LocalInvocationIndex + 2u;
    })";
    
        constexpr unsigned int numInvocations = 3;
        int arrayStride1 = 0, arrayStride2 = 0;
        GLenum props[] = {GL_ARRAY_STRIDE};
        GLBuffer shaderStorageBuffer1, shaderStorageBuffer2;
    
        ANGLE_GL_COMPUTE_PROGRAM(program1, kCS1);
        ANGLE_GL_COMPUTE_PROGRAM(program2, kCS2);
        EXPECT_GL_NO_ERROR();
    
        unsigned int outVarIndex1 =
            glGetProgramResourceIndex(program1.get(), GL_BUFFER_VARIABLE, "Output.result1");
        glGetProgramResourceiv(program1.get(), GL_BUFFER_VARIABLE, outVarIndex1, 1, props, 1, 0,
                               &arrayStride1);
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer1);
        glBufferData(GL_SHADER_STORAGE_BUFFER, numInvocations * arrayStride1, nullptr, GL_STREAM_READ);
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer1);
        EXPECT_GL_NO_ERROR();
    
        unsigned int outVarIndex2 =
            glGetProgramResourceIndex(program2.get(), GL_BUFFER_VARIABLE, "Output.result2");
        glGetProgramResourceiv(program2.get(), GL_BUFFER_VARIABLE, outVarIndex2, 1, props, 1, 0,
                               &arrayStride2);
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer2);
        glBufferData(GL_SHADER_STORAGE_BUFFER, numInvocations * arrayStride2, nullptr, GL_STREAM_READ);
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, shaderStorageBuffer2);
        EXPECT_GL_NO_ERROR();
    
        glUseProgram(program1.get());
        glDispatchCompute(1, 1, 1);
        EXPECT_GL_NO_ERROR();
        glUseProgram(program2.get());
        glDispatchCompute(1, 1, 1);
        EXPECT_GL_NO_ERROR();
    
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer1);
        const void *ptr1 =
            glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 3 * arrayStride1, GL_MAP_READ_BIT);
        for (unsigned int idx = 0; idx < numInvocations; idx++)
        {
            EXPECT_EQ(idx + 1, *((const GLuint *)((const GLbyte *)ptr1 + idx * arrayStride1)));
        }
        glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
        EXPECT_GL_NO_ERROR();
    
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer2);
        const void *ptr2 =
            glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 3 * arrayStride2, GL_MAP_READ_BIT);
        EXPECT_GL_NO_ERROR();
        for (unsigned int idx = 0; idx < numInvocations; idx++)
        {
            EXPECT_EQ(idx + 2, *((const GLuint *)((const GLbyte *)ptr2 + idx * arrayStride2)));
        }
        glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
        EXPECT_GL_NO_ERROR();
    
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
        EXPECT_GL_NO_ERROR();
    }
    
    // Test that function calling is supported in SSBO access chain.
    TEST_P(ShaderStorageBufferTest31, FunctionCallInSSBOAccessChain)
    {
        constexpr char kComputeShaderSource[] = R"(#version 310 es
    layout (local_size_x=4) in;
    highp uint getIndex (in highp uvec2 localID, uint element)
    {
        return localID.x + element;
    }
    layout(binding=0, std430) buffer Storage
    {
        highp uint values[];
    } sb_store;
    
    void main()
    {
        sb_store.values[getIndex(gl_LocalInvocationID.xy, 0u)] = gl_LocalInvocationIndex;
    }
    )";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
        EXPECT_GL_NO_ERROR();
    }
    
    // Test that unary operator is supported in SSBO access chain.
    TEST_P(ShaderStorageBufferTest31, UnaryOperatorInSSBOAccessChain)
    {
        constexpr char kComputeShaderSource[] = R"(#version 310 es
    layout (local_size_x=4) in;
    layout(binding=0, std430) buffer Storage
    {
        highp uint values[];
    } sb_store;
    
    void main()
    {
        uint invocationNdx = gl_LocalInvocationIndex;
        sb_store.values[++invocationNdx] = invocationNdx;
    }
    )";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
        EXPECT_GL_NO_ERROR();
    }
    
    // Test that ternary operator is supported in SSBO access chain.
    TEST_P(ShaderStorageBufferTest31, TernaryOperatorInSSBOAccessChain)
    {
        constexpr char kComputeShaderSource[] = R"(#version 310 es
    layout (local_size_x=4) in;
    layout(binding=0, std430) buffer Storage
    {
        highp uint values[];
    } sb_store;
    
    void main()
    {
        sb_store.values[gl_LocalInvocationIndex > 2u ? gl_NumWorkGroups.x : gl_NumWorkGroups.y]
                = gl_LocalInvocationIndex;
    }
    )";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
        EXPECT_GL_NO_ERROR();
    }
    
    // Tests that alignment is correct for bools inside a SSB and that the values
    // are written correctly by a trivial shader. Currently tests only the alignment
    // of the initial block.
    TEST_P(ShaderStorageBufferTest31, LoadAndStoreBooleanValue)
    {
        // TODO(jiajia.qin@intel.com): Figure out why it fails on Intel Linux platform.
        // http://anglebug.com/1951
        ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux());
    
        constexpr char kComputeShaderSource[] = R"(#version 310 es
    layout (local_size_x=1) in;
    layout(binding=0, std140) buffer Storage0
    {
        bool b1;
        bool b2;
        bool b3;
    } sb_load;
    layout(binding=1, std140) buffer Storage1
    {
        bool b1;
        bool b2;
        bool b3;
    } sb_store;
    void main()
    {
       sb_store.b1 = sb_load.b1;
       sb_store.b2 = sb_load.b2;
       sb_store.b3 = sb_load.b3;
    }
    )";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
        EXPECT_GL_NO_ERROR();
    
        glUseProgram(program);
    
        constexpr GLuint kB1Value                 = 1u;
        constexpr GLuint kB2Value[2]              = {0u, 1u};
        constexpr unsigned int kBytesPerComponent = sizeof(GLuint);
        // Create shader storage buffer
        GLBuffer shaderStorageBuffer[2];
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, 3 * kBytesPerComponent, nullptr, GL_STATIC_DRAW);
        GLint offset = 0;
        // upload data to sb_load.b1
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kBytesPerComponent, &kB1Value);
        offset += kBytesPerComponent;
        // upload data to sb_load.b2
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, 2 * kBytesPerComponent, kB2Value);
    
        constexpr GLuint kStoreBufferContents[3] = {0x1BCD1234, 0x2BCD1234, 0x3BCD1234};
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, 3 * kBytesPerComponent, kStoreBufferContents,
                     GL_STATIC_DRAW);
    
        // Bind shader storage buffer
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]);
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]);
    
        glDispatchCompute(1, 1, 1);
        glFinish();
    
        // Read back shader storage buffer
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        const GLuint *ptr = reinterpret_cast<const GLuint *>(
            glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 3 * kBytesPerComponent, GL_MAP_READ_BIT));
        EXPECT_EQ(kB1Value, ptr[0]);
        EXPECT_EQ(kB2Value[0], ptr[1]);
        EXPECT_EQ(kB2Value[1], ptr[2]);
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Tests that alignment is correct for bvecs3 inside a SSB and that the
    // values are written correctly by a trivial shader. Currently tests only the
    // alignment of the initial block.
    TEST_P(ShaderStorageBufferTest31, LoadAndStoreBooleanVec3)
    {
        // TODO(jiajia.qin@intel.com): Figure out why it fails on Intel Linux platform.
        // http://anglebug.com/1951
        ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux());
    
        ANGLE_SKIP_TEST_IF(IsAMD() && IsWindows() && IsOpenGL());
    
        constexpr char kComputeShaderSource[] = R"(#version 310 es
    layout (local_size_x=1) in;
    layout(binding=0, std140) buffer Storage0
    {
        bvec3 b;
    } sb_load;
    layout(binding=1, std140) buffer Storage1
    {
        bvec3 b;
    } sb_store;
    void main()
    {
       sb_store.b = sb_load.b;
    }
    )";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
        EXPECT_GL_NO_ERROR();
    
        glUseProgram(program);
    
        constexpr GLuint kBValues[3]              = {1u, 0u, 1u};
        constexpr unsigned int kBytesPerComponent = sizeof(GLuint);
        // Create shader storage buffer
        GLBuffer shaderStorageBuffer[2];
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, 3 * kBytesPerComponent, nullptr, GL_STATIC_DRAW);
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, 3 * kBytesPerComponent, &kBValues);
    
        constexpr GLuint kStoreBufferContents[3] = {0x1BCD1234, 0x2BCD1234, 0x3BCD1234};
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, 3 * kBytesPerComponent, kStoreBufferContents,
                     GL_STATIC_DRAW);
    
        // Bind shader storage buffer
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]);
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]);
    
        glDispatchCompute(1, 1, 1);
        glFinish();
    
        // Read back shader storage buffer
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        const GLuint *ptr = reinterpret_cast<const GLuint *>(
            glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 3 * kBytesPerComponent, GL_MAP_READ_BIT));
        EXPECT_EQ(kBValues[0], ptr[0]);
        EXPECT_EQ(kBValues[1], ptr[1]);
        EXPECT_EQ(kBValues[2], ptr[2]);
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Tests that alignment is correct for bool + bvecs2 inside a SSB and that the
    // values are written correctly by a trivial shader. Currently tests only the
    // alignment of the initial block. Compare to LoadAndStoreBooleanVec3 to see how
    // the alignment rules affect the memory layout.
    TEST_P(ShaderStorageBufferTest31, LoadAndStoreBooleanVarAndVec2)
    {
        // TODO(jiajia.qin@intel.com): Figure out why it fails on Intel Linux platform.
        // http://anglebug.com/1951
        ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux());
    
        ANGLE_SKIP_TEST_IF(IsAMD() && IsWindows() && IsOpenGL());
    
        constexpr char kComputeShaderSource[] = R"(#version 310 es
    layout (local_size_x=1) in;
    layout(binding=0, std140) buffer Storage0
    {
        bool b1;
        bvec2 b2;
    } sb_load;
    layout(binding=1, std140) buffer Storage1
    {
        bool b1;
        bvec2 b2;
    } sb_store;
    void main()
    {
       sb_store.b1 = sb_load.b1;
       sb_store.b2 = sb_load.b2;
    }
    )";
        // https://www.khronos.org/registry/OpenGL/specs/es/3.1/es_spec_3.1.pdf
        // 7.6.2.2 Standard Uniform Block Layout
    
        // ... A structure and each structure member have a base offset and a base
        // alignment, from which an aligned offset is computed by rounding the base
        // offset up to a multiple of the base alignment. The base offset of the
        // first member of a structure is taken from the aligned offset of the
        // structure itself. ... The members of a toplevel uniform block are laid
        // out in buffer storage by treating the uniform block as a structure with a
        // base offset of zero.
    
        // 1. If the member is a scalar consuming N basic machine units, the base
        // alignment is N.
    
        // 2. If the member is a two- or four-component vector with components
        // consuming N basic machine units, the base alignment is 2N or 4N,
        // respectively
    
        // b1 N == 4, basic offset 0, alignment 4, is at 0..3
        // b2 N == 4, basic offset 4, alignment 2*4 = 8, is at 8..16.
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
        EXPECT_GL_NO_ERROR();
    
        glUseProgram(program);
        constexpr GLuint kAlignPadding  = 0x1abcd789u;
        constexpr GLuint kBValues[]     = {1u, kAlignPadding, 0u, 1u};
        constexpr unsigned int kSsbSize = sizeof(kBValues);
        // Create shader storage buffer
        GLBuffer shaderStorageBuffer[2];
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kSsbSize, nullptr, GL_STATIC_DRAW);
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, kSsbSize, &kBValues);
    
        constexpr GLuint kStoreBufferContents[4] = {0x1BCD1234, 0x2BCD1234, 0x3BCD1234, 0x3BCD1277};
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kSsbSize, kStoreBufferContents, GL_STATIC_DRAW);
    
        // Bind shader storage buffer
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]);
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]);
    
        glDispatchCompute(1, 1, 1);
        glFinish();
    
        // Read back shader storage buffer
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        const GLuint *ptr = reinterpret_cast<const GLuint *>(
            glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kSsbSize, GL_MAP_READ_BIT));
        EXPECT_EQ(kBValues[0], ptr[0]);
        // Index 1 is padding.
        EXPECT_EQ(kBValues[2], ptr[2]);
        EXPECT_EQ(kBValues[3], ptr[3]);
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Test that non-structure array of arrays is supported in SSBO.
    TEST_P(ShaderStorageBufferTest31, SimpleArrayOfArrays)
    {
        constexpr char kComputeShaderSource[] = R"(#version 310 es
    layout (local_size_x=1) in;
    layout(binding=0, std140) buffer Storage0
    {
        uint a[2][2][2];
        uint b;
    } sb_load;
    layout(binding=1, std140) buffer Storage1
    {
        uint a[2][2][2];
        uint b;
    } sb_store;
    void main()
    {
       sb_store.a[0][0][0] = sb_load.a[0][0][0];
       sb_store.a[0][0][1] = sb_load.a[0][0][1];
       sb_store.a[0][1][0] = sb_load.a[0][1][0];
       sb_store.a[0][1][1] = sb_load.a[0][1][1];
       sb_store.a[1][0][0] = sb_load.a[1][0][0];
       sb_store.a[1][0][1] = sb_load.a[1][0][1];
       sb_store.a[1][1][0] = sb_load.a[1][1][0];
       sb_store.a[1][1][1] = sb_load.a[1][1][1];
       sb_store.b = sb_load.b;
    }
    )";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
        glUseProgram(program);
    
        constexpr unsigned int kBytesPerComponent = sizeof(GLuint);
        // The array stride are rounded up to the base alignment of a vec4 for std140 layout.
        constexpr unsigned int kArrayStride                 = 16;
        constexpr unsigned int kDimension0                  = 2;
        constexpr unsigned int kDimension1                  = 2;
        constexpr unsigned int kDimension2                  = 2;
        constexpr unsigned int kAElementCount               = kDimension0 * kDimension1 * kDimension2;
        constexpr unsigned int kAComponentCountPerDimension = kArrayStride / kBytesPerComponent;
        constexpr unsigned int kTotalSize = kArrayStride * kAElementCount + kBytesPerComponent;
    
        constexpr GLuint kInputADatas[kAElementCount * kAComponentCountPerDimension] = {
            1u, 0u, 0u, 0u, 2u, 0u, 0u, 0u, 3u, 0u, 0u, 0u, 4u, 0u, 0u, 0u,
            5u, 0u, 0u, 0u, 6u, 0u, 0u, 0u, 7u, 0u, 0u, 0u, 8u, 0u, 0u, 0u};
        constexpr GLuint kInputBData = 9u;
    
        // Create shader storage buffer
        GLBuffer shaderStorageBuffer[2];
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kTotalSize, nullptr, GL_STATIC_DRAW);
        GLint offset = 0;
        // upload data to sb_load.a
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kAElementCount * kArrayStride, kInputADatas);
        offset += (kAElementCount * kArrayStride);
        // upload data to sb_load.b
        glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kBytesPerComponent, &kInputBData);
    
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kTotalSize, nullptr, GL_STATIC_DRAW);
    
        // Bind shader storage buffer
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]);
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]);
    
        glDispatchCompute(1, 1, 1);
        glFinish();
    
        // Read back shader storage buffer
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        constexpr GLuint kExpectedADatas[kAElementCount] = {1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u};
        const GLuint *ptr                                = reinterpret_cast<const GLuint *>(
            glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kTotalSize, GL_MAP_READ_BIT));
        for (unsigned i = 0u; i < kDimension0; i++)
        {
            for (unsigned j = 0u; j < kDimension1; j++)
            {
                for (unsigned k = 0u; k < kDimension2; k++)
                {
                    unsigned index = i * (kDimension1 * kDimension2) + j * kDimension2 + k;
                    EXPECT_EQ(kExpectedADatas[index],
                              *(ptr + index * (kArrayStride / kBytesPerComponent)));
                }
            }
        }
    
        ptr += (kAElementCount * (kArrayStride / kBytesPerComponent));
        EXPECT_EQ(kInputBData, *ptr);
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Test that the length of unsized array is supported.
    TEST_P(ShaderStorageBufferTest31, UnsizedArrayLength)
    {
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
    layout (local_size_x=1) in;
    layout(std430, binding = 0) buffer Storage0 {
      uint buf1[2];
      uint buf2[];
    } sb_load;
    layout(std430, binding = 1) buffer Storage1 {
      int unsizedArrayLength;
      uint buf1[2];
      uint buf2[];
    } sb_store;
    
    void main()
    {
      sb_store.unsizedArrayLength = sb_store.buf2.length();
      for (int i = 0; i < sb_load.buf1.length(); i++) {
        sb_store.buf1[i] = sb_load.buf1[i];
      }
      for (int i = 0; i < sb_load.buf2.length(); i++) {
        sb_store.buf2[i] = sb_load.buf2[i];
      }
    }
    )";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
        glUseProgram(program);
    
        constexpr unsigned int kBytesPerComponent                       = sizeof(unsigned int);
        constexpr unsigned int kLoadBlockElementCount                   = 5;
        constexpr unsigned int kStoreBlockElementCount                  = 6;
        constexpr unsigned int kInputValues[kLoadBlockElementCount]     = {1u, 2u, 3u, 4u, 5u};
        constexpr unsigned int kExpectedValues[kStoreBlockElementCount] = {3u, 1u, 2u, 3u, 4u, 5u};
        GLBuffer shaderStorageBuffer[2];
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kLoadBlockElementCount * kBytesPerComponent,
                     &kInputValues, GL_STATIC_DRAW);
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kStoreBlockElementCount * kBytesPerComponent, nullptr,
                     GL_STATIC_DRAW);
    
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]);
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]);
    
        glDispatchCompute(1, 1, 1);
        glFinish();
    
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        const GLuint *ptr = reinterpret_cast<const GLuint *>(
            glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kStoreBlockElementCount * kBytesPerComponent,
                             GL_MAP_READ_BIT));
        for (unsigned int i = 0; i < kStoreBlockElementCount; i++)
        {
            EXPECT_EQ(kExpectedValues[i], *(ptr + i));
        }
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Test that compond assignment operator for buffer variable is correctly handled.
    TEST_P(ShaderStorageBufferTest31, CompoundAssignmentOperator)
    {
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
    layout (local_size_x=1) in;
    layout(binding=0, std140) buffer Storage0
    {
        uint b;
    } sb_load;
    layout(binding=1, std140) buffer Storage1
    {
        uint b;
    } sb_store;
    void main()
    {
        uint temp = 2u;
        temp += sb_load.b;
        sb_store.b += temp;
        sb_store.b += sb_load.b;
    }
    )";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
        glUseProgram(program);
    
        constexpr unsigned int kBytesPerComponent = sizeof(unsigned int);
        constexpr unsigned int kInputValue        = 1u;
        constexpr unsigned int kExpectedValue     = 5u;
        GLBuffer shaderStorageBuffer[2];
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kBytesPerComponent, &kInputValue, GL_STATIC_DRAW);
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kBytesPerComponent, &kInputValue, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]);
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]);
    
        glDispatchCompute(1, 1, 1);
        glFinish();
    
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        const GLuint *ptr = reinterpret_cast<const GLuint *>(
            glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kBytesPerComponent, GL_MAP_READ_BIT));
        EXPECT_EQ(kExpectedValue, *ptr);
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Test that readonly binary operator for buffer variable is correctly handled.
    TEST_P(ShaderStorageBufferTest31, ReadonlyBinaryOperator)
    {
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
     layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
     layout(std430, binding = 0) buffer blockIn1 {
         uvec2 data1;
     };
     layout(std430, binding = 1) buffer blockIn2 {
         uvec2 data2;
     };
     layout(std430, binding = 2) buffer blockIn3 {
         uvec2 data;
     } instanceIn3;
     layout(std430, binding = 3) buffer blockOut {
         uvec2 data;
     } instanceOut;
     void main()
     {
         instanceOut.data = data1 + data2 + instanceIn3.data;
     }
     )";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
        glUseProgram(program);
    
        constexpr unsigned int kComponentCount                = 2;
        constexpr unsigned int kBytesPerComponent             = sizeof(unsigned int);
        constexpr unsigned int kInputValues1[kComponentCount] = {1u, 2u};
        constexpr unsigned int kInputValues2[kComponentCount] = {3u, 4u};
        constexpr unsigned int kInputValues3[kComponentCount] = {5u, 6u};
        // Create shader storage buffer
        GLBuffer shaderStorageBuffer[4];
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kComponentCount * kBytesPerComponent, kInputValues1,
                     GL_STATIC_DRAW);
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kComponentCount * kBytesPerComponent, kInputValues2,
                     GL_STATIC_DRAW);
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[2]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kComponentCount * kBytesPerComponent, kInputValues3,
                     GL_STATIC_DRAW);
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[3]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kComponentCount * kBytesPerComponent, nullptr,
                     GL_STATIC_DRAW);
    
        // Bind shader storage buffer
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]);
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]);
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, shaderStorageBuffer[2]);
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, shaderStorageBuffer[3]);
    
        glDispatchCompute(1, 1, 1);
        glFinish();
    
        // Read back shader storage buffer
        constexpr unsigned int kExpectedValues[kComponentCount] = {9u, 12u};
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[3]);
        const GLuint *ptr = reinterpret_cast<const GLuint *>(glMapBufferRange(
            GL_SHADER_STORAGE_BUFFER, 0, kComponentCount * kBytesPerComponent, GL_MAP_READ_BIT));
        for (unsigned int idx = 0; idx < kComponentCount; idx++)
        {
            EXPECT_EQ(kExpectedValues[idx], *(ptr + idx));
        }
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Test that ssbo as an argument of a function can be translated.
    TEST_P(ShaderStorageBufferTest31, SSBOAsFunctionArgument)
    {
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
    layout(local_size_x = 1) in;
    
    layout(std430, binding = 0) buffer Block
    {
        uint var1;
        uint var2;
    };
    
    bool compare(uint a, uint b)
    {
        return a == b;
    }
    
    uint increase(inout uint a)
    {
        a++;
        return a;
    }
    
    void main(void)
    {
        bool isEqual = compare(var1, 2u);
        if (isEqual)
        {
            var2 += increase(var1);
        }
    }
    )";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
        glUseProgram(program);
    
        constexpr unsigned int kBytesPerComponent = sizeof(unsigned int);
        constexpr unsigned int kInputValues[2]    = {2u, 2u};
        constexpr unsigned int kExpectedValues[2] = {3u, 5u};
        GLBuffer shaderStorageBuffer;
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer);
        glBufferData(GL_SHADER_STORAGE_BUFFER, 2 * kBytesPerComponent, &kInputValues, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer);
    
        glDispatchCompute(1, 1, 1);
        glFinish();
    
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer);
        const GLuint *ptr = reinterpret_cast<const GLuint *>(
            glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 2 * kBytesPerComponent, GL_MAP_READ_BIT));
        EXPECT_EQ(kExpectedValues[0], *ptr);
        EXPECT_EQ(kExpectedValues[1], *(ptr + 1));
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Test that ssbo as unary operand works well.
    TEST_P(ShaderStorageBufferTest31, SSBOAsUnaryOperand)
    {
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
    layout (local_size_x=1) in;
    layout(binding=0, std140) buffer Storage0
    {
        uint b;
    } sb_load;
    layout(binding=1, std140) buffer Storage1
    {
        uint i;
    } sb_store;
    void main()
    {
        sb_store.i = +sb_load.b;
        ++sb_store.i;
    }
    )";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
        glUseProgram(program);
    
        constexpr unsigned int kBytesPerComponent = sizeof(unsigned int);
        constexpr unsigned kInputValue            = 1u;
        constexpr unsigned int kExpectedValue     = 2u;
        GLBuffer shaderStorageBuffer[2];
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kBytesPerComponent, &kInputValue, GL_STATIC_DRAW);
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        glBufferData(GL_SHADER_STORAGE_BUFFER, kBytesPerComponent, &kInputValue, GL_STATIC_DRAW);
    
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]);
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]);
    
        glDispatchCompute(1, 1, 1);
        glFinish();
    
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]);
        const GLuint *ptr = reinterpret_cast<const GLuint *>(
            glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kBytesPerComponent, GL_MAP_READ_BIT));
        EXPECT_EQ(kExpectedValue, *ptr);
    
        EXPECT_GL_NO_ERROR();
    }
    
    // Test that uniform can be used as the index of buffer variable.
    TEST_P(ShaderStorageBufferTest31, UniformUsedAsIndexOfBufferVariable)
    {
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
    layout (local_size_x=4) in;
    layout(std140, binding = 0) uniform CB
    {
        uint index;
    } cb;
    
    layout(binding=0, std140) buffer Storage0
    {
        uint data[];
    } sb_load;
    layout(binding=1, std140) buffer Storage1
    {
        uint data[];
    } sb_store;
    void main()
    {
        sb_store.data[gl_LocalInvocationIndex] = sb_load.data[gl_LocalInvocationID.x + cb.index];
    }
    )";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
        EXPECT_GL_NO_ERROR();
    }
    
    // Test that inactive but statically used SSBOs with unsized array are handled correctly.
    //
    // Glslang wrapper used to replace the layout/qualifier of an inactive SSBO with |struct|,
    // effectively turning the interface block declaration into a struct definition.  This generally
    // worked except for SSBOs with an unsized array.  This test makes sure this special case is
    // now properly handled.
    TEST_P(ShaderStorageBufferTest31, InactiveButStaticallyUsedWithUnsizedArray)
    {
        constexpr char kComputeShaderSource[] =
            R"(#version 310 es
    layout (local_size_x=1) in;
    layout(binding=0, std140) buffer Storage
    {
        uint data[];
    } sb;
    void main()
    {
        if (false)
        {
            sb.data[0] = 1u;
        }
    }
    )";
    
        ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource);
        EXPECT_GL_NO_ERROR();
    
        glUseProgram(program);
        glDispatchCompute(1, 1, 1);
        EXPECT_GL_NO_ERROR();
    }
    
    ANGLE_INSTANTIATE_TEST_ES31(ShaderStorageBufferTest31);
    
    }  // namespace